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

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