PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.4.2
MxChat – AI Chatbot & Content Generation for WordPress v2.4.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
mxchat-basic / admin / class-ajax-handler.php

class-ajax-handler.php in MxChat – AI Chatbot & Content Generation for WordPress 2.4.2, at admin/class-ajax-handler.php

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