options = get_option('mxchat_options'); $this->chat_count = get_option('mxchat_chat_count', 0); $this->is_activated = $this->is_license_active(); // Initialize default options if they are not set if (!$this->options) { $this->initialize_default_options(); } // Add admin menu and initialize settings add_action('admin_menu', array($this, 'mxchat_add_plugin_page')); add_action('admin_init', array($this, 'mxchat_page_init')); add_action('admin_init', array($this, 'mxchat_prompts_page_init')); add_action('admin_enqueue_scripts', array($this, 'mxchat_enqueue_admin_assets')); add_action('wp_ajax_mxchat_delete_chat_history', array($this, 'mxchat_delete_chat_history')); add_action('admin_post_mxchat_submit_content', array($this, 'mxchat_handle_content_submission')); add_action('admin_post_mxchat_delete_prompt', array($this, 'mxchat_handle_delete_prompt')); add_action('wp_ajax_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history')); add_action('wp_ajax_nopriv_mxchat_fetch_chat_history', array($this, 'mxchat_fetch_chat_history')); add_action('admin_post_mxchat_submit_sitemap', array($this, 'mxchat_handle_sitemap_submission')); add_action('wp_footer', array($this, 'mxchat_append_chatbot_to_body')); add_action('admin_head-mxchat-prompts', array($this, 'mxchat_enqueue_admin_assets')); add_action('admin_head-toplevel_page_mxchat-max', array($this, 'mxchat_enqueue_admin_assets')); add_action('wp_ajax_mxchat_activate_license', array($this, 'mxchat_handle_activate_license')); add_action('admin_notices', array($this, 'mxchat_display_admin_notice')); add_action('wp_ajax_mxchat_save_inline_prompt', array($this, 'mxchat_save_inline_prompt')); add_action('admin_post_mxchat_delete_all_prompts', array($this, 'mxchat_handle_delete_all_prompts')); add_action('admin_post_mxchat_add_intent', array($this, 'mxchat_handle_add_intent')); add_action('admin_post_mxchat_delete_intent', array($this, 'mxchat_handle_delete_intent')); add_action('admin_post_mxchat_update_intent_threshold', array($this, 'mxchat_handle_update_intent_threshold')); add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing')); add_action('admin_post_mxchat_edit_intent', array($this, 'handle_edit_intent')); add_action('save_post', array($this, 'handle_post_update'), 10, 3); add_action('post_updated', array($this, 'handle_post_update'), 10, 3); add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback')); add_action('wp_trash_post', array($this, 'mxchat_handle_post_delete')); add_action('before_delete_post', array($this, 'mxchat_handle_post_delete')); add_action('wp_ajax_mxchat_export_transcripts', array($this, 'export_chat_transcripts')); add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback')); if (isset($this->options['enable_woocommerce_integration']) && ($this->options['enable_woocommerce_integration'] === '1' || $this->options['enable_woocommerce_integration'] === 'on')) { //error_log('MxChat Admin: WooCommerce integration is enabled, adding hooks'); add_action('wp_insert_post', array($this, 'mxchat_handle_product_change'), 10, 3); add_action('wp_trash_post', array($this, 'mxchat_handle_product_delete')); add_action('before_delete_post', array($this, 'mxchat_handle_product_delete')); } } // Method to check if the license is active private function is_license_active() { $license_status = get_option('mxchat_license_status', esc_html__('inactive', 'mxchat')); return $license_status === esc_html__('active', 'mxchat'); } // Initialize default options private function initialize_default_options() { $default_options = array( 'api_key' => '', 'xai_api_key' => '', 'claude_api_key' => '', 'deepseek_api_key' => '', 'system_prompt_instructions' => esc_html__('[EXAMPLE INSTRUCTIONS] You are an AI Chatbot assistant for this website. The primary subject you should focus on is [insert proper subject here]. Your main goal is to assist visitors with questions related to this specific topic. Here are some key things to keep in mind: - Your name is [Chatbot Name]. - Stay focused on topics related to [insert proper subject here]. If a visitor asks about an unrelated topic, politely redirect the conversation to how you can assist them with this subject. - Do not make up links to pages. Only use specific links you see in your knowledgebase from [insert your domain]. Do not make assumptions or make up URLs. - Keep your responses short, concise, and to the point. Provide clear and direct answers suitable for a chatbot interaction. - Provide answers based on the knowledge available to you. If you do not have an answer to a specific question, let the visitor know that you don’t have the information.', 'mxchat'), 'model' => esc_html__('gpt-4o', 'mxchat'), 'rate_limit_logged_out' => esc_html__('100', 'mxchat'), 'role_rate_limits' => array(), 'rate_limit_message' => esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'), 'enable_email_block' => '', 'email_blocker_header_content' => esc_html__("

Welcome to Our Chat!

\n

Let's get started. Enter your email to begin chatting with us.

", 'mxchat'), 'email_blocker_button_text' => esc_html__('Start Chat', 'mxchat'), 'top_bar_title' => esc_html__('MxChat', 'mxchat'), 'intro_message' => esc_html__('Hello! How can I assist you today?', 'mxchat'), 'input_copy' => esc_html__('How can I assist?', 'mxchat'), 'append_to_body' => esc_html__('off', 'mxchat'), 'close_button_color' => esc_html__('#fff', 'mxchat'), 'chatbot_bg_color' => esc_html__('#fff', 'mxchat'), 'user_message_bg_color' => esc_html__('#fff', 'mxchat'), 'user_message_font_color' => esc_html__('#212121', 'mxchat'), 'bot_message_bg_color' => esc_html__('#212121', 'mxchat'), 'bot_message_font_color' => esc_html__('#fff', 'mxchat'), 'top_bar_bg_color' => esc_html__('#212121', 'mxchat'), 'send_button_font_color' => esc_html__('#212121', 'mxchat'), 'chat_input_font_color' => esc_html__('#212121', 'mxchat'), 'chatbot_background_color' => esc_html__('#212121', 'mxchat'), 'icon_color' => esc_html__('#fff', 'mxchat'), 'enable_woocommerce_integration' => esc_html__('0', 'mxchat'), 'link_target_toggle' => esc_html__('off', 'mxchat'), 'pre_chat_message' => esc_html__('Hey there! Ask me anything!', 'mxchat'), // New fields for Loops Integration 'loops_api_key' => '', 'loops_mailing_list' => '', 'triggered_phrase_response' => esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'), 'email_capture_response' => esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'), 'popular_question_1' => '', 'popular_question_2' => '', 'popular_question_3' => '', 'pdf_intent_trigger_text' => esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'), 'pdf_intent_success_text' => esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'), 'pdf_intent_error_text' => esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'), 'pdf_max_pages' => 69, // Live Agent Integration 'live_agent_webhook_url' => '', 'live_agent_secret_key' => '', 'live_agent_bot_token' => '', 'live_agent_message_bg_color' => esc_html__('#ffffff', 'mxchat'), 'live_agent_message_font_color' => esc_html__('#333333', 'mxchat'), 'chat_toolbar_toggle' => esc_html__('off', 'mxchat'), 'mode_indicator_bg_color' => esc_html__('#767676', 'mxchat'), 'mode_indicator_font_color' => esc_html__('#ffffff', 'mxchat'), 'toolbar_icon_color' => esc_html__('#212121', 'mxchat'), ); // Merge existing options with defaults $existing_options = get_option('mxchat_options', array()); $merged_options = wp_parse_args($existing_options, $default_options); // Update the options if they have changed if ($existing_options !== $merged_options) { update_option('mxchat_options', $merged_options); } // Add default limits for each role $roles = wp_roles()->get_names(); foreach ($roles as $role_id => $role_name) { $default_options['role_rate_limits'][$role_id] = esc_html__('100', 'mxchat'); } return $default_options; // Update the $this->options property $this->options = $merged_options; } public function mxchat_add_plugin_page() { // Main menu page add_menu_page( esc_html__('MxChat Settings', 'mxchat'), esc_html__('MxChat', 'mxchat'), 'manage_options', 'mxchat-max', array($this, 'mxchat_create_admin_page'), 'dashicons-testimonial', 6 ); // Submenu page for Knowledge add_submenu_page( 'mxchat-max', esc_html__('Prompts', 'mxchat'), esc_html__('Knowledge', 'mxchat'), 'manage_options', 'mxchat-prompts', array($this, 'mxchat_create_prompts_page') ); add_submenu_page( 'mxchat-max', esc_html__('Chat Transcripts', 'mxchat'), esc_html__('Transcripts', 'mxchat'), 'manage_options', 'mxchat-transcripts', array($this, 'mxchat_create_transcripts_page') ); add_submenu_page( 'mxchat-max', esc_html__('MxChat Intents', 'mxchat'), esc_html__('Intents', 'mxchat'), 'manage_options', 'mxchat-intents', array($this, 'mxchat_intents_page_html') ); add_submenu_page( 'mxchat-max', esc_html__('Add Ons', 'mxchat'), esc_html__('Add Ons', 'mxchat'), 'manage_options', 'mxchat-addons', array($this, 'mxchat_create_addons_page') ); // Submenu page for Activation Key add_submenu_page( 'mxchat-max', esc_html__('Pro Upgrade', 'mxchat'), esc_html__('Pro Upgrade', 'mxchat'), 'manage_options', 'mxchat-activation', array($this, 'mxchat_create_activation_page') ); } public function mxchat_create_addons_page() { require_once plugin_dir_path(__FILE__) . 'class-mxchat-addons.php'; $addons_page = new MxChat_Addons(); $addons_page->render_page(); } public function mxchat_save_setting_callback() { check_ajax_referer('mxchat_save_setting_nonce'); if (!current_user_can('manage_options')) { wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]); } $name = isset($_POST['name']) ? $_POST['name'] : ''; // Strip slashes from the value before saving $value = isset($_POST['value']) ? stripslashes($_POST['value']) : ''; if (empty($name)) { wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]); } // Load the full options array $options = get_option('mxchat_options', []); // Handle special cases switch ($name) { case 'additional_popular_questions': $questions = json_decode($value, true); // No need for stripslashes here if (is_array($questions)) { $options[$name] = $questions; // Also update old option for backwards compatibility update_option('additional_popular_questions', $questions); } break; case 'user_message_bg_color': case 'user_message_font_color': case 'bot_message_bg_color': case 'bot_message_font_color': case 'top_bar_bg_color': case 'send_button_font_color': case 'chatbot_background_color': case 'icon_color': case 'chat_input_font_color': case 'live_agent_message_bg_color': case 'live_agent_message_font_color': case 'mode_indicator_bg_color': case 'mode_indicator_font_color': case 'toolbar_icon_color': // Store color values directly $options[$name] = $value; break; case 'live_agent_status': // Set the new value $options[$name] = ($value === 'on') ? 'on' : 'off'; break; case 'enable_woocommerce_integration': // Handle values that used to be 1/0 $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off'; break; default: // Handle role rate limits if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) { // Extract role ID from the name preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches); if (isset($matches[1])) { $role_id = $matches[1]; // Initialize role_rate_limits if it doesn't exist if (!isset($options['role_rate_limits'])) { $options['role_rate_limits'] = []; } // Update the specific role's rate limit $options['role_rate_limits'][$role_id] = sanitize_text_field($value); break; } } // Handle toggles if (strpos($name, 'toggle') !== false || in_array($name, [ 'chat_persistence_toggle', 'privacy_toggle', 'complianz_toggle', 'chat_toolbar_toggle' // Removed live_agent_status from here ])) { $options[$name] = ($value === 'on') ? 'on' : 'off'; } else { // Store all other values directly $options[$name] = $value; } break; } // Save all updates $updated = update_option('mxchat_options', $options); // Handle backwards compatibility for certain fields $legacy_fields = ['brave_api_key', 'brave_image_count', 'brave_safe_search', 'brave_news_count', 'brave_country', 'brave_language', 'similarity_threshold']; if (in_array($name, $legacy_fields)) { // Also update the individual option for backwards compatibility update_option($name, $value); } if ($updated) { wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]); } else { wp_send_json_error(['message' => esc_html__('Update failed or no changes', 'mxchat')]); } } /** * Handles AJAX auto-save for prompts and auto-sync settings. */ public function mxchat_save_prompts_setting_callback() { // Check the correct nonce for this action. check_ajax_referer( 'mxchat_prompts_setting_nonce', '_ajax_nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( [ 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ] ); } $name = isset( $_POST['name'] ) ? sanitize_text_field( wp_unslash( $_POST['name'] ) ) : ''; $value = isset( $_POST['value'] ) ? sanitize_text_field( wp_unslash( $_POST['value'] ) ) : ''; if ( empty( $name ) ) { wp_send_json_error( [ 'message' => esc_html__( 'Invalid field name', 'mxchat' ) ] ); } // Handle standalone auto-sync settings. if ( in_array( $name, [ 'mxchat_auto_sync_posts', 'mxchat_auto_sync_pages' ], true ) ) { // Convert checkbox value to "1" if checked, "0" if not. $value = ( $value === 'on' ) ? '1' : '0'; update_option( $name, $value ); wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] ); } // Handle fields stored in the 'mxchat_prompts_options' array. // Handle fields stored in the 'mxchat_prompts_options' array. if ( false !== strpos( $name, 'mxchat_prompts_options[' ) ) { // Extract the key name using regex. if ( preg_match( '/mxchat_prompts_options\[(.*?)\]/', $name, $matches ) && ! empty( $matches[1] ) ) { $key = sanitize_text_field( $matches[1] ); $options = get_option( 'mxchat_prompts_options', [] ); // Remove Pinecone-specific settings from the main plugin handling. if ( 'mxchat_use_pinecone' === $key ) { // Optionally, you could simply ignore this field or set a default value. // For example, ensure it's always 0 or remove it entirely: // $options[ $key ] = '0'; // Or, if you prefer, just return an error so that the main plugin doesn't handle it: wp_send_json_error( [ 'message' => esc_html__( 'Pinecone settings are now handled in the add-on.', 'mxchat' ) ] ); } else { // For all other fields, use the existing logic. $options[ $key ] = ( $value === 'on' || $value === '1' ) ? '1' : $value; } update_option( 'mxchat_prompts_options', $options ); wp_send_json_success( [ 'message' => esc_html__( 'Setting saved', 'mxchat' ) ] ); } } wp_send_json_error( [ 'message' => esc_html__( 'Field not recognized', 'mxchat' ) ] ); } public function mxchat_display_admin_notice() { // Success notice if ($message = get_transient('mxchat_admin_notice_success')) { ?>

is_activated): ?>

.

.

.

.

.

[mxchat_chatbot floating="yes"] [mxchat_chatbot floating="no"].

.

prefix . 'mxchat_chat_transcripts'; // Get basic stats $total_chats = $wpdb->get_var("SELECT COUNT(DISTINCT session_id) FROM $table_name"); $total_messages = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); // Count unique users with detailed breakdown $total_users = $wpdb->get_var(" SELECT COUNT(DISTINCT CASE WHEN user_email != '' AND user_email IS NOT NULL THEN user_email WHEN user_id != 0 THEN CONCAT('user_', user_id) WHEN user_identifier NOT LIKE 'Tech-Savvy User' AND user_identifier NOT LIKE 'Detail-Oriented User' AND user_identifier NOT LIKE 'Language Learner' AND user_identifier NOT LIKE 'Casual Browser' AND user_identifier NOT LIKE 'Policy Enforcer' AND user_identifier NOT LIKE 'Researcher' AND user_identifier NOT LIKE 'Loyalty Member' AND user_identifier NOT LIKE 'Gift Buyer' AND user_identifier NOT LIKE 'Parent or Caregiver' THEN user_identifier ELSE session_id END ) FROM $table_name WHERE role != 'assistant' "); // Get user type breakdown $registered_users = $wpdb->get_var(" SELECT COUNT(DISTINCT user_email) FROM $table_name WHERE user_email != '' AND user_email IS NOT NULL "); $guest_users = $wpdb->get_var(" SELECT COUNT(DISTINCT user_identifier) FROM $table_name WHERE (user_email = '' OR user_email IS NULL) AND role != 'assistant' AND user_identifier NOT LIKE 'Tech-Savvy User' AND user_identifier NOT LIKE 'Detail-Oriented User' AND user_identifier NOT LIKE 'Language Learner' AND user_identifier NOT LIKE 'Casual Browser' AND user_identifier NOT LIKE 'Policy Enforcer' AND user_identifier NOT LIKE 'Researcher' AND user_identifier NOT LIKE 'Loyalty Member' AND user_identifier NOT LIKE 'Gift Buyer' AND user_identifier NOT LIKE 'Parent or Caregiver' "); // Get agent test messages count $agent_tests = $wpdb->get_var(" SELECT COUNT(DISTINCT session_id) FROM $table_name WHERE user_identifier IN ( 'Tech-Savvy User', 'Detail-Oriented User', 'Language Learner', 'Casual Browser', 'Policy Enforcer', 'Researcher', 'Loyalty Member', 'Gift Buyer', 'Parent or Caregiver' ) "); ?>

