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

695 lines 29.3 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_activate_license', array($this, 'mxchat_handle_activate_license'));
35 add_action('wp_ajax_mxchat_check_license', array($this, 'mxchat_check_license_status'));
36
37 // Actions & Intents AJAX
38 add_action('wp_ajax_mxchat_toggle_action', array($this, 'mxchat_toggle_action'));
39 add_action('wp_ajax_mxchat_update_intent_threshold', array($this, 'mxchat_update_intent_threshold'));
40 }
41
42 // ========================================
43 // SETTINGS AJAX HANDLERS
44 // ========================================
45
46 /**
47 * Validates and saves chat settings via AJAX request
48 */
49 public function mxchat_save_setting_callback() {
50 check_ajax_referer('mxchat_save_setting_nonce');
51 if (!current_user_can('manage_options')) {
52 ('MXChat Save: Unauthorized access attempt');
53 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
54 }
55
56 $name = isset($_POST['name']) ? $_POST['name'] : '';
57 // Strip slashes from the value before saving
58 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
59
60 //error_log('MXChat Save: Processing field name: ' . $name);
61 //error_log('MXChat Save: Field value: ' . $value);
62
63 if (empty($name)) {
64 //error_log('MXChat Save: Empty field name detected');
65 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
66 }
67
68 // Load the full options array
69 $options = get_option('mxchat_options', []);
70 //error_log('MXChat Save: Current options array: ' . print_r($options, true));
71
72 // Handle special cases
73 switch ($name) {
74 case 'additional_popular_questions':
75 //error_log('MXChat Save: Processing additional_popular_questions');
76 $questions = json_decode($value, true); // No need for stripslashes here
77 if (is_array($questions)) {
78 $options[$name] = $questions;
79 // Also update old option for backwards compatibility
80 update_option('additional_popular_questions', $questions);
81 //error_log('MXChat Save: Saved ' . count($questions) . ' additional questions');
82 } else {
83 //error_log('MXChat Save: Failed to decode questions JSON');
84 }
85 break;
86 case 'email_blocker_header_content':
87 //error_log('MXChat Save: Processing email_blocker_header_content');
88 // Allow HTML content but sanitize it safely
89 $options[$name] = wp_kses_post($value);
90 break;
91 case 'similarity_threshold':
92 //error_log('MXChat Save: Processing similarity_threshold');
93 // Save to the options array
94 $options[$name] = $value;
95 break;
96 case 'user_message_bg_color':
97 case 'user_message_font_color':
98 case 'bot_message_bg_color':
99 case 'bot_message_font_color':
100 case 'top_bar_bg_color':
101 case 'send_button_font_color':
102 case 'chatbot_background_color':
103 case 'icon_color':
104 case 'chat_input_font_color':
105 case 'live_agent_message_bg_color':
106 case 'live_agent_message_font_color':
107 case 'mode_indicator_bg_color':
108 case 'mode_indicator_font_color':
109 case 'toolbar_icon_color':
110 //error_log('MXChat Save: Processing color value: ' . $name);
111 // Store color values directly
112 $options[$name] = $value;
113 break;
114 case 'live_agent_status':
115 //error_log('MXChat Save: Processing live_agent_status');
116 // Set the new value
117 $options[$name] = ($value === 'on') ? 'on' : 'off';
118 break;
119 case 'enable_woocommerce_integration':
120 //error_log('MXChat Save: Processing enable_woocommerce_integration');
121 // Handle values that used to be 1/0
122 $options[$name] = ($value === 'on' || $value === '1') ? 'on' : 'off';
123 break;
124 default:
125 // First check for rate limits settings
126 if (strpos($name, 'mxchat_options[rate_limits]') !== false) {
127 //error_log('MXChat Save: Detected rate_limits field: ' . $name);
128
129 // Extract role ID and setting from the name
130 preg_match('/\[rate_limits\]\[(.*?)\]\[(.*?)\]/', $name, $matches);
131 //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
132
133 if (isset($matches[1]) && isset($matches[2])) {
134 $role_id = $matches[1];
135 $setting_key = $matches[2]; // limit, timeframe, or message
136
137 //error_log('MXChat Save: Role ID = ' . $role_id . ', Setting Key = ' . $setting_key);
138
139 // Initialize rate_limits if it doesn't exist
140 if (!isset($options['rate_limits'])) {
141 // //error_log('MXChat Save: Initializing rate_limits array');
142 $options['rate_limits'] = [];
143 }
144
145 // Initialize role settings if it doesn't exist
146 if (!isset($options['rate_limits'][$role_id])) {
147 //error_log('MXChat Save: Initializing rate_limits for role: ' . $role_id);
148 $options['rate_limits'][$role_id] = [
149 'limit' => ($role_id === 'logged_out') ? '10' : '100',
150 'timeframe' => 'daily',
151 'message' => 'Rate limit exceeded. Please try again later.'
152 ];
153 }
154
155 // Update the specific setting
156 $options['rate_limits'][$role_id][$setting_key] = $value;
157 //error_log('MXChat Save: Updated rate_limits[' . $role_id . '][' . $setting_key . '] = ' . $value);
158 } else {
159 //error_log('MXChat Save: Failed to parse rate_limits pattern: ' . $name);
160 }
161 }
162 // Then check for role rate limits (old format)
163 else if (strpos($name, 'mxchat_options[role_rate_limits]') !== false) {
164 //error_log('MXChat Save: Processing role_rate_limits field: ' . $name);
165 // Extract role ID from the name
166 preg_match('/\[role_rate_limits\]\[(.*?)\]/', $name, $matches);
167 //error_log('MXChat Save: Regex matches: ' . print_r($matches, true));
168
169 if (isset($matches[1])) {
170 $role_id = $matches[1];
171 // Initialize role_rate_limits if it doesn't exist
172 if (!isset($options['role_rate_limits'])) {
173 //error_log('MXChat Save: Initializing role_rate_limits array');
174 $options['role_rate_limits'] = [];
175 }
176 // Update the specific role's rate limit
177 $options['role_rate_limits'][$role_id] = sanitize_text_field($value);
178 //error_log('MXChat Save: Updated role_rate_limits[' . $role_id . '] = ' . $value);
179 } else {
180 //error_log('MXChat Save: Failed to parse role_rate_limits pattern: ' . $name);
181 }
182 }
183 // Handle toggles
184 else if (strpos($name, 'toggle') !== false || in_array($name, [
185 'chat_persistence_toggle',
186 'privacy_toggle',
187 'complianz_toggle',
188 'chat_toolbar_toggle',
189 'show_pdf_upload_button',
190 'show_word_upload_button'
191 ])) {
192 //error_log('MXChat Save: Processing toggle: ' . $name);
193 $options[$name] = ($value === 'on') ? 'on' : 'off';
194 } else {
195 //error_log('MXChat Save: Processing standard field: ' . $name);
196 // Store all other values directly
197 $options[$name] = $value;
198 }
199 break;
200 }
201
202 // Save all updates to the options array
203 $updated = update_option('mxchat_options', $options);
204 //error_log('MXChat Save: Update result: ' . ($updated ? 'success' : 'unchanged') . ' for field: ' . $name);
205 //error_log('MXChat Save: Updated options array: ' . print_r($options, true));
206
207 // Always return success even if WordPress says nothing changed
208 // (which happens when the value is the same as before)
209 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
210 }
211
212
213 /**
214 * Handles AJAX request for saving chat settings
215 */
216 public function mxchat_save_prompts_setting_callback() {
217 check_ajax_referer('mxchat_prompts_setting_nonce');
218
219 if (!current_user_can('manage_options')) {
220 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
221 }
222
223 $name = isset($_POST['name']) ? $_POST['name'] : '';
224 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
225
226 //error_log('[MXCHAT-PROMPTS] Saving setting: ' . $name . ' = ' . $value);
227
228 if (empty($name)) {
229 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
230 }
231
232 // Handle Pinecone settings - BYPASS WORDPRESS SANITIZATION
233 if (strpos($name, 'mxchat_pinecone_addon_options') !== false) {
234 //error_log('[MXCHAT-PROMPTS] Processing Pinecone setting: ' . $name);
235
236 // Extract the field name
237 if (preg_match('/mxchat_pinecone_addon_options\[([^\]]+)\]/', $name, $matches)) {
238 $field_name = $matches[1];
239 //error_log('[MXCHAT-PROMPTS] Extracted field name: ' . $field_name);
240
241 // Get current options directly from database - NO WordPress filters
242 global $wpdb;
243 $current_options_raw = $wpdb->get_var(
244 $wpdb->prepare(
245 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
246 'mxchat_pinecone_addon_options'
247 )
248 );
249
250 // FIX: Handle the case where the option doesn't exist yet
251 if ($current_options_raw === null) {
252 // Option doesn't exist, create it with default values
253 $current_options = array(
254 'mxchat_use_pinecone' => '0',
255 'mxchat_pinecone_api_key' => '',
256 'mxchat_pinecone_host' => '',
257 'mxchat_pinecone_index' => '',
258 'mxchat_pinecone_environment' => ''
259 );
260 //error_log('[MXCHAT-PROMPTS] Option does not exist, creating with defaults');
261 } else {
262 // Unserialize the raw data
263 $current_options = maybe_unserialize($current_options_raw);
264 if (!is_array($current_options)) {
265 // Fallback to defaults if unserialization fails
266 $current_options = array(
267 'mxchat_use_pinecone' => '0',
268 'mxchat_pinecone_api_key' => '',
269 'mxchat_pinecone_host' => '',
270 'mxchat_pinecone_index' => '',
271 'mxchat_pinecone_environment' => ''
272 );
273 //error_log('[MXCHAT-PROMPTS] Failed to unserialize, using defaults');
274 }
275 }
276
277 //error_log('[MXCHAT-PROMPTS] Current options from DB: ' . print_r($current_options, true));
278
279 // Update the specific field with proper sanitization
280 switch ($field_name) {
281 case 'mxchat_use_pinecone':
282 $new_value = ($value === '1') ? '1' : '0';
283 break;
284 case 'mxchat_pinecone_api_key':
285 case 'mxchat_pinecone_host':
286 case 'mxchat_pinecone_index':
287 case 'mxchat_pinecone_environment':
288 $new_value = sanitize_text_field($value);
289 if ($field_name === 'mxchat_pinecone_host') {
290 $new_value = str_replace(['https://', 'http://'], '', $new_value);
291 }
292 break;
293 default:
294 wp_send_json_error(['message' => esc_html__('Unknown Pinecone field', 'mxchat')]);
295 }
296
297 $current_options[$field_name] = $new_value;
298 //error_log('[MXCHAT-PROMPTS] New value for ' . $field_name . ': "' . $new_value . '"');
299 //error_log('[MXCHAT-PROMPTS] Updated options: ' . print_r($current_options, true));
300
301 // Save directly to database to bypass WordPress sanitization
302 $serialized_options = maybe_serialize($current_options);
303
304 // FIX: Use INSERT ... ON DUPLICATE KEY UPDATE or separate INSERT/UPDATE logic
305 $option_exists = $wpdb->get_var(
306 $wpdb->prepare(
307 "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
308 'mxchat_pinecone_addon_options'
309 )
310 );
311
312 if ($option_exists > 0) {
313 // Update existing option
314 $save_result = $wpdb->update(
315 $wpdb->options,
316 array('option_value' => $serialized_options),
317 array('option_name' => 'mxchat_pinecone_addon_options'),
318 array('%s'),
319 array('%s')
320 );
321 //error_log('[MXCHAT-PROMPTS] Updated existing option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
322 } else {
323 // Insert new option
324 $save_result = $wpdb->insert(
325 $wpdb->options,
326 array(
327 'option_name' => 'mxchat_pinecone_addon_options',
328 'option_value' => $serialized_options,
329 'autoload' => 'yes'
330 ),
331 array('%s', '%s', '%s')
332 );
333 //error_log('[MXCHAT-PROMPTS] Inserted new option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
334 }
335
336 // Clear any WordPress option cache to ensure get_option() returns fresh data
337 wp_cache_delete('mxchat_pinecone_addon_options', 'options');
338
339 // IMPROVED VERIFICATION - Check if the database operation succeeded
340 if ($save_result !== false) {
341 // Double-check by reading fresh from database
342 $verification_raw = $wpdb->get_var(
343 $wpdb->prepare(
344 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
345 'mxchat_pinecone_addon_options'
346 )
347 );
348 $verification_options = maybe_unserialize($verification_raw);
349 $verified_value = isset($verification_options[$field_name]) ? $verification_options[$field_name] : 'NOT_FOUND';
350
351 //error_log('[MXCHAT-PROMPTS] Final verification - Expected: "' . $new_value . '", Got: "' . $verified_value . '"');
352
353 // Use loose comparison (==) instead of strict (===) to avoid type issues
354 if ($verified_value == $new_value || $save_result > 0) {
355 wp_send_json_success(['message' => esc_html__('Pinecone setting saved', 'mxchat')]);
356 } else {
357 // Still return success if the DB operation worked, even if verification is quirky
358 //error_log('[MXCHAT-PROMPTS] Verification mismatch but DB operation succeeded');
359 wp_send_json_success(['message' => esc_html__('Pinecone setting saved (DB success)', 'mxchat')]);
360 }
361 } else {
362 wp_send_json_error(['message' => esc_html__('Database save failed', 'mxchat')]);
363 }
364 } else {
365 wp_send_json_error(['message' => esc_html__('Invalid field name format', 'mxchat')]);
366 }
367
368 return; // Exit here for Pinecone settings
369 }
370 // Handle auto-sync settings (existing functionality)
371 if (strpos($name, 'mxchat_auto_sync_') === 0) {
372 $value = ($value === 'on' || $value === '1') ? '1' : '0';
373 $updated = update_option($name, $value);
374
375 if ($updated || get_option($name) === $value) {
376 wp_send_json_success(['message' => esc_html__('Auto-sync setting saved', 'mxchat')]);
377 } else {
378 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
379 }
380 }
381
382 // Handle other prompts options
383 $options = get_option('mxchat_prompts_options', []);
384 $options[$name] = $value;
385 $updated = update_option('mxchat_prompts_options', $options);
386
387 if ($updated) {
388 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
389 } else {
390 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
391 }
392 }
393
394
395 /**
396 * Handles AJAX request for Pinecone settings migration
397 */
398 public function ajax_migrate_pinecone_settings() {
399 // Verify nonce
400 if (!wp_verify_nonce($_POST['_ajax_nonce'] ?? '', 'mxchat_save_setting_nonce')) {
401 wp_send_json_error('Invalid nonce');
402 }
403
404 // Check permissions
405 if (!current_user_can('manage_options')) {
406 wp_send_json_error('Unauthorized access');
407 }
408
409 // Check if old Pinecone addon options exist
410 $old_options = get_option('mxchat_pinecone_addon_options', array());
411
412 if (empty($old_options)) {
413 wp_send_json_success(array('migrated' => false, 'message' => 'No old settings found'));
414 }
415
416 // Get current core plugin options
417 $current_options = get_option('mxchat_pinecone_addon_options', array());
418
419 // Only migrate if core options are empty or if explicitly requested
420 $should_migrate = empty($current_options) ||
421 (empty($current_options['mxchat_pinecone_api_key']) && !empty($old_options['mxchat_pinecone_api_key']));
422
423 if ($should_migrate) {
424 // Migrate settings with proper sanitization
425 $migrated_options = array(
426 'mxchat_use_pinecone' => $old_options['mxchat_use_pinecone'] ?? '0',
427 'mxchat_pinecone_api_key' => sanitize_text_field($old_options['mxchat_pinecone_api_key'] ?? ''),
428 'mxchat_pinecone_host' => sanitize_text_field($old_options['mxchat_pinecone_host'] ?? ''),
429 'mxchat_pinecone_index' => sanitize_text_field($old_options['mxchat_pinecone_index'] ?? ''),
430 'mxchat_pinecone_environment' => sanitize_text_field($old_options['mxchat_pinecone_environment'] ?? '')
431 );
432
433 update_option('mxchat_pinecone_addon_options', $migrated_options);
434
435 wp_send_json_success(array(
436 'migrated' => true,
437 'message' => 'Settings migrated successfully from Pinecone add-on'
438 ));
439 } else {
440 wp_send_json_success(array(
441 'migrated' => false,
442 'message' => 'Settings already exist in core plugin'
443 ));
444 }
445 }
446
447
448
449 // ========================================
450 // LICENSE AJAX HANDLERS
451 // ========================================
452
453 /**
454 * Validates and activates chat license via AJAX
455 */
456 public function mxchat_handle_activate_license() {
457 // Check nonce
458 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
459 wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
460 return;
461 }
462
463 // Verify user capabilities
464 if (!current_user_can('manage_options')) {
465 wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
466 return;
467 }
468
469 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
470 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
471
472 if (empty($license_key) || empty($customer_email)) {
473 wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
474 return;
475 }
476
477 $product_id = 'MxChatPRO';
478
479 // **CHANGED TO HTTPS**
480 $response = wp_remote_get(
481 add_query_arg(
482 array(
483 'wc-api' => 'software-api',
484 'request' => 'activation',
485 'email' => $customer_email,
486 'license_key' => $license_key,
487 'product_id' => $product_id
488 ),
489 'https://mxchat.ai/' // **CHANGED FROM HTTP TO HTTPS**
490 ),
491 array(
492 'timeout' => 60, // 60 seconds timeout
493 'sslverify' => true // **CHANGED TO TRUE FOR HTTPS**
494 )
495 );
496
497 if (is_wp_error($response)) {
498 $error_message = $response->get_error_message();
499 //error_log('MxChat License Activation Error: ' . $error_message);
500 wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $error_message);
501 return;
502 }
503
504 $response_code = wp_remote_retrieve_response_code($response);
505 $body = wp_remote_retrieve_body($response);
506
507 // Log response for debugging
508 //error_log('MxChat License Response Code: ' . $response_code);
509 //error_log('MxChat License Response Body: ' . $body);
510
511 if ($response_code !== 200) {
512 wp_send_json_error(esc_html__('Server returned error code: ', 'mxchat') . $response_code);
513 return;
514 }
515
516 $data = json_decode($body);
517
518 if ($data && isset($data->activated) && $data->activated) {
519 update_option('mxchat_license_status', 'active');
520 update_option('mxchat_pro_email', $customer_email);
521 update_option('mxchat_activation_key', $license_key);
522 delete_option('mxchat_license_error'); // Clear any previous errors
523 wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
524 } else {
525 $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
526 update_option('mxchat_license_status', 'inactive');
527 update_option('mxchat_license_error', $error_message);
528 wp_send_json_error($error_message);
529 }
530 }
531
532
533 /**
534 * Validates license via AJAX with email and key
535 */
536 public function mxchat_check_license_status() {
537 // Verify nonce
538 check_ajax_referer($this->mxchat_get_nonce_action(), 'security');
539
540 $email = sanitize_email($_POST['email']);
541 $key = sanitize_text_field($_POST['key']);
542
543 // Check if this license is actually active in your system
544 $is_active = (get_option('mxchat_license_status') === 'active' &&
545 get_option('mxchat_pro_email') === $email &&
546 get_option('mxchat_activation_key') === $key);
547
548 wp_send_json(array(
549 'is_active' => $is_active
550 ));
551 }
552
553
554 // ========================================
555 // ACTIONS & INTENTS AJAX HANDLERS
556 // ========================================
557
558 /**
559 * Validates nonce and returns JSON error on failure
560 */
561 public function mxchat_toggle_action() {
562 // Check nonce
563 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
564 wp_send_json_error(array('message' => 'Security check failed'));
565 return;
566 }
567
568 // Check permissions
569 if (!current_user_can('manage_options')) {
570 wp_send_json_error(array('message' => 'Permission denied'));
571 return;
572 }
573
574 // Validate params
575 $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
576 $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;
577
578 if (!$intent_id) {
579 wp_send_json_error(array('message' => 'Invalid action ID'));
580 return;
581 }
582
583 // Update the intent/action status in the database
584 global $wpdb;
585 $table_name = $wpdb->prefix . 'mxchat_intents';
586
587 // Using the 'enabled' field - add this field if it doesn't exist
588 $result = $wpdb->update(
589 $table_name,
590 array('enabled' => $enabled ? 1 : 0),
591 array('id' => $intent_id),
592 array('%d'),
593 array('%d')
594 );
595
596 if ($result === false) {
597 wp_send_json_error(array('message' => 'Database error'));
598 return;
599 }
600
601 wp_send_json_success();
602 }
603
604
605 /**
606 * Validates permissions for AJAX request handling
607 */
608 public function mxchat_update_intent_threshold() {
609 // Check permissions
610 if (!current_user_can('manage_options')) {
611 if (wp_doing_ajax()) {
612 wp_send_json_error(array('message' => 'Unauthorized user'));
613 return;
614 }
615 wp_die(esc_html__('Unauthorized user', 'mxchat'));
616 }
617
618 // Verify nonce
619 check_admin_referer('mxchat_update_intent_threshold_nonce');
620
621 // Process the update if we have valid data
622 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
623 global $wpdb;
624 $table_name = $wpdb->prefix . 'mxchat_intents';
625 $intent_id = intval($_POST['intent_id']);
626 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
627 $similarity_threshold = $threshold_percentage / 100;
628
629 $result = $wpdb->update(
630 $table_name,
631 ['similarity_threshold' => $similarity_threshold],
632 ['id' => $intent_id],
633 ['%f'],
634 ['%d']
635 );
636
637 // Handle AJAX requests
638 if (wp_doing_ajax()) {
639 if ($result === false) {
640 wp_send_json_error(array('message' => 'Failed to update threshold'));
641 } else {
642 wp_send_json_success(array('threshold' => $threshold_percentage));
643 }
644 return;
645 }
646 }
647
648 // Redirect for regular form submissions
649 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
650 exit;
651 }
652
653 // ========================================
654 // HELPER METHODS
655 // ========================================
656
657 /**
658 * Returns a specific nonce action string
659 */
660 private function mxchat_get_nonce_action() {
661 return 'mxchat_license_nonce';
662 }
663
664 /**
665 * Checks if a value has changed from old to new
666 */
667 private function mxchat_has_value_changed($old_value, $new_value) {
668 // Handle null values
669 if ($old_value === null && $new_value === '') {
670 return false;
671 }
672
673 // Handle array values (like additional_popular_questions)
674 if (is_array($old_value) && is_array($new_value)) {
675 // Convert both to JSON for comparison to handle ordering differences
676 return json_encode($old_value) !== json_encode($new_value);
677 }
678
679 // Handle toggle/checkbox values consistently
680 if (in_array($old_value, ['on', '1', 1, true]) && in_array($new_value, ['on', '1', 1, true])) {
681 return false;
682 }
683 if (in_array($old_value, ['off', '0', 0, false, '']) && in_array($new_value, ['off', '0', 0, false, ''])) {
684 return false;
685 }
686
687 // Default direct comparison
688 return $old_value !== $new_value;
689 }
690
691 }
692
693 // Initialize the AJAX handler
694 new MxChat_Ajax_Handler();
695