PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.1.4
MxChat – AI Chatbot & Content Generation for WordPress v3.1.4
3.2.21 3.2.20 3.2.19 3.2.18 3.2.17 3.2.16 3.2.15 3.2.14 3.2.12 3.2.13 3.2.11 3.2.10 3.2.9 3.2.8 3.2.7 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 2.0.3 2.0.4 2.0.5 2.0.6 All 152 releases
mxchat-basic / admin / class-ajax-handler.php

class-ajax-handler.php in MxChat – AI Chatbot & Content Generation for WordPress 3.1.4, at admin/class-ajax-handler.php

1,273 lines 53.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * File: admin/class-ajax-handler.php
4 *
5 * Handles all AJAX requests for MxChat admin functionality
6 */
7
8 if (!defined('ABSPATH')) {
9 exit; // Exit if accessed directly
10 }
11
12 class MxChat_Ajax_Handler {
13
14 private $pinecone_manager = null;
15
16 /**
17 * Constructor - Register all AJAX hooks
18 */
19 public function __construct() {
20 $this->mxchat_init_ajax_hooks();
21 }
22
23
24 /**
25 * Register all AJAX action hooks
26 */
27 private function mxchat_init_ajax_hooks() {
28 // Settings AJAX
29 add_action('wp_ajax_mxchat_save_setting', array($this, 'mxchat_save_setting_callback'));
30 add_action('wp_ajax_mxchat_save_prompts_setting', array($this, 'mxchat_save_prompts_setting_callback'));
31 add_action('wp_ajax_migrate_pinecone_settings', array($this, 'ajax_migrate_pinecone_settings'));
32
33 // License AJAX
34 add_action('wp_ajax_mxchat_handle_activate_license', array($this, 'mxchat_handle_activate_license'));
35 add_action('wp_ajax_mxchat_check_license_status', array($this, 'mxchat_check_license_status'));
36 add_action('wp_ajax_mxchat_deactivate_license', array($this, 'mxchat_deactivate_license'));
37
38 // Actions & Intents AJAX
39 add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
40 add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
41
42 add_action('wp_ajax_mxchat_save_selected_bot', array($this, 'mxchat_save_selected_bot'));
43 add_action('wp_ajax_mxchat_check_api_keys', array($this, 'mxchat_check_api_keys'));
44
45 // Debug & Optimization AJAX
46 add_action('wp_ajax_mxchat_toggle_debug_mode', array($this, 'mxchat_toggle_debug_mode_callback'));
47 add_action('wp_ajax_mxchat_get_debug_log', array($this, 'mxchat_get_debug_log_callback'));
48 add_action('wp_ajax_mxchat_clear_debug_log', array($this, 'mxchat_clear_debug_log_callback'));
49 add_action('wp_ajax_mxchat_export_settings', array($this, 'mxchat_export_settings_callback'));
50 add_action('wp_ajax_mxchat_reset_all_settings', array($this, 'mxchat_reset_all_settings_callback'));
51 }
52
53 // ========================================
54 // SETTINGS AJAX HANDLERS
55 // ========================================
56
57 /**
58 * Validates and saves chat settings via AJAX request
59 */
60 public function mxchat_save_setting_callback() {
61 check_ajax_referer('mxchat_save_setting_nonce');
62 if (!current_user_can('manage_options')) {
63 ('MXChat Save: Unauthorized access attempt');
64 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
65 }
66
67 $name = isset($_POST['name']) ? $_POST['name'] : '';
68 // Strip slashes from the value before saving
69 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
70
71 //error_log('MXChat Save: Processing field name: ' . $name);
72 //error_log('MXChat Save: Field value: ' . $value);
73
74 if (empty($name)) {
75 //error_log('MXChat Save: Empty field name detected');
76 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
77 }
78
79 // Load the full options array
80 $options = get_option('mxchat_options', []);
81 //error_log('MXChat Save: Current options array: ' . print_r($options, true));
82
83 // Extract field name from mxchat_options[field_name] format if present
84 // But preserve the full name for special cases like rate_limits that need the full path
85 $field_name = $name;
86 if (preg_match('/^mxchat_options\[([^\[\]]+)\]$/', $name, $matches)) {
87 $field_name = $matches[1];
88 }
89
90 // Handle special cases
91 switch ($field_name) {
92 case 'model':
93 //error_log('MXChat Save: Processing model selection');
94 //error_log('MXChat Save: Model value received: ' . $value);
95 //error_log('MXChat Save: Value type: ' . gettype($value));
96 //error_log('MXChat Save: Value length: ' . strlen($value));
97 //error_log('MXChat Save: Value === "openrouter": ' . ($value === 'openrouter' ? 'YES' : 'NO'));
98
99 // Allow 'openrouter' or validate against whitelist
100 if ($value === 'openrouter') {
101 //error_log('MXChat Save: Setting model to openrouter');
102 $options['model'] = 'openrouter';
103 } else {
104 //error_log('MXChat Save: Checking against whitelist');
105 $allowed_models = array(
106 'gemini-3-pro-preview', 'gemini-3-flash-preview', 'gemini-2.5-pro', 'gemini-2.5-flash', 'gemini-2.5-flash-lite',
107 'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash',
108 'grok-4-0709', 'grok-4-1-fast-reasoning', 'grok-4-1-fast-non-reasoning', 'grok-3-beta', 'grok-3-fast-beta', 'grok-3-mini-beta',
109 'grok-3-mini-fast-beta', 'grok-2',
110 'deepseek-chat',
111 'claude-opus-4-6', 'claude-opus-4-5', 'claude-sonnet-4-6',
112 'claude-sonnet-4-5-20250929', 'claude-opus-4-1-20250805', 'claude-haiku-4-5-20251001',
113 'claude-opus-4-20250514', 'claude-sonnet-4-20250514',
114 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.4-nano', 'gpt-5.3-chat-latest',
115 'gpt-5.2', 'gpt-5.1-chat-latest', 'gpt-5.1-2025-11-13', 'gpt-5', 'gpt-5-mini', 'gpt-5-nano',
116 );
117
118 //error_log('MXChat Save: in_array result: ' . (in_array($value, $allowed_models) ? 'YES' : 'NO'));
119
120 if (in_array($value, $allowed_models)) {
121 //error_log('MXChat Save: Model is in whitelist, saving');
122 $options['model'] = sanitize_text_field($value);
123 } else {
124 //error_log('MXChat Save: Invalid model rejected: ' . $value);
125 //error_log('MXChat Save: Allowed models: ' . print_r($allowed_models, true));
126 wp_send_json_error(['message' => esc_html__('Invalid model selected', 'mxchat')]);
127 return;
128 }
129 }
130 break;
131
132 case 'openrouter_selected_model':
133 //error_log('MXChat Save: Processing OpenRouter model: ' . $value);
134 $options['openrouter_selected_model'] = sanitize_text_field($value);
135 // Force immediate save for new keys
136 //error_log('MXChat Save: OpenRouter model saved immediately');
137 break;
138
139 case 'openrouter_selected_model_name':
140 //error_log('MXChat Save: Processing OpenRouter model name: ' . $value);
141 $options['openrouter_selected_model_name'] = sanitize_text_field($value);
142 // Force immediate save for new keys
143 //error_log('MXChat Save: OpenRouter model name saved immediately');
144 break;
145
146 case 'openrouter_api_key':
147 //error_log('MXChat Save: Processing OpenRouter API key');
148 $options['openrouter_api_key'] = sanitize_text_field($value);
149 break;
150
151 // REMOVED DUPLICATE case 'openrouter_selected_model_name' HERE!
152
153 case 'additional_popular_questions':
154 //error_log('MXChat Save: Processing additional_popular_questions');
155 $questions = json_decode($value, true); // No need for stripslashes here
156 if (is_array($questions)) {
157 $options[$field_name] = $questions;
158 // Also update old option for backwards compatibility
159 update_option('additional_popular_questions', $questions);
160 //error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
161 } else {
162 //error_log('MXChat Save: Failed to decode questions JSON');
163 }
164 break;
165 case 'email_blocker_header_content':
166 //error_log('MXChat Save: Processing email_blocker_header_content');
167 // Allow HTML content but sanitize it safely
168 $options[$field_name] = wp_kses_post($value);
169 break;
170 case 'email_blocker_button_text':
171 //error_log('MXChat Save: Processing email_blocker_button_text');
172 $options[$field_name] = sanitize_text_field($value);
173 break;
174 case 'name_field_placeholder':
175 //error_log('MXChat Save: Processing name_field_placeholder');
176 $options[$field_name] = sanitize_text_field($value);
177 break;
178 case 'similarity_threshold':
179 //error_log('MXChat Save: Processing similarity_threshold');
180 // Validate and save - enforce min 20, max 85
181 $threshold = intval($value);
182 if ($threshold < 20) $threshold = 20;
183 if ($threshold > 85) $threshold = 85;
184 $options[$field_name] = $threshold;
185 break;
186 case 'rag_sources_limit':
187 //error_log('MXChat Save: Processing rag_sources_limit');
188 // Validate and save - enforce min 3, max 10, default 6
189 $rag_limit = intval($value);
190 if ($rag_limit < 3) $rag_limit = 3;
191 if ($rag_limit > 10) $rag_limit = 10;
192 $options[$field_name] = $rag_limit;
193 break;
194 case 'live_agent_status':
195 //error_log('MXChat Save: Processing live_agent_status');
196 // Set the new value
197 $options[$field_name] = ($value === 'on') ? 'on' : 'off';
198 break;
199 case 'enable_web_search':
200 //error_log('MXChat Save: Processing enable_web_search');
201 $options[$field_name] = ($value === 'on') ? 'on' : 'off';
202 break;
203 case 'enable_woocommerce_integration':
204 //error_log('MXChat Save: Processing enable_woocommerce_integration');
205 // Handle values that used to be 1/0
206 $options[$field_name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
207 break;
208 case 'post_type_visibility_mode':
209 // Validate mode value
210 $allowed_modes = array('all', 'include', 'exclude');
211 $options[$field_name] = in_array($value, $allowed_modes) ? $value : 'all';
212 break;
213 case 'post_type_visibility_list':
214 // Handle JSON array of post types
215 $post_types = json_decode($value, true);
216 if (is_array($post_types)) {
217 // Sanitize each post type slug
218 $options[$field_name] = array_map('sanitize_key', $post_types);
219 } else {
220 $options[$field_name] = array();
221 }
222 break;
223 case 'script_loading_strategy':
224 // Validate script loading strategy value
225 $allowed_strategies = array('default', 'defer', 'delay_1s', 'delay_3s', 'delay_5s', 'on_interaction');
226 $options[$field_name] = in_array($value, $allowed_strategies) ? $value : 'default';
227 break;
228 default:
229 // Handle transcripts options
230 if (strpos($name, 'mxchat_transcripts_options') !== false) {
231 // Extract field name from mxchat_transcripts_options[field_name]
232 if (preg_match('/mxchat_transcripts_options\[([^\]]+)\]/', $name, $matches)) {
233 $field_name = $matches[1];
234
235 // Get current transcripts options
236 $transcripts_options = get_option('mxchat_transcripts_options', array());
237
238 // Ensure it's an array
239 if (!is_array($transcripts_options)) {
240 $transcripts_options = array();
241 }
242
243 // Handle checkbox values (convert 'on'/'off' to 1/0)
244 if ($value === 'on' || $value === '1') {
245 $transcripts_options[$field_name] = 1;
246 } else if ($value === 'off' || $value === '0' || $value === '') {
247 $transcripts_options[$field_name] = 0;
248 } else {
249 // For text/select fields, sanitize appropriately
250 if ($field_name === 'mxchat_notification_email') {
251 $transcripts_options[$field_name] = sanitize_email($value);
252 } else {
253 $transcripts_options[$field_name] = sanitize_text_field($value);
254 }
255 }
256
257 // Use direct database update to bypass any filters
258 global $wpdb;
259
260 // Serialize the options array
261 $serialized = maybe_serialize($transcripts_options);
262
263 // Check if the option already exists in the database
264 $existing = $wpdb->get_var("SELECT option_id FROM {$wpdb->options} WHERE option_name = 'mxchat_transcripts_options'");
265
266 if ($existing) {
267 // Option exists, do an update
268 $result = $wpdb->update(
269 $wpdb->options,
270 array('option_value' => $serialized),
271 array('option_name' => 'mxchat_transcripts_options'),
272 array('%s'),
273 array('%s')
274 );
275 } else {
276 // Option doesn't exist (new install), do an insert
277 $result = $wpdb->insert(
278 $wpdb->options,
279 array(
280 'option_name' => 'mxchat_transcripts_options',
281 'option_value' => $serialized,
282 'autoload' => 'yes'
283 ),
284 array('%s', '%s', '%s')
285 );
286 }
287
288 // Clear all caches after direct DB update
289 wp_cache_delete('mxchat_transcripts_options', 'options');
290 wp_cache_delete('alloptions', 'options');
291 wp_cache_flush();
292
293 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
294 return;
295 }
296 }
297 // First check for rate limits settings
298 else if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
299 //error_log('MXChat Save: Detected rate_limits field: ' . $name);
300
301 // Extract role ID and setting from the name
302 preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
303 //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
304
305 if (isset($matches[1]) && isset($matches[2])) {
306 $role_id = $matches[1];
307 $setting_key = $matches[2]; // limit, timeframe, or message
308
309 //error_log('MXChat Save: Role ID = ' . $role_id . ', Setting Key = ' . $setting_key);
310
311 // Initialize rate_limits if it doesn't exist
312 if (!isset($options['rate_limits'])) {
313 // //error_log('MXChat Save: Initializing rate_limits array');
314 $options['rate_limits'] = [];
315 }
316
317 // Initialize role settings if it doesn't exist
318 if (!isset($options['rate_limits'][$role_id])) {
319 //error_log('MXChat Save: Initializing rate_limits for role: ' . $role_id);
320 $options['rate_limits'][$role_id] = [
321 'limit' => ($role_id === 'logged_out') ? '10' : '100',
322 'timeframe' => 'daily',
323 'message' => 'Rate limit exceeded. Please try again later.'
324 ];
325 }
326
327 // Update the specific setting
328 $options['rate_limits'][$role_id][$setting_key] = $value;
329 //error_log('MXChat Save: Updated rate_limits[' . $role_id . '][' . $setting_key . '] = ' . $value);
330 } else {
331 //error_log('MXChat Save: Failed to parse rate_limits pattern: ' . $name);
332 }
333 }
334 // Then check for role rate limits (old format)
335 else if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
336 //error_log('MXChat Save: Processing role_rate_limits field: ' . $name);
337 // Extract role ID from the name
338 preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
339 //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
340
341 if (isset($matches[1])) {
342 $role_id = $matches[1];
343 // Initialize role_rate_limits if it doesn't exist
344 if (!isset($options['role_rate_limits'])) {
345 //error_log('MXChat Save: Initializing role_rate_limits array');
346 $options['role_rate_limits'] = [];
347 }
348 // Update the specific role's rate limit
349 $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
350 //error_log('MXChat Save: Updated role_rate_limits[' . $role_id . '] = ' . $value);
351 } else {
352 //error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
353 }
354 }
355 // Handle toggles - check both extracted field_name and original name for toggle detection
356 else if (strpos($field_name, 'toggle') !== false || in_array($field_name, [
357 'chat_persistence_toggle',
358 'privacy_toggle',
359 'complianz_toggle',
360 'chat_toolbar_toggle',
361 'show_pdf_upload_button',
362 'show_word_upload_button',
363 'enable_streaming_toggle',
364 'contextual_awareness_toggle',
365 'citation_links_toggle',
366 'enable_email_block',
367 'enable_name_field',
368 'show_frontend_debugger'
369 ])) {
370 //error_log('MXChat Save: Processing toggle: ' . $field_name);
371 $options[$field_name] = ($value === 'on') ? 'on' : 'off';
372 } else {
373 //error_log('MXChat Save: Processing standard field: ' . $field_name);
374 // Store all other values directly using the extracted field name
375 $options[$field_name] = $value;
376 }
377 break;
378 }
379
380 // Save all updates to the options array
381 $updated = update_option('mxchat_options', $options);
382 //error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
383 //error_log('MXChat Save: Updated options array: ' . print_r($options, true));
384
385 // Log the save action if debug mode is enabled
386 if ( class_exists( 'MxChat_Admin' ) ) {
387 MxChat_Admin::mxchat_log_debug(
388 'settings_save',
389 sprintf( 'Field saved: %s', $field_name ),
390 array(
391 'field' => $field_name,
392 'updated' => $updated,
393 )
394 );
395 }
396
397 // Always return success even if WordPress says nothing changed
398 // (which happens when the value is the same as before)
399 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
400 }
401
402 /**
403 * Save the selected bot for knowledge base operations
404 */
405 public function mxchat_save_selected_bot() {
406 // Check nonce
407 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'mxchat_save_setting_nonce')) {
408 wp_send_json_error('Invalid nonce');
409 }
410
411 // Check permissions
412 if (!current_user_can('manage_options')) {
413 wp_send_json_error('Unauthorized');
414 }
415
416 $bot_id = isset($_POST['bot_id']) ? sanitize_key($_POST['bot_id']) : 'default';
417
418 // Save as user meta for the current user
419 $user_id = get_current_user_id();
420 update_user_meta($user_id, 'mxchat_selected_knowledge_bot', $bot_id);
421
422 // Also save as an option for site-wide default
423 update_option('mxchat_current_knowledge_bot', $bot_id);
424
425 // No cache clearing needed since we removed caching
426
427 wp_send_json_success(array(
428 'message' => 'Bot selection saved',
429 'bot_id' => $bot_id
430 ));
431 }
432
433 /**
434 * Handles AJAX request for saving chat settings
435 */
436 public function mxchat_save_prompts_setting_callback() {
437 check_ajax_referer('mxchat_prompts_setting_nonce');
438
439 if (!current_user_can('manage_options')) {
440 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
441 }
442
443 $name = isset($_POST['name']) ? $_POST['name'] : '';
444 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
445
446 //error_log('[MXCHAT-PROMPTS] Saving setting: ' . $name . ' = ' . $value);
447
448 if (empty($name)) {
449 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
450 }
451
452 // Handle Pinecone settings - BYPASS WORDPRESS SANITIZATION
453 if (strpos($name, 'mxchat_pinecone_addon_options') !== false) {
454 //error_log('[MXCHAT-PROMPTS] Processing Pinecone setting: ' . $name);
455
456 // Extract the field name
457 if (preg_match('/mxchat_pinecone_addon_options\[([^\]]+)\]/', $name, $matches)) {
458 $field_name = $matches[1];
459 //error_log('[MXCHAT-PROMPTS] Extracted field name: ' . $field_name);
460
461 // Get current options directly from database - NO WordPress filters
462 global $wpdb;
463 $current_options_raw = $wpdb->get_var(
464 $wpdb->prepare(
465 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
466 'mxchat_pinecone_addon_options'
467 )
468 );
469
470 // FIX: Handle the case where the option doesn't exist yet
471 if ($current_options_raw === null) {
472 // Option doesn't exist, create it with default values
473 $current_options = array(
474 'mxchat_use_pinecone' => '0',
475 'mxchat_pinecone_api_key' => '',
476 'mxchat_pinecone_host' => '',
477 'mxchat_pinecone_index' => '',
478 'mxchat_pinecone_environment' => ''
479 );
480 //error_log('[MXCHAT-PROMPTS] Option does not exist, creating with defaults');
481 } else {
482 // Unserialize the raw data
483 $current_options = maybe_unserialize($current_options_raw);
484 if (!is_array($current_options)) {
485 // Fallback to defaults if unserialization fails
486 $current_options = array(
487 'mxchat_use_pinecone' => '0',
488 'mxchat_pinecone_api_key' => '',
489 'mxchat_pinecone_host' => '',
490 'mxchat_pinecone_index' => '',
491 'mxchat_pinecone_environment' => ''
492 );
493 //error_log('[MXCHAT-PROMPTS] Failed to unserialize, using defaults');
494 }
495 }
496
497 //error_log('[MXCHAT-PROMPTS] Current options from DB: ' . print_r($current_options, true));
498
499 // Update the specific field with proper sanitization
500 switch ($field_name) {
501 case 'mxchat_use_pinecone':
502 $new_value = ($value === '1') ? '1' : '0';
503 break;
504 case 'mxchat_pinecone_api_key':
505 case 'mxchat_pinecone_host':
506 case 'mxchat_pinecone_index':
507 case 'mxchat_pinecone_environment':
508 $new_value = sanitize_text_field($value);
509 if ($field_name === 'mxchat_pinecone_host') {
510 $new_value = str_replace(['https://', 'http://'], '', $new_value);
511 }
512 break;
513 default:
514 wp_send_json_error(['message' => esc_html__('Unknown Pinecone field', 'mxchat')]);
515 }
516
517 $current_options[$field_name] = $new_value;
518 //error_log('[MXCHAT-PROMPTS] New value for ' . $field_name . ': "' . $new_value . '"');
519 //error_log('[MXCHAT-PROMPTS] Updated options: ' . print_r($current_options, true));
520
521 // Save directly to database to bypass WordPress sanitization
522 $serialized_options = maybe_serialize($current_options);
523
524 // FIX: Use INSERT ... ON DUPLICATE KEY UPDATE or separate INSERT/UPDATE logic
525 $option_exists = $wpdb->get_var(
526 $wpdb->prepare(
527 "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
528 'mxchat_pinecone_addon_options'
529 )
530 );
531
532 if ($option_exists > 0) {
533 // Update existing option
534 $save_result = $wpdb->update(
535 $wpdb->options,
536 array('option_value' => $serialized_options),
537 array('option_name' => 'mxchat_pinecone_addon_options'),
538 array('%s'),
539 array('%s')
540 );
541 //error_log('[MXCHAT-PROMPTS] Updated existing option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
542 } else {
543 // Insert new option
544 $save_result = $wpdb->insert(
545 $wpdb->options,
546 array(
547 'option_name' => 'mxchat_pinecone_addon_options',
548 'option_value' => $serialized_options,
549 'autoload' => 'yes'
550 ),
551 array('%s', '%s', '%s')
552 );
553 //error_log('[MXCHAT-PROMPTS] Inserted new option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
554 }
555
556 // Clear any WordPress option cache to ensure get_option() returns fresh data
557 wp_cache_delete('mxchat_pinecone_addon_options', 'options');
558
559 // IMPROVED VERIFICATION - Check if the database operation succeeded
560 if ($save_result !== false) {
561 // Double-check by reading fresh from database
562 $verification_raw = $wpdb->get_var(
563 $wpdb->prepare(
564 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
565 'mxchat_pinecone_addon_options'
566 )
567 );
568 $verification_options = maybe_unserialize($verification_raw);
569 $verified_value = isset($verification_options[$field_name]) ? $verification_options[$field_name] : 'NOT_FOUND';
570
571 //error_log('[MXCHAT-PROMPTS] Final verification - Expected: "' . $new_value . '", Got: "' . $verified_value . '"');
572
573 // Use loose comparison (==) instead of strict (===) to avoid type issues
574 if ($verified_value == $new_value || $save_result > 0) {
575 wp_send_json_success(['message' => esc_html__('Pinecone setting saved', 'mxchat')]);
576 } else {
577 // Still return success if the DB operation worked, even if verification is quirky
578 //error_log('[MXCHAT-PROMPTS] Verification mismatch but DB operation succeeded');
579 wp_send_json_success(['message' => esc_html__('Pinecone setting saved (DB success)', 'mxchat')]);
580 }
581 } else {
582 wp_send_json_error(['message' => esc_html__('Database save failed', 'mxchat')]);
583 }
584 } else {
585 wp_send_json_error(['message' => esc_html__('Invalid field name format', 'mxchat')]);
586 }
587
588 return; // Exit here for Pinecone settings
589 }
590 // Handle auto-sync settings (existing functionality)
591 if (strpos($name, 'mxchat_auto_sync_') === 0) {
592 $value = ($value === 'on' || $value === '1') ? '1' : '0';
593 $updated = update_option($name, $value);
594
595 if ($updated || get_option($name) === $value) {
596 wp_send_json_success(['message' => esc_html__('Auto-sync setting saved', 'mxchat')]);
597 } else {
598 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
599 }
600 }
601
602 // Handle chunking settings - use direct DB access to bypass WordPress filters
603 if (strpos($name, 'mxchat_chunk') === 0 || $name === 'mxchat_chunking_enabled') {
604 global $wpdb;
605
606 // Get current options directly from database
607 $current_options_raw = $wpdb->get_var(
608 $wpdb->prepare(
609 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
610 'mxchat_options'
611 )
612 );
613
614 $options = $current_options_raw !== null ? maybe_unserialize($current_options_raw) : array();
615 if (!is_array($options)) {
616 $options = array();
617 }
618
619 // Update the specific chunking field
620 if ($name === 'mxchat_chunking_enabled') {
621 $options['chunking_enabled'] = in_array($value, array('on', '1', 'true', true), true);
622 } elseif ($name === 'mxchat_chunk_size') {
623 $options['chunk_size'] = max(1000, min(10000, intval($value)));
624 }
625
626 // Save directly to database
627 $serialized_options = maybe_serialize($options);
628
629 $option_exists = $wpdb->get_var(
630 $wpdb->prepare(
631 "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
632 'mxchat_options'
633 )
634 );
635
636 if ($option_exists > 0) {
637 $save_result = $wpdb->update(
638 $wpdb->options,
639 array('option_value' => $serialized_options),
640 array('option_name' => 'mxchat_options'),
641 array('%s'),
642 array('%s')
643 );
644 } else {
645 $save_result = $wpdb->insert(
646 $wpdb->options,
647 array(
648 'option_name' => 'mxchat_options',
649 'option_value' => $serialized_options,
650 'autoload' => 'yes'
651 ),
652 array('%s', '%s', '%s')
653 );
654 }
655
656 // Clear object cache for this option
657 wp_cache_delete('mxchat_options', 'options');
658
659 if ($save_result !== false) {
660 wp_send_json_success(['message' => esc_html__('Chunking setting saved', 'mxchat')]);
661 } else {
662 wp_send_json_error(['message' => esc_html__('Failed to save chunking setting', 'mxchat')]);
663 }
664 return;
665 }
666
667 // Handle ACF field exclusion toggles
668 if (strpos($name, 'mxchat_acf_field_') === 0) {
669 // Extract field name from the input name (e.g., mxchat_acf_field_private_notes -> private_notes)
670 $field_name = str_replace('mxchat_acf_field_', '', $name);
671 $is_enabled = ($value === 'on' || $value === '1');
672
673 // Get current excluded fields
674 $excluded_fields = get_option('mxchat_acf_excluded_fields', array());
675 if (!is_array($excluded_fields)) {
676 $excluded_fields = array();
677 }
678
679 if ($is_enabled) {
680 // Remove from exclusion list (field should be included)
681 $excluded_fields = array_values(array_diff($excluded_fields, array($field_name)));
682 } else {
683 // Add to exclusion list (field should be excluded)
684 if (!in_array($field_name, $excluded_fields)) {
685 $excluded_fields[] = $field_name;
686 }
687 }
688
689 $updated = update_option('mxchat_acf_excluded_fields', $excluded_fields);
690
691 if ($updated || true) { // Always report success since the state may already be correct
692 wp_send_json_success([
693 'message' => $is_enabled
694 ? sprintf(esc_html__('Field "%s" will be included in imports', 'mxchat'), $field_name)
695 : sprintf(esc_html__('Field "%s" will be excluded from imports', 'mxchat'), $field_name)
696 ]);
697 } else {
698 wp_send_json_error(['message' => esc_html__('Failed to save ACF field setting', 'mxchat')]);
699 }
700 return;
701 }
702
703 // Handle custom post meta whitelist
704 if ($name === 'mxchat_custom_meta_whitelist') {
705 $updated = update_option('mxchat_custom_meta_whitelist', sanitize_textarea_field($value));
706
707 if ($updated || true) { // Always report success since the state may already be correct
708 wp_send_json_success([
709 'message' => esc_html__('Custom meta whitelist saved', 'mxchat')
710 ]);
711 } else {
712 wp_send_json_error(['message' => esc_html__('Failed to save custom meta whitelist', 'mxchat')]);
713 }
714 return;
715 }
716
717 // Handle other prompts options
718 $options = get_option('mxchat_prompts_options', []);
719 $options[$name] = $value;
720 $updated = update_option('mxchat_prompts_options', $options);
721
722 if ($updated) {
723 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
724 } else {
725 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
726 }
727 }
728
729
730 /**
731 * Handles AJAX request for Pinecone settings migration
732 */
733 public function ajax_migrate_pinecone_settings() {
734 // Verify nonce
735 if (!wp_verify_nonce($_POST['_ajax_nonce'] ?? '', 'mxchat_save_setting_nonce')) {
736 wp_send_json_error('Invalid nonce');
737 }
738
739 // Check permissions
740 if (!current_user_can('manage_options')) {
741 wp_send_json_error('Unauthorized access');
742 }
743
744 // Check if old Pinecone addon options exist
745 $old_options = get_option('mxchat_pinecone_addon_options', array());
746
747 if (empty($old_options)) {
748 wp_send_json_success(array('migrated' => false, 'message' => 'No old settings found'));
749 }
750
751 // Get current core plugin options
752 $current_options = get_option('mxchat_pinecone_addon_options', array());
753
754 // Only migrate if core options are empty or if explicitly requested
755 $should_migrate = empty($current_options) ||
756 (empty($current_options['mxchat_pinecone_api_key']) && !empty($old_options['mxchat_pinecone_api_key']));
757
758 if ($should_migrate) {
759 // Migrate settings with proper sanitization
760 $migrated_options = array(
761 'mxchat_use_pinecone' => $old_options['mxchat_use_pinecone'] ?? '0',
762 'mxchat_pinecone_api_key' => sanitize_text_field($old_options['mxchat_pinecone_api_key'] ?? ''),
763 'mxchat_pinecone_host' => sanitize_text_field($old_options['mxchat_pinecone_host'] ?? ''),
764 'mxchat_pinecone_index' => sanitize_text_field($old_options['mxchat_pinecone_index'] ?? ''),
765 'mxchat_pinecone_environment' => sanitize_text_field($old_options['mxchat_pinecone_environment'] ?? '')
766 );
767
768 update_option('mxchat_pinecone_addon_options', $migrated_options);
769
770 wp_send_json_success(array(
771 'migrated' => true,
772 'message' => 'Settings migrated successfully from Pinecone add-on'
773 ));
774 } else {
775 wp_send_json_success(array(
776 'migrated' => false,
777 'message' => 'Settings already exist in core plugin'
778 ));
779 }
780 }
781
782
783 // ========================================
784 // LICENSE AJAX HANDLERS
785 // ========================================
786
787 /**
788 * Validates and activates chat license via AJAX
789 */
790 public function mxchat_handle_activate_license() {
791 // Check nonce
792 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
793 wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
794 return;
795 }
796
797 // Verify user capabilities
798 if (!current_user_can('manage_options')) {
799 wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
800 return;
801 }
802
803 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
804 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
805
806 if (empty($license_key) || empty($customer_email)) {
807 wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
808 return;
809 }
810
811 $product_id = 'MxChatPRO';
812 $domain = parse_url(home_url(), PHP_URL_HOST); // Get the current domain
813
814 // Call WooCommerce Software API for activation (not just validation)
815 $response = wp_remote_get(
816 add_query_arg(
817 array(
818 'wc-api' => 'software-api',
819 'request' => 'activation',
820 'email' => $customer_email,
821 'license_key' => $license_key,
822 'product_id' => $product_id,
823 'instance' => $domain, // THIS IS KEY - include the domain as instance
824 'platform' => 'wordpress' // Optional but good to include
825 ),
826 'https://mxchat.ai/'
827 ),
828 array(
829 'timeout' => 60,
830 'sslverify' => true
831 )
832 );
833
834 if (is_wp_error($response)) {
835 $error_message = $response->get_error_message();
836 //error_log('MxChat License Activation Error: ' . $error_message);
837 wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $error_message);
838 return;
839 }
840
841 $response_code = wp_remote_retrieve_response_code($response);
842 $body = wp_remote_retrieve_body($response);
843
844 // Log response for debugging
845 //error_log('MxChat License Response Code: ' . $response_code);
846 //error_log('MxChat License Response Body: ' . $body);
847
848 if ($response_code !== 200) {
849 wp_send_json_error(esc_html__('Server returned error code: ', 'mxchat') . $response_code);
850 return;
851 }
852
853 $data = json_decode($body);
854
855 if ($data && isset($data->activated) && $data->activated) {
856 // Success - save local options
857 update_option('mxchat_license_status', 'active');
858 update_option('mxchat_pro_email', $customer_email);
859 update_option('mxchat_activation_key', $license_key);
860 delete_option('mxchat_license_error');
861
862 // Also track on your website (this is your existing domain tracking)
863 $this->track_domain_on_website($license_key, $customer_email, $domain);
864
865 wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
866 } else {
867 $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
868 update_option('mxchat_license_status', 'inactive');
869 update_option('mxchat_license_error', $error_message);
870
871 //error_log('MxChat Activation failed: ' . $error_message);
872 wp_send_json_error($error_message);
873 }
874 }
875
876 /**
877 * Track domain on your website (separate from WooCommerce activation)
878 */
879 private function track_domain_on_website($license_key, $email, $domain) {
880 // This calls your website's tracking API
881 wp_remote_post('https://mxchat.ai/mxchat-api/activate-license', array(
882 'body' => array(
883 'mxchat_pro_email' => $email,
884 'mxchat_activation_key' => $license_key,
885 'domain' => $domain
886 ),
887 'timeout' => 10,
888 'sslverify' => true
889 ));
890 }
891
892 /**
893 * Validates license via AJAX with email and key
894 */
895 public function mxchat_check_license_status() {
896 // Verify nonce
897 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
898 wp_send_json_error('Security check failed');
899 return;
900 }
901
902 // Add isset checks for safety
903 $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
904 $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
905
906 // Check if this license is actually active in your system
907 $is_active = (get_option('mxchat_license_status') === 'active' &&
908 get_option('mxchat_pro_email') === $email &&
909 get_option('mxchat_activation_key') === $key);
910
911 wp_send_json(array(
912 'is_active' => $is_active
913 ));
914 }
915
916 /**
917 * Handle license deactivation - Complete version for plugin
918 */
919 function mxchat_deactivate_license() {
920 // Add debugging
921 //error_log('MxChat deactivate function called');
922
923 // Check nonce
924 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
925 //error_log('MxChat deactivate: Nonce check failed');
926 wp_send_json_error('Security check failed.');
927 return;
928 }
929
930 //error_log('MxChat deactivate: Nonce check passed');
931
932 $license_key = get_option('mxchat_activation_key');
933 $email = get_option('mxchat_pro_email');
934 $domain = parse_url(home_url(), PHP_URL_HOST);
935
936 //error_log('MxChat deactivate: License: ' . $license_key . ', Email: ' . $email . ', Domain: ' . $domain);
937
938 if (empty($license_key) || empty($email)) {
939 //error_log('MxChat deactivate: No active license found');
940 wp_send_json_error('No active license found.');
941 return;
942 }
943
944 // Clear local license data first
945 delete_option('mxchat_license_status');
946 delete_option('mxchat_pro_email');
947 delete_option('mxchat_activation_key');
948 delete_option('mxchat_license_error');
949
950 //error_log('MxChat deactivate: Local data cleared');
951
952 // Notify your website's API to properly deactivate
953 $response = wp_remote_post('https://mxchat.ai/mxchat-api/deactivate-license', array(
954 'body' => array(
955 'license_key' => $license_key,
956 'email' => $email,
957 'domain' => $domain
958 ),
959 'timeout' => 15,
960 'sslverify' => true
961 ));
962
963 if (is_wp_error($response)) {
964 //error_log('MxChat deactivate: Server error - ' . $response->get_error_message());
965 wp_send_json_success(array(
966 'message' => 'License deactivated locally. Server could not be contacted to free activation slot.',
967 'server_notified' => false
968 ));
969 return;
970 }
971
972 $response_body = wp_remote_retrieve_body($response);
973 $response_data = json_decode($response_body, true);
974
975 //error_log('MxChat deactivate: Server response - ' . $response_body);
976
977 if (isset($response_data['success']) && $response_data['success']) {
978 //error_log('MxChat deactivate: Success with server notification');
979 wp_send_json_success(array(
980 'message' => 'License deactivated successfully. Activation slot has been freed up.',
981 'server_notified' => true
982 ));
983 } else {
984 //error_log('MxChat deactivate: Server responded but deactivation may have failed');
985 wp_send_json_success(array(
986 'message' => 'License deactivated locally. Please check your account dashboard to verify the activation was freed.',
987 'server_notified' => false
988 ));
989 }
990 }
991
992
993 // ========================================
994 // ACTIONS & INTENTS AJAX HANDLERS
995 // ========================================
996
997 /**
998 * Validates nonce and returns JSON error on failure
999 */
1000 public function mxchat_toggle_action() {
1001 // Check nonce
1002 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
1003 wp_send_json_error(array('message' => 'Security check failed'));
1004 return;
1005 }
1006
1007 // Check permissions
1008 if (!current_user_can('manage_options')) {
1009 wp_send_json_error(array('message' => 'Permission denied'));
1010 return;
1011 }
1012
1013 // Validate params
1014 $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
1015 $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;
1016
1017 if (!$intent_id) {
1018 wp_send_json_error(array('message' => 'Invalid action ID'));
1019 return;
1020 }
1021
1022 // Update the intent/action status in the database
1023 global $wpdb;
1024 $table_name = $wpdb->prefix . 'mxchat_intents';
1025
1026 // Using the 'enabled' field - add this field if it doesn't exist
1027 $result = $wpdb->update(
1028 $table_name,
1029 array('enabled' => $enabled ? 1 : 0),
1030 array('id' => $intent_id),
1031 array('%d'),
1032 array('%d')
1033 );
1034
1035 if ($result === false) {
1036 wp_send_json_error(array('message' => 'Database error'));
1037 return;
1038 }
1039
1040 wp_send_json_success();
1041 }
1042
1043
1044 /**
1045 * Validates permissions for AJAX request handling
1046 */
1047 public function mxchat_update_intent_threshold() {
1048 // Check permissions
1049 if (!current_user_can('manage_options')) {
1050 if (wp_doing_ajax()) {
1051 wp_send_json_error(array('message' => 'Unauthorized user'));
1052 return;
1053 }
1054 wp_die(esc_html__('Unauthorized user', 'mxchat'));
1055 }
1056
1057 // Verify nonce
1058 check_admin_referer('mxchat_update_intent_threshold_nonce');
1059
1060 // Process the update if we have valid data
1061 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
1062 global $wpdb;
1063 $table_name = $wpdb->prefix . 'mxchat_intents';
1064 $intent_id = intval($_POST['intent_id']);
1065 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
1066 $similarity_threshold = $threshold_percentage / 100;
1067
1068 $result = $wpdb->update(
1069 $table_name,
1070 ['similarity_threshold' => $similarity_threshold],
1071 ['id' => $intent_id],
1072 ['%f'],
1073 ['%d']
1074 );
1075
1076 // Handle AJAX requests
1077 if (wp_doing_ajax()) {
1078 if ($result === false) {
1079 wp_send_json_error(array('message' => 'Failed to update threshold'));
1080 } else {
1081 wp_send_json_success(array('threshold' => $threshold_percentage));
1082 }
1083 return;
1084 }
1085 }
1086
1087 // Redirect for regular form submissions
1088 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
1089 exit;
1090 }
1091
1092 // ========================================
1093 // HELPER METHODS
1094 // ========================================
1095
1096 /**
1097 * Returns a specific nonce action string
1098 */
1099 private function mxchat_get_nonce_action() {
1100 return 'mxchat_license_nonce';
1101 }
1102
1103 /**
1104 * Check API key status for all providers
1105 */
1106 public function mxchat_check_api_keys() {
1107 // Check nonce
1108 if (!wp_verify_nonce($_POST['nonce'] ?? '', 'mxchat_save_setting_nonce')) {
1109 wp_send_json_error('Invalid nonce');
1110 }
1111
1112 // Check permissions
1113 if (!current_user_can('manage_options')) {
1114 wp_send_json_error('Unauthorized');
1115 }
1116
1117 // Get current options
1118 $options = get_option('mxchat_options', array());
1119
1120 // Check which API keys are present
1121 $api_key_status = array(
1122 'openai' => !empty($options['api_key']),
1123 'claude' => !empty($options['claude_api_key']),
1124 'xai' => !empty($options['xai_api_key']),
1125 'deepseek' => !empty($options['deepseek_api_key']),
1126 'gemini' => !empty($options['gemini_api_key']),
1127 'openrouter' => !empty($options['openrouter_api_key']),
1128 'voyage' => !empty($options['voyage_api_key'])
1129 );
1130
1131 wp_send_json_success($api_key_status);
1132 }
1133
1134 // ========================================
1135 // DEBUG & OPTIMIZATION AJAX HANDLERS
1136 // ========================================
1137
1138 /**
1139 * Toggle debug mode on/off
1140 */
1141 public function mxchat_toggle_debug_mode_callback() {
1142 // Verify nonce
1143 if ( ! check_ajax_referer( 'mxchat_save_setting_nonce', '_ajax_nonce', false ) ) {
1144 wp_send_json_error( array( 'message' => esc_html__( 'Security check failed', 'mxchat' ) ) );
1145 }
1146
1147 // Check permissions
1148 if ( ! current_user_can( 'manage_options' ) ) {
1149 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ) );
1150 }
1151
1152 $enabled = isset( $_POST['enabled'] ) && $_POST['enabled'] === 'on';
1153
1154 $options = get_option( 'mxchat_options', array() );
1155
1156 if ( $enabled ) {
1157 $options['debug_mode'] = 'on';
1158 update_option( 'mxchat_options', $options );
1159 MxChat_Admin::mxchat_log_debug( 'debug_mode', 'Debug mode enabled' );
1160 } else {
1161 // Log before disabling
1162 MxChat_Admin::mxchat_log_debug( 'debug_mode', 'Debug mode disabled' );
1163 $options['debug_mode'] = 'off';
1164 update_option( 'mxchat_options', $options );
1165 }
1166
1167 wp_send_json_success( array(
1168 'message' => $enabled ? esc_html__( 'Debug mode enabled', 'mxchat' ) : esc_html__( 'Debug mode disabled', 'mxchat' ),
1169 'enabled' => $enabled,
1170 ) );
1171 }
1172
1173 /**
1174 * Get the debug log entries
1175 */
1176 public function mxchat_get_debug_log_callback() {
1177 // Verify nonce
1178 if ( ! check_ajax_referer( 'mxchat_save_setting_nonce', '_ajax_nonce', false ) ) {
1179 wp_send_json_error( array( 'message' => esc_html__( 'Security check failed', 'mxchat' ) ) );
1180 }
1181
1182 // Check permissions
1183 if ( ! current_user_can( 'manage_options' ) ) {
1184 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ) );
1185 }
1186
1187 $log = MxChat_Admin::mxchat_get_debug_log();
1188
1189 wp_send_json_success( array(
1190 'log' => $log,
1191 'count' => count( $log ),
1192 ) );
1193 }
1194
1195 /**
1196 * Clear the debug log
1197 */
1198 public function mxchat_clear_debug_log_callback() {
1199 // Verify nonce
1200 if ( ! check_ajax_referer( 'mxchat_save_setting_nonce', '_ajax_nonce', false ) ) {
1201 wp_send_json_error( array( 'message' => esc_html__( 'Security check failed', 'mxchat' ) ) );
1202 }
1203
1204 // Check permissions
1205 if ( ! current_user_can( 'manage_options' ) ) {
1206 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ) );
1207 }
1208
1209 MxChat_Admin::mxchat_clear_debug_log();
1210
1211 // Log that the log was cleared (this will be the first entry in the new log)
1212 MxChat_Admin::mxchat_log_debug( 'debug_log', 'Debug log cleared by user' );
1213
1214 wp_send_json_success( array( 'message' => esc_html__( 'Debug log cleared', 'mxchat' ) ) );
1215 }
1216
1217 /**
1218 * Export settings as JSON
1219 */
1220 public function mxchat_export_settings_callback() {
1221 // Verify nonce
1222 if ( ! check_ajax_referer( 'mxchat_save_setting_nonce', '_ajax_nonce', false ) ) {
1223 wp_send_json_error( array( 'message' => esc_html__( 'Security check failed', 'mxchat' ) ) );
1224 }
1225
1226 // Check permissions
1227 if ( ! current_user_can( 'manage_options' ) ) {
1228 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ) );
1229 }
1230
1231 $export = MxChat_Admin::mxchat_export_settings();
1232
1233 // Log the export
1234 MxChat_Admin::mxchat_log_debug( 'settings_export', 'Settings exported by user' );
1235
1236 wp_send_json_success( array(
1237 'settings' => $export,
1238 'filename' => 'mxchat-settings-' . gmdate( 'Y-m-d-His' ) . '.json',
1239 ) );
1240 }
1241
1242 /**
1243 * Reset all settings to defaults
1244 */
1245 public function mxchat_reset_all_settings_callback() {
1246 // Verify nonce
1247 if ( ! check_ajax_referer( 'mxchat_save_setting_nonce', '_ajax_nonce', false ) ) {
1248 wp_send_json_error( array( 'message' => esc_html__( 'Security check failed', 'mxchat' ) ) );
1249 }
1250
1251 // Check permissions
1252 if ( ! current_user_can( 'manage_options' ) ) {
1253 wp_send_json_error( array( 'message' => esc_html__( 'Unauthorized', 'mxchat' ) ) );
1254 }
1255
1256 // Require confirmation code
1257 $confirmation = isset( $_POST['confirmation'] ) ? sanitize_text_field( wp_unslash( $_POST['confirmation'] ) ) : '';
1258
1259 if ( strtoupper( $confirmation ) !== 'RESET' ) {
1260 wp_send_json_error( array( 'message' => esc_html__( 'Invalid confirmation code. Please type RESET to confirm.', 'mxchat' ) ) );
1261 }
1262
1263 // Perform the reset
1264 MxChat_Admin::mxchat_reset_all_settings();
1265
1266 wp_send_json_success( array( 'message' => esc_html__( 'All settings have been reset to defaults. The page will reload.', 'mxchat' ) ) );
1267 }
1268
1269 }
1270
1271 // Initialize the AJAX handler
1272 new MxChat_Ajax_Handler();
1273