Chat Transcripts

💬
📝
👥
prefix . 'mxchat_chat_transcripts'; // Get all transcripts ordered by session and timestamp $results = $wpdb->get_results( "SELECT session_id, user_email, user_identifier, role, message, timestamp FROM {$table_name} ORDER BY session_id, timestamp ASC" ); if (empty($results)) { wp_send_json_error(array('message' => 'No transcripts found.')); wp_die(); } // Set headers for CSV download header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="chat-transcripts-' . date('Y-m-d') . '.csv"'); header('Pragma: no-cache'); header('Expires: 0'); // Create output stream $output = fopen('php://output', 'w'); // Add UTF-8 BOM for proper Excel encoding fputs($output, "\xEF\xBB\xBF"); // Add CSV headers fputcsv($output, array( 'Session ID', 'Email', 'User Identifier', 'Role', 'Message', 'Timestamp' )); // Add data rows foreach ($results as $row) { fputcsv($output, array( $row->session_id, $row->user_email, $row->user_identifier, $row->role, $row->message, $row->timestamp )); } fclose($output); wp_die(); } public function mxchat_fetch_chat_history() { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_chat_transcripts'; if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have sufficient permissions to view this page.', 'mxchat')); } // First get unique session IDs ordered by most recent message in each session $session_ids = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT session_id FROM {$table_name} GROUP BY session_id ORDER BY MAX(timestamp) DESC" ) ); if (empty($session_ids)) { wp_die(esc_html__('No chat history available.', 'mxchat')); } ob_start(); echo '
'; // Iterate through sessions from newest to oldest foreach ($session_ids as $session_id) { // Get the email associated with this session (if available) $email = $wpdb->get_var( $wpdb->prepare( "SELECT user_email FROM {$table_name} WHERE session_id = %s AND user_email != '' ORDER BY timestamp ASC LIMIT 1", $session_id ) ); // Debug: Check email retrieval if (empty($email)) { //error_log("No email found for session {$session_id}"); } else { //error_log("Email for session {$session_id}: " . $email); } // Get messages for this session ordered by timestamp $messages = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_name} WHERE session_id = %s ORDER BY timestamp ASC", $session_id ) ); // Start session block echo '
'; echo '
'; // Wrap checkbox and session ID in one block echo '
'; echo ' '; echo '' . esc_html__('Session ID:', 'mxchat') . ' ' . esc_html($session_id); echo '
'; // Place email directly below the session ID block if (!empty($email)) { echo '
'; echo '' . esc_html__('Email:', 'mxchat') . ' ' . esc_html($email); echo '
'; } echo '
'; echo '
'; // Display messages for this session foreach ($messages as $transcript) { $formatted_timestamp = date_i18n('F j, Y g:i a', strtotime($transcript->timestamp)); // Determine message styling switch ($transcript->role) { case 'assistant': case 'bot': $message_class = 'bot-message'; $display_role = esc_html__('Chatbot', 'mxchat'); break; case 'user': $message_class = 'user-message'; $display_role = !empty($transcript->user_identifier) ? sanitize_text_field($transcript->user_identifier) : esc_html__('User', 'mxchat'); break; case 'agent': $message_class = 'agent-message'; $display_role = esc_html__('Agent', 'mxchat'); break; default: $message_class = 'unknown-message'; $display_role = esc_html__('Unknown', 'mxchat'); } // Process message content $message_content = wp_kses( stripslashes($transcript->message), [ 'b' => [], 'strong' => [], 'i' => [], 'em' => [], 'u' => [], 'br' => [], 'p' => [], 'ul' => [], 'ol' => [], 'li' => [], 'a' => ['href' => [], 'title' => []] ] ); $message_content = nl2br($message_content); // Render message echo '
'; echo '
' . esc_html($display_role) . '
'; echo '
' . $message_content . '
'; echo '
' . esc_html($formatted_timestamp) . '
'; echo '
'; } // Close session block echo '
'; } echo '
'; $output = ob_get_clean(); echo $output; wp_die(); } public function mxchat_create_prompts_page() { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; // Display success message if all prompts were deleted if (isset($_GET['all_deleted']) && $_GET['all_deleted'] === 'true') { echo '

' . esc_html__('All knowledge has been deleted successfully.', 'mxchat') . '

'; } // Set up pagination and search query $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field($_GET['_wpnonce']) : ''; $search_query = (!empty($nonce) && wp_verify_nonce($nonce, 'mxchat_prompts_search_nonce') && isset($_GET['search'])) ? sanitize_text_field($_GET['search']) : ''; $current_page = isset($_GET['paged']) ? absint($_GET['paged']) : 1; $per_page = 10; $offset = ($current_page - 1) * $per_page; // Modify query to handle search input $sql_search = ""; if ($search_query) { $sql_search = $wpdb->prepare("WHERE article_content LIKE %s", '%' . $wpdb->esc_like($search_query) . '%'); } // Retrieve total number of prompts, considering search filter $total_prompts = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} {$sql_search}"); $total_pages = ceil($total_prompts / $per_page); // Retrieve prompts from the database $prompts = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$table_name} {$sql_search} ORDER BY timestamp DESC LIMIT %d OFFSET %d", $per_page, $offset ) ); // Add the pagination links generation here $page_links = paginate_links(array( 'base' => add_query_arg(array( 'paged' => '%#%', 'search' => urlencode($search_query), '_wpnonce' => wp_create_nonce('mxchat_prompts_search_nonce') ), admin_url('admin.php?page=mxchat-prompts')), 'format' => '', 'prev_text' => __('« Previous', 'mxchat'), 'next_text' => __('Next »', 'mxchat'), 'total' => $total_pages, 'current' => $current_page, )); // Retrieve processing statuses $pdf_url = get_transient('mxchat_last_pdf_url'); $sitemap_url = get_transient('mxchat_last_sitemap_url'); $pdf_status = $pdf_url ? $this->get_pdf_processing_status($pdf_url) : false; $sitemap_status = $sitemap_url ? $this->get_sitemap_processing_status($sitemap_url) : false; if ($pdf_status && $pdf_status['status'] === 'complete') { delete_transient('mxchat_last_pdf_url'); $pdf_status = false; } if ($sitemap_status && $sitemap_status['status'] === 'complete') { delete_transient('mxchat_last_sitemap_url'); $sitemap_status = false; } $is_processing = ($pdf_status && ($pdf_status['status'] === 'processing' || $pdf_status['status'] === 'error')) || ($sitemap_status && ($sitemap_status['status'] === 'processing' || $sitemap_status['status'] === 'error')); ?>

Knowledge Base Manager

