PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.1.4
MxChat – AI Chatbot & Content Generation for WordPress v3.1.4
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
← All changes | mxchat-basic.php +23 -387 3.2.143.1.4 View file →
@@ -2,9 +2,9 @@
2 2 /**
3 3 * Plugin Name: MxChat
4 4 * Plugin URI: https://mxchat.ai/
5 5 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
6 - * Version: 3.2.14
6 + * Version: 3.1.4
7 7 * Author: MxChat
8 8 * Author URI: https://mxchat.ai
9 9 * License: GPLv2 or later
10 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
@@ -29,27 +29,8 @@
29 29 }
30 30 define('MXCHAT_VERSION', $version);
31 31 }
32 32
33 -/**
34 - * Honest, versioned User-Agent for MXChat remote-content ingestion fetches
35 - * (Knowledge Base PDF import, URL import, sitemap / website crawl).
36 - *
37 - * WAF rulesets (SiteGround/ModSecurity, Wordfence, Cloudflare managed rules)
38 - * flag stale spoofed-browser UAs as scrapers and return 403 — which silently
39 - * broke the single most common KB source: self-hosted media on the site's own
40 - * domain. A truthful crawler identifier is the industry norm for well-behaved
41 - * bots and lets a site owner allowlist "MXChatBot" in their WAF. Filterable so
42 - * a locked-down host can supply a different string without a code change.
43 - */
44 -if (!function_exists('mxchat_ingest_user_agent')) {
45 - function mxchat_ingest_user_agent() {
46 - $version = defined('MXCHAT_VERSION') ? MXCHAT_VERSION : '1.0';
47 - $ua = 'MXChatBot/' . $version . ' (+https://mxchat.ai/bot)';
48 - return apply_filters('mxchat_ingest_user_agent', $ua);
49 - }
50 -}
51 -
52 33 function mxchat_load_textdomain() {
53 34 $domain = 'mxchat';
54 35 $locale = determine_locale();
55 36
@@ -65,84 +46,8 @@
65 46 }
66 47 add_action('init', 'mxchat_load_textdomain');
67 48
68 49 /**
69 - * One-time migration: gemini-3-pro-preview was shut down by Google on March 9, 2026.
70 - * Existing installs with the dead ID get auto-remapped to gemini-3.1-pro-preview
71 - * (Google's official migration target) the first time admin_init fires after update.
72 - */
73 -add_action('admin_init', function () {
74 - if (get_option('mxchat_gemini_3_remap_done')) {
75 - return;
76 - }
77 - $opts = get_option('mxchat_options');
78 - if (is_array($opts) && isset($opts['model']) && $opts['model'] === 'gemini-3-pro-preview') {
79 - $opts['model'] = 'gemini-3.1-pro-preview';
80 - update_option('mxchat_options', $opts);
81 - }
82 - if (is_array($opts) && isset($opts['content_model']) && $opts['content_model'] === 'gemini-3-pro-preview') {
83 - $opts['content_model'] = 'gemini-3.1-pro-preview';
84 - update_option('mxchat_options', $opts);
85 - }
86 - update_option('mxchat_gemini_3_remap_done', 1);
87 -});
88 -
89 -/**
90 - * One-time migration: the Grok 2 family was retired by xAI (grok-2, grok-2-1212,
91 - * grok-2-latest, grok-2-vision-1212 all return 400 "Model not found"). Existing
92 - * installs with the dead ID get auto-remapped to grok-4-1-fast-non-reasoning
93 - * (modern, fast, broadly available) the first time admin_init fires after update.
94 - */
95 -add_action('admin_init', function () {
96 - if (get_option('mxchat_grok_2_remap_done')) {
97 - return;
98 - }
99 - $opts = get_option('mxchat_options');
100 - if (is_array($opts) && isset($opts['model']) && $opts['model'] === 'grok-2') {
101 - $opts['model'] = 'grok-4-1-fast-non-reasoning';
102 - update_option('mxchat_options', $opts);
103 - }
104 - if (is_array($opts) && isset($opts['content_model']) && $opts['content_model'] === 'grok-2') {
105 - $opts['content_model'] = 'grok-4-1-fast-non-reasoning';
106 - update_option('mxchat_options', $opts);
107 - }
108 - update_option('mxchat_grok_2_remap_done', 1);
109 -});
110 -
111 -/**
112 - * One-time migration: Anthropic retired the Claude 4 (2025-05-14) snapshots on
113 - * June 15, 2026 — claude-opus-4-20250514 and claude-sonnet-4-20250514 now return
114 - * an API error. Existing installs with a dead ID get auto-remapped to the current
115 - * equivalents Anthropic recommends (Opus 4.8 / Sonnet 4.6) the first time admin_init
116 - * fires after update. Mirrors the gemini-3-pro-preview / grok-2 rescues above.
117 - */
118 -add_action('admin_init', function () {
119 - if (get_option('mxchat_claude_4_retire_remap_done')) {
120 - return;
121 - }
122 - $map = array(
123 - 'claude-opus-4-20250514' => 'claude-opus-4-8',
124 - 'claude-sonnet-4-20250514' => 'claude-sonnet-4-6',
125 - );
126 - $opts = get_option('mxchat_options');
127 - if (is_array($opts)) {
128 - $changed = false;
129 - if (isset($opts['model']) && isset($map[$opts['model']])) {
130 - $opts['model'] = $map[$opts['model']];
131 - $changed = true;
132 - }
133 - if (isset($opts['content_model']) && isset($map[$opts['content_model']])) {
134 - $opts['content_model'] = $map[$opts['content_model']];
135 - $changed = true;
136 - }
137 - if ($changed) {
138 - update_option('mxchat_options', $opts);
139 - }
140 - }
141 - update_option('mxchat_claude_4_retire_remap_done', 1);
142 -});
143 -
144 -/**
145 50 * Exclude MxChat assets from caching plugin optimizations
146 51 *
147 52 * This prevents issues with WP Rocket, LiteSpeed Cache, Autoptimize, WP Super Cache,
148 53 * W3 Total Cache, SG Optimizer, and similar plugins that may break the chatbot by
@@ -345,67 +250,11 @@
345 250 $rejected[] = 'wp-admin/admin-ajax.php';
346 251 return $rejected;
347 252 });
348 253
349 -// ── Page-cache bypass for chat AJAX (companion to the 3.2.6 nonce-race hotfix)
350 -// Each cache plugin gets its own filter export so that visitors hitting an
351 -// edge-cached page never receive cached chat-AJAX responses. The chat send /
352 -// stream send / file upload all POST to /wp-admin/admin-ajax.php with
353 -// `action=mxchat_*`. Without these exports, a cache plugin can stale a response
354 -// and break the per-session nonce flow on the first message.
355 -
356 -// WP Rocket — `rocket_cache_reject_uri` takes a flat array of regex strings.
357 -add_filter('rocket_cache_reject_uri', function($uris) {
358 - if (!is_array($uris)) $uris = array();
359 - $uris[] = '/wp-admin/admin-ajax\.php\?action=mxchat_.*';
360 - return $uris;
361 -});
362 -
363 -// LiteSpeed Cache — `litespeed_cache_no_cache_for_request` short-circuits
364 -// caching when the request matches our chat-AJAX pattern.
365 -add_filter('litespeed_cache_no_cache_for_request', function($no_cache) {
366 - if ($no_cache) return $no_cache;
367 - if (!empty($_SERVER['REQUEST_URI']) &&
368 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
369 - !empty($_REQUEST['action']) &&
370 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
371 - return true;
372 - }
373 - return $no_cache;
374 -});
375 -
376 -// W3 Total Cache — `w3tc_pgcache_request_skip_uri` flips page-cache off when
377 -// the URI matches.
378 -add_filter('w3tc_pgcache_request_skip_uri', function($skip) {
379 - if ($skip) return $skip;
380 - if (!empty($_SERVER['REQUEST_URI']) &&
381 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
382 - !empty($_REQUEST['action']) &&
383 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
384 - return true;
385 - }
386 - return $skip;
387 -});
388 -
389 -// FlyingPress — `flying_press_cacheable` takes a boolean and is run per
390 -// request. Same pattern as LiteSpeed / W3TC.
391 -add_filter('flying_press_cacheable', function($cacheable) {
392 - if (!$cacheable) return $cacheable;
393 - if (!empty($_SERVER['REQUEST_URI']) &&
394 - strpos($_SERVER['REQUEST_URI'], '/wp-admin/admin-ajax.php') !== false &&
395 - !empty($_REQUEST['action']) &&
396 - strpos((string) $_REQUEST['action'], 'mxchat_') === 0) {
397 - return false;
398 - }
399 - return $cacheable;
400 -});
401 -
402 254 // Include classes with error handling
403 255 function mxchat_include_classes() {
404 256 $class_files = array(
405 - 'includes/class-mxchat-model-catalog.php',
406 - 'includes/class-mxchat-live-agent-schedule.php',
407 - 'includes/class-mxchat-tool-registry.php',
408 257 'includes/class-mxchat-integrator.php',
409 258 'includes/class-mxchat-admin.php',
410 259 'includes/class-mxchat-public.php',
411 260 'includes/class-mxchat-utils.php',
@@ -413,11 +262,8 @@
413 262 'includes/class-mxchat-meta-box.php',
414 263 'includes/class-mxchat-chunker.php',
415 264 'includes/class-mxchat-word-handler.php',
416 265 'includes/class-mxchat-content-generator.php',
417 - 'includes/class-mxchat-cache-purge.php',
418 - 'includes/class-mxchat-editor-assistant.php',
419 - 'includes/class-rest-api.php',
420 266 'admin/class-ajax-handler.php',
421 267 'admin/class-pinecone-manager.php',
422 268 'admin/class-knowledge-manager.php'
423 269 );
@@ -429,38 +275,8 @@
429 275 } else {
430 276 //error_log('MxChat: Missing class file - ' . $file);
431 277 }
432 278 }
433 -
434 - // Register the native function-calling admin-post save handler (a41dee).
435 - if (class_exists('MxChat_Tool_Registry')) {
436 - MxChat_Tool_Registry::init();
437 - }
438 -
439 - // Editor Assistant — free, OFF-by-default block-editor AI actions (plan-8cb0cb).
440 - // init() wires REST + streaming AJAX + sidebar enqueue ONLY when the
441 - // mxchat_editor_assistant_enabled option is 'on'; otherwise zero footprint.
442 - if (class_exists('MxChat_Editor_Assistant')) {
443 - MxChat_Editor_Assistant::init();
444 - }
445 -
446 - // Admin pages that aren't classes (procedural include).
447 - if (is_admin()) {
448 - $admin_api_page = plugin_dir_path(__FILE__) . 'includes/admin-api-page.php';
449 - if (file_exists($admin_api_page)) {
450 - require_once $admin_api_page;
451 - }
452 - // f7c7d4 renamed this file admin-dashboard-page.php → admin-onboarding-page.php.
453 - // The require MUST live here (admin bootstrap) and not just inside
454 - // mxchat_add_plugin_page() on the admin_menu hook — admin_menu does NOT
455 - // fire on admin-ajax.php requests, so the wizard's AJAX handlers
456 - // (plan-905439: mxchat_onboarding_kb_status / save_step / mark_step /
457 - // auto_graduate + the f7c7d4 dismiss handler) would never register.
458 - $admin_onboarding_page = plugin_dir_path(__FILE__) . 'includes/admin-onboarding-page.php';
459 - if (file_exists($admin_onboarding_page)) {
460 - require_once $admin_onboarding_page;
461 - }
462 - }
463 279 }
464 280
465 281 /**
466 282 * Lazy-load the PDF parser library only when needed.
@@ -792,33 +608,8 @@
792 608 update_option('mxchat_content_type_migration_version', '2.5.6');
793 609 }
794 610
795 611 /**
796 - * 3.2.4: Backfill the active embedding model option for installs that already
797 - * have KB content but no stamped model. The mismatch warning compares this
798 - * against the user's currently selected model — no per-row column needed.
799 - */
800 -function mxchat_backfill_active_embedding_model() {
801 - global $wpdb;
802 -
803 - if (get_option('mxchat_active_embedding_model', '') !== '') {
804 - return;
805 - }
806 -
807 - $kb_table = $wpdb->prefix . 'mxchat_system_prompt_content';
808 - if ($wpdb->get_var("SHOW TABLES LIKE '{$kb_table}'") !== $kb_table) {
809 - return;
810 - }
811 -
812 - $kb_count = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$kb_table}");
813 - if ($kb_count > 0) {
814 - $options = get_option('mxchat_options', array());
815 - $current_model = $options['embedding_model'] ?? 'text-embedding-ada-002';
816 - update_option('mxchat_active_embedding_model', $current_model, false);
817 - }
818 -}
819 -
820 -/**
821 612 * 2.5.2: Create queue processing tables for reliable background processing
822 613 */
823 614 function mxchat_create_queue_tables() {
824 615 global $wpdb;
@@ -890,34 +681,8 @@
890 681 dbDelta($sql);
891 682 }
892 683
893 684 /**
894 - * Create per-session satisfaction ratings table (v3.2.6)
895 - * Stores one 👍/👎 rating + optional feedback per chat session.
896 - */
897 -function mxchat_create_session_ratings_table() {
898 - global $wpdb;
899 - $charset_collate = $wpdb->get_charset_collate();
900 -
901 - $table_name = $wpdb->prefix . 'mxchat_session_ratings';
902 - $sql = "CREATE TABLE $table_name (
903 - id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
904 - session_id varchar(255) NOT NULL,
905 - bot_id varchar(50) NOT NULL DEFAULT 'default',
906 - rating_value tinyint(1) NOT NULL,
907 - rating_feedback text DEFAULT NULL,
908 - created_at datetime NOT NULL,
909 - PRIMARY KEY (id),
910 - UNIQUE KEY session_id (session_id),
911 - KEY bot_id (bot_id),
912 - KEY created_at (created_at)
913 - ) $charset_collate;";
914 -
915 - require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
916 - dbDelta($sql);
917 -}
918 -
919 -/**
920 685 * 2.5.2: Fix URL column size to support long URLs (especially with UTF-8 encoding)
921 686 * This fixes "url, source_url. The supplied values may be too long" errors
922 687 */
923 688 function mxchat_fix_url_column_size() {
@@ -976,9 +741,9 @@
976 741 if (in_array($current_model, $deprecated_claude_models, true)) {
977 742 $options['model'] = 'claude-opus-4-6';
978 743 $migrated = true;
979 744 $migration_message = sprintf(
980 - 'Your chatbot model has been automatically updated from %s to Claude Opus 4.6 due to Anthropic deprecating older Claude models.',
745 + __('Your chatbot model has been automatically updated from %s to Claude Opus 4.6 due to Anthropic deprecating older Claude models.', 'mxchat'),
981 746 $current_model
982 747 );
983 748 }
984 749
@@ -985,9 +750,9 @@
985 750 // Migrate deprecated Claude Haiku 3.5 to Claude Haiku 4.5
986 751 if ($current_model === 'claude-3-5-haiku-20241022') {
987 752 $options['model'] = 'claude-haiku-4-5-20251001';
988 753 $migrated = true;
989 - $migration_message = 'Your chatbot model has been automatically updated from Claude Haiku 3.5 to Claude Haiku 4.5 due to Anthropic deprecating the older model.';
754 + $migration_message = __('Your chatbot model has been automatically updated from Claude Haiku 3.5 to Claude Haiku 4.5 due to Anthropic deprecating the older model.', 'mxchat');
990 755 }
991 756
992 757 // Migrate deprecated GPT-4 series and GPT-3.5 Turbo to GPT-5.1 Chat Latest
993 758 if (in_array($current_model, array('gpt-4o', 'gpt-4.1-2025-04-14', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'), true)) {
@@ -993,9 +758,9 @@
993 758 if (in_array($current_model, array('gpt-4o', 'gpt-4.1-2025-04-14', 'gpt-4-turbo', 'gpt-4', 'gpt-3.5-turbo'), true)) {
994 759 $options['model'] = 'gpt-5.1-chat-latest';
995 760 $migrated = true;
996 761 $migration_message = sprintf(
997 - 'Your chatbot model has been automatically updated from %s to GPT-5.1 Chat Latest due to OpenAI deprecating older models.',
762 + __('Your chatbot model has been automatically updated from %s to GPT-5.1 Chat Latest due to OpenAI deprecating older models.', 'mxchat'),
998 763 $current_model
999 764 );
1000 765 }
1001 766
@@ -1003,9 +768,9 @@
1003 768 if (in_array($current_model, array('gpt-4o-mini', 'gpt-4.1-mini'), true)) {
1004 769 $options['model'] = 'gpt-5-mini';
1005 770 $migrated = true;
1006 771 $migration_message = sprintf(
1007 - 'Your chatbot model has been automatically updated from %s to GPT-5 Mini due to OpenAI deprecating GPT-4 series models.',
772 + __('Your chatbot model has been automatically updated from %s to GPT-5 Mini due to OpenAI deprecating GPT-4 series models.', 'mxchat'),
1008 773 $current_model
1009 774 );
1010 775 }
1011 776
@@ -1072,26 +837,13 @@
1072 837 enabled_bots LONGTEXT DEFAULT NULL,
1073 838 PRIMARY KEY (id)
1074 839 ) $charset_collate;";
1075 840
1076 - // Individual Intent Phrases Table - each phrase gets its own embedding vector
1077 - $intent_phrases_table = $wpdb->prefix . 'mxchat_intent_phrases';
1078 - $sql_intent_phrases_table = "CREATE TABLE $intent_phrases_table (
1079 - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
1080 - intent_id BIGINT(20) UNSIGNED NOT NULL,
1081 - phrase TEXT NOT NULL,
1082 - embedding_vector LONGTEXT NOT NULL,
1083 - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
1084 - PRIMARY KEY (id),
1085 - KEY intent_id (intent_id)
1086 - ) $charset_collate;";
1087 -
1088 841 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
1089 842
1090 843 // Create other tables
1091 844 dbDelta($sql_system_prompt);
1092 845 dbDelta($sql_intents_table);
1093 - dbDelta($sql_intent_phrases_table);
1094 846
1095 847 // Create URL click tracking table
1096 848 mxchat_create_url_clicks_table();
1097 849
@@ -1103,11 +855,8 @@
1103 855
1104 856 // Create transcript translations table
1105 857 mxchat_create_translations_table();
1106 858
1107 - // Create per-session satisfaction ratings table (v3.2.6)
1108 - mxchat_create_session_ratings_table();
1109 -
1110 859 // Ensure additional columns in system prompt table
1111 860 $existing_system_columns = $wpdb->get_results("SHOW COLUMNS FROM $system_prompt_table");
1112 861 if (!empty($existing_system_columns)) {
1113 862 $existing_system_column_names = array_column($existing_system_columns, 'Field');
@@ -1150,11 +899,8 @@
1150 899
1151 900 // Run migration for existing installations
1152 901 mxchat_migrate_pinecone_roles_add_bot_id();
1153 902
1154 - // 3.2.4: Backfill active embedding model option (replaces 3.2.3 column-based tracking)
1155 - mxchat_backfill_active_embedding_model();
1156 -
1157 903 // Setup cron jobs
1158 904 mxchat_setup_cron_jobs();
1159 905
1160 906 // Update version
@@ -1174,35 +920,28 @@
1174 920 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
1175 921 // Set flag to use fallback system
1176 922 update_option('mxchat_use_fallback_rate_limits', true);
1177 923 update_option('mxchat_next_rate_limit_check', time() + 3600);
1178 - // Deliberately NO early return (plan-bc08a6): transcript cleanup below
1179 - // must still be scheduled. DISABLE_WP_CRON only changes HOW cron events
1180 - // execute (a server-side runner hitting wp-cron.php instead of loopback
1181 - // spawns) — scheduling still just writes the cron option. The old early
1182 - // return here meant a deactivate/reactivate cycle on a DISABLE_WP_CRON
1183 - // site permanently lost the transcript cleanup event while the retention
1184 - // setting still claimed to be active.
924 + return;
925 + }
926 +
927 + // Schedule the rate limit reset cron job
928 + $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
929 +
930 + if ($result === false) {
931 + // Fallback if scheduling fails
932 + update_option('mxchat_use_fallback_rate_limits', true);
933 + update_option('mxchat_next_rate_limit_check', time() + 3600);
1185 934 } else {
1186 - // Schedule the rate limit reset cron job
1187 - $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
1188 -
1189 - if ($result === false) {
1190 - // Fallback if scheduling fails
1191 - update_option('mxchat_use_fallback_rate_limits', true);
1192 - update_option('mxchat_next_rate_limit_check', time() + 3600);
1193 - } else {
1194 - // Clear fallback flags if cron scheduling succeeded
1195 - delete_option('mxchat_use_fallback_rate_limits');
1196 - }
935 + // Clear fallback flags if cron scheduling succeeded
936 + delete_option('mxchat_use_fallback_rate_limits');
1197 937 }
1198 -
1199 - // Schedule transcript cleanup if configured (bucket dropdown OR custom retention-days > 0)
938 +
939 + // Schedule transcript cleanup if configured
1200 940 $transcript_options = get_option('mxchat_transcripts_options', array());
1201 941 $cleanup_interval = isset($transcript_options['mxchat_auto_delete_transcripts']) ? $transcript_options['mxchat_auto_delete_transcripts'] : 'never';
1202 - $custom_retention = isset($transcript_options['mxchat_retention_days']) ? (int) $transcript_options['mxchat_retention_days'] : 0;
1203 -
1204 - if ($cleanup_interval !== 'never' || $custom_retention > 0) {
942 +
943 + if ($cleanup_interval !== 'never') {
1205 944 // Check if not already scheduled
1206 945 if (!wp_next_scheduled('mxchat_cleanup_old_transcripts')) {
1207 946 // Schedule to run daily at 3 AM
1208 947 $next_run = strtotime('tomorrow 3:00 AM');
@@ -1330,20 +1069,8 @@
1330 1069 if (version_compare($current_version, '3.1.2', '<')) {
1331 1070 mxchat_migrate_transcripts_charset();
1332 1071 }
1333 1072
1334 - // 3.1.7: Clean up stale shared session email/name entries
1335 - if (version_compare($current_version, '3.1.7', '<')) {
1336 - delete_option('mxchat_email_null');
1337 - delete_option('mxchat_name_null');
1338 - }
1339 -
1340 - // 3.2.4: Backfill active embedding model option for the warning UI
1341 - // (replaces the per-row column tracking from 3.2.3, which was reverted)
1342 - if (version_compare($current_version, '3.2.4', '<')) {
1343 - mxchat_backfill_active_embedding_model();
1344 - }
1345 -
1346 1073 // Run full activation to ensure everything is up to date
1347 1074 mxchat_activate();
1348 1075
1349 1076 // Run migration functions
@@ -1514,9 +1241,9 @@
1514 1241 // Check if we're upgrading TO 3.0.1 and haven't handled this yet
1515 1242 if (version_compare($current_version, $target_version, '<') && !$update_handled) {
1516 1243 // Check if Pro is activated - only show to Pro users
1517 1244 $license_status = get_option('mxchat_license_status', 'inactive');
1518 - $is_pro = ($license_status === 'active');
1245 + $is_pro = ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
1519 1246
1520 1247 if ($is_pro) {
1521 1248 // Set flag to show the theme migration notification banner
1522 1249 update_option('mxchat_show_theme_migration_notice', true);
@@ -1568,23 +1295,8 @@
1568 1295 if (class_exists('MxChat_Content_Generator')) {
1569 1296 new MxChat_Content_Generator();
1570 1297 }
1571 1298
1572 - // Initialize cache purge globally — settings writes can happen on any
1573 - // request type (admin screens, admin-ajax autosave, wp-cli), and the
1574 - // deferred-purge cron event fires on front-end requests.
1575 - if (class_exists('MxChat_Cache_Purge')) {
1576 - MxChat_Cache_Purge::init();
1577 - }
1578 -
1579 - // Initialize REST API globally — endpoints must be registered on
1580 - // every request (admin and frontend) so they're reachable via /wp-json/.
1581 - // Endpoints are auth-gated and locked until the site owner generates
1582 - // a token in MxChat → API Access.
1583 - if (class_exists('MxChat_Rest_Api')) {
1584 - new MxChat_Rest_Api();
1585 - }
1586 -
1587 1299 // Initialize public classes
1588 1300 if (class_exists('MxChat_Public')) {
1589 1301 $mxchat_public = new MxChat_Public();
1590 1302 }
@@ -1629,25 +1341,11 @@
1629 1341
1630 1342 mxchat_migrate_pinecone_roles_add_bot_id();
1631 1343 mxchat_migrate_add_content_type_column();
1632 1344 mxchat_migrate_add_translations_table();
1633 - mxchat_migrate_add_session_ratings_table();
1634 1345 }
1635 1346
1636 1347 /**
1637 - * Migration: Create per-session satisfaction ratings table (v3.2.6)
1638 - * For users upgrading from versions before 3.2.6
1639 - */
1640 -function mxchat_migrate_add_session_ratings_table() {
1641 - $migration_key = 'mxchat_session_ratings_table_created';
1642 - if (get_option($migration_key)) {
1643 - return;
1644 - }
1645 - mxchat_create_session_ratings_table();
1646 - update_option($migration_key, '3.2.6');
1647 -}
1648 -
1649 -/**
1650 1348 * Migration: Create transcript translations table (v3.0.4)
1651 1349 * For users upgrading from versions before 3.0.4
1652 1350 */
1653 1351 function mxchat_migrate_add_translations_table() {
@@ -1690,67 +1388,5 @@
1690 1388 return $schedules;
1691 1389 });
1692 1390
1693 1391 // Register deactivation hook
1694 -register_deactivation_hook(__FILE__, 'mxchat_deactivate');
1695 -
1696 -/**
1697 - * Per-session satisfaction rating: AJAX save handler (v3.2.6).
1698 - * Records one 👍/👎 + optional feedback per chat session. The UNIQUE KEY on
1699 - * session_id makes this naturally idempotent — only the first rating per
1700 - * session is stored; duplicate POSTs are silent no-ops.
1701 - */
1702 -function mxchat_save_session_rating() {
1703 - global $wpdb;
1704 -
1705 - $session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
1706 - $bot_id = isset($_POST['bot_id']) ? sanitize_text_field(wp_unslash($_POST['bot_id'])) : 'default';
1707 - $rating_raw = isset($_POST['rating']) ? (int) $_POST['rating'] : 0;
1708 - $feedback = isset($_POST['feedback']) ? sanitize_textarea_field(wp_unslash($_POST['feedback'])) : '';
1709 -
1710 - if ($session_id === '' || ($rating_raw !== 1 && $rating_raw !== -1)) {
1711 - wp_send_json_error(array('message' => 'invalid_input'), 400);
1712 - }
1713 -
1714 - if (strlen($feedback) > 1000) {
1715 - $feedback = substr($feedback, 0, 1000);
1716 - }
1717 -
1718 - $table_name = $wpdb->prefix . 'mxchat_session_ratings';
1719 - $existing = $wpdb->get_var($wpdb->prepare(
1720 - "SELECT id FROM $table_name WHERE session_id = %s LIMIT 1",
1721 - $session_id
1722 - ));
1723 -
1724 - if ($existing) {
1725 - if ($feedback !== '') {
1726 - $wpdb->update(
1727 - $table_name,
1728 - array('rating_feedback' => $feedback),
1729 - array('id' => (int) $existing),
1730 - array('%s'),
1731 - array('%d')
1732 - );
1733 - }
1734 - wp_send_json_success(array('updated' => true));
1735 - }
1736 -
1737 - $inserted = $wpdb->insert(
1738 - $table_name,
1739 - array(
1740 - 'session_id' => $session_id,
1741 - 'bot_id' => $bot_id !== '' ? $bot_id : 'default',
1742 - 'rating_value' => $rating_raw,
1743 - 'rating_feedback' => $feedback !== '' ? $feedback : null,
1744 - 'created_at' => current_time('mysql'),
1745 - ),
1746 - array('%s', '%s', '%d', '%s', '%s')
1747 - );
1748 -
1749 - if ($inserted === false) {
1750 - wp_send_json_error(array('message' => 'db_insert_failed'), 500);
1751 - }
1752 -
1753 - wp_send_json_success(array('saved' => true));
1754 -}
1755 -add_action('wp_ajax_mxchat_save_rating', 'mxchat_save_session_rating');
1756 -add_action('wp_ajax_nopriv_mxchat_save_rating', 'mxchat_save_session_rating');
1392 +register_deactivation_hook(__FILE__, 'mxchat_deactivate');