File: /var/www/html/www.winghung.com/wp-content/plugins/mxchat-basic/admin/class-ajax-handler.php
<?php
/**
* File: admin/class-ajax-handler.php
*
* Handles all AJAX requests for MxChat admin functionality
*/
if (!defined('ABSPATH')) {
exit; // Exit if accessed directly
}
class MxChat_Ajax_Handler {
private $pinecone_manager = null;
/**
* Constructor - Register all AJAX hooks
*/
public function __construct() {
$this->mxchat_init_ajax_hooks();
}
/**
* Register all AJAX action hooks
*/
private function mxchat_init_ajax_hooks() {
// Settings AJAX
add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
add_action('wp_ajax_migrate_pinecone_settings', array($this, 'ajax_migrate_pinecone_settings'));
// License AJAX
add_action('wp_ajax_mxchat_handle_activate_license', array($this, 'mxchat_handle_activate_license'));
add_action('wp_ajax_mxchat_check_license_status', array($this, 'mxchat_check_license_status'));
add_action('wp_ajax_mxchat_deactivate_license', array($this, 'mxchat_deactivate_license'));
// Actions & Intents AJAX
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('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
}
// ========================================
// SETTINGS AJAX HANDLERS
// ========================================
/**
* Validates and saves chat settings via AJAX request
*/
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 'model':
//error_log('MXChat Save: Processing model selection');
//error_log('MXChat Save: Model value received: ' . $value);
//error_log('MXChat Save: Value type: ' . gettype($value));
//error_log('MXChat Save: Value length: ' . strlen($value));
//error_log('MXChat Save: Value === "openrouter": ' . ($value === 'openrouter' ? 'YES' : 'NO'));
// Allow 'openrouter' or validate against whitelist
if ($value === 'openrouter') {
//error_log('MXChat Save: Setting model to openrouter');
$options['model'] = 'openrouter';
} else {
//error_log('MXChat Save: Checking against whitelist');
$allowed_models = array(
'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash',
'grok-4-0709', 'grok-3-beta', 'grok-3-fast-beta', 'grok-3-mini-beta',
'grok-3-mini-fast-beta', 'grok-2',
'deepseek-chat',
'claude-sonnet-4-5-20250929', 'claude-opus-4-1-20250805', 'claude-haiku-4-5-20251001',
'claude-opus-4-20250514', 'claude-sonnet-4-20250514', 'claude-3-7-sonnet-20250219',
'claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229',
'claude-3-haiku-20240307',
'gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1-2025-04-14',
'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo',
);
//error_log('MXChat Save: in_array result: ' . (in_array($value, $allowed_models) ? 'YES' : 'NO'));
if (in_array($value, $allowed_models)) {
//error_log('MXChat Save: Model is in whitelist, saving');
$options['model'] = sanitize_text_field($value);
} else {
//error_log('MXChat Save: Invalid model rejected: ' . $value);
//error_log('MXChat Save: Allowed models: ' . print_r($allowed_models, true));
wp_send_json_error(['message' => esc_html__('Invalid model selected', 'mxchat')]);
return;
}
}
break;
case 'openrouter_selected_model':
//error_log('MXChat Save: Processing OpenRouter model: ' . $value);
$options['openrouter_selected_model'] = sanitize_text_field($value);
// Force immediate save for new keys
//error_log('MXChat Save: OpenRouter model saved immediately');
break;
case 'openrouter_selected_model_name':
//error_log('MXChat Save: Processing OpenRouter model name: ' . $value);
$options['openrouter_selected_model_name'] = sanitize_text_field($value);
// Force immediate save for new keys
//error_log('MXChat Save: OpenRouter model name saved immediately');
break;
case 'openrouter_api_key':
//error_log('MXChat Save: Processing OpenRouter API key');
$options['openrouter_api_key'] = sanitize_text_field($value);
break;
// REMOVED DUPLICATE case 'openrouter_selected_model_name' HERE!
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 'email_blocker_button_text':
//error_log('MXChat Save: Processing email_blocker_button_text');
$options[$name] = sanitize_text_field($value);
break;
case 'name_field_placeholder':
//error_log('MXChat Save: Processing name_field_placeholder');
$options[$name] = sanitize_text_field($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':
case 'quick_questions_toggle_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',
'enable_streaming_toggle',
'contextual_awareness_toggle',
'enable_email_block',
'enable_name_field'
])) {
//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')]);
}
/**
* Save the selected bot for knowledge base operations
*/
public function mxchat_save_selected_bot() {
// Check nonce
if (!wp_verify_nonce($_POST['nonce'] ?? '', 'mxchat_save_setting_nonce')) {
wp_send_json_error('Invalid nonce');
}
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
$bot_id = isset($_POST['bot_id']) ? sanitize_key($_POST['bot_id']) : 'default';
// Save as user meta for the current user
$user_id = get_current_user_id();
update_user_meta($user_id, 'mxchat_selected_knowledge_bot', $bot_id);
// Also save as an option for site-wide default
update_option('mxchat_current_knowledge_bot', $bot_id);
// No cache clearing needed since we removed caching
wp_send_json_success(array(
'message' => 'Bot selection saved',
'bot_id' => $bot_id
));
}
/**
* Handles AJAX request for saving chat settings
*/
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'
)
);
// FIX: Handle the case where the option doesn't exist yet
if ($current_options_raw === null) {
// Option doesn't exist, create it with default values
$current_options = array(
'mxchat_use_pinecone' => '0',
'mxchat_pinecone_api_key' => '',
'mxchat_pinecone_host' => '',
'mxchat_pinecone_index' => '',
'mxchat_pinecone_environment' => ''
);
//error_log('[MXCHAT-PROMPTS] Option does not exist, creating with defaults');
} else {
// Unserialize the raw data
$current_options = maybe_unserialize($current_options_raw);
if (!is_array($current_options)) {
// Fallback to defaults if unserialization fails
$current_options = array(
'mxchat_use_pinecone' => '0',
'mxchat_pinecone_api_key' => '',
'mxchat_pinecone_host' => '',
'mxchat_pinecone_index' => '',
'mxchat_pinecone_environment' => ''
);
//error_log('[MXCHAT-PROMPTS] Failed to unserialize, using defaults');
}
}
//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);
// FIX: Use INSERT ... ON DUPLICATE KEY UPDATE or separate INSERT/UPDATE logic
$option_exists = $wpdb->get_var(
$wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
'mxchat_pinecone_addon_options'
)
);
if ($option_exists > 0) {
// Update existing option
$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] Updated existing option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
} else {
// Insert new option
$save_result = $wpdb->insert(
$wpdb->options,
array(
'option_name' => 'mxchat_pinecone_addon_options',
'option_value' => $serialized_options,
'autoload' => 'yes'
),
array('%s', '%s', '%s')
);
//error_log('[MXCHAT-PROMPTS] Inserted new option, 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')]);
}
}
/**
* Handles AJAX request for Pinecone settings migration
*/
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'
));
}
}
// ========================================
// LICENSE AJAX HANDLERS
// ========================================
/**
* Validates and activates chat license via AJAX
*/
public function mxchat_handle_activate_license() {
// Check nonce
if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
return;
}
// Verify user capabilities
if (!current_user_can('manage_options')) {
wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
return;
}
$license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
$customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
if (empty($license_key) || empty($customer_email)) {
wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
return;
}
$product_id = 'MxChatPRO';
$domain = parse_url(home_url(), PHP_URL_HOST); // Get the current domain
// Call WooCommerce Software API for activation (not just validation)
$response = wp_remote_get(
add_query_arg(
array(
'wc-api' => 'software-api',
'request' => 'activation',
'email' => $customer_email,
'license_key' => $license_key,
'product_id' => $product_id,
'instance' => $domain, // THIS IS KEY - include the domain as instance
'platform' => 'wordpress' // Optional but good to include
),
'https://mxchat.ai/'
),
array(
'timeout' => 60,
'sslverify' => true
)
);
if (is_wp_error($response)) {
$error_message = $response->get_error_message();
//error_log('MxChat License Activation Error: ' . $error_message);
wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $error_message);
return;
}
$response_code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
// Log response for debugging
//error_log('MxChat License Response Code: ' . $response_code);
//error_log('MxChat License Response Body: ' . $body);
if ($response_code !== 200) {
wp_send_json_error(esc_html__('Server returned error code: ', 'mxchat') . $response_code);
return;
}
$data = json_decode($body);
if ($data && isset($data->activated) && $data->activated) {
// Success - save local options
update_option('mxchat_license_status', 'active');
update_option('mxchat_pro_email', $customer_email);
update_option('mxchat_activation_key', $license_key);
delete_option('mxchat_license_error');
// Also track on your website (this is your existing domain tracking)
$this->track_domain_on_website($license_key, $customer_email, $domain);
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);
//error_log('MxChat Activation failed: ' . $error_message);
wp_send_json_error($error_message);
}
}
/**
* Track domain on your website (separate from WooCommerce activation)
*/
private function track_domain_on_website($license_key, $email, $domain) {
// This calls your website's tracking API
wp_remote_post('https://mxchat.ai/mxchat-api/activate-license', array(
'body' => array(
'mxchat_pro_email' => $email,
'mxchat_activation_key' => $license_key,
'domain' => $domain
),
'timeout' => 10,
'sslverify' => true
));
}
/**
* Validates license via AJAX with email and key
*/
public function mxchat_check_license_status() {
// Verify nonce
if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
wp_send_json_error('Security check failed');
return;
}
// Add isset checks for safety
$email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
$key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
// Check if this license is actually active in your system
$is_active = (get_option('mxchat_license_status') === 'active' &&
get_option('mxchat_pro_email') === $email &&
get_option('mxchat_activation_key') === $key);
wp_send_json(array(
'is_active' => $is_active
));
}
/**
* Handle license deactivation - Complete version for plugin
*/
function mxchat_deactivate_license() {
// Add debugging
//error_log('MxChat deactivate function called');
// Check nonce
if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
//error_log('MxChat deactivate: Nonce check failed');
wp_send_json_error('Security check failed.');
return;
}
//error_log('MxChat deactivate: Nonce check passed');
$license_key = get_option('mxchat_activation_key');
$email = get_option('mxchat_pro_email');
$domain = parse_url(home_url(), PHP_URL_HOST);
//error_log('MxChat deactivate: License: ' . $license_key . ', Email: ' . $email . ', Domain: ' . $domain);
if (empty($license_key) || empty($email)) {
//error_log('MxChat deactivate: No active license found');
wp_send_json_error('No active license found.');
return;
}
// Clear local license data first
delete_option('mxchat_license_status');
delete_option('mxchat_pro_email');
delete_option('mxchat_activation_key');
delete_option('mxchat_license_error');
//error_log('MxChat deactivate: Local data cleared');
// Notify your website's API to properly deactivate
$response = wp_remote_post('https://mxchat.ai/mxchat-api/deactivate-license', array(
'body' => array(
'license_key' => $license_key,
'email' => $email,
'domain' => $domain
),
'timeout' => 15,
'sslverify' => true
));
if (is_wp_error($response)) {
//error_log('MxChat deactivate: Server error - ' . $response->get_error_message());
wp_send_json_success(array(
'message' => 'License deactivated locally. Server could not be contacted to free activation slot.',
'server_notified' => false
));
return;
}
$response_body = wp_remote_retrieve_body($response);
$response_data = json_decode($response_body, true);
//error_log('MxChat deactivate: Server response - ' . $response_body);
if (isset($response_data['success']) && $response_data['success']) {
//error_log('MxChat deactivate: Success with server notification');
wp_send_json_success(array(
'message' => 'License deactivated successfully. Activation slot has been freed up.',
'server_notified' => true
));
} else {
//error_log('MxChat deactivate: Server responded but deactivation may have failed');
wp_send_json_success(array(
'message' => 'License deactivated locally. Please check your account dashboard to verify the activation was freed.',
'server_notified' => false
));
}
}
// ========================================
// ACTIONS & INTENTS AJAX HANDLERS
// ========================================
/**
* Validates nonce and returns JSON error on failure
*/
public function mxchat_toggle_action() {
// Check nonce
if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
wp_send_json_error(array('message' => 'Security check failed'));
return;
}
// Check permissions
if (!current_user_can('manage_options')) {
wp_send_json_error(array('message' => 'Permission denied'));
return;
}
// Validate params
$intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
$enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;
if (!$intent_id) {
wp_send_json_error(array('message' => 'Invalid action ID'));
return;
}
// Update the intent/action status in the database
global $wpdb;
$table_name = $wpdb->prefix . 'mxchat_intents';
// Using the 'enabled' field - add this field if it doesn't exist
$result = $wpdb->update(
$table_name,
array('enabled' => $enabled ? 1 : 0),
array('id' => $intent_id),
array('%d'),
array('%d')
);
if ($result === false) {
wp_send_json_error(array('message' => 'Database error'));
return;
}
wp_send_json_success();
}
/**
* Validates permissions for AJAX request handling
*/
public function mxchat_update_intent_threshold() {
// Check permissions
if (!current_user_can('manage_options')) {
if (wp_doing_ajax()) {
wp_send_json_error(array('message' => 'Unauthorized user'));
return;
}
wp_die(esc_html__('Unauthorized user', 'mxchat'));
}
// Verify nonce
check_admin_referer('mxchat_update_intent_threshold_nonce');
// Process the update if we have valid data
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;
$result = $wpdb->update(
$table_name,
['similarity_threshold' => $similarity_threshold],
['id' => $intent_id],
['%f'],
['%d']
);
// Handle AJAX requests
if (wp_doing_ajax()) {
if ($result === false) {
wp_send_json_error(array('message' => 'Failed to update threshold'));
} else {
wp_send_json_success(array('threshold' => $threshold_percentage));
}
return;
}
}
// Redirect for regular form submissions
wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
exit;
}
// ========================================
// HELPER METHODS
// ========================================
/**
* Returns a specific nonce action string
*/
private function mxchat_get_nonce_action() {
return 'mxchat_license_nonce';
}
}
// Initialize the AJAX handler
new MxChat_Ajax_Handler();