id); ?>
article_content))); ?>
source_url)) : ?>
prefix . 'mxchat_system_prompt_content'; // Delete all prompts from the table $wpdb->query("DELETE FROM {$table_name}"); // Clear relevant cache wp_cache_delete('all_prompts', 'mxchat_prompts'); // Redirect back with a success message $redirect_url = add_query_arg(array( 'page' => 'mxchat-prompts', 'all_deleted' => 'true' ), admin_url('admin.php')); wp_safe_redirect($redirect_url); exit; } public function mxchat_handle_delete_prompt() { // Sanitize and validate nonce $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : ''; if (empty($nonce) || !wp_verify_nonce($nonce, 'mxchat_delete_prompt_nonce')) { wp_die(esc_html__('Nonce verification failed.', 'mxchat')); } // Check permissions if (!current_user_can('manage_options')) { wp_die(esc_html__('You do not have sufficient permissions to delete prompts.', 'mxchat')); } // Validate and sanitize ID parameter $id = isset($_GET['id']) ? intval($_GET['id']) : 0; if ($id <= 0) { wp_die(esc_html__('Invalid prompt ID.', 'mxchat')); } global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; // Clear cache and delete prompt wp_cache_delete('prompt_' . $id, 'mxchat_prompts'); $wpdb->delete($table_name, array('id' => $id), array('%d')); wp_safe_redirect(add_query_arg(array('page' => 'mxchat-prompts', 'deleted' => 'true'), admin_url('admin.php'))); exit; } public function mxchat_generate_embedding($text) { $options = get_option('mxchat_options'); $api_key = $options['api_key'] ?? esc_html__('default_api_key', 'mxchat'); $response = wp_remote_post('https://api.openai.com/v1/embeddings', array( 'body' => wp_json_encode(array( 'model' => esc_html__('text-embedding-ada-002', 'mxchat'), 'input' => $text )), 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => esc_html__('application/json', 'mxchat') ), )); if (is_wp_error($response)) { return null; } $response_data = json_decode(wp_remote_retrieve_body($response), true); return $response_data['data'][0]['embedding'] ?? null; } public function mxchat_delete_chat_history() { if (!current_user_can('manage_options')) { echo wp_json_encode(['error' => esc_html__('You do not have sufficient permissions.', 'mxchat')]); wp_die(); } check_ajax_referer('mxchat_delete_chat_history', 'security'); global $wpdb; $table_name = $wpdb->prefix . 'mxchat_chat_transcripts'; if (isset($_POST['delete_session_ids']) && is_array($_POST['delete_session_ids'])) { foreach ($_POST['delete_session_ids'] as $session_id) { $session_id_sanitized = sanitize_text_field($session_id); // Clear relevant cache before deletion $cache_key = 'chat_session_' . $session_id_sanitized; wp_cache_delete($cache_key, 'mxchat_chat_sessions'); // Perform the deletion $wpdb->delete($table_name, ['session_id' => $session_id_sanitized]); } // Optionally, clear a general cache if you have one wp_cache_delete('all_chat_sessions', 'mxchat_chat_sessions'); echo wp_json_encode(['success' => esc_html__('Selected chat sessions have been deleted.', 'mxchat')]); } else { echo wp_json_encode(['error' => esc_html__('No chat sessions selected for deletion.', 'mxchat')]); } wp_die(); } public function mxchat_save_inline_prompt() { // Check for nonce security check_ajax_referer('mxchat_save_inline_nonce'); // Verify permissions if (!current_user_can('manage_options')) { wp_send_json_error(esc_html__('Permission denied.', 'mxchat')); return; } global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; // Validate and sanitize input data $prompt_id = isset($_POST['id']) ? absint($_POST['id']) : 0; $article_content = isset($_POST['article_content']) ? sanitize_textarea_field($_POST['article_content']) : ''; $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; if ($prompt_id > 0 && !empty($article_content)) { // Re-generate the embedding vector for the updated content $embedding_vector = $this->mxchat_generate_embedding($article_content); if (is_array($embedding_vector)) { // Serialize the embedding vector before storing it $embedding_vector_serialized = serialize($embedding_vector); // Update the prompt in the database $updated = $wpdb->update( $table_name, array( 'article_content' => $article_content, 'embedding_vector' => $embedding_vector_serialized, 'source_url' => $article_url, ), array('id' => $prompt_id), array('%s', '%s', '%s'), array('%d') ); if ($updated !== false) { wp_send_json_success(); } else { wp_send_json_error(esc_html__('Database update failed.', 'mxchat')); } } else { wp_send_json_error(esc_html__('Embedding generation failed.', 'mxchat')); } } else { wp_send_json_error(esc_html__('Invalid data.', 'mxchat')); } } // Add this method to handle post updates public function handle_post_update($post_id, $post, $update) { // Basic validation checks if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE || wp_is_post_revision($post_id)) { return; } // Only process published content if (!in_array($post->post_status, array('publish'))) { return; } // Check if sync is enabled for this post type $post_type = $post->post_type; if (!in_array($post_type, array('post', 'page'))) { return; } $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages'; if (get_option($sync_option) != '1') { return; } // Prepare content and URL $content = wp_strip_all_tags($post->post_content); $url = get_permalink($post_id); // Generate embedding vector $embedding_vector = $this->mxchat_generate_embedding($content); if (!$embedding_vector) { return; } // Check for Pinecone addon and its settings $pinecone_settings = get_option('mxchat_pinecone_addon_options'); $use_pinecone = false; if ($pinecone_settings && is_array($pinecone_settings)) { // Check if Pinecone is enabled and all required settings are present $use_pinecone = ( isset($pinecone_settings['mxchat_use_pinecone']) && $pinecone_settings['mxchat_use_pinecone'] === '1' && !empty($pinecone_settings['mxchat_pinecone_api_key']) && !empty($pinecone_settings['mxchat_pinecone_host']) && !empty($pinecone_settings['mxchat_pinecone_index']) && !empty($pinecone_settings['mxchat_pinecone_environment']) ); } if ($use_pinecone) { // Use Pinecone $pinecone_result = $this->store_in_pinecone_main( $embedding_vector, $content, $url, $pinecone_settings['mxchat_pinecone_api_key'], $pinecone_settings['mxchat_pinecone_environment'], $pinecone_settings['mxchat_pinecone_index'] ); if (!$pinecone_result['success']) { //error_log('MXChat: Pinecone sync failed - falling back to WordPress DB'); $this->store_in_wordpress_db($content, $url, $embedding_vector); } } else { // Fallback to WordPress DB storage $this->store_in_wordpress_db($content, $url, $embedding_vector); } } // Modified storage function to add type field private function store_in_pinecone_main($embedding_vector, $content, $url, $api_key, $environment, $index_name, $vector_id = null) { $vector_id = $vector_id ?: md5($url); $options = get_option('mxchat_pinecone_addon_options'); $host = $options['mxchat_pinecone_host'] ?? ''; if (empty($host)) { return array( 'success' => false, 'message' => 'Pinecone host is not configured. Please set the host in your settings.' ); } // Determine if this is a product URL $is_product = (strpos($url, '/product/') !== false || strpos($url, '/shop/') !== false); $api_endpoint = "https://{$host}/vectors/upsert"; $request_body = array( 'vectors' => array( array( 'id' => $vector_id, 'values' => $embedding_vector, 'metadata' => array( 'text' => $content, 'source_url' => $url, 'type' => $is_product ? 'product' : 'content', 'last_updated' => time() ) ) ) ); $response = wp_remote_post($api_endpoint, array( 'headers' => array( 'Api-Key' => $api_key, 'accept' => 'application/json', 'content-type' => 'application/json' ), 'body' => wp_json_encode($request_body), 'timeout' => 30, 'data_format' => 'body' )); if (is_wp_error($response)) { return array( 'success' => false, 'message' => $response->get_error_message() ); } $response_code = wp_remote_retrieve_response_code($response); if ($response_code !== 200) { $body = wp_remote_retrieve_body($response); return array( 'success' => false, 'message' => sprintf( 'Pinecone API error (HTTP %d): %s', $response_code, $body ) ); } return array( 'success' => true, 'message' => 'Successfully stored in Pinecone' ); } // Helper method for WordPress DB storage private function store_in_wordpress_db($content, $url, $embedding_vector) { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; $existing_entry = $wpdb->get_row( $wpdb->prepare("SELECT id FROM $table_name WHERE source_url = %s", $url) ); if ($existing_entry) { $wpdb->update( $table_name, array( 'article_content' => $content, 'embedding_vector' => serialize($embedding_vector), 'timestamp' => current_time('mysql') ), array('id' => $existing_entry->id), array('%s', '%s', '%s') ); } else { $wpdb->insert( $table_name, array( 'article_content' => $content, 'source_url' => $url, 'embedding_vector' => serialize($embedding_vector), 'timestamp' => current_time('mysql') ), array('%s', '%s', '%s', '%s') ); } } /** * Handle deletion of posts and pages from both Pinecone and WordPress DB * * @param int $post_id The ID of the post being deleted * @return void */ public function mxchat_handle_post_delete($post_id) { // Get post data before it's deleted $post = get_post($post_id); // Basic validation if (!$post || wp_is_post_revision($post_id)) { return; } // Check post type $post_type = $post->post_type; if (!in_array($post_type, array('post', 'page'))) { return; } // Check if sync is enabled for this post type $sync_option = ($post_type === 'post') ? 'mxchat_auto_sync_posts' : 'mxchat_auto_sync_pages'; if (get_option($sync_option) != '1') { return; } // Get the URL before post is deleted $source_url = get_permalink($post_id); if (!$source_url) { //error_log('MXChat: Failed to get permalink for post ' . $post_id); return; } // Check for Pinecone addon and its settings $pinecone_settings = get_option('mxchat_pinecone_addon_options'); $use_pinecone = false; if ($pinecone_settings && is_array($pinecone_settings)) { $use_pinecone = ( isset($pinecone_settings['mxchat_use_pinecone']) && $pinecone_settings['mxchat_use_pinecone'] === '1' && !empty($pinecone_settings['mxchat_pinecone_api_key']) && !empty($pinecone_settings['mxchat_pinecone_host']) && !empty($pinecone_settings['mxchat_pinecone_index']) && !empty($pinecone_settings['mxchat_pinecone_environment']) ); } $deletion_successful = false; if ($use_pinecone) { try { $pinecone_result = $this->delete_from_pinecone( array($source_url), $pinecone_settings['mxchat_pinecone_api_key'], $pinecone_settings['mxchat_pinecone_environment'], $pinecone_settings['mxchat_pinecone_index'] ); if (!$pinecone_result['success']) { //error_log('MXChat: Pinecone deletion failed for URL: ' . $source_url . ' - ' . $pinecone_result['message']); } else { $deletion_successful = true; } } catch (Exception $e) { //error_log('MXChat: Exception during Pinecone deletion - ' . $e->getMessage()); } } // Always attempt WordPress DB deletion, regardless of Pinecone status try { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; $result = $wpdb->delete( $table_name, array('source_url' => $source_url), array('%s') ); if ($result === false) { //error_log('MXChat: WordPress DB deletion failed for URL: ' . $source_url); } else { $deletion_successful = true; } } catch (Exception $e) { //error_log('MXChat: Exception during WordPress DB deletion - ' . $e->getMessage()); } if (!$deletion_successful) { //error_log('MXChat: Complete deletion failure for post ID: ' . $post_id . ' URL: ' . $source_url); } } public function mxchat_handle_content_submission() { // Check if the form was submitted and the user has permission. if (!isset($_POST['submit_content']) || !current_user_can('manage_options')) { return; } // Verify the nonce. $nonce = isset($_POST['mxchat_submit_content_nonce']) ? sanitize_text_field(wp_unslash($_POST['mxchat_submit_content_nonce'])) : ''; if (!wp_verify_nonce($nonce, 'mxchat_submit_content_action')) { wp_die(esc_html__('Nonce verification failed.', 'mxchat')); } // Sanitize the inputs. $article_content = sanitize_textarea_field($_POST['article_content']); $article_url = isset($_POST['article_url']) ? esc_url_raw($_POST['article_url']) : ''; // Generate the embedding vector. $embedding_vector = $this->mxchat_generate_embedding($article_content); if (!is_array($embedding_vector)) { set_transient('mxchat_admin_notice_error', esc_html__('Embedding generation failed. Please ensure your API key is correct and try again.', 'mxchat'), 30 ); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } // Store in the WordPress database. global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; // Ensure the source_url column exists. if ($wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$table_name} LIKE %s", 'source_url')) != 'source_url') { $wpdb->query($wpdb->prepare("ALTER TABLE {$table_name} ADD source_url VARCHAR(255) DEFAULT ''")); } // Serialize and store the embedding vector. $embedding_vector_serialized = serialize($embedding_vector); $inserted = $wpdb->insert( $table_name, array( 'article_content' => $article_content, 'embedding_vector' => $embedding_vector_serialized, 'source_url' => $article_url, ), array('%s', '%s', '%s') ); if ($inserted === false) { set_transient('mxchat_admin_notice_error', esc_html__('Error inserting content into the database. Please try again.', 'mxchat'), 30 ); } else { set_transient('mxchat_admin_notice_success', esc_html__('Content successfully submitted!', 'mxchat'), 30 ); } wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } private function is_pdf_url($url, $response) { $content_type = wp_remote_retrieve_header($response, 'content-type'); $file_extension = strtolower(pathinfo($url, PATHINFO_EXTENSION)); return strpos($content_type, 'pdf') !== false || $file_extension === 'pdf'; } private function handle_pdf_for_knowledge_base($pdf_url, $response) { if (!current_user_can('manage_options')) { //error_log(esc_html__('Unauthorized PDF processing attempt', 'mxchat')); return false; } $pdf_url = esc_url_raw($pdf_url); $upload_dir = wp_upload_dir(); if (isset($upload_dir['error']) && $upload_dir['error'] !== false) { //error_log(sprintf(esc_html__('Upload directory error: %s', 'mxchat'), esc_html($upload_dir['error']))); return false; } $pdf_filename = sanitize_file_name('mxchat_kb_' . time() . '.pdf'); $pdf_path = trailingslashit($upload_dir['path']) . $pdf_filename; $response_body = wp_remote_retrieve_body($response); if (empty($response_body)) { //error_log(esc_html__('Empty PDF response body', 'mxchat')); return false; } if (!wp_mkdir_p(dirname($pdf_path))) { //error_log(sprintf(esc_html__('Failed to create directory for PDF: %s', 'mxchat'), esc_html($pdf_path))); return false; } try { file_put_contents($pdf_path, $response_body); if (!file_exists($pdf_path)) { throw new Exception(__('Failed to save PDF file', 'mxchat')); } $parser = new \Smalot\PdfParser\Parser(); $pdf = $parser->parseFile($pdf_path); $total_pages = absint(count($pdf->getPages())); if ($total_pages < 1) { throw new Exception(__('Invalid PDF: no pages found', 'mxchat')); } wp_schedule_single_event(time(), 'mxchat_process_pdf_pages', array( 'pdf_path' => $pdf_path, 'pdf_url' => $pdf_url, 'total_pages' => $total_pages, 'batch_size' => absint(15), 'batch_pause' => absint(10) )); $status_data = array( 'total_pages' => $total_pages, 'processed_pages' => 0, 'status' => 'processing', 'last_update' => time() ); set_transient( sanitize_key('mxchat_pdf_status_' . md5($pdf_url)), array_map('sanitize_text_field', $status_data), DAY_IN_SECONDS ); return __('scheduled', 'mxchat'); } catch (Exception $e) { //error_log(sprintf(esc_html__('Error preparing PDF for processing: %s', 'mxchat'), esc_html($e->getMessage()))); if (file_exists($pdf_path)) { wp_delete_file($pdf_path); } return false; } } public static function process_pdf_pages_cron($pdf_path, $pdf_url, $total_pages, $batch_size, $batch_pause) { // Validate inputs $pdf_path = sanitize_text_field($pdf_path); $pdf_url = esc_url_raw($pdf_url); $total_pages = absint($total_pages); $batch_size = absint($batch_size); $batch_pause = absint($batch_pause); try { if (!file_exists($pdf_path)) { throw new Exception(sprintf('PDF file not found at path: %s', esc_html($pdf_path))); } $parser = new \Smalot\PdfParser\Parser(); $pdf = $parser->parseFile($pdf_path); $pages = $pdf->getPages(); // Get current progress $status_key = sanitize_key('mxchat_pdf_status_' . md5($pdf_url)); $status = get_transient($status_key); if (!$status || !is_array($status)) { throw new Exception('Invalid status data retrieved from transient'); } $start_page = absint($status['processed_pages']); $end_page = min($start_page + $batch_size, $total_pages); $instance = new self(); // Create an instance of the class for ($i = $start_page; $i < $end_page; $i++) { $text = $pages[$i]->getText(); $sanitized_content = $instance->mxchat_sanitize_content_for_api($text); // Call via instance if (!empty($sanitized_content)) { $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); // Call via instance if (is_array($embedding_vector)) { $metadata = array( 'document_type' => 'pdf', 'total_pages' => $total_pages, 'current_page' => $i + 1, 'prev_page' => $i > 0 ? $i : null, 'next_page' => $i < ($total_pages - 1) ? $i + 2 : null, 'source_url' => $pdf_url ); $content_with_metadata = wp_json_encode($metadata) . "\n---\n" . $sanitized_content; $page_url = esc_url($pdf_url . "#page=" . ($i + 1)); $options = get_option('mxchat_options'); MxChat_Utils::submit_content_to_db($content_with_metadata, $page_url, $options['api_key']); } } // Update progress with sanitized data $status['processed_pages'] = absint($i + 1); $status['last_update'] = time(); set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); } // Schedule next batch if needed if ($end_page < $total_pages) { wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_pdf_pages', array( 'pdf_path' => $pdf_path, 'pdf_url' => $pdf_url, 'total_pages' => $total_pages, 'batch_size' => $batch_size, 'batch_pause' => $batch_pause )); } else { // Processing complete $status['status'] = 'complete'; set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); if (file_exists($pdf_path)) { wp_delete_file($pdf_path); } } } catch (\Exception $e) { $status['status'] = 'error'; $status['error'] = sanitize_text_field($e->getMessage()); set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); if (file_exists($pdf_path)) { wp_delete_file($pdf_path); } } } public function get_pdf_processing_status($pdf_url) { $pdf_url = esc_url_raw($pdf_url); $status = get_transient(sanitize_key('mxchat_pdf_status_' . md5($pdf_url))); if (!$status || !is_array($status)) { return false; } return array( 'total_pages' => absint($status['total_pages']), 'processed_pages' => absint($status['processed_pages']), 'percentage' => ($status['total_pages'] > 0) ? round((absint($status['processed_pages']) / absint($status['total_pages'])) * 100) : 0, 'status' => sanitize_text_field($status['status']), 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat') ); } public function mxchat_handle_sitemap_submission() { // Check if the form was submitted and verify permissions if (!isset($_POST['submit_sitemap']) || !current_user_can('manage_options')) { wp_die(esc_html__('Unauthorized access', 'mxchat')); } // Verify nonce check_admin_referer('mxchat_submit_sitemap_action', 'mxchat_submit_sitemap_nonce'); // Validate URL if (!isset($_POST['sitemap_url']) || empty($_POST['sitemap_url'])) { set_transient('mxchat_admin_notice_error', esc_html__('Please provide a valid URL.', 'mxchat'), 30 ); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } $submitted_url = esc_url_raw($_POST['sitemap_url']); $response = wp_remote_get($submitted_url, array('timeout' => 30)); if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) { $error_message = is_wp_error($response) ? $response->get_error_message() : __('Failed to fetch URL', 'mxchat'); set_transient('mxchat_admin_notice_error', sprintf( esc_html__('Failed to fetch the URL: %s', 'mxchat'), esc_html($error_message) ), 30 ); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } $content_type = wp_remote_retrieve_header($response, 'content-type'); $body_content = wp_remote_retrieve_body($response); if (empty($body_content)) { set_transient('mxchat_admin_notice_error', esc_html__('Empty response received from URL.', 'mxchat'), 30 ); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } // Handle PDF URL if ($this->is_pdf_url($submitted_url, $response)) { $result = $this->handle_pdf_for_knowledge_base($submitted_url, $response); if ($result === 'scheduled') { set_transient( 'mxchat_last_pdf_url', sanitize_text_field($submitted_url), DAY_IN_SECONDS ); set_transient('mxchat_admin_notice_info', esc_html__('PDF processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'), 30 ); } else { set_transient('mxchat_admin_notice_error', esc_html__('Failed to start PDF processing. Please try again.', 'mxchat'), 30 ); } wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } // Handle Sitemap XML if (strpos($content_type, 'xml') !== false || strpos($body_content, 'handle_sitemap_for_knowledge_base($xml, $submitted_url); if ($result === 'scheduled') { set_transient( 'mxchat_last_sitemap_url', sanitize_text_field($submitted_url), DAY_IN_SECONDS ); set_transient('mxchat_admin_notice_info', esc_html__('Sitemap processing has started in the background. You can check the progress in the Knowledge Base section.', 'mxchat'), 30 ); } else { set_transient('mxchat_admin_notice_error', esc_html__('Failed to start sitemap processing. Please try again.', 'mxchat'), 30 ); } wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } // Handle Regular URL $page_content = $this->mxchat_extract_main_content($body_content); $sanitized_content = $this->mxchat_sanitize_content_for_api($page_content); if (empty($sanitized_content)) { set_transient('mxchat_admin_notice_error', esc_html__('No valid content found on the provided URL.', 'mxchat'), 30 ); wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } $embedding_vector = $this->mxchat_generate_embedding($sanitized_content); if (is_array($embedding_vector)) { MxChat_Utils::submit_content_to_db( $sanitized_content, $submitted_url, $this->options['api_key'] ); set_transient('mxchat_admin_notice_success', esc_html__('URL content successfully submitted!', 'mxchat'), 30 ); } else { set_transient('mxchat_admin_notice_error', esc_html__('Failed to generate embedding for the URL content. Please check your API key and try again.', 'mxchat'), 30 ); } wp_safe_redirect(esc_url(admin_url('admin.php?page=mxchat-prompts'))); exit; } private function handle_sitemap_for_knowledge_base($xml, $sitemap_url) { if (!current_user_can('manage_options')) { //error_log(esc_html__('Unauthorized sitemap processing attempt', 'mxchat')); return false; } try { $sitemap_url = esc_url_raw($sitemap_url); if (!$xml || !is_object($xml)) { throw new Exception(__('Invalid XML object provided', 'mxchat')); } $urls = []; foreach ($xml->url as $url_element) { $url = esc_url_raw((string)$url_element->loc); if ($url) { $urls[] = $url; } } $total_urls = absint(count($urls)); if ($total_urls < 1) { throw new Exception(__('No valid URLs found in sitemap', 'mxchat')); } wp_schedule_single_event(time(), 'mxchat_process_sitemap_urls', array( 'urls' => $urls, 'sitemap_url' => $sitemap_url, 'total_urls' => $total_urls, 'batch_size' => absint(10), 'batch_pause' => absint(5) )); $status_data = array( 'total_urls' => $total_urls, 'processed_urls' => 0, 'status' => 'processing', 'last_update' => time() ); set_transient( sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)), array_map('sanitize_text_field', $status_data), DAY_IN_SECONDS ); return __('scheduled', 'mxchat'); } catch (\Exception $e) { //error_log(sprintf(esc_html__('Error preparing sitemap for processing: %s', 'mxchat'), esc_html($e->getMessage()))); return false; } } public static function process_sitemap_urls_cron($urls, $sitemap_url, $total_urls, $batch_size, $batch_pause) { // Validate inputs $sitemap_url = esc_url_raw($sitemap_url); $total_urls = absint($total_urls); $batch_size = absint($batch_size); $batch_pause = absint($batch_pause); if (!is_array($urls) || empty($urls)) { return; } try { $status_key = sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url)); $status = get_transient($status_key); if (!$status || !is_array($status)) { throw new Exception('Invalid status data retrieved from transient'); } $start_url = absint($status['processed_urls']); $end_url = min($start_url + $batch_size, $total_urls); $instance = new self(); // Track failures in batch $batch_stats = [ 'processed' => 0, 'failed' => 0, 'last_error' => '' ]; for ($i = $start_url; $i < $end_url; $i++) { $page_url = esc_url_raw($urls[$i]); $page_response = wp_remote_get($page_url); if (is_wp_error($page_response) || wp_remote_retrieve_response_code($page_response) !== 200) { $batch_stats['failed']++; continue; } $page_html = wp_remote_retrieve_body($page_response); $page_content = $instance->mxchat_extract_main_content($page_html); $sanitized_content = $instance->mxchat_sanitize_content_for_api($page_content); if (!empty($sanitized_content)) { $embedding_vector = $instance->mxchat_generate_embedding($sanitized_content); if (is_array($embedding_vector)) { $options = get_option('mxchat_options'); $submission_result = MxChat_Utils::submit_content_to_db($sanitized_content, $page_url, $options['api_key']); if (is_wp_error($submission_result)) { $batch_stats['failed']++; $batch_stats['last_error'] = $submission_result->get_error_message(); continue; } $batch_stats['processed']++; } else { $batch_stats['failed']++; $batch_stats['last_error'] = 'Failed to generate embedding. Please check OpenAI API key.'; } } $status['processed_urls'] = absint($i + 1); $status['last_update'] = time(); $status['failed_urls'] = absint($status['failed_urls'] ?? 0) + $batch_stats['failed']; $status['last_error'] = $batch_stats['last_error']; set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); } // If all URLs in this batch failed, stop processing if ($batch_stats['processed'] === 0 && $batch_stats['failed'] > 0) { $status['status'] = 'error'; $status['error'] = sprintf( 'Processing stopped: %d consecutive failures. Last error: %s', $batch_stats['failed'], $batch_stats['last_error'] ); set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); return; } if ($end_url < $total_urls) { wp_schedule_single_event(time() + $batch_pause, 'mxchat_process_sitemap_urls', array( 'urls' => array_map('esc_url_raw', $urls), 'sitemap_url' => $sitemap_url, 'total_urls' => $total_urls, 'batch_size' => $batch_size, 'batch_pause' => $batch_pause, )); } else { $status['status'] = 'complete'; set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); } } catch (\Exception $e) { $status['status'] = 'error'; $status['error'] = sanitize_text_field($e->getMessage()); set_transient($status_key, array_map('sanitize_text_field', $status), DAY_IN_SECONDS); } } public function get_sitemap_processing_status($sitemap_url) { $sitemap_url = esc_url_raw($sitemap_url); $status = get_transient(sanitize_key('mxchat_sitemap_status_' . md5($sitemap_url))); if (!$status || !is_array($status)) { return false; } return array( 'total_urls' => absint($status['total_urls']), 'processed_urls' => absint($status['processed_urls']), 'failed_urls' => absint($status['failed_urls'] ?? 0), 'percentage' => ($status['total_urls'] > 0) ? round((absint($status['processed_urls']) / absint($status['total_urls'])) * 100) : 0, 'status' => sanitize_text_field($status['status']), 'last_update' => human_time_diff(absint($status['last_update']), time()) . ' ' . esc_html__('ago', 'mxchat'), 'error' => isset($status['error']) ? sanitize_text_field($status['error']) : '', 'last_error' => isset($status['last_error']) ? sanitize_text_field($status['last_error']) : '' ); } public function mxchat_stop_processing() { // Verify permissions if (!current_user_can('manage_options')) { wp_die(esc_html__('Unauthorized access', 'mxchat')); } // Verify nonce check_admin_referer('mxchat_stop_processing_action', 'mxchat_stop_processing_nonce'); // Get the last sitemap URL and clear its transient $sitemap_url = get_transient('mxchat_last_sitemap_url'); if ($sitemap_url) { delete_transient('mxchat_sitemap_status_' . md5($sitemap_url)); delete_transient('mxchat_last_sitemap_url'); } // Get the last PDF URL and clear its transient $pdf_url = get_transient('mxchat_last_pdf_url'); if ($pdf_url) { delete_transient('mxchat_pdf_status_' . md5($pdf_url)); delete_transient('mxchat_last_pdf_url'); } // Unschedule any pending sitemap events $timestamp = wp_next_scheduled('mxchat_process_sitemap_urls'); if ($timestamp) { wp_unschedule_event($timestamp, 'mxchat_process_sitemap_urls'); } // Redirect back with a success message set_transient('mxchat_admin_notice_success', esc_html__('Processing has been stopped successfully.', 'mxchat'), 30 ); wp_safe_redirect(admin_url('admin.php?page=mxchat-prompts')); exit; } private function mxchat_sanitize_content_for_api($content) { // Remove script, style tags, and HTML comments $content = preg_replace('/]*>(.*?)<\/script>/is', '', $content); $content = preg_replace('/]*>(.*?)<\/style>/is', '', $content); $content = preg_replace('//', '', $content); // Remove all HTML tags and decode HTML entities $content = wp_strip_all_tags($content); $content = html_entity_decode($content, ENT_QUOTES | ENT_HTML5); // Trim and normalize whitespace $content = trim(preg_replace('/\s+/', ' ', $content)); return $content; } public function mxchat_handle_product_change($post_id, $post, $update) { if ($post->post_type !== 'product') { return; } if ($post->post_status === 'publish') { add_action('shutdown', function() use ($post_id) { $product = wc_get_product($post_id); if ($product) { $this->mxchat_store_product_embedding($product); } }); } } private function mxchat_store_product_embedding($product) { if (!isset($this->options['enable_woocommerce_integration']) || !in_array($this->options['enable_woocommerce_integration'], ['1', 'on'])) { return; } $source_url = get_permalink($product->get_id()); // Build description $regular_price = $product->get_regular_price(); $sale_price = $product->get_sale_price(); $description = $product->get_description() . "\n\n" . esc_html__('Short Description:', 'mxchat') . " " . $product->get_short_description() . "\n" . esc_html__('Price:', 'mxchat') . " " . $regular_price . "\n" . esc_html__('Sale Price:', 'mxchat') . " " . ($sale_price ?: esc_html__('N/A', 'mxchat')) . "\n" . esc_html__('SKU:', 'mxchat') . " " . $product->get_sku(); // Generate embedding vector $embedding_vector = $this->mxchat_generate_embedding($description); if (!is_array($embedding_vector)) { return; } // Check for Pinecone addon and its settings $pinecone_settings = get_option('mxchat_pinecone_addon_options'); $use_pinecone = false; if ($pinecone_settings && is_array($pinecone_settings)) { $use_pinecone = ( isset($pinecone_settings['mxchat_use_pinecone']) && $pinecone_settings['mxchat_use_pinecone'] === '1' && !empty($pinecone_settings['mxchat_pinecone_api_key']) && !empty($pinecone_settings['mxchat_pinecone_host']) && !empty($pinecone_settings['mxchat_pinecone_index']) && !empty($pinecone_settings['mxchat_pinecone_environment']) ); } if ($use_pinecone) { $result = $this->store_in_pinecone_main( $embedding_vector, $description, $source_url, $pinecone_settings['mxchat_pinecone_api_key'], $pinecone_settings['mxchat_pinecone_environment'], $pinecone_settings['mxchat_pinecone_index'] ); if (!$result['success']) { // Fallback to WordPress DB if Pinecone storage fails $this->store_in_wordpress_db($description, $source_url, $embedding_vector); } } else { // Use WordPress DB storage $this->store_in_wordpress_db($description, $source_url, $embedding_vector); } } public function mxchat_handle_product_delete($post_id) { if (get_post_type($post_id) !== 'product') { return; } $source_url = get_permalink($post_id); // Check for Pinecone addon and its settings $pinecone_settings = get_option('mxchat_pinecone_addon_options'); $use_pinecone = false; if ($pinecone_settings && is_array($pinecone_settings)) { $use_pinecone = ( isset($pinecone_settings['mxchat_use_pinecone']) && $pinecone_settings['mxchat_use_pinecone'] === '1' && !empty($pinecone_settings['mxchat_pinecone_api_key']) && !empty($pinecone_settings['mxchat_pinecone_host']) && !empty($pinecone_settings['mxchat_pinecone_index']) && !empty($pinecone_settings['mxchat_pinecone_environment']) ); } if ($use_pinecone) { $this->delete_from_pinecone( array($source_url), $pinecone_settings['mxchat_pinecone_api_key'], $pinecone_settings['mxchat_pinecone_environment'], $pinecone_settings['mxchat_pinecone_index'] ); } else { // Fallback to WordPress DB deletion global $wpdb; $table_name = $wpdb->prefix . 'mxchat_system_prompt_content'; $wpdb->delete($table_name, array('source_url' => $source_url), array('%s')); } } /** * Delete vectors from Pinecone by source URL * * @param array $urls Array of source URLs to delete * @param string $api_key Pinecone API key * @param string $environment Pinecone environment * @param string $index_name Pinecone index name * @return array Associative array with 'success' boolean and 'message' string */ private function delete_from_pinecone($urls, $api_key, $environment, $index_name) { // Get the Pinecone host from options (matching your store_in_pinecone_main pattern) $options = get_option('mxchat_pinecone_addon_options'); $host = $options['mxchat_pinecone_host'] ?? ''; if (empty($host)) { return array( 'success' => false, 'message' => 'Pinecone host is not configured. Please set the host in your settings.' ); } // Build API endpoint using the configured host $api_endpoint = "https://{$host}/vectors/delete"; // Create vector IDs from URLs (matching your store method's ID generation) $vector_ids = array_map('md5', $urls); // Prepare the delete request body $request_body = array( 'ids' => $vector_ids, 'filter' => array( 'source_url' => array( '$in' => $urls ) ) ); // Make the deletion request $response = wp_remote_post($api_endpoint, array( 'headers' => array( 'Api-Key' => $api_key, 'accept' => 'application/json', 'content-type' => 'application/json' ), 'body' => wp_json_encode($request_body), 'timeout' => 30, 'data_format' => 'body' )); // Handle WordPress HTTP API errors if (is_wp_error($response)) { return array( 'success' => false, 'message' => $response->get_error_message() ); } // Check response status $response_code = wp_remote_retrieve_response_code($response); if ($response_code !== 200) { $body = wp_remote_retrieve_body($response); return array( 'success' => false, 'message' => sprintf( 'Pinecone API error (HTTP %d): %s', $response_code, $body ) ); } // Parse response body $body = wp_remote_retrieve_body($response); $response_data = json_decode($body, true); // Final validation of the response if (json_last_error() !== JSON_ERROR_NONE) { return array( 'success' => false, 'message' => 'Failed to parse Pinecone response: ' . json_last_error_msg() ); } return array( 'success' => true, 'message' => sprintf('Successfully deleted %d vectors from Pinecone', count($vector_ids)) ); } public function mxchat_create_activation_page() { $license_status = get_option('mxchat_license_status', 'inactive'); $license_error = get_option('mxchat_license_error', ''); ?>

