PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.4.6
MxChat – AI Chatbot & Content Generation for WordPress v2.4.6
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 +37 -572 3.2.72.4.6 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,78 +71,15 @@
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) {
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 -
76 + switch ($name) {
221 77 case 'additional_popular_questions':
222 78 //error_log('MXChat Save: Processing additional_popular_questions');
223 79 $questions = json_decode($value, true); // No need for stripslashes here
224 80 if (is_array($questions)) {
225 - $options[$field_name] = $questions;
81 + $options[$name] = $questions;
226 82 // Also update old option for backwards compatibility
227 83 update_option('additional_popular_questions', $questions);
228 84 //error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
229 85 } else {
@@ -232,150 +88,55 @@
232 88 break;
233 89 case 'email_blocker_header_content':
234 90 //error_log('MXChat Save: Processing email_blocker_header_content');
235 91 // Allow HTML content but sanitize it safely
236 - $options[$field_name] = wp_kses_post($value);
92 + $options[$name] = wp_kses_post($value);
237 93 break;
238 94 case 'email_blocker_button_text':
239 95 //error_log('MXChat Save: Processing email_blocker_button_text');
240 - $options[$field_name] = sanitize_text_field($value);
96 + $options[$name] = sanitize_text_field($value);
241 97 break;
242 98 case 'name_field_placeholder':
243 99 //error_log('MXChat Save: Processing name_field_placeholder');
244 - $options[$field_name] = sanitize_text_field($value);
100 + $options[$name] = sanitize_text_field($value);
245 101 break;
246 102 case 'similarity_threshold':
247 103 //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;
104 + // Save to the options array
105 + $options[$name] = $value;
253 106 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;
107 + case 'user_message_bg_color':
108 + case 'user_message_font_color':
109 + case 'bot_message_bg_color':
110 + case 'bot_message_font_color':
111 + case 'top_bar_bg_color':
112 + case 'send_button_font_color':
113 + case 'chatbot_background_color':
114 + case 'icon_color':
115 + case 'chat_input_font_color':
116 + case 'live_agent_message_bg_color':
117 + case 'live_agent_message_font_color':
118 + case 'mode_indicator_bg_color':
119 + case 'mode_indicator_font_color':
120 + case 'toolbar_icon_color':
121 + case 'quick_questions_toggle_color':
122 + //error_log('MXChat Save: Processing color value: ' . $name);
123 + // Store color values directly
124 + $options[$name] = $value;
261 125 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 126 case 'live_agent_status':
270 127 //error_log('MXChat Save: Processing live_agent_status');
271 128 // Set the new value
272 - $options[$field_name] = ($value === 'on') ? 'on' : 'off';
129 + $options[$name] = ($value === 'on') ? 'on' : 'off';
273 130 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 131 case 'enable_woocommerce_integration':
279 132 //error_log('MXChat Save: Processing enable_woocommerce_integration');
280 133 // Handle values that used to be 1/0
281 - $options[$field_name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
134 + $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
282 135 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 136 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 137 // First check for rate limits settings
377 - else if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
138 + if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
378 139 //error_log('MXChat Save: Detected rate_limits field: ' . $name);
379 140
380 141 // Extract role ID and setting from the name
381 142 preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
@@ -430,10 +191,10 @@
430 191 } else {
431 192 //error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
432 193 }
433 194 }
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, [
195 + // Handle toggles
196 + else if (strpos($name, 'toggle') !== false || in_array($name, [
436 197 'chat_persistence_toggle',
437 198 'privacy_toggle',
438 199 'complianz_toggle',
439 200 'chat_toolbar_toggle',
@@ -440,20 +201,17 @@
440 201 'show_pdf_upload_button',
441 202 'show_word_upload_button',
442 203 'enable_streaming_toggle',
443 204 'contextual_awareness_toggle',
444 - 'citation_links_toggle',
445 205 'enable_email_block',
446 - 'enable_name_field',
447 - 'custom_provider_for_embeddings',
448 - 'custom_provider_for_images'
206 + 'enable_name_field'
449 207 ])) {
450 - //error_log('MXChat Save: Processing toggle: ' . $field_name);
451 - $options[$field_name] = ($value === 'on') ? 'on' : 'off';
208 + //error_log('MXChat Save: Processing toggle: ' . $name);
209 + $options[$name] = ($value === 'on') ? 'on' : 'off';
452 210 } 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;
211 + //error_log('MXChat Save: Processing standard field: ' . $name);
212 + // Store all other values directly
213 + $options[$name] = $value;
456 214 }
457 215 break;
458 216 }
459 217
@@ -461,20 +219,8 @@
461 219 $updated = update_option('mxchat_options', $options);
462 220 //error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
463 221 //error_log('MXChat Save: Updated options array: ' . print_r($options, true));
464 222
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 223 // Always return success even if WordPress says nothing changed
478 224 // (which happens when the value is the same as before)
479 225 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
480 226 }
@@ -678,123 +424,8 @@
678 424 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
679 425 }
680 426 }
681 427
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 428 // Handle other prompts options
798 429 $options = get_option('mxchat_prompts_options', []);
799 430 $options[$name] = $value;
800 431 $updated = update_option('mxchat_prompts_options', $options);
@@ -1178,174 +809,8 @@
1178 809 */
1179 810 private function mxchat_get_nonce_action() {
1180 811 return 'mxchat_license_nonce';
1181 812 }
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 813
1349 814 }
1350 815
1351 816 // Initialize the AJAX handler