PluginProbe
MxChat – AI Chatbot & Content Generation for WordPress / 2.6.1
MxChat – AI Chatbot & Content Generation for WordPress v2.6.1
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 2.6.1, at mxchat-basic.php

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