Activate MxChat Pro

prefix . 'mxchat_intents'; $page = isset($_GET['paged']) ? max(1, intval($_GET['paged'])) : 1; $per_page = 20; $offset = ($page - 1) * $per_page; // Success message if (isset($_GET['updated']) && $_GET['updated'] === 'true') { echo '

' . esc_html__('Intent updated successfully.', 'mxchat') . '

'; } // Filtering logic $where = '1=1'; $search_term = isset($_GET['s']) ? trim($_GET['s']) : ''; $callback_filter = isset($_GET['callback_filter']) ? sanitize_text_field($_GET['callback_filter']) : ''; if ($search_term) { $search_term_like = '%' . $wpdb->esc_like($search_term) . '%'; $where .= $wpdb->prepare(' AND (intent_label LIKE %s OR phrases LIKE %s)', $search_term_like, $search_term_like); } if ($callback_filter) { $where .= $wpdb->prepare(' AND callback_function = %s', $callback_filter); } // Pagination $total_intents = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where"); $total_pages = ceil($total_intents / $per_page); // Get intents $intents = $wpdb->get_results($wpdb->prepare( "SELECT * FROM $table_name WHERE $where LIMIT %d OFFSET %d", $per_page, $offset )); // Get callbacks $available_callbacks = $this->mxchat_get_available_callbacks(); ?>

Intent Manager

