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
mxchat-basic / admin / class-ajax-handler.php

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

818 lines 33.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
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 // No cache clearing needed since we removed caching
252
253 wp_send_json_success(array(
254 'message' => 'Bot selection saved',
255 'bot_id' => $bot_id
256 ));
257 }
258
259 /**
260 * Handles AJAX request for saving chat settings
261 */
262 public function mxchat_save_prompts_setting_callback() {
263 check_ajax_referer('mxchat_prompts_setting_nonce');
264
265 if (!current_user_can('manage_options')) {
266 wp_send_json_error(['message' => esc_html__('Unauthorized', 'mxchat')]);
267 }
268
269 $name = isset($_POST['name']) ? $_POST['name'] : '';
270 $value = isset($_POST['value']) ? stripslashes($_POST['value']) : '';
271
272 //error_log('[MXCHAT-PROMPTS] Saving setting: ' . $name . ' = ' . $value);
273
274 if (empty($name)) {
275 wp_send_json_error(['message' => esc_html__('Invalid field name', 'mxchat')]);
276 }
277
278 // Handle Pinecone settings - BYPASS WORDPRESS SANITIZATION
279 if (strpos($name, 'mxchat_pinecone_addon_options') !== false) {
280 //error_log('[MXCHAT-PROMPTS] Processing Pinecone setting: ' . $name);
281
282 // Extract the field name
283 if (preg_match('/mxchat_pinecone_addon_options\[([^\]]+)\]/', $name, $matches)) {
284 $field_name = $matches[1];
285 //error_log('[MXCHAT-PROMPTS] Extracted field name: ' . $field_name);
286
287 // Get current options directly from database - NO WordPress filters
288 global $wpdb;
289 $current_options_raw = $wpdb->get_var(
290 $wpdb->prepare(
291 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
292 'mxchat_pinecone_addon_options'
293 )
294 );
295
296 // FIX: Handle the case where the option doesn't exist yet
297 if ($current_options_raw === null) {
298 // Option doesn't exist, create it with default values
299 $current_options = array(
300 'mxchat_use_pinecone' => '0',
301 'mxchat_pinecone_api_key' => '',
302 'mxchat_pinecone_host' => '',
303 'mxchat_pinecone_index' => '',
304 'mxchat_pinecone_environment' => ''
305 );
306 //error_log('[MXCHAT-PROMPTS] Option does not exist, creating with defaults');
307 } else {
308 // Unserialize the raw data
309 $current_options = maybe_unserialize($current_options_raw);
310 if (!is_array($current_options)) {
311 // Fallback to defaults if unserialization fails
312 $current_options = array(
313 'mxchat_use_pinecone' => '0',
314 'mxchat_pinecone_api_key' => '',
315 'mxchat_pinecone_host' => '',
316 'mxchat_pinecone_index' => '',
317 'mxchat_pinecone_environment' => ''
318 );
319 //error_log('[MXCHAT-PROMPTS] Failed to unserialize, using defaults');
320 }
321 }
322
323 //error_log('[MXCHAT-PROMPTS] Current options from DB: ' . print_r($current_options, true));
324
325 // Update the specific field with proper sanitization
326 switch ($field_name) {
327 case 'mxchat_use_pinecone':
328 $new_value = ($value === '1') ? '1' : '0';
329 break;
330 case 'mxchat_pinecone_api_key':
331 case 'mxchat_pinecone_host':
332 case 'mxchat_pinecone_index':
333 case 'mxchat_pinecone_environment':
334 $new_value = sanitize_text_field($value);
335 if ($field_name === 'mxchat_pinecone_host') {
336 $new_value = str_replace(['https://', 'http://'], '', $new_value);
337 }
338 break;
339 default:
340 wp_send_json_error(['message' => esc_html__('Unknown Pinecone field', 'mxchat')]);
341 }
342
343 $current_options[$field_name] = $new_value;
344 //error_log('[MXCHAT-PROMPTS] New value for ' . $field_name . ': "' . $new_value . '"');
345 //error_log('[MXCHAT-PROMPTS] Updated options: ' . print_r($current_options, true));
346
347 // Save directly to database to bypass WordPress sanitization
348 $serialized_options = maybe_serialize($current_options);
349
350 // FIX: Use INSERT ... ON DUPLICATE KEY UPDATE or separate INSERT/UPDATE logic
351 $option_exists = $wpdb->get_var(
352 $wpdb->prepare(
353 "SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name = %s",
354 'mxchat_pinecone_addon_options'
355 )
356 );
357
358 if ($option_exists > 0) {
359 // Update existing option
360 $save_result = $wpdb->update(
361 $wpdb->options,
362 array('option_value' => $serialized_options),
363 array('option_name' => 'mxchat_pinecone_addon_options'),
364 array('%s'),
365 array('%s')
366 );
367 //error_log('[MXCHAT-PROMPTS] Updated existing option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
368 } else {
369 // Insert new option
370 $save_result = $wpdb->insert(
371 $wpdb->options,
372 array(
373 'option_name' => 'mxchat_pinecone_addon_options',
374 'option_value' => $serialized_options,
375 'autoload' => 'yes'
376 ),
377 array('%s', '%s', '%s')
378 );
379 //error_log('[MXCHAT-PROMPTS] Inserted new option, result: ' . ($save_result !== false ? 'SUCCESS' : 'FAILED'));
380 }
381
382 // Clear any WordPress option cache to ensure get_option() returns fresh data
383 wp_cache_delete('mxchat_pinecone_addon_options', 'options');
384
385 // IMPROVED VERIFICATION - Check if the database operation succeeded
386 if ($save_result !== false) {
387 // Double-check by reading fresh from database
388 $verification_raw = $wpdb->get_var(
389 $wpdb->prepare(
390 "SELECT option_value FROM {$wpdb->options} WHERE option_name = %s",
391 'mxchat_pinecone_addon_options'
392 )
393 );
394 $verification_options = maybe_unserialize($verification_raw);
395 $verified_value = isset($verification_options[$field_name]) ? $verification_options[$field_name] : 'NOT_FOUND';
396
397 //error_log('[MXCHAT-PROMPTS] Final verification - Expected: "' . $new_value . '", Got: "' . $verified_value . '"');
398
399 // Use loose comparison (==) instead of strict (===) to avoid type issues
400 if ($verified_value == $new_value || $save_result > 0) {
401 wp_send_json_success(['message' => esc_html__('Pinecone setting saved', 'mxchat')]);
402 } else {
403 // Still return success if the DB operation worked, even if verification is quirky
404 //error_log('[MXCHAT-PROMPTS] Verification mismatch but DB operation succeeded');
405 wp_send_json_success(['message' => esc_html__('Pinecone setting saved (DB success)', 'mxchat')]);
406 }
407 } else {
408 wp_send_json_error(['message' => esc_html__('Database save failed', 'mxchat')]);
409 }
410 } else {
411 wp_send_json_error(['message' => esc_html__('Invalid field name format', 'mxchat')]);
412 }
413
414 return; // Exit here for Pinecone settings
415 }
416 // Handle auto-sync settings (existing functionality)
417 if (strpos($name, 'mxchat_auto_sync_') === 0) {
418 $value = ($value === 'on' || $value === '1') ? '1' : '0';
419 $updated = update_option($name, $value);
420
421 if ($updated || get_option($name) === $value) {
422 wp_send_json_success(['message' => esc_html__('Auto-sync setting saved', 'mxchat')]);
423 } else {
424 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
425 }
426 }
427
428 // Handle other prompts options
429 $options = get_option('mxchat_prompts_options', []);
430 $options[$name] = $value;
431 $updated = update_option('mxchat_prompts_options', $options);
432
433 if ($updated) {
434 wp_send_json_success(['message' => esc_html__('Setting saved', 'mxchat')]);
435 } else {
436 wp_send_json_error(['message' => esc_html__('No changes detected', 'mxchat')]);
437 }
438 }
439
440
441 /**
442 * Handles AJAX request for Pinecone settings migration
443 */
444 public function ajax_migrate_pinecone_settings() {
445 // Verify nonce
446 if (!wp_verify_nonce($_POST['_ajax_nonce'] ?? '', 'mxchat_save_setting_nonce')) {
447 wp_send_json_error('Invalid nonce');
448 }
449
450 // Check permissions
451 if (!current_user_can('manage_options')) {
452 wp_send_json_error('Unauthorized access');
453 }
454
455 // Check if old Pinecone addon options exist
456 $old_options = get_option('mxchat_pinecone_addon_options', array());
457
458 if (empty($old_options)) {
459 wp_send_json_success(array('migrated' => false, 'message' => 'No old settings found'));
460 }
461
462 // Get current core plugin options
463 $current_options = get_option('mxchat_pinecone_addon_options', array());
464
465 // Only migrate if core options are empty or if explicitly requested
466 $should_migrate = empty($current_options) ||
467 (empty($current_options['mxchat_pinecone_api_key']) && !empty($old_options['mxchat_pinecone_api_key']));
468
469 if ($should_migrate) {
470 // Migrate settings with proper sanitization
471 $migrated_options = array(
472 'mxchat_use_pinecone' => $old_options['mxchat_use_pinecone'] ?? '0',
473 'mxchat_pinecone_api_key' => sanitize_text_field($old_options['mxchat_pinecone_api_key'] ?? ''),
474 'mxchat_pinecone_host' => sanitize_text_field($old_options['mxchat_pinecone_host'] ?? ''),
475 'mxchat_pinecone_index' => sanitize_text_field($old_options['mxchat_pinecone_index'] ?? ''),
476 'mxchat_pinecone_environment' => sanitize_text_field($old_options['mxchat_pinecone_environment'] ?? '')
477 );
478
479 update_option('mxchat_pinecone_addon_options', $migrated_options);
480
481 wp_send_json_success(array(
482 'migrated' => true,
483 'message' => 'Settings migrated successfully from Pinecone add-on'
484 ));
485 } else {
486 wp_send_json_success(array(
487 'migrated' => false,
488 'message' => 'Settings already exist in core plugin'
489 ));
490 }
491 }
492
493
494 // ========================================
495 // LICENSE AJAX HANDLERS
496 // ========================================
497
498 /**
499 * Validates and activates chat license via AJAX
500 */
501 public function mxchat_handle_activate_license() {
502 // Check nonce
503 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
504 wp_send_json_error(esc_html__('Invalid security token', 'mxchat'));
505 return;
506 }
507
508 // Verify user capabilities
509 if (!current_user_can('manage_options')) {
510 wp_send_json_error(esc_html__('Unauthorized access', 'mxchat'));
511 return;
512 }
513
514 $license_key = isset($_POST['mxchat_activation_key']) ? sanitize_text_field($_POST['mxchat_activation_key']) : '';
515 $customer_email = isset($_POST['mxchat_pro_email']) ? sanitize_email($_POST['mxchat_pro_email']) : '';
516
517 if (empty($license_key) || empty($customer_email)) {
518 wp_send_json_error(esc_html__('Email or License Key is missing', 'mxchat'));
519 return;
520 }
521
522 $product_id = 'MxChatPRO';
523 $domain = parse_url(home_url(), PHP_URL_HOST); // Get the current domain
524
525 // Call WooCommerce Software API for activation (not just validation)
526 $response = wp_remote_get(
527 add_query_arg(
528 array(
529 'wc-api' => 'software-api',
530 'request' => 'activation',
531 'email' => $customer_email,
532 'license_key' => $license_key,
533 'product_id' => $product_id,
534 'instance' => $domain, // THIS IS KEY - include the domain as instance
535 'platform' => 'wordpress' // Optional but good to include
536 ),
537 'https://mxchat.ai/'
538 ),
539 array(
540 'timeout' => 60,
541 'sslverify' => true
542 )
543 );
544
545 if (is_wp_error($response)) {
546 $error_message = $response->get_error_message();
547 //error_log('MxChat License Activation Error: ' . $error_message);
548 wp_send_json_error(esc_html__('Activation failed due to a server error: ', 'mxchat') . $error_message);
549 return;
550 }
551
552 $response_code = wp_remote_retrieve_response_code($response);
553 $body = wp_remote_retrieve_body($response);
554
555 // Log response for debugging
556 //error_log('MxChat License Response Code: ' . $response_code);
557 //error_log('MxChat License Response Body: ' . $body);
558
559 if ($response_code !== 200) {
560 wp_send_json_error(esc_html__('Server returned error code: ', 'mxchat') . $response_code);
561 return;
562 }
563
564 $data = json_decode($body);
565
566 if ($data && isset($data->activated) && $data->activated) {
567 // Success - save local options
568 update_option('mxchat_license_status', 'active');
569 update_option('mxchat_pro_email', $customer_email);
570 update_option('mxchat_activation_key', $license_key);
571 delete_option('mxchat_license_error');
572
573 // Also track on your website (this is your existing domain tracking)
574 $this->track_domain_on_website($license_key, $customer_email, $domain);
575
576 wp_send_json_success(array('message' => esc_html__('License activated successfully', 'mxchat')));
577 } else {
578 $error_message = isset($data->error) ? $data->error : esc_html__('Activation failed', 'mxchat');
579 update_option('mxchat_license_status', 'inactive');
580 update_option('mxchat_license_error', $error_message);
581
582 //error_log('MxChat Activation failed: ' . $error_message);
583 wp_send_json_error($error_message);
584 }
585 }
586
587 /**
588 * Track domain on your website (separate from WooCommerce activation)
589 */
590 private function track_domain_on_website($license_key, $email, $domain) {
591 // This calls your website's tracking API
592 wp_remote_post('https://mxchat.ai/mxchat-api/activate-license', array(
593 'body' => array(
594 'mxchat_pro_email' => $email,
595 'mxchat_activation_key' => $license_key,
596 'domain' => $domain
597 ),
598 'timeout' => 10,
599 'sslverify' => true
600 ));
601 }
602
603 /**
604 * Validates license via AJAX with email and key
605 */
606 public function mxchat_check_license_status() {
607 // Verify nonce
608 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
609 wp_send_json_error('Security check failed');
610 return;
611 }
612
613 // Add isset checks for safety
614 $email = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
615 $key = isset($_POST['key']) ? sanitize_text_field($_POST['key']) : '';
616
617 // Check if this license is actually active in your system
618 $is_active = (get_option('mxchat_license_status') === 'active' &&
619 get_option('mxchat_pro_email') === $email &&
620 get_option('mxchat_activation_key') === $key);
621
622 wp_send_json(array(
623 'is_active' => $is_active
624 ));
625 }
626
627 /**
628 * Handle license deactivation - Complete version for plugin
629 */
630 function mxchat_deactivate_license() {
631 // Add debugging
632 //error_log('MxChat deactivate function called');
633
634 // Check nonce
635 if (!check_ajax_referer('mxchat_activate_license_nonce', 'security', false)) {
636 //error_log('MxChat deactivate: Nonce check failed');
637 wp_send_json_error('Security check failed.');
638 return;
639 }
640
641 //error_log('MxChat deactivate: Nonce check passed');
642
643 $license_key = get_option('mxchat_activation_key');
644 $email = get_option('mxchat_pro_email');
645 $domain = parse_url(home_url(), PHP_URL_HOST);
646
647 //error_log('MxChat deactivate: License: ' . $license_key . ', Email: ' . $email . ', Domain: ' . $domain);
648
649 if (empty($license_key) || empty($email)) {
650 //error_log('MxChat deactivate: No active license found');
651 wp_send_json_error('No active license found.');
652 return;
653 }
654
655 // Clear local license data first
656 delete_option('mxchat_license_status');
657 delete_option('mxchat_pro_email');
658 delete_option('mxchat_activation_key');
659 delete_option('mxchat_license_error');
660
661 //error_log('MxChat deactivate: Local data cleared');
662
663 // Notify your website's API to properly deactivate
664 $response = wp_remote_post('https://mxchat.ai/mxchat-api/deactivate-license', array(
665 'body' => array(
666 'license_key' => $license_key,
667 'email' => $email,
668 'domain' => $domain
669 ),
670 'timeout' => 15,
671 'sslverify' => true
672 ));
673
674 if (is_wp_error($response)) {
675 //error_log('MxChat deactivate: Server error - ' . $response->get_error_message());
676 wp_send_json_success(array(
677 'message' => 'License deactivated locally. Server could not be contacted to free activation slot.',
678 'server_notified' => false
679 ));
680 return;
681 }
682
683 $response_body = wp_remote_retrieve_body($response);
684 $response_data = json_decode($response_body, true);
685
686 //error_log('MxChat deactivate: Server response - ' . $response_body);
687
688 if (isset($response_data['success']) && $response_data['success']) {
689 //error_log('MxChat deactivate: Success with server notification');
690 wp_send_json_success(array(
691 'message' => 'License deactivated successfully. Activation slot has been freed up.',
692 'server_notified' => true
693 ));
694 } else {
695 //error_log('MxChat deactivate: Server responded but deactivation may have failed');
696 wp_send_json_success(array(
697 'message' => 'License deactivated locally. Please check your account dashboard to verify the activation was freed.',
698 'server_notified' => false
699 ));
700 }
701 }
702
703
704 // ========================================
705 // ACTIONS & INTENTS AJAX HANDLERS
706 // ========================================
707
708 /**
709 * Validates nonce and returns JSON error on failure
710 */
711 public function mxchat_toggle_action() {
712 // Check nonce
713 if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'mxchat_actions_nonce')) {
714 wp_send_json_error(array('message' => 'Security check failed'));
715 return;
716 }
717
718 // Check permissions
719 if (!current_user_can('manage_options')) {
720 wp_send_json_error(array('message' => 'Permission denied'));
721 return;
722 }
723
724 // Validate params
725 $intent_id = isset($_POST['intent_id']) ? intval($_POST['intent_id']) : 0;
726 $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : false;
727
728 if (!$intent_id) {
729 wp_send_json_error(array('message' => 'Invalid action ID'));
730 return;
731 }
732
733 // Update the intent/action status in the database
734 global $wpdb;
735 $table_name = $wpdb->prefix . 'mxchat_intents';
736
737 // Using the 'enabled' field - add this field if it doesn't exist
738 $result = $wpdb->update(
739 $table_name,
740 array('enabled' => $enabled ? 1 : 0),
741 array('id' => $intent_id),
742 array('%d'),
743 array('%d')
744 );
745
746 if ($result === false) {
747 wp_send_json_error(array('message' => 'Database error'));
748 return;
749 }
750
751 wp_send_json_success();
752 }
753
754
755 /**
756 * Validates permissions for AJAX request handling
757 */
758 public function mxchat_update_intent_threshold() {
759 // Check permissions
760 if (!current_user_can('manage_options')) {
761 if (wp_doing_ajax()) {
762 wp_send_json_error(array('message' => 'Unauthorized user'));
763 return;
764 }
765 wp_die(esc_html__('Unauthorized user', 'mxchat'));
766 }
767
768 // Verify nonce
769 check_admin_referer('mxchat_update_intent_threshold_nonce');
770
771 // Process the update if we have valid data
772 if (isset($_POST['intent_id'], $_POST['intent_threshold'])) {
773 global $wpdb;
774 $table_name = $wpdb->prefix . 'mxchat_intents';
775 $intent_id = intval($_POST['intent_id']);
776 $threshold_percentage = max(70, min(95, intval($_POST['intent_threshold'])));
777 $similarity_threshold = $threshold_percentage / 100;
778
779 $result = $wpdb->update(
780 $table_name,
781 ['similarity_threshold' => $similarity_threshold],
782 ['id' => $intent_id],
783 ['%f'],
784 ['%d']
785 );
786
787 // Handle AJAX requests
788 if (wp_doing_ajax()) {
789 if ($result === false) {
790 wp_send_json_error(array('message' => 'Failed to update threshold'));
791 } else {
792 wp_send_json_success(array('threshold' => $threshold_percentage));
793 }
794 return;
795 }
796 }
797
798 // Redirect for regular form submissions
799 wp_safe_redirect(admin_url('admin.php?page=mxchat-actions&updated=true'));
800 exit;
801 }
802
803 // ========================================
804 // HELPER METHODS
805 // ========================================
806
807 /**
808 * Returns a specific nonce action string
809 */
810 private function mxchat_get_nonce_action() {
811 return 'mxchat_license_nonce';
812 }
813
814 }
815
816 // Initialize the AJAX handler
817 new MxChat_Ajax_Handler();
818