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('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
add_action('admin_post_mxchat_stop_processing', array($this, 'mxchat_stop_processing'));
add_action('admin_post_mxchat_edit_intent', array($this, 'mxchat_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'));
add_action('admin_init', array($this, 'mxchat_transcripts_page_init'));
add_action('wp_ajax_mxchat_check_license_status', array($this, 'mxchat_check_license_status'));
add_action('wp_ajax_mxchat_get_content_list', array($this, 'ajax_mxchat_get_content_list'));
add_action('wp_ajax_mxchat_process_selected_content', array($this, 'ajax_mxchat_process_selected_content'));
add_action('wp_ajax_mxchat_migrate_pinecone_settings', array($this, 'ajax_migrate_pinecone_settings'));
add_action('admin_init', array($this, 'register_pinecone_settings'));
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'));
}
add_action('wp_ajax_mxchat_get_status_updates', array($this, 'ajax_get_status_updates'));
add_action('admin_notices', array($this, 'display_admin_notices'));
}
private function is_license_active() {
// Get the raw value without translation
$license_status = get_option('mxchat_license_status', 'inactive');
// Check against multiple possible values, bypassing translation issues
return ($license_status === 'active' || $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' => '',
'voyage_api_key' => '',
'gemini_api_key' => '',
'embedding_model' => 'text-embedding-ada-002',
'system_prompt_instructions' => 'You are an AI Chatbot assistant for this website. Your main goal is to assist visitors with questions and provide helpful information. Here are your key guidelines:
# Response Style - CRITICALLY IMPORTANT
- MAXIMUM LENGTH: 1-3 short sentences per response
- Ultra-concise: Get straight to the answer with no filler
- No introductions like "Sure!" or "I\'d be happy to help"
- No phrases like "based on my knowledge" or "according to information"
- No explanatory text before giving the answer
- No summaries or repetition
- Hyperlink all URLs
- Respond in user\'s language
- Minor chit chat or conversation is okay, but try to keep it focused on [insert topic]
# Knowledge Base Requirements - PREVENT HALLUCINATIONS
- ONLY answer using information explicitly provided in OFFICIAL KNOWLEDGE DATABASE CONTENT sections marked with ===== delimiters
- If required information is NOT in the knowledge database: "I don\'t have enough information in my knowledge base to answer that question accurately."
- NEVER invent or hallucinate URLs, links, product specs, procedures, dates, statistics, names, contacts, or company information
- When knowledge base information is unclear or contradictory, acknowledge the limitation rather than guessing
- If asked about something not in knowledge base, explicitly state information is not available - DO NOT provide general information
- Better to admit insufficient information than provide inaccurate answers',
'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' => __("
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' => __('Hello! How can I assist you today?', 'mxchat'),
'ai_agent_text' => esc_html__('AI Agent', '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' => __('Would you like to join our mailing list? Please provide your email below.', 'mxchat'),
'email_capture_response' => __('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' => __("Please provide the URL to the PDF you'd like to discuss.", 'mxchat'),
'pdf_intent_success_text' => __("I've processed the PDF. What questions do you have about it?", 'mxchat'),
'pdf_intent_error_text' => __("Sorry, I couldn't process the PDF. Please ensure it's a valid file.", 'mxchat'),
'pdf_max_pages' => 69,
'show_pdf_upload_button' => 'on',
'show_word_upload_button' => 'on',
// 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 Actions', 'mxchat'),
esc_html__('Actions', 'mxchat'),
'manage_options',
'mxchat-actions',
array($this, 'mxchat_actions_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')) {
('MXChat Save: Unauthorized access attempt');
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']) : '';
//error_log('MXChat Save: Processing field name: ' . $name);
//error_log('MXChat Save: Field value: ' . $value);
if (empty($name)) {
//error_log('MXChat Save: Empty field name detected');
wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
}
// Load the full options array
$options = get_option('mxchat_options', []);
//error_log('MXChat Save: Current options array: ' . print_r($options, true));
// Handle special cases
switch ($name) {
case 'additional_popular_questions':
//error_log('MXChat Save: Processing 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);
//error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
} else {
//error_log('MXChat Save: Failed to decode questions JSON');
}
break;
case 'email_blocker_header_content':
//error_log('MXChat Save: Processing email_blocker_header_content');
// Allow HTML content but sanitize it safely
$options[$name] = wp_kses_post($value);
break;
case 'similarity_threshold':
//error_log('MXChat Save: Processing similarity_threshold');
// Save to the options array
$options[$name] = $value;
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':
//error_log('MXChat Save: Processing color value: ' . $name);
// Store color values directly
$options[$name] = $value;
break;
case 'live_agent_status':
//error_log('MXChat Save: Processing live_agent_status');
// Set the new value
$options[$name] = ($value === 'on') ? 'on' : 'off';
break;
case 'enable_woocommerce_integration':
//error_log('MXChat Save: Processing enable_woocommerce_integration');
// Handle values that used to be 1/0
$options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
break;
default:
// First check for rate limits settings
if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
//error_log('MXChat Save: Detected rate_limits field: ' . $name);
// Extract role ID and setting from the name
preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
//error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
if (isset($matches[1]) && isset($matches[2])) {
$role_id = $matches[1];
$setting_key = $matches[2]; // limit, timeframe, or message
//error_log('MXChat Save: Role ID = ' . $role_id . ', Setting Key = ' . $setting_key);
// Initialize rate_limits if it doesn't exist
if (!isset($options['rate_limits'])) {
// //error_log('MXChat Save: Initializing rate_limits array');
$options['rate_limits'] = [];
}
// Initialize role settings if it doesn't exist
if (!isset($options['rate_limits'][$role_id])) {
//error_log('MXChat Save: Initializing rate_limits for role: ' . $role_id);
$options['rate_limits'][$role_id] = [
'limit' => ($role_id === 'logged_out') ? '10' : '100',
'timeframe' => 'daily',
'message' => 'Rate limit exceeded. Please try again later.'
];
}
// Update the specific setting
$options['rate_limits'][$role_id][$setting_key] = $value;
//error_log('MXChat Save: Updated rate_limits[' . $role_id . '][' . $setting_key . '] = ' . $value);
} else {
//error_log('MXChat Save: Failed to parse rate_limits pattern: ' . $name);
}
}
// Then check for role rate limits (old format)
else if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
//error_log('MXChat Save: Processing role_rate_limits field: ' . $name);
// Extract role ID from the name
preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
//error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
if (isset($matches[1])) {
$role_id = $matches[1];
// Initialize role_rate_limits if it doesn't exist
if (!isset($options['role_rate_limits'])) {
//error_log('MXChat Save: Initializing role_rate_limits array');
$options['role_rate_limits'] = [];
}
// Update the specific role's rate limit
$options['role_rate_limits'][$role_id] = sanitize_text_field($value);
//error_log('MXChat Save: Updated role_rate_limits[' . $role_id . '] = ' . $value);
} else {
//error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
}
}
// Handle toggles
else if (strpos($name, 'toggle') !== false || in_array($name, [
'chat_persistence_toggle',
'privacy_toggle',
'complianz_toggle',
'chat_toolbar_toggle',
'show_pdf_upload_button',
'show_word_upload_button'
])) {
//error_log('MXChat Save: Processing toggle: ' . $name);
$options[$name] = ($value === 'on') ? 'on' : 'off';
} else {
//error_log('MXChat Save: Processing standard field: ' . $name);
// Store all other values directly
$options[$name] = $value;
}
break;
}
// Save all updates to the options array
$updated = update_option('mxchat_options', $options);
//error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
//error_log('MXChat Save: Updated options array: ' . print_r($options, true));
// Always return success even if WordPress says nothing changed
// (which happens when the value is the same as before)
wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
}
/**
* Helper function to compare if a value has changed
* Handles various data types appropriately
*/
private function has_value_changed($old_value, $new_value) {
// Handle null values
if ($old_value === null && $new_value === '') {
return false;
}
// Handle array values (like additional_popular_questions)
if (is_array($old_value) && is_array($new_value)) {
// Convert both to JSON for comparison to handle ordering differences
return json_encode($old_value) !== json_encode($new_value);
}
// Handle toggle/checkbox values consistently
if (in_array($old_value, ['on', '1', 1, true]) && in_array($new_value, ['on', '1', 1, true])) {
return false;
}
if (in_array($old_value, ['off', '0', 0, false, '']) && in_array($new_value, ['off', '0', 0, false, ''])) {
return false;
}
// Default direct comparison
return $old_value !== $new_value;
}
/**
* AJAX handler for migrating Pinecone settings from old add-on
*/
public function ajax_migrate_pinecone_settings() {
// Verify nonce
if (!wp_verify_nonce($_POST['_ajax_nonce'] ?? '', 'mxchat_save_setting_nonce')) {
wp_send_json_error('Invalid nonce');
}
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Unauthorized access');
}
// Check if old Pinecone addon options exist
$old_options = get_option('mxchat_pinecone_addon_options', array());
if (empty($old_options)) {
wp_send_json_success(array('migrated' => false, 'message' => 'No old settings found'));
}
// Get current core plugin options
$current_options = get_option('mxchat_pinecone_addon_options', array());
// Only migrate if core options are empty or if explicitly requested
$should_migrate = empty($current_options) ||
(empty($current_options['mxchat_pinecone_api_key']) && !empty($old_options['mxchat_pinecone_api_key']));
if ($should_migrate) {
// Migrate settings with proper sanitization
$migrated_options = array(
'mxchat_use_pinecone' => $old_options['mxchat_use_pinecone'] ?? '0',
'mxchat_pinecone_api_key' => sanitize_text_field($old_options['mxchat_pinecone_api_key'] ?? ''),
'mxchat_pinecone_host' => sanitize_text_field($old_options['mxchat_pinecone_host'] ?? ''),
'mxchat_pinecone_index' => sanitize_text_field($old_options['mxchat_pinecone_index'] ?? ''),
'mxchat_pinecone_environment' => sanitize_text_field($old_options['mxchat_pinecone_environment'] ?? '')
);
update_option('mxchat_pinecone_addon_options', $migrated_options);
wp_send_json_success(array(
'migrated' => true,
'message' => 'Settings migrated successfully from Pinecone add-on'
));
} else {
wp_send_json_success(array(
'migrated' => false,
'message' => 'Settings already exist in core plugin'
));
}
}
/**
* Fixed AJAX handler that bypasses the problematic sanitization
*/
/**
* Fixed AJAX handler with improved verification
*/
public function mxchat_save_prompts_setting_callback() {
check_ajax_referer('mxchat_prompts_setting_nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
}
$name = isset($_POST['name']) ? $_POST['name'] : '';
$value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
error_log('[MXCHAT-PROMPTS] Saving setting: ' . $name . ' = ' . $value);
if (empty($name)) {
wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
}
// Handle Pinecone settings - BYPASS WORDPRESS SANITIZATION
if (strpos($name, 'mxchat_pinecone_addon_options') !== false) {
error_log('[MXCHAT-PROMPTS] Processing Pinecone setting: ' . $name);
// Extract the field name
if (preg_match('/mxchat_pinecone_addon_options\[([^\]]+)\]/', $name, $matches)) {
$field_name = $matches[1];
error_log('[MXCHAT-PROMPTS] Extracted field name: ' . $field_name);
// Get current options directly from database - NO WordPress filters
global $wpdb;
$current_options_raw = $wpdb->get_var(
$wpdb->prepare(
"SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
'mxchat_pinecone_addon_options'
)
);
// Unserialize the raw data
$current_options = maybe_unserialize($current_options_raw);
if (!is_array($current_options)) {
$current_options = array();
}
error_log('[MXCHAT-PROMPTS] Current options from DB: ' . print_r($current_options, true));
// Update the specific field with proper sanitization
switch ($field_name) {
case 'mxchat_use_pinecone':
$new_value = ($value === '1') ? '1' : '0';
break;
case 'mxchat_pinecone_api_key':
case 'mxchat_pinecone_host':
case 'mxchat_pinecone_index':
case 'mxchat_pinecone_environment':
$new_value = sanitize_text_field($value);
if ($field_name === 'mxchat_pinecone_host') {
$new_value = str_replace(['https://', 'http://'], '', $new_value);
}
break;
default:
wp_send_json_error(['message' => esc_html__('Unknown Pinecone field', 'mxchat')]);
}
$current_options[$field_name] = $new_value;
error_log('[MXCHAT-PROMPTS] New value for ' . $field_name . ': "' . $new_value . '"');
error_log('[MXCHAT-PROMPTS] Updated options: ' . print_r($current_options, true));
// Save directly to database to bypass WordPress sanitization
$serialized_options = maybe_serialize($current_options);
$save_result = $wpdb->update(
$wpdb->options,
array('option_value' => $serialized_options),
array('option_name' => 'mxchat_pinecone_addon_options'),
array('%s'),
array('%s')
);
error_log('[MXCHAT-PROMPTS] Direct DB save result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
// Clear any WordPress option cache to ensure get_option() returns fresh data
wp_cache_delete('mxchat_pinecone_addon_options', 'options');
// IMPROVED VERIFICATION - Check if the database operation succeeded
if ($save_result !== false) {
// Double-check by reading fresh from database
$verification_raw = $wpdb->get_var(
$wpdb->prepare(
"SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
'mxchat_pinecone_addon_options'
)
);
$verification_options = maybe_unserialize($verification_raw);
$verified_value = isset($verification_options[$field_name]) ? $verification_options[$field_name] : 'NOT_FOUND';
error_log('[MXCHAT-PROMPTS] Final verification - Expected: "' . $new_value . '", Got: "' . $verified_value . '"');
// Use loose comparison (==) instead of strict (===) to avoid type issues
if ($verified_value == $new_value || $save_result > 0) {
wp_send_json_success(['message' => esc_html__('Pinecone setting saved', 'mxchat')]);
} else {
// Still return success if the DB operation worked, even if verification is quirky
error_log('[MXCHAT-PROMPTS] Verification mismatch but DB operation succeeded');
wp_send_json_success(['message' => esc_html__('Pinecone setting saved (DB success)', 'mxchat')]);
}
} else {
wp_send_json_error(['message' => esc_html__('Database save failed', 'mxchat')]);
}
} else {
wp_send_json_error(['message' => esc_html__('Invalid field name format', 'mxchat')]);
}
return; // Exit here for Pinecone settings
}
// Handle auto-sync settings (existing functionality)
if (strpos($name, 'mxchat_auto_sync_') === 0) {
$value = ($value === 'on' || $value === '1') ? '1' : '0';
$updated = update_option($name, $value);
if ($updated || get_option($name) === $value) {
wp_send_json_success(['message' => esc_html__('Auto-sync setting saved', 'mxchat')]);
} else {
wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
}
}
// Handle other prompts options
$options = get_option('mxchat_prompts_options', []);
$options[$name] = $value;
$updated = update_option('mxchat_prompts_options', $options);
if ($updated) {
wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
} else {
wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
}
}
/**
* Register Pinecone settings (add this to your admin_init hook)
*/
public function register_pinecone_settings() {
register_setting(
'mxchat_pinecone_addon_options',
'mxchat_pinecone_addon_options',
array(
'type' => 'array',
'sanitize_callback' => array($this, 'sanitize_pinecone_settings'),
'default' => array(
'mxchat_use_pinecone' => '0',
'mxchat_pinecone_api_key' => '',
'mxchat_pinecone_host' => '',
'mxchat_pinecone_index' => '',
'mxchat_pinecone_environment' => ''
)
)
);
}
/**
* Sanitize Pinecone settings
*/
public function sanitize_pinecone_settings($input) {
$sanitized = array();
$sanitized['mxchat_use_pinecone'] = isset($input['mxchat_use_pinecone']) ? '1' : '0';
$sanitized['mxchat_pinecone_api_key'] = sanitize_text_field($input['mxchat_pinecone_api_key'] ?? '');
$sanitized['mxchat_pinecone_host'] = sanitize_text_field($input['mxchat_pinecone_host'] ?? '');
$sanitized['mxchat_pinecone_index'] = sanitize_text_field($input['mxchat_pinecone_index'] ?? '');
$sanitized['mxchat_pinecone_environment'] = sanitize_text_field($input['mxchat_pinecone_environment'] ?? '');
// Remove https:// from host if present
$sanitized['mxchat_pinecone_host'] = str_replace(['https://', 'http://'], '', $sanitized['mxchat_pinecone_host']);
return $sanitized;
}
public function mxchat_display_admin_notice() {
// Success notice
if ($message = get_transient('mxchat_admin_notice_success')) {
?>
MxChat Settings
is_activated): ?>
🚀 Limited Lifetime Offer: Save 30% on MxChat Pro, Agency, or Agency Plus!
Unlock unlimited access to our growing collection of powerful add-ons including Admin AI Assistant (ChatGPT-like experience in your admin panel), Forms Builder, Theme Customizer, WooCommerce, Perplexity, and more – all included with your lifetime license!
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
💬
📝
👥
' . esc_html__('Configure email notifications for new chat transcripts. You will receive an email notification when a new chat session begins.', 'mxchat') . '';
}
public function mxchat_enable_notifications_callback() {
$options = get_option('mxchat_transcripts_options', array());
$enabled = isset($options['mxchat_enable_notifications']) ? $options['mxchat_enable_notifications'] : 0;
?>
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'));
}
// Get pagination parameters
$page = isset($_POST['page']) ? absint($_POST['page']) : 1;
$per_page = isset($_POST['per_page']) ? absint($_POST['per_page']) : 50;
$offset = ($page - 1) * $per_page;
// Get search parameter if any
$search = isset($_POST['search']) ? sanitize_text_field($_POST['search']) : '';
// Build the query based on whether we have a search term
$search_condition = '';
$search_params = array();
if (!empty($search)) {
$search_condition = "WHERE (
session_id LIKE %s
OR user_email LIKE %s
OR user_identifier LIKE %s
OR message LIKE %s
)";
$search_params = array(
'%' . $wpdb->esc_like($search) . '%',
'%' . $wpdb->esc_like($search) . '%',
'%' . $wpdb->esc_like($search) . '%',
'%' . $wpdb->esc_like($search) . '%'
);
}
// First count total sessions for pagination
if (!empty($search)) {
$count_query = $wpdb->prepare(
"SELECT COUNT(DISTINCT session_id)
FROM {$table_name}
{$search_condition}",
$search_params
);
} else {
$count_query = "SELECT COUNT(DISTINCT session_id) FROM {$table_name}";
}
$total_sessions = $wpdb->get_var($count_query);
// Get paginated session IDs ordered by most recent message in each session
if (!empty($search)) {
$session_query = $wpdb->prepare(
"SELECT DISTINCT t.session_id
FROM {$table_name} t
{$search_condition}
GROUP BY t.session_id
ORDER BY MAX(t.timestamp) DESC
LIMIT %d OFFSET %d",
array_merge($search_params, array($per_page, $offset))
);
} else {
$session_query = $wpdb->prepare(
"SELECT DISTINCT session_id
FROM {$table_name}
GROUP BY session_id
ORDER BY MAX(timestamp) DESC
LIMIT %d OFFSET %d",
$per_page, $offset
);
}
$session_ids = $wpdb->get_col($session_query);
if (empty($session_ids)) {
ob_start();
echo '
';
echo esc_html__('No chat history found.', 'mxchat');
if (!empty($search)) {
echo ' ' . esc_html__('Try adjusting your search criteria.', 'mxchat');
}
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
)
);
// 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 '
Pinecone is optional and not required for MxChat to function. It provides enhanced search performance for larger knowledge bases. When enabled, content will be stored in Pinecone instead of the WordPress database, but you can use MxChat without it.', 'mxchat'),
array('strong' => array())
); ?>