callback_function; $callback_label = isset($available_callbacks[$callback_function]['label']) ? $available_callbacks[$callback_function]['label'] : $callback_function; $threshold_value = isset($intent->similarity_threshold) ? round($intent->similarity_threshold * 100) : 85; // Check if this is a form intent (its intent_label starts with "Form ") $is_form_intent = strpos($intent->intent_label, 'Form ') === 0; ?> >
intent_label, $matches); $form_id = isset($matches[1]) ? intval($matches[1]) : 0; if ($form_id) { global $wpdb; $forms_table = $wpdb->prefix . 'mxchat_forms'; $form = $wpdb->get_row($wpdb->prepare("SELECT title FROM $forms_table WHERE id = %d", $form_id)); if ($form && !empty($form->title)) { echo esc_html($form->title); } else { echo esc_html($intent->intent_label); } } else { echo esc_html($intent->intent_label); } echo ' Form Intent'; } else { echo esc_html($intent->intent_label); } ?> phrases); ?>
%
intent_label, $matches); $form_id = isset($matches[1]) ? $matches[1] : ''; ?>
1) : ?>
add_query_arg('paged', '%#%'), 'format' => '', 'prev_text' => __('« Previous', 'mxchat'), 'next_text' => __('Next »', 'mxchat'), 'total' => $total_pages, 'current' => $page )); ?>
mxchat_generate_embedding($phrase, $this->options['api_key']); if (is_array($embedding_vector)) { $vectors[] = $embedding_vector; } else { wp_die(esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase)); } } if (empty($vectors)) { wp_die(esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat')); } // Create combined vector just like in intent creation $combined_vector = $this->mxchat_average_vectors($vectors); $serialized_vector = maybe_serialize($combined_vector); global $wpdb; $table_name = $wpdb->prefix . 'mxchat_intents'; // Update the intent with both new phrases and the combined vector $result = $wpdb->update( $table_name, array( 'phrases' => implode(', ', $phrases_array), 'embedding_vector' => $serialized_vector ), array('id' => $intent_id), array('%s', '%s'), array('%d') ); if (false === $result) { wp_die(esc_html__('Failed to update intent.', 'mxchat')); } // Redirect back to the intents page with a success message $redirect_url = add_query_arg( array( 'page' => 'mxchat-intents', 'updated' => 'true' ), admin_url('admin.php') ); wp_safe_redirect($redirect_url); exit; } public function mxchat_handle_update_intent_threshold() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__('Unauthorized user', 'mxchat') ); } check_admin_referer('mxchat_update_intent_threshold_nonce'); if (isset($_POST['intent_id'], $_POST['intent_threshold'])) { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_intents'; $intent_id = intval($_POST['intent_id']); $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold']))); $similarity_threshold = $threshold_percentage / 100; $wpdb->update( $table_name, ['similarity_threshold' => $similarity_threshold], ['id' => $intent_id], ['%f'], ['%d'] ); } wp_safe_redirect(admin_url('admin.php?page=mxchat-intents')); exit; } public function mxchat_handle_add_intent() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__('Unauthorized user', 'mxchat') ); } check_admin_referer('mxchat_add_intent_nonce'); global $wpdb; $table_name = $wpdb->prefix . 'mxchat_intents'; $intent_label = isset($_POST['intent_label']) ? sanitize_text_field($_POST['intent_label']) : ''; $phrases_input = isset($_POST['phrases']) ? sanitize_textarea_field($_POST['phrases']) : ''; $callback_function = isset($_POST['callback_function']) ? sanitize_text_field($_POST['callback_function']) : ''; $default_threshold = 0.85; if (empty($intent_label) || empty($callback_function) || empty($phrases_input)) { wp_die( esc_html__('Invalid input. Please ensure all fields are filled out.', 'mxchat') ); } $available_callbacks = $this->mxchat_get_available_callbacks(); if (!array_key_exists($callback_function, $available_callbacks)) { wp_die( esc_html__('Invalid callback function selected.', 'mxchat') ); } $is_pro_only = $available_callbacks[$callback_function]['pro_only']; if ($is_pro_only && !$this->is_activated) { wp_die( esc_html__('This callback function is available in the Pro version only.', 'mxchat') ); } $phrases_array = array_map('sanitize_text_field', array_filter(array_map('trim', explode(',', $phrases_input)))); if (empty($phrases_array)) { wp_die( esc_html__('Please enter at least one valid phrase.', 'mxchat') ); } $vectors = []; foreach ($phrases_array as $phrase) { $embedding_vector = $this->mxchat_generate_embedding($phrase, $this->options['api_key']); if (is_array($embedding_vector)) { $vectors[] = $embedding_vector; } else { wp_die( esc_html__('Error generating embedding for phrase: ', 'mxchat') . esc_html($phrase) ); } } if (!empty($vectors)) { $combined_vector = $this->mxchat_average_vectors($vectors); $serialized_vector = maybe_serialize($combined_vector); $result = $wpdb->insert($table_name, [ 'intent_label' => $intent_label, 'phrases' => implode(', ', $phrases_array), 'embedding_vector' => $serialized_vector, 'callback_function' => $callback_function, 'similarity_threshold' => $default_threshold, ]); if ($result === false) { wp_die( esc_html__('Database error: ', 'mxchat') . esc_html($wpdb->last_error) ); } } else { wp_die( esc_html__('No valid embeddings generated. Please check your phrases.', 'mxchat') ); } wp_safe_redirect(admin_url('admin.php?page=mxchat-intents')); exit; } private function mxchat_get_available_callbacks($grouped = false) { $callbacks = [ 'mxchat_handle_email_capture' => [ 'label' => __('Email Capture', 'mxchat'), 'pro_only' => false, 'group' => __('Customer Engagement', 'mxchat'), ], 'mxchat_generate_image' => [ 'label' => __('Generate Image', 'mxchat'), 'pro_only' => true, 'group' => __('Other Features', 'mxchat'), ], 'mxchat_handle_search_request' => [ 'label' => __('Brave Web Search', 'mxchat'), 'pro_only' => false, 'group' => __('Search Features', 'mxchat'), ], 'mxchat_handle_image_search_request' => [ 'label' => __('Brave Image Search', 'mxchat'), 'pro_only' => false, 'group' => __('Search Features', 'mxchat'), ], 'mxchat_handle_pdf_discussion' => [ 'label' => __('Chat with PDF', 'mxchat'), 'pro_only' => true, 'group' => __('Other Features', 'mxchat'), ], 'mxchat_live_agent_handover' => [ 'label' => __('Live Agent', 'mxchat'), 'pro_only' => true, 'group' => __('Customer Engagement', 'mxchat'), ], 'mxchat_handle_switch_to_chatbot_intent' => [ 'label' => __('Back to Chatbot', 'mxchat'), 'pro_only' => true, 'group' => __('Customer Engagement', 'mxchat'), ], ]; // Allow other plugins to add their callbacks $callbacks = apply_filters('mxchat_available_callbacks', $callbacks); // Return grouped structure if requested if ($grouped) { $grouped_callbacks = []; foreach ($callbacks as $key => $data) { $group_label = $data['group'] ?? __('Other Features', 'mxchat'); $grouped_callbacks[$group_label][$key] = [ 'label' => $data['label'], 'pro_only' => $data['pro_only'], ]; } return $grouped_callbacks; } return $callbacks; } private function mxchat_average_vectors($vectors) { $vector_length = count($vectors[0]); $sum_vector = array_fill(0, $vector_length, 0); foreach ($vectors as $vector) { for ($i = 0; $i < $vector_length; $i++) { $sum_vector[$i] += $vector[$i]; } } // Divide each component by the number of vectors to get the average $num_vectors = count($vectors); for ($i = 0; $i < $vector_length; $i++) { $sum_vector[$i] /= $num_vectors; } return $sum_vector; } public function mxchat_handle_delete_intent() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__('Unauthorized user', 'mxchat') ); } check_admin_referer('mxchat_delete_intent_nonce'); if (isset($_POST['intent_id'])) { global $wpdb; $table_name = $wpdb->prefix . 'mxchat_intents'; $intent_id = intval($_POST['intent_id']); $wpdb->delete($table_name, ['id' => $intent_id], ['%d']); } wp_safe_redirect(admin_url('admin.php?page=mxchat-intents')); exit; } public function mxchat_page_init() { // Register settings register_setting( 'mxchat_option_group', 'mxchat_options', array($this, 'mxchat_sanitize') ); register_setting( 'mxchat_option_group', 'mxchat_similarity_threshold', array( 'type' => 'number', 'sanitize_callback' => function($value) { $value = absint($value); // Ensure it's an integer return min(max($value, 70), 85); // Enforce range }, 'default' => 80, ) ); // Chatbot Settings Section add_settings_section( 'mxchat_chatbot_section', esc_html__('Chatbot Settings', 'mxchat'), null, 'mxchat-chatbot' ); // Similarity Threshold Slider add_settings_field( 'similarity_threshold', // Field ID esc_html__('Similarity Threshold', 'mxchat'), // Field title array($this, 'mxchat_similarity_threshold_callback'), // Callback function 'mxchat-chatbot', // Page 'mxchat_chatbot_section' // Section ); add_settings_field( 'append_to_body', esc_html__('Auto-Display Chatbot', 'mxchat'), array($this, 'mxchat_append_to_body_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); // Existing fields... add_settings_field( 'api_key', esc_html__('OpenAI API Key', 'mxchat'), array($this, 'api_key_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'xai_api_key', esc_html__('X.AI API Key', 'mxchat'), array($this, 'xai_api_key_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'claude_api_key', esc_html__('Claude API Key', 'mxchat'), array($this, 'claude_api_key_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'deepseek_api_key', esc_html__('DeepSeek API Key', 'mxchat'), array($this, 'deepseek_api_key_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'system_prompt_instructions', esc_html__('AI Instructions (Behavior)', 'mxchat'), array($this, 'system_prompt_instructions_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'model', esc_html__('Model', 'mxchat'), array($this, 'mxchat_model_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'top_bar_title', esc_html__('Top Bar Title', 'mxchat'), array($this, 'mxchat_top_bar_title_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'enable_email_block', esc_html__('Require Email To Chat', 'mxchat'), array($this, 'enable_email_block_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'email_blocker_header_content', esc_html__('Require Email Chat Content', 'mxchat'), array($this, 'email_blocker_header_content_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'email_blocker_button_text', esc_html__('Require Email Chat Button Text', 'mxchat'), [$this, 'email_blocker_button_text_callback'], 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'intro_message', esc_html__('Introductory Message', 'mxchat'), array($this, 'mxchat_intro_message_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'input_copy', esc_html__('Input Copy', 'mxchat'), array($this, 'mxchat_input_copy_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'rate_limit_logged_out', __('Rate Limit for Logged-out Users', 'mxchat'), array($this, 'mxchat_rate_limit_logged_out_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'rate_limit_roles', __('Rate Limits by User Role', 'mxchat'), array($this, 'mxchat_rate_limit_roles_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'rate_limit_message', esc_html__('Rate Limit Message', 'mxchat'), array($this, 'mxchat_rate_limit_message_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'pre_chat_message', esc_html__('Chat Teaser Pop-up', 'mxchat'), array($this, 'mxchat_pre_chat_message_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'privacy_toggle', esc_html__('Toggle Privacy Notice', 'mxchat'), array($this, 'mxchat_privacy_toggle_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'complianz_toggle', esc_html__('Enable Complianz', 'mxchat'), array($this, 'mxchat_complianz_toggle_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'link_target_toggle', esc_html__('Open Links in a New Tab', 'mxchat'), array($this, 'mxchat_link_target_toggle_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'chat_persistence_toggle', esc_html__('Enable Chat Persistence', 'mxchat'), array($this, 'mxchat_chat_persistence_toggle_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'popular_question_1', esc_html__('Quick Question 1', 'mxchat'), array($this, 'mxchat_popular_question_1_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'popular_question_2', esc_html__('Quick Question 2', 'mxchat'), array($this, 'mxchat_popular_question_2_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'popular_question_3', esc_html__('Quick Question 3', 'mxchat'), array($this, 'mxchat_popular_question_3_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); add_settings_field( 'additional_popular_questions', esc_html__('Additional Quick Questions', 'mxchat'), array($this, 'mxchat_additional_popular_questions_callback'), 'mxchat-chatbot', 'mxchat_chatbot_section' ); // Loops Settings Section add_settings_section( 'mxchat_loops_section', esc_html__('Loops Settings', 'mxchat'), null, 'mxchat-embed' ); // Loops Settings Fields add_settings_field( 'loops_api_key', esc_html__('Loops API Key', 'mxchat'), array($this, 'mxchat_loops_api_key_callback'), 'mxchat-embed', 'mxchat_loops_section' ); add_settings_field( 'loops_mailing_list', esc_html__('Loops Mailing List', 'mxchat'), array($this, 'mxchat_loops_mailing_list_callback'), 'mxchat-embed', 'mxchat_loops_section' ); add_settings_field( 'triggered_phrase_response', esc_html__('Triggered Phrase Response', 'mxchat'), array($this, 'mxchat_triggered_phrase_response_callback'), 'mxchat-embed', 'mxchat_loops_section' ); add_settings_field( 'email_capture_response', esc_html__('Email Capture Response', 'mxchat'), array($this, 'mxchat_email_capture_response_callback'), 'mxchat-embed', 'mxchat_loops_section' ); // Brave Search Settings Fields add_settings_section( 'mxchat_brave_section', __('Brave Search Settings', 'mxchat'), array($this, 'mxchat_brave_section_callback'), 'mxchat-embed' ); add_settings_field( 'brave_api_key', __('Brave API Key', 'mxchat'), array($this, 'mxchat_brave_api_key_callback'), 'mxchat-embed', 'mxchat_brave_section' ); add_settings_field( 'brave_image_count', __('Number of Images to Return', 'mxchat'), array($this, 'mxchat_brave_image_count_callback'), 'mxchat-embed', 'mxchat_brave_section' ); add_settings_field( 'brave_safe_search', __('Safe Search', 'mxchat'), array($this, 'mxchat_brave_safe_search_callback'), 'mxchat-embed', 'mxchat_brave_section' ); add_settings_field( 'brave_news_count', __('Number of News Articles', 'mxchat'), array($this, 'mxchat_brave_news_count_callback'), 'mxchat-embed', 'mxchat_brave_section' ); add_settings_field( 'brave_country', __('Country', 'mxchat'), array($this, 'mxchat_brave_country_callback'), 'mxchat-embed', 'mxchat_brave_section' ); add_settings_field( 'brave_language', __('Language', 'mxchat'), array($this, 'mxchat_brave_language_callback'), 'mxchat-embed', 'mxchat_brave_section' ); // Chat with PDF Intent Settings Fields add_settings_section( 'mxchat_pdf_intent_section', __('Toolbar Settings & Intents', 'mxchat'), array($this, 'mxchat_pdf_intent_section_callback'), 'mxchat-embed' ); add_settings_field( 'chat_toolbar_toggle', __('Show Chat Toolbar', 'mxchat'), array($this, 'mxchat_chat_toolbar_toggle_callback'), 'mxchat-embed', 'mxchat_pdf_intent_section' ); add_settings_field( 'pdf_intent_trigger_text', __('Intent Trigger Text', 'mxchat'), array($this, 'mxchat_pdf_intent_trigger_text_callback'), 'mxchat-embed', 'mxchat_pdf_intent_section' ); add_settings_field( 'pdf_intent_success_text', __('Success Text', 'mxchat'), array($this, 'mxchat_pdf_intent_success_text_callback'), 'mxchat-embed', 'mxchat_pdf_intent_section' ); add_settings_field( 'pdf_intent_error_text', __('Error Text', 'mxchat'), array($this, 'mxchat_pdf_intent_error_text_callback'), 'mxchat-embed', 'mxchat_pdf_intent_section' ); // Add PDF Maximum Pages Field add_settings_field( 'pdf_max_pages', __('Maximum Document Pages', 'mxchat'), array($this, 'mxchat_pdf_max_pages_callback'), 'mxchat-embed', 'mxchat_pdf_intent_section' ); // Live Agent Settings Fields add_settings_section( 'mxchat_live_agent_section', __('Live Agent Settings', 'mxchat'), array($this, 'mxchat_live_agent_section_callback'), 'mxchat-embed' ); // Live Agent Status Fields (add at top of live agent settings) add_settings_field( 'live_agent_status', __('Live Agent Status', 'mxchat'), array($this, 'mxchat_live_agent_status_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); add_settings_field( 'live_agent_notification_message', __('Notification Message', 'mxchat'), array($this, 'mxchat_live_agent_notification_message_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); add_settings_field( 'live_agent_away_message', __('Away Message', 'mxchat'), array($this, 'mxchat_live_agent_away_message_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); add_settings_field( 'live_agent_webhook_url', __('Slack Webhook URL', 'mxchat'), array($this, 'mxchat_live_agent_webhook_url_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); add_settings_field( 'live_agent_secret_key', __('Slack Secret Key', 'mxchat'), array($this, 'mxchat_live_agent_secret_key_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); // Live Agent Integration Fields add_settings_field( 'live_agent_bot_token', __('Slack Bot OAuth Token', 'mxchat'), array($this, 'mxchat_live_agent_bot_token_callback'), 'mxchat-embed', 'mxchat_live_agent_section' ); // General Settings Section add_settings_section( 'mxchat_general_section', esc_html__('Frequently Asked Questions (FAQ)', 'mxchat'), null, 'mxchat-general' ); } public function mxchat_prompts_page_init() { // Register all settings as a single array register_setting( 'mxchat_prompts_options', 'mxchat_prompts_options', array( 'type' => 'array', 'description' => __('MXChat Knowledge Base Settings', 'mxchat'), 'default' => array( 'mxchat_auto_sync_posts' => 0, 'mxchat_auto_sync_pages' => 0, 'mxchat_use_pinecone' => 0, 'mxchat_pinecone_api_key' => '', 'mxchat_pinecone_environment' => '', 'mxchat_pinecone_index' => '', 'mxchat_pinecone_host' => '', // Add this line ), 'sanitize_callback' => array($this, 'sanitize_prompts_options'), ) ); // Add settings saved message add_action('admin_notices', array($this, 'sync_settings_notice')); } /** * Sanitize all prompts options * * @param array $input The unsanitized options array * @return array The sanitized options array */ public function sanitize_prompts_options($input) { // Log the incoming input. //error_log('Sanitizing inputs: ' . print_r($input, true)); $sanitized = array(); // Boolean options $sanitized['mxchat_auto_sync_posts'] = isset($input['mxchat_auto_sync_posts']) ? 1 : 0; $sanitized['mxchat_auto_sync_pages'] = isset($input['mxchat_auto_sync_pages']) ? 1 : 0; $sanitized['mxchat_use_pinecone'] = !empty($input['mxchat_use_pinecone']) ? 1 : 0; // API Key: if less than 32 characters, flag as invalid. $api_key = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? ''); if (!empty($api_key) && strlen($api_key) < 32) { add_settings_error( 'mxchat_prompts_options', 'invalid_api_key', __('The Pinecone API key appears to be invalid. Please check your API key.', 'mxchat') ); $existing_options = get_option('mxchat_prompts_options', array()); $sanitized['mxchat_pinecone_api_key'] = $existing_options['mxchat_pinecone_api_key'] ?? ''; } else { $sanitized['mxchat_pinecone_api_key'] = $api_key; } // Environment and Index Name $sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? ''); $sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? ''); // Host: Remove protocol and validate format. $host = sanitize_text_field($input['mxchat_pinecone_host'] ?? ''); $host = preg_replace('#^https?://#', '', $host); //error_log('Host after removing protocol: ' . $host); if (!empty($host)) { if (!preg_match('/^[\w-]+\.svc\.[\w-]+\.pinecone\.io$/', $host)) { add_settings_error( 'mxchat_prompts_options', 'invalid_host', __('The Pinecone host appears to be invalid. It should look like "mxchat-vectors-zrmsquq.svc.aped-4627-b74a.pinecone.io"', 'mxchat') ); $existing_options = get_option('mxchat_prompts_options', array()); $sanitized['mxchat_pinecone_host'] = $existing_options['mxchat_pinecone_host'] ?? ''; } else { $sanitized['mxchat_pinecone_host'] = $host; } } else { $sanitized['mxchat_pinecone_host'] = ''; } //error_log('Final sanitized array: ' . print_r($sanitized, true)); return $sanitized; } public function sync_settings_notice() { // Only show notice on our plugin page if (!isset($_GET['page']) || $_GET['page'] !== 'mxchat-prompts') { return; } // Check if settings were updated if (isset($_GET['settings-updated'])) { ?>

'software-api', 'request' => 'activation', 'email' => $customer_email, 'license_key' => $license_key, 'product_id' => $product_id ), 'http://mxchat.ai/' ) ); if (is_wp_error($response)) { wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $response->get_error_message()); return; } $body = wp_remote_retrieve_body($response); $data = json_decode($body); if ($data && isset($data->activated) && $data->activated) { update_option('mxchat_license_status', 'active'); update_option('mxchat_pro_email', $customer_email); update_option('mxchat_activation_key', $license_key); wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat'))); } else { $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat'); update_option('mxchat_license_status', 'inactive'); update_option('mxchat_license_error', $error_message); wp_send_json_error($error_message); } } public function mxchat_rate_limit_logged_out_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved rate limit or use the default value $default_rate_limit = '10'; $selected_rate_limit = isset($all_options['rate_limit_logged_out']) ? $all_options['rate_limit_logged_out'] : $default_rate_limit; // Define available rate limits $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Output the dropdown echo '
'; echo ''; echo '

