PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.2.7
MxChat – AI Chatbot & Content Generation for WordPress v3.2.7
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.2.7, at admin/class-ajax-handler.php

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