PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 3.0.4
MxChat – AI Chatbot & Content Generation for WordPress v3.0.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
mxchat-basic / mxchat-basic.php

mxchat-basic.php in MxChat – AI Chatbot & Content Generation for WordPress 3.0.4, at mxchat-basic.php

1,069 lines 39.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: MxChat
4 * Plugin URI: https://mxchat.ai/
5 * Description: AI chatbot for WordPress with OpenAI, Claude, xAI, DeepSeek, live agent, PDF uploads, WooCommerce, and training on website data.
6 * Version: 3.0.4
7 * Author: MxChat
8 * Author URI: https://mxchat.ai
9 * License: GPLv2 or later
10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
11 * Text Domain: mxchat
12 * Domain Path: /languages
13 */
14
15 if (!defined('ABSPATH')) {
16 exit; // Exit if accessed directly.
17 }
18
19 // Define plugin version constant for asset versioning
20 // Reads version from plugin header automatically
21 if (!defined('MXCHAT_VERSION')) {
22 $plugin_data = get_file_data(__FILE__, array('Version' => 'Version'), 'plugin');
23 define('MXCHAT_VERSION', $plugin_data['Version']);
24 }
25
26 function mxchat_load_textdomain() {
27 $domain = 'mxchat';
28 $locale = determine_locale();
29
30 // First, try to load from /wp-content/languages/plugins/ (preserved during updates)
31 $mo_file = WP_LANG_DIR . '/plugins/' . $domain . '-' . $locale . '.mo';
32 if (file_exists($mo_file)) {
33 load_textdomain($domain, $mo_file);
34 return;
35 }
36
37 // Fallback to plugin's /languages directory
38 load_plugin_textdomain($domain, false, dirname(plugin_basename(__FILE__)) . '/languages');
39 }
40 add_action('init', 'mxchat_load_textdomain');
41
42 // Include classes with error handling
43 function mxchat_include_classes() {
44 $class_files = array(
45 'includes/class-mxchat-integrator.php',
46 'includes/class-mxchat-admin.php',
47 'includes/class-mxchat-public.php',
48 'includes/class-mxchat-utils.php',
49 'includes/class-mxchat-user.php',
50 'includes/class-mxchat-meta-box.php',
51 'includes/class-mxchat-chunker.php',
52 'includes/pdf-parser/alt_autoload.php',
53 'includes/class-mxchat-word-handler.php',
54 'admin/class-ajax-handler.php',
55 'admin/class-pinecone-manager.php',
56 'admin/class-knowledge-manager.php'
57 );
58
59 foreach ($class_files as $file) {
60 $file_path = plugin_dir_path(__FILE__) . $file;
61 if (file_exists($file_path)) {
62 require_once $file_path;
63 } else {
64 //error_log('MxChat: Missing class file - ' . $file);
65 }
66 }
67 }
68
69 /**
70 * Create URL click tracking table
71 */
72 function mxchat_create_url_clicks_table() {
73 global $wpdb;
74
75 $table_name = $wpdb->prefix . 'mxchat_url_clicks';
76
77 $charset_collate = $wpdb->get_charset_collate();
78
79 $sql = "CREATE TABLE $table_name (
80 id mediumint(9) NOT NULL AUTO_INCREMENT,
81 session_id varchar(100) NOT NULL,
82 clicked_url text NOT NULL,
83 message_context text,
84 click_timestamp datetime DEFAULT CURRENT_TIMESTAMP,
85 user_ip varchar(45),
86 user_agent text,
87 PRIMARY KEY (id),
88 KEY session_id (session_id),
89 KEY click_timestamp (click_timestamp)
90 ) $charset_collate;";
91
92 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
93 dbDelta($sql);
94 }
95
96 /**
97 * FIXED: Robust table creation and column management
98 */
99 function mxchat_create_chat_transcripts_table() {
100 global $wpdb;
101
102 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
103 $charset_collate = $wpdb->get_charset_collate();
104
105 // Create table with ALL columns including user_name from the start
106 $sql = "CREATE TABLE $table_name (
107 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
108 user_id MEDIUMINT(9) DEFAULT 0,
109 session_id VARCHAR(255) NOT NULL,
110 role VARCHAR(255) NOT NULL,
111 message TEXT NOT NULL,
112 user_email VARCHAR(255) DEFAULT NULL,
113 user_name VARCHAR(100) DEFAULT NULL,
114 user_identifier VARCHAR(255) DEFAULT NULL,
115 originating_page_url TEXT DEFAULT NULL,
116 originating_page_title VARCHAR(500) DEFAULT NULL,
117 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
118 PRIMARY KEY (id),
119 KEY session_id (session_id),
120 KEY user_email (user_email),
121 KEY timestamp (timestamp)
122 ) $charset_collate;";
123
124 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
125 $result = dbDelta($sql);
126
127 // Log the result for debugging
128 if (empty($result)) {
129 //error_log("MxChat: dbDelta returned empty result for chat transcripts table");
130 } else {
131 //error_log("MxChat: dbDelta result: " . print_r($result, true));
132 }
133
134 // IMPORTANT: Ensure all columns exist for existing installations
135 mxchat_ensure_all_columns($table_name);
136 }
137
138 /**
139 * Ensure all required columns exist (for upgrades)
140 */
141 function mxchat_ensure_all_columns($table_name) {
142 global $wpdb;
143
144 // First check if table exists
145 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
146 if (!$table_exists) {
147 //error_log("MxChat: Table $table_name does not exist, cannot add columns");
148 return;
149 }
150
151 // Define all required columns and their types
152 $required_columns = [
153 'user_identifier' => 'VARCHAR(255) DEFAULT NULL',
154 'user_email' => 'VARCHAR(255) DEFAULT NULL',
155 'user_name' => 'VARCHAR(100) DEFAULT NULL',
156 'originating_page_url' => 'TEXT DEFAULT NULL',
157 'originating_page_title' => 'VARCHAR(500) DEFAULT NULL',
158 'rag_context' => 'LONGTEXT DEFAULT NULL'
159 ];
160
161 // Get existing columns
162 $existing_columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name");
163 if (empty($existing_columns)) {
164 //error_log("MxChat: Could not get columns for table $table_name");
165 return;
166 }
167
168 $existing_column_names = array_column($existing_columns, 'Field');
169
170 // Add missing columns
171 foreach ($required_columns as $column_name => $column_definition) {
172 if (!in_array($column_name, $existing_column_names)) {
173 $alter_sql = "ALTER TABLE $table_name ADD COLUMN $column_name $column_definition";
174 $result = $wpdb->query($alter_sql);
175
176 if ($result === false) {
177 //error_log("MxChat: Failed to add column $column_name to $table_name. Error: " . $wpdb->last_error);
178 } else {
179 //error_log("MxChat: Successfully added column $column_name to $table_name");
180 }
181 }
182 }
183 }
184
185 /**
186 * Add role restriction column to knowledge base table
187 */
188 function mxchat_add_role_restriction_column() {
189 global $wpdb;
190 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
191
192 // Check if table exists first
193 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
194 if (!$table_exists) {
195 //error_log("MxChat: System prompt content table does not exist, cannot add role_restriction column");
196 return;
197 }
198
199 // Check if column already exists
200 $column_exists = $wpdb->get_results(
201 $wpdb->prepare(
202 "SHOW COLUMNS FROM {$table_name} LIKE %s",
203 'role_restriction'
204 )
205 );
206
207 if (empty($column_exists)) {
208 $alter_sql = "ALTER TABLE {$table_name} ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url";
209 $result = $wpdb->query($alter_sql);
210
211 if ($result === false) {
212 //error_log("MxChat: Failed to add role_restriction column. Error: " . $wpdb->last_error);
213 } else {
214 //error_log("MxChat: Successfully added role_restriction column");
215
216 // Set all existing records to 'public' (everyone can access)
217 $update_result = $wpdb->query(
218 "UPDATE {$table_name}
219 SET role_restriction = 'public'
220 WHERE role_restriction IS NULL OR role_restriction = ''"
221 );
222
223 if ($update_result !== false) {
224 //error_log("MxChat: Updated {$update_result} existing records to public access");
225 }
226 }
227 }
228 }
229
230 /**
231 * Add enabled_bots column to intents table for multi-bot action filtering
232 */
233 function mxchat_add_enabled_bots_column() {
234 global $wpdb;
235 $table_name = $wpdb->prefix . 'mxchat_intents';
236
237 // Check if table exists first
238 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
239 if (!$table_exists) {
240 //error_log("MxChat: Intents table does not exist, cannot add enabled_bots column");
241 return;
242 }
243
244 // Check if column already exists
245 $column_exists = $wpdb->get_results(
246 $wpdb->prepare(
247 "SHOW COLUMNS FROM {$table_name} LIKE %s",
248 'enabled_bots'
249 )
250 );
251
252 if (empty($column_exists)) {
253 $alter_sql = "ALTER TABLE {$table_name} ADD COLUMN enabled_bots LONGTEXT DEFAULT NULL AFTER enabled";
254 $result = $wpdb->query($alter_sql);
255
256 if ($result === false) {
257 //error_log("MxChat: Failed to add enabled_bots column. Error: " . $wpdb->last_error);
258 } else {
259 //error_log("MxChat: Successfully added enabled_bots column");
260
261 // Set all existing actions to work with 'default' bot for backward compatibility
262 $default_bots = json_encode(['default']);
263 $update_result = $wpdb->query(
264 $wpdb->prepare(
265 "UPDATE {$table_name}
266 SET enabled_bots = %s
267 WHERE enabled_bots IS NULL OR enabled_bots = ''",
268 $default_bots
269 )
270 );
271
272 if ($update_result !== false) {
273 //error_log("MxChat: Updated {$update_result} existing actions to work with default bot");
274 }
275 }
276 }
277 }
278
279 /**
280 * Create Pinecone role restrictions table with multi-bot support
281 */
282 function mxchat_create_pinecone_roles_table() {
283 global $wpdb;
284
285 $table_name = $wpdb->prefix . 'mxchat_pinecone_roles';
286 $charset_collate = $wpdb->get_charset_collate();
287
288 $sql = "CREATE TABLE $table_name (
289 id mediumint(9) NOT NULL AUTO_INCREMENT,
290 vector_id varchar(255) NOT NULL,
291 bot_id varchar(50) NOT NULL DEFAULT 'default',
292 source_url text,
293 role_restriction varchar(50) DEFAULT 'public',
294 updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
295 PRIMARY KEY (id),
296 UNIQUE KEY vector_bot (vector_id, bot_id),
297 KEY role_restriction (role_restriction),
298 KEY bot_id (bot_id)
299 ) $charset_collate;";
300
301 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
302 dbDelta($sql);
303 }
304
305 /**
306 * Add bot_id column to mxchat_pinecone_roles table for multi-bot support
307 * This migration runs once to update existing installations
308 */
309 function mxchat_migrate_pinecone_roles_add_bot_id() {
310 global $wpdb;
311
312 // Check if migration already ran
313 $migration_version = get_option('mxchat_pinecone_roles_migration_version', '0');
314 if (version_compare($migration_version, '2.5.2', '>=')) {
315 return; // Already migrated
316 }
317
318 $table_name = $wpdb->prefix . 'mxchat_pinecone_roles';
319
320 // Check if table exists
321 if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") != $table_name) {
322 return; // Table doesn't exist yet
323 }
324
325 // Check if bot_id column already exists
326 $column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} LIKE 'bot_id'");
327
328 if (empty($column_exists)) {
329 // Add bot_id column
330 $wpdb->query("ALTER TABLE {$table_name} ADD COLUMN bot_id VARCHAR(50) NOT NULL DEFAULT 'default' AFTER vector_id");
331
332 // Update the unique key to include bot_id
333 $wpdb->query("ALTER TABLE {$table_name} DROP INDEX vector_id");
334 $wpdb->query("ALTER TABLE {$table_name} ADD UNIQUE KEY vector_bot (vector_id, bot_id)");
335
336 // Add index for bot_id
337 $wpdb->query("ALTER TABLE {$table_name} ADD KEY bot_id (bot_id)");
338
339 error_log('MxChat: Successfully added bot_id column to mxchat_pinecone_roles table');
340 }
341
342 // Mark migration as complete
343 update_option('mxchat_pinecone_roles_migration_version', '2.5.2');
344 }
345
346 /**
347 * 2.5.6: Add content_type column to mxchat_system_prompt_content table
348 * Enables filtering knowledge base by content type (posts, pages, PDFs, etc.)
349 */
350 function mxchat_migrate_add_content_type_column() {
351 global $wpdb;
352
353 // Check if migration already ran
354 $migration_version = get_option('mxchat_content_type_migration_version', '0');
355 if (version_compare($migration_version, '2.5.6', '>=')) {
356 return;
357 }
358
359 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
360
361 // Check if table exists
362 if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") != $table_name) {
363 return;
364 }
365
366 // Check if content_type column already exists
367 $column_exists = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} LIKE 'content_type'");
368
369 if (empty($column_exists)) {
370 // Add content_type column with default value 'content' for backwards compatibility
371 $wpdb->query("ALTER TABLE {$table_name} ADD COLUMN content_type VARCHAR(50) DEFAULT 'content' AFTER role_restriction");
372
373 // Add index for better query performance
374 $wpdb->query("ALTER TABLE {$table_name} ADD KEY content_type (content_type)");
375
376 error_log('MxChat: Successfully added content_type column to mxchat_system_prompt_content table');
377 }
378
379 // Mark migration as complete
380 update_option('mxchat_content_type_migration_version', '2.5.6');
381 }
382
383 /**
384 * 2.5.2: Create queue processing tables for reliable background processing
385 */
386 function mxchat_create_queue_tables() {
387 global $wpdb;
388 $charset_collate = $wpdb->get_charset_collate();
389
390 // Main queue table
391 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
392 $sql_queue = "CREATE TABLE $queue_table (
393 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
394 queue_id varchar(64) NOT NULL,
395 item_type varchar(20) NOT NULL,
396 item_data longtext NOT NULL,
397 status varchar(20) NOT NULL DEFAULT 'pending',
398 bot_id varchar(50) NOT NULL DEFAULT 'default',
399 priority int(11) NOT NULL DEFAULT 0,
400 attempts int(11) NOT NULL DEFAULT 0,
401 max_attempts int(11) NOT NULL DEFAULT 3,
402 error_message text DEFAULT NULL,
403 created_at datetime NOT NULL,
404 started_at datetime DEFAULT NULL,
405 completed_at datetime DEFAULT NULL,
406 PRIMARY KEY (id),
407 KEY queue_id (queue_id),
408 KEY status (status),
409 KEY item_type (item_type),
410 KEY priority (priority)
411 ) $charset_collate;";
412
413 // Queue metadata table
414 $meta_table = $wpdb->prefix . 'mxchat_queue_meta';
415 $sql_meta = "CREATE TABLE $meta_table (
416 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
417 queue_id varchar(64) NOT NULL,
418 meta_key varchar(255) NOT NULL,
419 meta_value longtext,
420 PRIMARY KEY (id),
421 KEY queue_id (queue_id),
422 KEY meta_key (meta_key)
423 ) $charset_collate;";
424
425 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
426 dbDelta($sql_queue);
427 dbDelta($sql_meta);
428
429 //error_log("MxChat: Queue tables created/updated successfully");
430 }
431
432 /**
433 * Create transcript translations table for persisting translations
434 */
435 function mxchat_create_translations_table() {
436 global $wpdb;
437 $charset_collate = $wpdb->get_charset_collate();
438
439 $table_name = $wpdb->prefix . 'mxchat_transcript_translations';
440 $sql = "CREATE TABLE $table_name (
441 id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
442 session_id varchar(255) NOT NULL,
443 language_code varchar(10) NOT NULL,
444 translations longtext NOT NULL,
445 created_at datetime NOT NULL,
446 updated_at datetime NOT NULL,
447 PRIMARY KEY (id),
448 UNIQUE KEY session_lang (session_id, language_code),
449 KEY session_id (session_id)
450 ) $charset_collate;";
451
452 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
453 dbDelta($sql);
454 }
455
456 /**
457 * 2.5.2: Fix URL column size to support long URLs (especially with UTF-8 encoding)
458 * This fixes "url, source_url. The supplied values may be too long" errors
459 */
460 function mxchat_fix_url_column_size() {
461 global $wpdb;
462 $table_name = $wpdb->prefix . 'mxchat_system_prompt_content';
463
464 // Check if table exists
465 $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
466 if (!$table_exists) {
467 return;
468 }
469
470 // Change url and source_url from VARCHAR to TEXT to handle long URLs
471 // This is especially important for URLs with UTF-8 encoded characters (Hebrew, Arabic, etc.)
472 $wpdb->query("ALTER TABLE {$table_name} MODIFY COLUMN url TEXT");
473 $wpdb->query("ALTER TABLE {$table_name} MODIFY COLUMN source_url TEXT");
474
475 //error_log("MxChat: Successfully updated url and source_url columns to TEXT type for long URL support");
476 }
477
478 /**
479 * Migrate deprecated AI models to their replacements
480 * Version 2.5.1: Migrate Claude 3.5 Sonnet (deprecated) to Claude 3.7 Sonnet
481 */
482 function mxchat_migrate_deprecated_models() {
483 $options = get_option('mxchat_options', array());
484
485 // Check if model is set and is the deprecated Claude 3.5 Sonnet
486 if (isset($options['model']) && $options['model'] === 'claude-3-5-sonnet-20241022') {
487 // Update to Claude 3.7 Sonnet (the replacement model)
488 $options['model'] = 'claude-3-7-sonnet-20250219';
489 update_option('mxchat_options', $options);
490
491 // Set a flag to show admin notice
492 update_option('mxchat_model_migrated_notice', true);
493
494 //error_log('MxChat: Migrated deprecated Claude 3.5 Sonnet to Claude 3.7 Sonnet');
495 }
496 }
497
498 /**
499 * Show admin notice after model migration
500 */
501 function mxchat_show_migration_notice() {
502 if (get_option('mxchat_model_migrated_notice')) {
503 ?>
504 <div class="notice notice-info is-dismissible">
505 <p>
506 <strong><?php esc_html_e('MxChat Model Updated', 'mxchat'); ?></strong><br>
507 <?php esc_html_e('Your chatbot model has been automatically updated from Claude 3.5 Sonnet to Claude 3.7 Sonnet due to the deprecation of the previous model by Anthropic. Claude 3.7 Sonnet offers improved performance and capabilities.', 'mxchat'); ?>
508 </p>
509 </div>
510 <?php
511 delete_option('mxchat_model_migrated_notice');
512 }
513 }
514
515 function mxchat_activate() {
516 global $wpdb;
517 $charset_collate = $wpdb->get_charset_collate();
518
519 //error_log("MxChat: Running activation function");
520
521 // Create chat transcripts table with improved function
522 mxchat_create_chat_transcripts_table();
523
524 // System Prompt Content Table - UPDATED: Use TEXT for url and source_url columns
525 $system_prompt_table = $wpdb->prefix . 'mxchat_system_prompt_content';
526 $sql_system_prompt = "CREATE TABLE $system_prompt_table (
527 id MEDIUMINT(9) NOT NULL AUTO_INCREMENT,
528 url TEXT NOT NULL,
529 article_content LONGTEXT NOT NULL,
530 embedding_vector LONGTEXT,
531 source_url TEXT DEFAULT NULL,
532 role_restriction VARCHAR(50) DEFAULT 'public',
533 content_type VARCHAR(50) DEFAULT 'content',
534 timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
535 PRIMARY KEY (id),
536 KEY content_type (content_type)
537 ) $charset_collate;";
538
539 // Intents Table - NOW INCLUDES enabled_bots column from the start
540 $intents_table = $wpdb->prefix . 'mxchat_intents';
541 $sql_intents_table = "CREATE TABLE $intents_table (
542 id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
543 intent_label VARCHAR(255) NOT NULL,
544 phrases TEXT NOT NULL,
545 embedding_vector LONGTEXT NOT NULL,
546 callback_function VARCHAR(255) NOT NULL,
547 similarity_threshold FLOAT DEFAULT 0.85,
548 enabled TINYINT(1) NOT NULL DEFAULT 1,
549 enabled_bots LONGTEXT DEFAULT NULL,
550 PRIMARY KEY (id)
551 ) $charset_collate;";
552
553 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
554
555 // Create other tables
556 dbDelta($sql_system_prompt);
557 dbDelta($sql_intents_table);
558
559 // Create URL click tracking table
560 mxchat_create_url_clicks_table();
561
562 // Create Pinecone roles table
563 mxchat_create_pinecone_roles_table();
564
565 // NEW 2.5.2: Create queue processing tables
566 mxchat_create_queue_tables();
567
568 // Create transcript translations table
569 mxchat_create_translations_table();
570
571 // Ensure additional columns in system prompt table
572 $existing_system_columns = $wpdb->get_results("SHOW COLUMNS FROM $system_prompt_table");
573 if (!empty($existing_system_columns)) {
574 $existing_system_column_names = array_column($existing_system_columns, 'Field');
575
576 if (!in_array('embedding_vector', $existing_system_column_names)) {
577 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN embedding_vector LONGTEXT");
578 }
579 if (!in_array('source_url', $existing_system_column_names)) {
580 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN source_url TEXT DEFAULT NULL");
581 }
582 if (!in_array('role_restriction', $existing_system_column_names)) {
583 $wpdb->query("ALTER TABLE $system_prompt_table ADD COLUMN role_restriction VARCHAR(50) DEFAULT 'public' AFTER source_url");
584 }
585 }
586
587 // Set default thresholds for existing intents
588 $wpdb->query("UPDATE {$intents_table} SET similarity_threshold = 0.85 WHERE similarity_threshold IS NULL");
589
590 // Ensure enabled column exists in intents table
591 $existing_intent_columns = $wpdb->get_results("SHOW COLUMNS FROM $intents_table");
592 if (!empty($existing_intent_columns)) {
593 $existing_intent_column_names = array_column($existing_intent_columns, 'Field');
594
595 if (!in_array('enabled', $existing_intent_column_names)) {
596 $wpdb->query("ALTER TABLE $intents_table ADD COLUMN enabled TINYINT(1) NOT NULL DEFAULT 1");
597 }
598
599 // Ensure enabled_bots column exists for existing installations
600 if (!in_array('enabled_bots', $existing_intent_column_names)) {
601 $wpdb->query("ALTER TABLE $intents_table ADD COLUMN enabled_bots LONGTEXT DEFAULT NULL AFTER enabled");
602
603 // Set existing actions to work with default bot
604 $default_bots = json_encode(['default']);
605 $wpdb->query($wpdb->prepare(
606 "UPDATE {$intents_table} SET enabled_bots = %s WHERE enabled_bots IS NULL",
607 $default_bots
608 ));
609 }
610 }
611
612 // Run migration for existing installations
613 mxchat_migrate_pinecone_roles_add_bot_id();
614
615 // Setup cron jobs
616 mxchat_setup_cron_jobs();
617
618 // Update version
619 update_option('mxchat_plugin_version', MXCHAT_VERSION);
620
621 //error_log("MxChat: Activation function completed");
622 }
623
624 /**
625 * Setup cron jobs on plugin activation
626 */
627 function mxchat_setup_cron_jobs() {
628 // Clear any existing cron jobs first
629 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
630
631 // Check if WordPress cron is disabled
632 if (defined('DISABLE_WP_CRON') && DISABLE_WP_CRON) {
633 // Set flag to use fallback system
634 update_option('mxchat_use_fallback_rate_limits', true);
635 update_option('mxchat_next_rate_limit_check', time() + 3600);
636 return;
637 }
638
639 // Schedule the rate limit reset cron job
640 $result = wp_schedule_event(time() + 300, 'hourly', 'mxchat_reset_rate_limits');
641
642 if ($result === false) {
643 // Fallback if scheduling fails
644 update_option('mxchat_use_fallback_rate_limits', true);
645 update_option('mxchat_next_rate_limit_check', time() + 3600);
646 } else {
647 // Clear fallback flags if cron scheduling succeeded
648 delete_option('mxchat_use_fallback_rate_limits');
649 }
650
651 // Schedule transcript cleanup if configured
652 $transcript_options = get_option('mxchat_transcripts_options', array());
653 $cleanup_interval = isset($transcript_options['mxchat_auto_delete_transcripts']) ? $transcript_options['mxchat_auto_delete_transcripts'] : 'never';
654
655 if ($cleanup_interval !== 'never') {
656 // Check if not already scheduled
657 if (!wp_next_scheduled('mxchat_cleanup_old_transcripts')) {
658 // Schedule to run daily at 3 AM
659 $next_run = strtotime('tomorrow 3:00 AM');
660 wp_schedule_event($next_run, 'daily', 'mxchat_cleanup_old_transcripts');
661 }
662 }
663 }
664
665 /**
666 * Clean up on plugin deactivation
667 */
668 function mxchat_deactivate() {
669 // Clear scheduled cron jobs
670 wp_clear_scheduled_hook('mxchat_reset_rate_limits');
671 wp_clear_scheduled_hook('mxchat_cleanup_old_transcripts');
672 wp_clear_scheduled_hook('mxchat_send_delayed_transcript');
673
674 // Clear fallback options
675 delete_option('mxchat_use_fallback_rate_limits');
676 delete_option('mxchat_next_rate_limit_check');
677 delete_option('mxchat_fallback_check_interval');
678
679 // NOTE: We do NOT delete queue tables on deactivation
680 // This preserves data if user accidentally deactivates the plugin
681 }
682
683 /**
684 * Check if fallback rate limit cleanup is needed
685 */
686 function mxchat_check_fallback_rate_limits() {
687 $use_fallback = get_option('mxchat_use_fallback_rate_limits', false);
688
689 if (!$use_fallback) {
690 return;
691 }
692
693 $next_check = get_option('mxchat_next_rate_limit_check', 0);
694
695 if (time() >= $next_check) {
696 // Only run reset if the MxChat_Integrator class exists
697 if (class_exists('MxChat_Integrator')) {
698 $integrator = new MxChat_Integrator();
699 if (method_exists($integrator, 'mxchat_reset_rate_limits')) {
700 $integrator->mxchat_reset_rate_limits();
701 update_option('mxchat_next_rate_limit_check', time() + 3600);
702 }
703 }
704 }
705 }
706
707 /**
708 * Robust update checking with role restriction migration, model deprecation, and queue tables
709 * CRITICAL: This runs on EVERY page load to ensure tables exist
710 */
711 function mxchat_check_for_update() {
712 global $wpdb;
713
714 try {
715 $current_version = get_option('mxchat_plugin_version', '0.0.0');
716 $plugin_version = MXCHAT_VERSION;
717
718 // Always ensure critical tables exist (even if version matches)
719 // This handles manual table deletion or fresh installs
720 $chat_table = $wpdb->prefix . 'mxchat_chat_transcripts';
721 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
722
723 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$chat_table'") === $chat_table;
724 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
725
726 if (!$chat_exists || !$queue_exists) {
727 //error_log("MxChat: Critical tables missing, running activation");
728 mxchat_activate();
729 }
730
731 // Version-specific migrations
732 if ($current_version !== $plugin_version) {
733 //error_log("MxChat: Version change detected: $current_version -> $plugin_version");
734
735 // Run live agent update BEFORE updating the stored version
736 mxchat_handle_live_agent_update();
737
738 // Run theme migration notice for 3.0.1 (AI theme CSS structure changes)
739 mxchat_handle_theme_migration_notice();
740
741 // Run role restriction migration for 2.4.1
742 if (version_compare($current_version, '2.4.1', '<')) {
743 mxchat_add_role_restriction_column();
744 }
745
746 // Run enabled_bots column migration for 2.4.4
747 if (version_compare($current_version, '2.4.4', '<')) {
748 mxchat_add_enabled_bots_column();
749 }
750
751 // Run model migration for 2.5.1 (Claude deprecation)
752 if (version_compare($current_version, '2.5.1', '<')) {
753 mxchat_migrate_deprecated_models();
754 }
755
756 // 2.5.2: Ensure queue tables exist and fix URL column sizes for all users upgrading to 2.5.2
757 if (version_compare($current_version, '2.5.2', '<')) {
758 mxchat_create_queue_tables();
759 mxchat_fix_url_column_size(); // NEW: Fix URL column size for long URLs
760 //error_log("MxChat: Queue tables created and URL columns updated for upgrade to 2.5.2");
761 }
762
763 // 2.6.0: Ensure rag_context column exists for retrieved documents feature
764 if (version_compare($current_version, '2.6.0', '<')) {
765 $chat_table = $wpdb->prefix . 'mxchat_chat_transcripts';
766 mxchat_ensure_all_columns($chat_table);
767 //error_log("MxChat: rag_context column migration for 2.6.0");
768 }
769
770 // Run full activation to ensure everything is up to date
771 mxchat_activate();
772
773 // Run migration functions
774 mxchat_migrate_live_agent_status();
775
776 // Add the cleanup function for version 2.1.8
777 if (version_compare($current_version, '2.1.8', '<')) {
778 $deleted = mxchat_cleanup_orphaned_chat_history();
779 }
780
781 // Update version LAST
782 update_option('mxchat_plugin_version', $plugin_version);
783
784 //error_log("MxChat: Updated from version $current_version to $plugin_version");
785 }
786
787 } catch (Exception $e) {
788 //error_log('MxChat update error: ' . $e->getMessage());
789 // Don't update version if there was an error
790 }
791 }
792
793 /**
794 * Ensure tables exist on every admin load for fresh installations
795 * This is a safety net for cases where activation hook doesn't fire
796 */
797 function mxchat_ensure_tables_exist() {
798 global $wpdb;
799
800 // Only run for admin users to avoid performance impact
801 if (!current_user_can('administrator')) {
802 return;
803 }
804
805 // Check if we've already verified tables in this session
806 static $tables_checked = false;
807 if ($tables_checked) {
808 return;
809 }
810 $tables_checked = true;
811
812 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
813 $queue_table = $wpdb->prefix . 'mxchat_processing_queue';
814
815 $chat_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name;
816 $queue_exists = $wpdb->get_var("SHOW TABLES LIKE '$queue_table'") === $queue_table;
817
818 if (!$chat_exists || !$queue_exists) {
819 //error_log("MxChat: Tables missing on admin load, running activation");
820 mxchat_activate();
821 }
822 }
823
824 /**
825 * Clean up orphaned chat history options from the wp_options table
826 * @return int Number of options deleted
827 */
828 function mxchat_cleanup_orphaned_chat_history() {
829 global $wpdb;
830 $count = 0;
831
832 // Get all option keys that match our pattern
833 $history_options = $wpdb->get_results(
834 "SELECT option_name FROM {$wpdb->options}
835 WHERE option_name LIKE 'mxchat_history_%'"
836 );
837
838 if (!empty($history_options)) {
839 foreach ($history_options as $option) {
840 // Extract the session ID from the option name
841 $session_id = str_replace('mxchat_history_', '', $option->option_name);
842
843 // Check if this session still exists in the custom table
844 $table_name = $wpdb->prefix . 'mxchat_chat_transcripts';
845 $exists = $wpdb->get_var(
846 $wpdb->prepare(
847 "SELECT COUNT(*) FROM {$table_name} WHERE session_id = %s",
848 $session_id
849 )
850 );
851
852 // If session doesn't exist in the main table, delete the option
853 if ($exists == 0) {
854 delete_option($option->option_name);
855 // Also delete related metadata
856 delete_option("mxchat_email_{$session_id}");
857 delete_option("mxchat_name_{$session_id}");
858 delete_option("mxchat_agent_name_{$session_id}");
859 $count++;
860 }
861 }
862 }
863
864 return $count;
865 }
866
867 function mxchat_migrate_live_agent_status() {
868 $options = get_option('mxchat_options', []);
869
870 // Check if live_agent_status exists
871 if (isset($options['live_agent_status'])) {
872 $current_status = $options['live_agent_status'];
873 $needs_update = false;
874
875 // Convert to new format if needed
876 if ($current_status === 'online') {
877 $options['live_agent_status'] = 'on';
878 $needs_update = true;
879 } else if ($current_status === 'offline') {
880 $options['live_agent_status'] = 'off';
881 $needs_update = true;
882 } else if (!in_array($current_status, ['on', 'off'])) {
883 // Default to off for any unexpected values
884 $options['live_agent_status'] = 'off';
885 $needs_update = true;
886 }
887
888 // Only update if needed
889 if ($needs_update) {
890 update_option('mxchat_options', $options);
891 }
892 } else {
893 // If status doesn't exist, set default to off
894 $options['live_agent_status'] = 'off';
895 update_option('mxchat_options', $options);
896 }
897 }
898
899 function mxchat_handle_live_agent_update() {
900 // Get the CURRENT stored version (before it gets updated)
901 $current_version = get_option('mxchat_plugin_version', '0.0.0');
902 $new_version = '2.2.2';
903
904 // Only run this once for the update to 2.2.2
905 $update_handled = get_option('mxchat_live_agent_update_2_2_2_handled', false);
906
907 // Check if we're upgrading TO 2.2.2 and haven't handled this yet
908 if (version_compare($current_version, $new_version, '<') && !$update_handled) {
909 $options = get_option('mxchat_options', array());
910
911 // Check if live agent was previously enabled
912 if (isset($options['live_agent_status']) && $options['live_agent_status'] === 'on') {
913 // Disable live agent
914 $options['live_agent_status'] = 'off';
915 update_option('mxchat_options', $options);
916
917 // Set flag to show the notification banner
918 update_option('mxchat_show_live_agent_disabled_notice', true);
919 }
920
921 // Mark this update as handled
922 update_option('mxchat_live_agent_update_2_2_2_handled', true);
923 }
924 }
925
926 /**
927 * Handle theme migration notice for version 3.0.1
928 * Shows a dismissible notice to Pro users about migrating AI-generated themes
929 */
930 function mxchat_handle_theme_migration_notice() {
931 // Get the CURRENT stored version (before it gets updated)
932 $current_version = get_option('mxchat_plugin_version', '0.0.0');
933 $target_version = '3.0.1';
934
935 // Only run this once for the update to 3.0.1
936 $update_handled = get_option('mxchat_theme_migration_update_3_0_1_handled', false);
937
938 // Check if we're upgrading TO 3.0.1 and haven't handled this yet
939 if (version_compare($current_version, $target_version, '<') && !$update_handled) {
940 // Check if Pro is activated - only show to Pro users
941 $license_status = get_option('mxchat_license_status', 'inactive');
942 $is_pro = ($license_status === 'active' || $license_status === esc_html__('active', 'mxchat'));
943
944 if ($is_pro) {
945 // Set flag to show the theme migration notification banner
946 update_option('mxchat_show_theme_migration_notice', true);
947 }
948
949 // Mark this update as handled (whether Pro or not)
950 update_option('mxchat_theme_migration_update_3_0_1_handled', true);
951 }
952 }
953
954 // Initialize plugin safely
955 function mxchat_init() {
956 // Include all class files first
957 mxchat_include_classes();
958
959 // Run update check (this also ensures tables exist)
960 mxchat_check_for_update();
961
962 // CRITICAL: Ensure tables exist on admin pages (safety net)
963 add_action('admin_init', 'mxchat_ensure_tables_exist', 1);
964
965 // Add fallback rate limit check
966 add_action('init', 'mxchat_check_fallback_rate_limits', 5);
967
968 // Add migration notice hook
969 add_action('admin_notices', 'mxchat_show_migration_notice');
970
971 // Initialize classes with error handling
972 try {
973 // Initialize admin classes
974 if (is_admin()) {
975 if (class_exists('MxChat_Knowledge_Manager')) {
976 $mxchat_knowledge_manager = new MxChat_Knowledge_Manager();
977
978 if (class_exists('MxChat_Admin')) {
979 $mxchat_admin = new MxChat_Admin($mxchat_knowledge_manager);
980 }
981 }
982
983 // Initialize meta box class
984 if (class_exists('MxChat_Meta_Box')) {
985 new MxChat_Meta_Box();
986 }
987 }
988
989 // Initialize public classes
990 if (class_exists('MxChat_Public')) {
991 $mxchat_public = new MxChat_Public();
992 }
993
994 if (class_exists('MxChat_Integrator')) {
995 global $mxchat_integrator;
996 $mxchat_integrator = new MxChat_Integrator();
997 }
998
999 } catch (Exception $e) {
1000 //error_log('MxChat initialization error: ' . $e->getMessage());
1001
1002 // Show admin notice if there's an error
1003 if (is_admin()) {
1004 add_action('admin_notices', function() use ($e) {
1005 echo '<div class="notice notice-error"><p>';
1006 echo '<strong>MxChat Error:</strong> Plugin initialization failed. ';
1007 echo 'Please check error logs or contact support. Error: ' . esc_html($e->getMessage());
1008 echo '</p></div>';
1009 });
1010 }
1011 }
1012 }
1013
1014 // Run initialization on plugins_loaded
1015 add_action('plugins_loaded', 'mxchat_init');
1016
1017 // Run migration check on admin init (for auto-updates without reactivation)
1018 add_action('admin_init', 'mxchat_check_and_run_migrations');
1019
1020 /**
1021 * Check and run migrations on admin init
1022 * This ensures migrations run even when plugin is auto-updated
1023 */
1024 function mxchat_check_and_run_migrations() {
1025 // Only run in admin and not on every request
1026 static $checked = false;
1027 if ($checked) {
1028 return;
1029 }
1030 $checked = true;
1031
1032 mxchat_migrate_pinecone_roles_add_bot_id();
1033 mxchat_migrate_add_content_type_column();
1034 mxchat_migrate_add_translations_table();
1035 }
1036
1037 /**
1038 * Migration: Create transcript translations table (v3.0.4)
1039 * For users upgrading from versions before 3.0.4
1040 */
1041 function mxchat_migrate_add_translations_table() {
1042 $migration_key = 'mxchat_translations_table_created';
1043
1044 // Check if migration already ran
1045 if (get_option($migration_key)) {
1046 return;
1047 }
1048
1049 // Create the translations table
1050 mxchat_create_translations_table();
1051
1052 // Mark migration as complete
1053 update_option($migration_key, '3.0.4');
1054 }
1055
1056 // Register activation hook
1057 register_activation_hook(__FILE__, 'mxchat_activate');
1058
1059 // Add cron schedule
1060 add_filter('cron_schedules', function($schedules) {
1061 $schedules['one_minute'] = array(
1062 'interval' => 60,
1063 'display' => 'Every Minute'
1064 );
1065 return $schedules;
1066 });
1067
1068 // Register deactivation hook
1069 register_deactivation_hook(__FILE__, 'mxchat_deactivate');