' . esc_html__('Set the maximum number of messages a logged-out user can send within a 24-hour period.', 'mxchat') . '

'; echo '
'; } // Add this new callback function public function mxchat_rate_limit_roles_callback() { $all_options = get_option('mxchat_options', []); $roles = wp_roles()->get_names(); $rate_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); echo '
'; foreach ($roles as $role_id => $role_name) { $default_rate_limit = '100'; $selected_rate_limit = isset($all_options['role_rate_limits'][$role_id]) ? $all_options['role_rate_limits'][$role_id] : $default_rate_limit; echo '
'; echo ''; echo ''; echo '
'; } echo '

' . esc_html__('Set the maximum number of messages users in each role can send within a 24-hour period.', 'mxchat') . '

'; echo '
'; } public function mxchat_rate_limit_message_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved message or use the default value $default_message = esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'); $rate_limit_message = isset($all_options['rate_limit_message']) ? $all_options['rate_limit_message'] : $default_message; // Output the textarea echo '
'; printf( '', esc_textarea($rate_limit_message) ); echo '

' . esc_html__('This message will be displayed when a user exceeds the rate limit.', 'mxchat') . '

'; echo '
'; } private function mxchat_add_option_field($id, $title, $callback = '') { add_settings_field( $id, __($title, 'mxchat'), $callback ? array($this, $callback) : array($this, $id . '_callback'), 'mxchat-max', 'mxchat_setting_section_id', $id === 'model' ? ['label_for' => 'model'] : [] ); } public function api_key_callback() { // Retrieve from your stored 'api_key' in the mxchat_options array $apiKey = isset($this->options['api_key']) ? esc_attr($this->options['api_key']) : ''; // Notice we changed the name to "api_key" (no array notation). echo ''; echo ''; echo '

' . wp_kses_post(__('The OpenAI API key is required even when using other models as it is used for vector embedding functionality. You must add credits to your OpenAI billing account before use.', 'mxchat')) . '

'; } public function xai_api_key_callback() { // Check if the feature is activated (paid feature) $disabled = $this->is_activated ? '' : 'disabled'; // Disable input if not activated $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // CSS class to style the wrapper based on activation status // Retrieve the X.AI API key value $xaiApiKey = isset($this->options['xai_api_key']) ? esc_attr($this->options['xai_api_key']) : ''; // Render the input field for the X.AI API key echo '
'; printf( '', $xaiApiKey, $disabled ); echo ''; // If the feature is not activated, show the overlay with a "Pro Only" message if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function claude_api_key_callback() { // Check if the feature is activated (paid feature) $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // Retrieve the Claude API key value directly like the others $claudeApiKey = isset($this->options['claude_api_key']) ? esc_attr($this->options['claude_api_key']) : ''; // Use direct name field like the other working API keys echo '
'; printf( '', $claudeApiKey, $disabled ); echo ''; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function deepseek_api_key_callback() { // Retrieve from your stored 'deepseek_api_key' in the mxchat_options array $apiKey = isset($this->options['deepseek_api_key']) ? esc_attr($this->options['deepseek_api_key']) : ''; // Notice we changed the name to "deepseek_api_key" (no array notation). echo ''; echo ''; } public function mxchat_loops_api_key_callback() { // Support both old and new format $loops_api_key = isset($this->options['loops_api_key']) ? esc_attr($this->options['loops_api_key']) : ''; echo '
'; echo sprintf( '', $loops_api_key ); echo ''; echo '
'; echo '

' . esc_html__('Enter your Loops API Key here. (See FAQ for details)', 'mxchat') . '

'; } public function mxchat_loops_mailing_list_callback() { // Add error handling and type checking $loops_api_key = ''; $selected_list = ''; // Safely get the API key if (isset($this->options['loops_api_key']) && is_string($this->options['loops_api_key'])) { $loops_api_key = $this->options['loops_api_key']; } // Safely get the selected list if (isset($this->options['loops_mailing_list']) && is_string($this->options['loops_mailing_list'])) { $selected_list = $this->options['loops_mailing_list']; } if (!empty($loops_api_key)) { $lists = $this->mxchat_fetch_loops_mailing_lists($loops_api_key); if (is_array($lists) && !empty($lists)) { echo ''; } else { echo '

' . esc_html__('No lists found. Please verify your API Key.', 'mxchat') . '

'; } } else { echo '

' . esc_html__('Enter a valid Loops API Key to load mailing lists.', 'mxchat') . '

'; } } public function mxchat_triggered_phrase_response_callback() { $default_response = __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'); $triggered_response = isset($this->options['triggered_phrase_response']) ? $this->options['triggered_phrase_response'] : $default_response; echo sprintf( '', esc_textarea($triggered_response) ); echo '

' . esc_html__('Enter the chatbot response when a trigger keyword is detected, prompting the user to share their email.', 'mxchat') . '

'; } public function mxchat_email_capture_response_callback() { $default_response = __('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'); $email_capture_response = isset($this->options['email_capture_response']) ? $this->options['email_capture_response'] : $default_response; echo sprintf( '', esc_textarea($email_capture_response) ); echo '

' . esc_html__('Enter the message to send when a user provides their email.', 'mxchat') . '

'; } public function mxchat_pre_chat_message_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved message or use the default value $default_message = __('Hey there! Ask me anything!', 'mxchat'); $pre_chat_message = isset($all_options['pre_chat_message']) ? $all_options['pre_chat_message'] : $default_message; // Output the textarea printf( '', esc_textarea($pre_chat_message) ); echo '

' . esc_html__('Set the message displayed to users before they start a chat. Use this to provide a friendly greeting or instructions.', 'mxchat') . '

'; } // Callback for AI Instructions textarea public function system_prompt_instructions_callback() { // Retrieve the current value of the system prompt instructions $instructions = isset($this->options['system_prompt_instructions']) ? esc_textarea($this->options['system_prompt_instructions']) : ''; // Render the textarea field printf( '', $instructions ); // Provide a helpful description echo '

' . esc_html__('Provide system-level instructions for the AI to guide its behavior. Be clear and concise for better results.', 'mxchat') . '

'; } public function mxchat_model_callback() { // Define available models grouped by provider $models = array( esc_html__('X.AI Models', 'mxchat') => array( 'grok-beta' => esc_html__('grok-beta (Early Beta)', 'mxchat'), 'grok-2' => esc_html__('Grok 2', 'mxchat') ), esc_html__('DeepSeek Models', 'mxchat') => array( 'deepseek-chat' => esc_html__('DeepSeek-V3', 'mxchat'), ), esc_html__('Claude Models', 'mxchat') => array( 'claude-3-5-sonnet-20241022' => esc_html__('Claude 3.5 Sonnet (Most Intelligent)', 'mxchat'), 'claude-3-opus-20240229' => esc_html__('Claude 3 Opus (Highly Complex Tasks)', 'mxchat'), 'claude-3-sonnet-20240229' => esc_html__('Claude 3 Sonnet (Balanced)', 'mxchat'), 'claude-3-haiku-20240307' => esc_html__('Claude 3 Haiku (Fastest)', 'mxchat') ), esc_html__('OpenAI Models', 'mxchat') => array( 'gpt-4o' => esc_html__('GPT-4o (Recommended)', 'mxchat'), 'gpt-4o-mini' => esc_html__('GPT-4o Mini (Fast and Lightweight)', 'mxchat'), 'gpt-4-turbo' => esc_html__('GPT-4 Turbo (High-Performance)', 'mxchat'), 'gpt-4' => esc_html__('GPT-4 (High Intelligence)', 'mxchat'), 'gpt-3.5-turbo' => esc_html__('GPT-3.5 Turbo (Affordable and Fast)', 'mxchat') ) ); // Retrieve the currently selected model from saved options $selected_model = isset($this->options['model']) ? esc_attr($this->options['model']) : 'gpt-4o'; // Begin the select dropdown echo ''; // Add a description below the dropdown echo '

' . esc_html__('Select the AI model to use for your chatbot. Pro-only models are marked accordingly.', 'mxchat') . '

'; } public function mxchat_top_bar_title_callback() { // Retrieve the current value of the top bar title from saved options $top_bar_title = isset($this->options['top_bar_title']) ? esc_attr($this->options['top_bar_title']) : ''; // Render the input field echo ''; // Add a description echo '

' . esc_html__('Enter the title text that will appear on the top bar of the chatbot.', 'mxchat') . '

'; } public function enable_email_block_callback() { // Load full plugin options array $all_options = get_option('mxchat_options', []); // Get the value, default to 'off' $enable_email_block = isset($all_options['enable_email_block']) ? $all_options['enable_email_block'] : 'off'; // Check if it's 'on' $checked = ($enable_email_block === 'on') ? 'checked' : ''; echo ''; echo '

' . esc_html__('Their email will appear at the top of the transcript. Email form will show for users who are not logged in or have not provided an email within 24h.', 'mxchat') . '

'; } public function email_blocker_header_content_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved content or default to empty $content = isset($all_options['email_blocker_header_content']) ? $all_options['email_blocker_header_content'] : ''; // Render the textarea echo ''; echo '

'; echo esc_html__('You may enter HTML here, such as <h2>Welcome</h2> or <p>Let\'s get started</p>.', 'mxchat'); echo '

'; } public function email_blocker_button_text_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved button text or default to empty $button_text = isset($all_options['email_blocker_button_text']) ? $all_options['email_blocker_button_text'] : ''; // Use esc_attr to safely render the existing text echo ''; echo '

'; echo esc_html__('Enter the text you want on the submit button, e.g. "Start Chat".', 'mxchat'); echo '

'; } public function mxchat_intro_message_callback() { // Load the entire 'mxchat_options' array $all_options = get_option('mxchat_options', []); // Retrieve the saved intro message or use the default $default_message = __('Hello! How can I assist you today?', 'mxchat'); $saved_message = isset($all_options['intro_message']) ? $all_options['intro_message'] : $default_message; // Output the textarea with the saved value ?>

', esc_attr($input_copy), esc_attr__('How can I assist?', 'mxchat') ); // Output the description echo '

' . esc_html__('This is the placeholder text for the chat input field.', 'mxchat') . '

'; } public function mxchat_user_message_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['user_message_font_color']) ? esc_attr($this->options['user_message_font_color']) : '#ffffff', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_bot_message_bg_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['bot_message_bg_color']) ? esc_attr($this->options['bot_message_bg_color']) : '#e1e1e1', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_bot_message_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['bot_message_font_color']) ? esc_attr($this->options['bot_message_font_color']) : '#333333', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_live_agent_message_bg_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['live_agent_message_bg_color']) ? esc_attr($this->options['live_agent_message_bg_color']) : '#ffffff', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_live_agent_message_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['live_agent_message_font_color']) ? esc_attr($this->options['live_agent_message_font_color']) : '#333333', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_mode_indicator_bg_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['mode_indicator_bg_color']) ? esc_attr($this->options['mode_indicator_bg_color']) : '#767676', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_mode_indicator_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['mode_indicator_font_color']) ? esc_attr($this->options['mode_indicator_font_color']) : '#ffffff', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_toolbar_icon_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['toolbar_icon_color']) ? esc_attr($this->options['toolbar_icon_color']) : '#212121', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_top_bar_bg_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['top_bar_bg_color']) ? esc_attr($this->options['top_bar_bg_color']) : '#00b294', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_send_button_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['send_button_font_color']) ? esc_attr($this->options['send_button_font_color']) : '#ffffff', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_chatbot_background_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['chatbot_background_color']) ? esc_attr($this->options['chatbot_background_color']) : '#000000', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_icon_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['icon_color']) ? esc_attr($this->options['icon_color']) : '#ffffff', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_custom_icon_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $custom_icon_url = isset($this->options['custom_icon']) ? esc_url($this->options['custom_icon']) : ''; echo '
'; echo sprintf( '', $custom_icon_url, esc_attr__('Enter PNG URL', 'mxchat'), esc_attr($disabled) ); // Preview container for the icon if (!empty($custom_icon_url)) { echo '
'; echo '' . esc_attr__('Custom Icon Preview', 'mxchat') . ''; echo '
'; } echo '

' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_title_icon_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; // Fixed the variable reference - it was using custom_icon instead of title_icon $title_icon_url = isset($this->options['title_icon']) ? esc_url($this->options['title_icon']) : ''; echo '
'; echo sprintf( '', $title_icon_url, esc_attr__('Enter PNG URL', 'mxchat'), esc_attr($disabled) ); // Preview container for the icon if (!empty($title_icon_url)) { echo '
'; echo '' . esc_attr__('Title Icon Preview', 'mxchat') . ''; echo '
'; } echo '

