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

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