PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.5.2
MxChat – AI Chatbot & Content Generation for WordPress v2.5.2
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
← All changes | admin/class-ajax-handler.php +49 -523 3.2.72.5.2 View file →
@@ -37,93 +37,12 @@
37 37
38 38 // Actions & Intents AJAX
39 39 add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
40 40 add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
41 -
41 +
42 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 43 }
55 44
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 45 // ========================================
127 46 // SETTINGS AJAX HANDLERS
128 47 // ========================================
129 48
@@ -152,17 +71,10 @@
152 71 // Load the full options array
153 72 $options = get_option('mxchat_options', []);
154 73 //error_log('MXChat Save: Current options array: ' . print_r($options, true));
155 74
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 75 // Handle special cases
164 - switch ($field_name) {
76 + switch ($name) {
165 77 case 'model':
166 78 //error_log('MXChat Save: Processing model selection');
167 79 //error_log('MXChat Save: Model value received: ' . $value);
168 80 //error_log('MXChat Save: Value type: ' . gettype($value));
@@ -174,15 +86,20 @@
174 86 //error_log('MXChat Save: Setting model to openrouter');
175 87 $options['model'] = 'openrouter';
176 88 } else {
177 89 //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();
90 + $allowed_models = array(
91 + 'gemini-2.0-flash', 'gemini-2.0-flash-lite', 'gemini-1.5-pro', 'gemini-1.5-flash',
92 + 'grok-4-0709', 'grok-3-beta', 'grok-3-fast-beta', 'grok-3-mini-beta',
93 + 'grok-3-mini-fast-beta', 'grok-2',
94 + 'deepseek-chat',
95 + 'claude-sonnet-4-5-20250929', 'claude-opus-4-1-20250805', 'claude-haiku-4-5-20251001',
96 + 'claude-opus-4-20250514', 'claude-sonnet-4-20250514', 'claude-3-7-sonnet-20250219',
97 + 'claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-sonnet-20240229',
98 + 'claude-3-haiku-20240307',
99 + 'gpt-5', 'gpt-5-mini', 'gpt-5-nano', 'gpt-4.1-2025-04-14',
100 + 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo',
101 + );
185 102
186 103 //error_log('MXChat Save: in_array result: ' . (in_array($value, $allowed_models) ? 'YES' : 'NO'));
187 104
188 105 if (in_array($value, $allowed_models)) {
@@ -221,9 +138,9 @@
221 138 case 'additional_popular_questions':
222 139 //error_log('MXChat Save: Processing additional_popular_questions');
223 140 $questions = json_decode($value, true); // No need for stripslashes here
224 141 if (is_array($questions)) {
225 - $options[$field_name] = $questions;
142 + $options[$name] = $questions;
226 143 // Also update old option for backwards compatibility
227 144 update_option('additional_popular_questions', $questions);
228 145 //error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
229 146 } else {
@@ -232,150 +149,55 @@
232 149 break;
233 150 case 'email_blocker_header_content':
234 151 //error_log('MXChat Save: Processing email_blocker_header_content');
235 152 // Allow HTML content but sanitize it safely
236 - $options[$field_name] = wp_kses_post($value);
153 + $options[$name] = wp_kses_post($value);
237 154 break;
238 155 case 'email_blocker_button_text':
239 156 //error_log('MXChat Save: Processing email_blocker_button_text');
240 - $options[$field_name] = sanitize_text_field($value);
157 + $options[$name] = sanitize_text_field($value);
241 158 break;
242 159 case 'name_field_placeholder':
243 160 //error_log('MXChat Save: Processing name_field_placeholder');
244 - $options[$field_name] = sanitize_text_field($value);
161 + $options[$name] = sanitize_text_field($value);
245 162 break;
246 163 case 'similarity_threshold':
247 164 //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;
165 + // Save to the options array
166 + $options[$name] = $value;
253 167 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;
168 + case 'user_message_bg_color':
169 + case 'user_message_font_color':
170 + case 'bot_message_bg_color':
171 + case 'bot_message_font_color':
172 + case 'top_bar_bg_color':
173 + case 'send_button_font_color':
174 + case 'chatbot_background_color':
175 + case 'icon_color':
176 + case 'chat_input_font_color':
177 + case 'live_agent_message_bg_color':
178 + case 'live_agent_message_font_color':
179 + case 'mode_indicator_bg_color':
180 + case 'mode_indicator_font_color':
181 + case 'toolbar_icon_color':
182 + case 'quick_questions_toggle_color':
183 + //error_log('MXChat Save: Processing color value: ' . $name);
184 + // Store color values directly
185 + $options[$name] = $value;
261 186 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 187 case 'live_agent_status':
270 188 //error_log('MXChat Save: Processing live_agent_status');
271 189 // Set the new value
272 - $options[$field_name] = ($value === 'on') ? 'on' : 'off';
190 + $options[$name] = ($value === 'on') ? 'on' : 'off';
273 191 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 192 case 'enable_woocommerce_integration':
279 193 //error_log('MXChat Save: Processing enable_woocommerce_integration');
280 194 // Handle values that used to be 1/0
281 - $options[$field_name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
195 + $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
282 196 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 197 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 198 // First check for rate limits settings
377 - else if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
199 + if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
378 200 //error_log('MXChat Save: Detected rate_limits field: ' . $name);
379 201
380 202 // Extract role ID and setting from the name
381 203 preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
@@ -430,10 +252,10 @@
430 252 } else {
431 253 //error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
432 254 }
433 255 }
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, [
256 + // Handle toggles
257 + else if (strpos($name, 'toggle') !== false || in_array($name, [
436 258 'chat_persistence_toggle',
437 259 'privacy_toggle',
438 260 'complianz_toggle',
439 261 'chat_toolbar_toggle',
@@ -440,20 +262,17 @@
440 262 'show_pdf_upload_button',
441 263 'show_word_upload_button',
442 264 'enable_streaming_toggle',
443 265 'contextual_awareness_toggle',
444 - 'citation_links_toggle',
445 266 'enable_email_block',
446 - 'enable_name_field',
447 - 'custom_provider_for_embeddings',
448 - 'custom_provider_for_images'
267 + 'enable_name_field'
449 268 ])) {
450 - //error_log('MXChat Save: Processing toggle: ' . $field_name);
451 - $options[$field_name] = ($value === 'on') ? 'on' : 'off';
269 + //error_log('MXChat Save: Processing toggle: ' . $name);
270 + $options[$name] = ($value === 'on') ? 'on' : 'off';
452 271 } 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;
272 + //error_log('MXChat Save: Processing standard field: ' . $name);
273 + // Store all other values directly
274 + $options[$name] = $value;
456 275 }
457 276 break;
458 277 }
459 278
@@ -461,20 +280,8 @@
461 280 $updated = update_option('mxchat_options', $options);
462 281 //error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
463 282 //error_log('MXChat Save: Updated options array: ' . print_r($options, true));
464 283
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 284 // Always return success even if WordPress says nothing changed
478 285 // (which happens when the value is the same as before)
479 286 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
480 287 }
@@ -678,123 +485,8 @@
678 485 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
679 486 }
680 487 }
681 488
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 489 // Handle other prompts options
798 490 $options = get_option('mxchat_prompts_options', []);
799 491 $options[$name] = $value;
800 492 $updated = update_option('mxchat_prompts_options', $options);
@@ -1178,174 +870,8 @@
1178 870 */
1179 871 private function mxchat_get_nonce_action() {
1180 872 return 'mxchat_license_nonce';
1181 873 }
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 874
1349 875 }
1350 876
1351 877 // Initialize the AJAX handler