' . esc_html__('Upload your PNG icon and paste the URL here. Recommended size: 48x48 pixels.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_chat_input_font_color_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; echo sprintf( '', isset($this->options['chat_input_font_color']) ? esc_attr($this->options['chat_input_font_color']) : '#555555', esc_attr($disabled) ); if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_append_to_body_callback() { // Get value from options array, default to 'off' $append_to_body = isset($this->options['append_to_body']) ? $this->options['append_to_body'] : 'off'; $checked = ($append_to_body === 'on') ? 'checked' : ''; echo ''; echo '

' . esc_html__('Show chatbot automatically on all pages. When disabled, you can place the chatbot manually using shortcode [mxchat_chatbot floating="yes"].', 'mxchat') . '

'; } public function mxchat_privacy_toggle_callback() { // Load from mxchat_options array $options = get_option('mxchat_options', []); // Get privacy toggle value with fallback $privacy_toggle = isset($options['privacy_toggle']) ? $options['privacy_toggle'] : 'off'; $checked = ($privacy_toggle === 'on') ? 'checked' : ''; // Get privacy text with fallback $privacy_text = isset($options['privacy_text']) ? $options['privacy_text'] : __('By chatting, you agree to our privacy policy.', 'mxchat'); // Output the toggle switch echo ''; echo '

' . esc_html__('Enable this option to display a privacy notice below the chat widget.', 'mxchat') . '

'; // Output the custom text input field echo sprintf( '', esc_textarea($privacy_text) ); echo '

' . esc_html__('Enter the privacy policy text. You can include HTML links.', 'mxchat') . '

'; } public function mxchat_complianz_toggle_callback() { // Load from mxchat_options array $options = get_option('mxchat_options', []); // Get complianz toggle value with fallback $complianz_toggle = isset($options['complianz_toggle']) ? $options['complianz_toggle'] : 'off'; $checked = ($complianz_toggle === 'on') ? 'checked' : ''; // Output the toggle switch echo ''; echo '

' . esc_html__('Enable this option to apply Complianz consent logic to the chatbot (must have Complianz Plugin).', 'mxchat') . '

'; } public function mxchat_link_target_toggle_callback() { // Load from mxchat_options array $options = get_option('mxchat_options', []); // Get link target toggle value with fallback $link_target_toggle = isset($options['link_target_toggle']) ? $options['link_target_toggle'] : 'off'; $checked = ($link_target_toggle === 'on') ? 'checked' : ''; // Output the toggle switch echo ''; echo '

' . esc_html__('Enable to open links in a new tab (default is to open in the same tab).', 'mxchat') . '

'; } public function mxchat_chat_persistence_toggle_callback() { // Load from mxchat_options array $options = get_option('mxchat_options', []); // Get chat persistence toggle value with fallback $chat_persistence_toggle = isset($options['chat_persistence_toggle']) ? $options['chat_persistence_toggle'] : 'off'; $checked = ($chat_persistence_toggle === 'on') ? 'checked' : ''; // Output the toggle switch echo ''; echo '

' . esc_html__('Enable to keep chat history when users navigate tabs or return to the site within 24 hours.', 'mxchat') . '

'; } public function mxchat_popular_question_1_callback() { // Load the full plugin options array $all_options = get_option('mxchat_options', []); // Retrieve the specific option for popular_question_1 $popular_question_1 = isset($all_options['popular_question_1']) ? $all_options['popular_question_1'] : ''; // Render the input field printf( '', esc_attr($popular_question_1), esc_attr__('Enter Quick Question 1', 'mxchat') ); // Add a description for the field echo '

' . esc_html__('This will be the first Quick Question in the chatbot, displayed above the input field.', 'mxchat') . '

'; } public function mxchat_popular_question_2_callback() { // Load the full plugin options array $all_options = get_option('mxchat_options', []); // Retrieve the specific option for popular_question_2 $popular_question_2 = isset($all_options['popular_question_2']) ? $all_options['popular_question_2'] : ''; // Render the input field printf( '', esc_attr($popular_question_2), esc_attr__('Enter Quick Question 2', 'mxchat') ); // Add a description for the field echo '

' . esc_html__('This will be the second Quick Question in the chatbot.', 'mxchat') . '

'; } public function mxchat_popular_question_3_callback() { // Load the full plugin options array $all_options = get_option('mxchat_options', []); // Retrieve the specific option for popular_question_3 $popular_question_3 = isset($all_options['popular_question_3']) ? $all_options['popular_question_3'] : ''; // Render the input field printf( '', esc_attr($popular_question_3), esc_attr(__('Enter Quick Question 3', 'mxchat')) ); // Add a description for the field echo '

' . esc_html__('This will be the third Quick Question in the chatbot.', 'mxchat') . '

'; } public function mxchat_additional_popular_questions_callback() { $options = get_option('mxchat_options', []); $additional_questions = isset($options['additional_popular_questions']) ? $options['additional_popular_questions'] : get_option('additional_popular_questions', array()); echo '
'; if (!empty($additional_questions)) { foreach ($additional_questions as $index => $question) { printf( '
', esc_attr($question), esc_attr(sprintf(__('Enter Additional Quick Question %d', 'mxchat'), $index + 4)), $index, esc_attr(__('Remove question', 'mxchat')), esc_html__('Remove', 'mxchat') ); } } else { printf( '
', esc_attr(__('Enter Additional Quick Question 4', 'mxchat')), esc_attr(__('Remove question', 'mxchat')), esc_html__('Remove', 'mxchat') ); } echo '
'; printf( '', esc_attr(__('Add question', 'mxchat')), esc_html__('Add Question', 'mxchat') ); echo '

' . esc_html__('Add as many Quick Questions as you need.', 'mxchat') . '

'; echo ''; } public function mxchat_brave_api_key_callback() { $brave_api_key = isset($this->options['brave_api_key']) ? esc_attr($this->options['brave_api_key']) : ''; echo '
'; echo sprintf( '', $brave_api_key ); echo ''; echo '
'; echo '

' . __('Enter your Brave Search API Key here. (See FAQ for details)', 'mxchat') . '

'; } public function mxchat_brave_image_count_callback() { $brave_image_count = isset($this->options['brave_image_count']) ? intval($this->options['brave_image_count']) : 4; echo sprintf( '', $brave_image_count ); echo '

' . __('Select the number of images to return (1-6).', 'mxchat') . '

'; } public function mxchat_brave_safe_search_callback() { $brave_safe_search = isset($this->options['brave_safe_search']) ? esc_attr($this->options['brave_safe_search']) : 'strict'; echo ''; echo '

' . esc_html__('Set the Safe Search level for image searches. Brave Search only supports "Strict" and "Off" options.', 'mxchat') . '

'; } public function mxchat_brave_news_count_callback() { $brave_news_count = isset($this->options['brave_news_count']) ? intval($this->options['brave_news_count']) : 3; echo sprintf( '', $brave_news_count ); echo '

' . esc_html__('Select the number of news articles to retrieve (1-10).', 'mxchat') . '

'; } public function mxchat_brave_country_callback() { $brave_country = isset($this->options['brave_country']) ? esc_attr($this->options['brave_country']) : 'us'; echo sprintf( '', $brave_country ); echo '

' . esc_html__('Enter the country code (e.g., "us" for United States).', 'mxchat') . '

'; } public function mxchat_brave_language_callback() { $brave_language = isset($this->options['brave_language']) ? esc_attr($this->options['brave_language']) : 'en'; echo sprintf( '', $brave_language ); echo '

' . esc_html__('Enter the language code (e.g., "en" for English).', 'mxchat') . '

'; } // Section Callback public function mxchat_pdf_intent_section_callback() { echo '

' . esc_html__('Configure the intent settings for the Chat with PDF feature.', 'mxchat') . '

'; } public function mxchat_chat_toolbar_toggle_callback() { // Get chat toolbar toggle value with fallback $chat_toolbar_toggle = isset($this->options['chat_toolbar_toggle']) ? $this->options['chat_toolbar_toggle'] : 'off'; $checked = ($chat_toolbar_toggle === 'on') ? 'checked' : ''; // Check if the plugin is activated (paid feature) $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; // Output the toggle switch echo ''; // Pro feature overlay if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; echo '

' . esc_html__('Enable to display the chat toolbar, adding two icons below the chatbot input field for uploading PDF and Word documents (default is hidden).', 'mxchat') . '

'; } public function mxchat_pdf_intent_trigger_text_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $default_text = __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'); echo '
'; echo sprintf( '', esc_attr__('Enter trigger text', 'mxchat'), esc_attr($disabled), isset($this->options['pdf_intent_trigger_text']) ? esc_textarea($this->options['pdf_intent_trigger_text']) : esc_textarea($default_text) ); echo '

' . esc_html__('Text displayed when the intent is triggered.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_pdf_intent_success_text_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $default_text = __("I've processed the PDF. What questions do you have about it?", 'mxchat'); echo '
'; echo sprintf( '', esc_attr__('Enter success text', 'mxchat'), esc_attr($disabled), isset($this->options['pdf_intent_success_text']) ? esc_textarea($this->options['pdf_intent_success_text']) : esc_textarea($default_text) ); echo '

' . esc_html__('Text displayed when the intent is successful.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_pdf_intent_error_text_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $default_text = __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'); echo '
'; echo sprintf( '', esc_attr__('Enter error text', 'mxchat'), esc_attr($disabled), isset($this->options['pdf_intent_error_text']) ? esc_textarea($this->options['pdf_intent_error_text']) : esc_textarea($default_text) ); echo '

' . esc_html__('Text displayed when an error occurs during the intent.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_pdf_max_pages_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $max_pages = isset($this->options['pdf_max_pages']) ? intval($this->options['pdf_max_pages']) : 69; echo '
'; echo sprintf( '', esc_attr($max_pages), esc_attr($disabled) ); echo '' . esc_html($max_pages) . ''; echo '

' . esc_html__('Set the maximum number of document pages users can upload for processing. (1-69 pages)', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo ''; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo ''; echo '
'; } echo '
'; } public function mxchat_live_agent_status_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $status = isset($this->options['live_agent_status']) ? $this->options['live_agent_status'] : 'off'; echo '
'; echo ''; echo ''; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } // Away Message Callback public function mxchat_live_agent_away_message_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $message = isset($this->options['live_agent_away_message']) ? $this->options['live_agent_away_message'] : __('Sorry, live agents are currently unavailable. I can continue helping you as an AI assistant.', 'mxchat'); echo '
'; printf( '', esc_attr($disabled), esc_textarea($message) ); echo '

' . esc_html__('Message shown when live agents are offline.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function mxchat_live_agent_notification_message_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $message = isset($this->options['live_agent_notification_message']) ? $this->options['live_agent_notification_message'] : __('Live agent has been notified.', 'mxchat'); echo '
'; printf( '', esc_attr($disabled), esc_textarea($message) ); echo '

' . esc_html__('Message shown when live transfer activated.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function mxchat_live_agent_webhook_url_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; $webhook_url = isset($this->options['live_agent_webhook_url']) ? esc_url($this->options['live_agent_webhook_url']) : esc_url(get_option('live_agent_webhook_url', '')); echo '
'; printf( '', $webhook_url, esc_attr($disabled) ); echo ''; echo '

' . esc_html__('Enter your Slack webhook URL for live agent notifications.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function mxchat_similarity_threshold_callback() { // Load from mxchat_options array $options = get_option('mxchat_options', []); // Get value with backwards compatibility $threshold = isset($options['similarity_threshold']) ? $options['similarity_threshold'] : get_option('mxchat_similarity_threshold', 80); echo '
'; echo sprintf( '', esc_attr($threshold) ); echo sprintf( '%s', esc_html($threshold) ); echo '
'; echo '

'; echo esc_html__('Set the similarity threshold (recommended: 75) to balance accuracy; too high might limit your bot\'s ability to find relevant knowledge.', 'mxchat'); echo '

'; } public function mxchat_live_agent_secret_key_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; printf( '', isset($this->options['live_agent_secret_key']) ? esc_attr($this->options['live_agent_secret_key']) : '', esc_attr($disabled) ); echo ''; echo '

' . esc_html__('Secret key for validating Slack requests. Keep this secure.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function mxchat_live_agent_bot_token_callback() { $disabled = $this->is_activated ? '' : 'disabled'; $class = $this->is_activated ? 'pro-feature-wrapper active' : 'pro-feature-wrapper inactive'; echo '
'; printf( '', isset($this->options['live_agent_bot_token']) ? esc_attr($this->options['live_agent_bot_token']) : '', esc_attr($disabled) ); echo ''; echo '

' . esc_html__('Your Slack Bot OAuth Token (starts with xoxb-). Keep this secure.', 'mxchat') . '

'; if (!$this->is_activated) { echo '
'; echo '' . esc_attr__('Pro Only', 'mxchat') . ''; echo '
'; } echo '
'; } public function mxchat_enqueue_admin_assets() { wp_enqueue_style('wp-color-picker'); // Get the plugin version or file modification time for cache busting $plugin_version = '2.0.5'; // Replace this with your plugin's version // File paths $color_picker_js_path = plugin_dir_path(__FILE__) . '../js/my-color-picker.js'; $embedding_check_js_path = plugin_dir_path(__FILE__) . '../js/embedding-check.js'; $admin_css_path = plugin_dir_path(__FILE__) . '../css/admin-style.css'; $knowledge_css_path = plugin_dir_path(__FILE__) . '../css/knowledge-style.css'; $intent_css_path = plugin_dir_path(__FILE__) . '../css/intent-style.css'; // Check if files exist and get modification times $color_picker_version = file_exists($color_picker_js_path) ? filemtime($color_picker_js_path) : $plugin_version; $embedding_check_version = file_exists($embedding_check_js_path) ? filemtime($embedding_check_js_path) : $plugin_version; $admin_css_version = file_exists($admin_css_path) ? filemtime($admin_css_path) : $plugin_version; $knowledge_css_version = file_exists($knowledge_css_path) ? filemtime($knowledge_css_path) : $plugin_version; $intent_css_version = file_exists($intent_css_path) ? filemtime($intent_css_path) : $plugin_version; // Enqueue scripts and styles with corrected paths wp_enqueue_script( 'mxchat-color-picker', plugin_dir_url(__FILE__) . '../js/my-color-picker.js', array('wp-color-picker'), $color_picker_version, true ); wp_enqueue_script( 'mxchat-embedding-check', plugin_dir_url(__FILE__) . '../js/embedding-check.js', array(), $embedding_check_version, true ); wp_enqueue_script( 'mxchat-admin-js', plugin_dir_url(__FILE__) . '../js/mxchat-admin.js', array('jquery'), $plugin_version, true ); wp_localize_script('mxchat-admin-js', 'mxchatAdmin', array( 'ajax_url' => admin_url('admin-ajax.php'), 'license_nonce' => wp_create_nonce('mxchat_activate_license_nonce'), 'inline_edit_nonce' => wp_create_nonce('mxchat_save_inline_nonce'), 'setting_nonce' => wp_create_nonce('mxchat_save_setting_nonce'), 'export_nonce' => wp_create_nonce('mxchat_export_transcripts'), )); // Enqueue the admin CSS wp_enqueue_style( 'mxchat-admin-css', plugin_dir_url(__FILE__) . '../css/admin-style.css', array(), $admin_css_version ); if (isset($_GET['page']) && $_GET['page'] === 'mxchat-transcripts') { wp_enqueue_style( 'mxchat-chat-transcripts-css', plugin_dir_url(__FILE__) . '../css/chat-transcripts.css', array(), $transcripts_css_version ); wp_enqueue_script( 'mxchat-transcripts-js', plugin_dir_url(__FILE__) . '../js/mxchat_transcripts.js', array('jquery'), $transcripts_js_version, true ); } wp_enqueue_style( 'mxchat-knowledge-css', plugin_dir_url(__FILE__) . '../css/knowledge-style.css', array(), $knowledge_css_version ); wp_enqueue_style( 'mxchat-intent-css', plugin_dir_url(__FILE__) . '../css/intent-style.css', array(), $intent_css_version ); // IMPORTANT: Use the same script handle as above for localizing mxchatPromptsAdmin wp_localize_script( 'mxchat-admin-js', 'mxchatPromptsAdmin', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'prompts_setting_nonce' => wp_create_nonce( 'mxchat_prompts_setting_nonce' ), ) ); // Localize the script for color picker and settings wp_localize_script('mxchat-color-picker', 'mxchatStyleSettings', array( 'ajax_url' => admin_url('admin-ajax.php'), 'link_target_toggle' => $this->options['link_target_toggle'] ?? 'off', 'user_message_bg_color' => $this->options['user_message_bg_color'] ?? '#fff', 'user_message_font_color' => $this->options['user_message_font_color'] ?? '#212121', 'bot_message_bg_color' => $this->options['bot_message_bg_color'] ?? '#212121', 'bot_message_font_color' => $this->options['bot_message_font_color'] ?? '#fff', 'top_bar_bg_color' => $this->options['top_bar_bg_color'] ?? '#212121', 'send_button_font_color' => $this->options['send_button_font_color'] ?? '#212121', 'close_button_color' => $this->options['close_button_color'] ?? '#fff', 'chatbot_background_color' => $this->options['chatbot_background_color'] ?? '#212121', 'icon_color' => $this->options['icon_color'] ?? '#fff', 'chat_input_font_color' => $this->options['chat_input_font_color'] ?? '#212121', 'pre_chat_message' => $this->options['pre_chat_message'] ?? esc_html__('Hey there! Ask me anything!', 'mxchat'), 'rate_limit_message' => $this->options['rate_limit_message'] ?? esc_html__('Rate limit exceeded. Please try again later.', 'mxchat'), 'loops_api_key' => $this->options['loops_api_key'] ?? '', 'loops_mailing_list' => $this->options['loops_mailing_list'] ?? '', 'triggered_phrase_response' => $this->options['triggered_phrase_response'] ?? esc_html__('Would you like to join our mailing list? Please provide your email below.', 'mxchat'), 'email_capture_response' => $this->options['email_capture_response'] ?? esc_html__('Thank you for providing your email! You\'ve been added to our list.', 'mxchat'), 'pdf_intent_trigger_text' => $this->options['pdf_intent_trigger_text'] ?? esc_html__("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'), 'pdf_intent_success_text' => $this->options['pdf_intent_success_text'] ?? esc_html__("I've processed the PDF. What questions do you have about it?", 'mxchat'), 'pdf_intent_error_text' => $this->options['pdf_intent_error_text'] ?? esc_html__("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'), 'pdf_max_pages' => $this->options['pdf_max_pages'] ?? 69, 'live_agent_webhook_url' => $this->options['live_agent_webhook_url'] ?? '', 'live_agent_secret_key' => $this->options['live_agent_secret_key'] ?? '', 'live_agent_bot_token' => $this->options['live_agent_bot_token'] ?? '', 'live_agent_message_bg_color' => $this->options['live_agent_message_bg_color'] ?? '#ffffff', 'live_agent_message_font_color' => $this->options['live_agent_message_font_color'] ?? '#333333', 'chat_toolbar_toggle' => $this->options['chat_toolbar_toggle'] ?? 'off', 'mode_indicator_bg_color' => $this->options['mode_indicator_bg_color'] ?? '#767676', 'mode_indicator_font_color' => $this->options['mode_indicator_font_color'] ?? '#ffffff', 'toolbar_icon_color' => $this->options['toolbar_icon_color'] ?? '#212121', )); } public function mxchat_sanitize($input) { $new_input = array(); if (isset($input['api_key'])) { $new_input['api_key'] = sanitize_text_field($input['api_key']); } if (isset($input['similarity_threshold'])) { $new_input['similarity_threshold'] = absint($input['similarity_threshold']); // Ensure it's an integer $new_input['similarity_threshold'] = min(max($new_input['similarity_threshold'], 70), 85); // Enforce range } if (isset($input['xai_api_key'])) { $new_input['xai_api_key'] = sanitize_text_field($input['xai_api_key']); } if (isset($input['claude_api_key'])) { $new_input['claude_api_key'] = sanitize_text_field($input['claude_api_key']); } if (isset($input['deepseek_api_key'])) { $new_input['deepseek_api_key'] = sanitize_text_field($input['deepseek_api_key']); } if (isset($input['enable_woocommerce_integration'])) { $new_input['enable_woocommerce_integration'] = $input['enable_woocommerce_integration'] === 'on' ? 'on' : 'off'; } if (isset($input['privacy_toggle'])) { $new_input['privacy_toggle'] = $input['privacy_toggle']; } if (isset($input['complianz_toggle'])) { $new_input['complianz_toggle'] = $input['complianz_toggle']; } // Handle custom privacy text input if (isset($input['privacy_text'])) { // Allow basic HTML for links $new_input['privacy_text'] = wp_kses_post($input['privacy_text']); } if (isset($input['system_prompt_instructions'])) { $new_input['system_prompt_instructions'] = sanitize_textarea_field($input['system_prompt_instructions']); } if (isset($input['mxchat_pro_email'])) { $new_input['mxchat_pro_email'] = sanitize_email($input['mxchat_pro_email']); } if (isset($input['mxchat_activation_key'])) { $new_input['mxchat_activation_key'] = sanitize_text_field($input['mxchat_activation_key']); } if (isset($input['append_to_body'])) { $new_input['append_to_body'] = $input['append_to_body'] === 'on' ? 'on' : 'off'; } if (isset($input['top_bar_title'])) { $new_input['top_bar_title'] = sanitize_text_field($input['top_bar_title']); } if (isset($input['enable_email_block'])) { $new_input['enable_email_block'] = sanitize_text_field($input['enable_email_block']); } if (isset($input['email_blocker_header_content'])) { // wp_kses_post() allows standard HTML tags permitted by WordPress $new_input['email_blocker_header_content'] = wp_kses_post($input['email_blocker_header_content']); } if (isset($input['email_blocker_button_text'])) { $new_input['email_blocker_button_text'] = sanitize_text_field($input['email_blocker_button_text']); } if (isset($input['intro_message'])) { $new_input['intro_message'] = sanitize_textarea_field($input['intro_message']); // Changed from sanitize_text_field } if (isset($input['input_copy'])) { $new_input['input_copy'] = sanitize_text_field($input['input_copy']); } if (isset($input['rate_limit_message'])) { $new_input['rate_limit_message'] = sanitize_text_field($input['rate_limit_message']); } if (isset($input['rate_limit_logged_out'])) { $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); // Add 'unlimited' to allowed values $rate_limit = sanitize_text_field($input['rate_limit_logged_out']); if (in_array($rate_limit, $allowed_limits, true)) { $new_input['rate_limit_logged_out'] = $rate_limit; } else { $new_input['rate_limit_logged_out'] = '10'; // Default for logged-out users } } // Handle role rate limits if (isset($input['role_rate_limits']) && is_array($input['role_rate_limits'])) { $allowed_limits = array('1', '3', '5', '10', '15', '20', '100', 'unlimited'); $new_input['role_rate_limits'] = array(); foreach ($input['role_rate_limits'] as $role_id => $rate_limit) { $rate_limit = sanitize_text_field($rate_limit); if (in_array($rate_limit, $allowed_limits, true)) { $new_input['role_rate_limits'][$role_id] = $rate_limit; } else { $new_input['role_rate_limits'][$role_id] = '100'; // Default } } } if (isset($input['pre_chat_message'])) { $new_input['pre_chat_message'] = sanitize_textarea_field($input['pre_chat_message']); } if (isset($input['model'])) { $allowed_models = array( 'grok-beta', 'grok-2', 'deepseek-chat', 'claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', 'claude-3-haiku-20240307', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo', ); if (in_array($input['model'], $allowed_models)) { $new_input['model'] = sanitize_text_field($input['model']); } } if (isset($input['close_button_color'])) { $new_input['close_button_color'] = sanitize_hex_color($input['close_button_color']); } if (isset($input['chatbot_bg_color'])) { $new_input['chatbot_bg_color'] = sanitize_hex_color($input['chatbot_bg_color']); } if (isset($input['woocommerce_consumer_key'])) { $new_input['woocommerce_consumer_key'] = sanitize_text_field($input['woocommerce_consumer_key']); } if (isset($input['woocommerce_consumer_secret'])) { $new_input['woocommerce_consumer_secret'] = sanitize_text_field($input['woocommerce_consumer_secret']); } if (isset($input['user_message_bg_color'])) { $new_input['user_message_bg_color'] = sanitize_hex_color($input['user_message_bg_color']); } if (isset($input['user_message_font_color'])) { $new_input['user_message_font_color'] = sanitize_hex_color($input['user_message_font_color']); } if (isset($input['bot_message_bg_color'])) { $new_input['bot_message_bg_color'] = sanitize_hex_color($input['bot_message_bg_color']); } if (isset($input['bot_message_font_color'])) { $new_input['bot_message_font_color'] = sanitize_hex_color($input['bot_message_font_color']); } if (isset($input['live_agent_message_bg_color'])) { $new_input['live_agent_message_bg_color'] = sanitize_hex_color($input['live_agent_message_bg_color']); } if (isset($input['live_agent_message_font_color'])) { $new_input['live_agent_message_font_color'] = sanitize_hex_color($input['live_agent_message_font_color']); } if (isset($input['mode_indicator_bg_color'])) { $new_input['mode_indicator_bg_color'] = sanitize_hex_color($input['mode_indicator_bg_color']); } if (isset($input['mode_indicator_font_color'])) { $new_input['mode_indicator_font_color'] = sanitize_hex_color($input['mode_indicator_font_color']); } if (isset($input['toolbar_icon_color'])) { $new_input['toolbar_icon_color'] = sanitize_hex_color($input['toolbar_icon_color']); } if (isset($input['top_bar_bg_color'])) { $new_input['top_bar_bg_color'] = sanitize_hex_color($input['top_bar_bg_color']); } if (isset($input['send_button_font_color'])) { $new_input['send_button_font_color'] = sanitize_hex_color($input['send_button_font_color']); } if (isset($input['chatbot_background_color'])) { $new_input['chatbot_background_color'] = sanitize_hex_color($input['chatbot_background_color']); } if (isset($input['icon_color'])) { $new_input['icon_color'] = sanitize_hex_color($input['icon_color']); } if (isset($input['custom_icon'])) { $new_input['custom_icon'] = esc_url_raw($input['custom_icon']); } if (isset($input['title_icon'])) { $new_input['title_icon'] = esc_url_raw($input['title_icon']); } if (isset($input['chat_input_font_color'])) { $new_input['chat_input_font_color'] = sanitize_hex_color($input['chat_input_font_color']); } // Sanitize link_target_toggle if (isset($input['link_target_toggle'])) { $new_input['link_target_toggle'] = $input['link_target_toggle'] === 'on' ? 'on' : 'off'; } // Sanitize Loops API Key if (isset($input['loops_api_key'])) { $new_input['loops_api_key'] = sanitize_text_field($input['loops_api_key']); } if (isset($input['chat_persistence_toggle'])) { $new_input['chat_persistence_toggle'] = $input['chat_persistence_toggle'] === 'on' ? 'on' : 'off'; } if (isset($input['popular_question_1'])) { $new_input['popular_question_1'] = sanitize_text_field($input['popular_question_1']); } if (isset($input['popular_question_2'])) { $new_input['popular_question_2'] = sanitize_text_field($input['popular_question_2']); } if (isset($input['popular_question_3'])) { $new_input['popular_question_3'] = sanitize_text_field($input['popular_question_3']); } if (isset($input['additional_popular_questions']) && is_array($input['additional_popular_questions'])) { $new_input['additional_popular_questions'] = array_map('sanitize_text_field', $input['additional_popular_questions']); } // Sanitize Loops Mailing List if (isset($input['loops_mailing_list'])) { $new_input['loops_mailing_list'] = sanitize_text_field($input['loops_mailing_list']); } // Sanitize Triggered Phrase Response if (isset($input['triggered_phrase_response'])) { $new_input['triggered_phrase_response'] = wp_kses_post($input['triggered_phrase_response']); } if (isset($input['email_capture_response'])) { $new_input['email_capture_response'] = sanitize_textarea_field($input['email_capture_response']); } // Sanitize Brave Search Settings if (isset($input['brave_api_key'])) { $new_input['brave_api_key'] = sanitize_text_field($input['brave_api_key']); } if (isset($input['brave_image_count'])) { $image_count = intval($input['brave_image_count']); $new_input['brave_image_count'] = ($image_count >=1 && $image_count <=6) ? $image_count : 4; } if (isset($input['brave_safe_search'])) { $allowed = array('strict', 'off'); $new_input['brave_safe_search'] = in_array($input['brave_safe_search'], $allowed, true) ? $input['brave_safe_search'] : 'strict'; } if (isset($input['brave_news_count'])) { $news_count = intval($input['brave_news_count']); $new_input['brave_news_count'] = ($news_count >=1 && $news_count <=10) ? $news_count : 3; } if (isset($input['brave_country'])) { $new_input['brave_country'] = sanitize_text_field($input['brave_country']); } if (isset($input['brave_language'])) { $new_input['brave_language'] = sanitize_text_field($input['brave_language']); } if (isset($input['chat_toolbar_toggle'])) { $new_input['chat_toolbar_toggle'] = $input['chat_toolbar_toggle'] === 'on' ? 'on' : 'off'; } if (isset($input['pdf_intent_trigger_text'])) { $new_input['pdf_intent_trigger_text'] = sanitize_text_field($input['pdf_intent_trigger_text']); } if (isset($input['pdf_intent_success_text'])) { $new_input['pdf_intent_success_text'] = sanitize_text_field($input['pdf_intent_success_text']); } if (isset($input['pdf_intent_error_text'])) { $new_input['pdf_intent_error_text'] = sanitize_text_field($input['pdf_intent_error_text']); } if (isset($input['pdf_max_pages'])) { $new_input['pdf_max_pages'] = intval($input['pdf_max_pages']); if ($new_input['pdf_max_pages'] < 1 || $new_input['pdf_max_pages'] > 69) { $new_input['pdf_max_pages'] = 69; // Default to 69 if out of range } } if (isset($input['live_agent_webhook_url'])) { $new_input['live_agent_webhook_url'] = esc_url_raw($input['live_agent_webhook_url']); } if (isset($input['live_agent_secret_key'])) { $new_input['live_agent_secret_key'] = sanitize_text_field($input['live_agent_secret_key']); } // Live Agent Integration if (isset($input['live_agent_bot_token'])) { $new_input['live_agent_bot_token'] = sanitize_text_field($input['live_agent_bot_token']); } if (isset($input['live_agent_status'])) { $new_input['live_agent_status'] = ($input['live_agent_status'] === 'on') ? 'on' : 'off'; } if (isset($input['live_agent_away_message'])) { $new_input['live_agent_away_message'] = sanitize_textarea_field($input['live_agent_away_message']); } if (isset($input['live_agent_notification_message'])) { $new_input['live_agent_notification_message'] = sanitize_textarea_field($input['live_agent_notification_message']); } return $new_input; } // Method to append the chatbot to the body public function mxchat_append_chatbot_to_body() { $options = get_option('mxchat_options'); if (isset($options['append_to_body']) && $options['append_to_body'] === 'on') { echo do_shortcode('[mxchat_chatbot floating="yes"]'); } } private static function mxchat_extract_main_content($html) { if (empty($html)) { //error_log('mxchat_extract_main_content: Empty HTML content received'); return ''; } try { $dom = new DOMDocument; libxml_use_internal_errors(true); // Suppress HTML parsing errors // Simple load of HTML, with @ to suppress warnings @$dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); $xpath = new DOMXPath($dom); // Simplified selectors focusing on common content areas $selectors = [ '//article', '//*[@id="content"]', '//*[@class="entry-content"]', '//main' ]; foreach ($selectors as $selector) { $nodes = $xpath->query($selector); if ($nodes && $nodes->length > 0) { $content = ''; foreach ($nodes as $node) { $content .= $dom->saveHTML($node); } if (!empty($content)) { return $content; } } } // Fallback: Return the entire body content if no specific selector matches $body = $dom->getElementsByTagName('body'); if ($body->length > 0) { return $dom->saveHTML($body->item(0)); } // Last resort: return the original HTML return $html; } catch (Exception $e) { //error_log('mxchat_extract_main_content error: ' . $e->getMessage()); return $html; // Return original HTML if parsing fails } finally { libxml_clear_errors(); } } private function mxchat_fetch_loops_mailing_lists($api_key) { $url = 'https://app.loops.so/api/v1/lists'; $response = wp_remote_get($url, array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Content-Type' => 'application/json' ) )); if (is_wp_error($response)) { return array(); } $body = wp_remote_retrieve_body($response); $lists = json_decode($body, true); return isset($lists) && is_array($lists) ? $lists : array(); } function mxchat_calculate_cosine_similarity($vec1, $vec2) { if (empty($vec1) || empty($vec2)) { return 0.0; } $dot_product = 0.0; $norm_a = 0.0; $norm_b = 0.0; for ($i = 0; $i < count($vec1); $i++) { $dot_product += $vec1[$i] * $vec2[$i]; $norm_a += pow($vec1[$i], 2); $norm_b += pow($vec2[$i], 2); } if ($norm_a == 0.0 || $norm_b == 0.0) { return 0.0; } else { return $dot_product / (sqrt($norm_a) * sqrt($norm_b)); } } } ?>