init_hooks(); } private function init_hooks() { register_activation_hook( AIOHM_KB_PLUGIN_FILE, array( $this, 'activate' ) ); register_deactivation_hook( AIOHM_KB_PLUGIN_FILE, array( $this, 'deactivate' ) ); // Ensure HTTPS URLs in admin add_filter( 'upload_dir', array( $this, 'force_https_upload_dir' ) ); add_action( 'plugins_loaded', array( $this, 'load_dependencies' ) ); add_action( 'init', array( $this, 'init_plugin' ) ); add_action( 'wp_enqueue_scripts', array( $this, 'register_frontend_assets' ) ); add_filter( 'plugin_action_links_' . plugin_basename( AIOHM_KB_PLUGIN_FILE ), array( $this, 'add_settings_link' ) ); add_action( 'aiohm_process_file_queue', array( $this, 'handle_background_file_processing' ) ); add_action( 'aiohm_cleanup_completed_queue', array( $this, 'cleanup_completed_queue' ) ); // Add WordPress 5.7+ script attributes support for enhanced security if ( version_compare( get_bloginfo( 'version' ), '5.7', '>=' ) ) { add_filter( 'wp_script_attributes', array( $this, 'add_script_security_attributes' ) ); } // Reduce heartbeat frequency to prevent 503 errors add_filter( 'heartbeat_settings', array( $this, 'modify_heartbeat_settings' ) ); // Disable heartbeat on admin pages to prevent conflicts add_action( 'admin_enqueue_scripts', array( $this, 'disable_heartbeat_on_admin' ) ); // Add AJAX error handling to prevent 503 errors add_action( 'wp_ajax_nopriv_handle_ajax_error', array( $this, 'handle_ajax_error' ) ); add_action( 'wp_ajax_handle_ajax_error', array( $this, 'handle_ajax_error' ) ); // Add shutdown function to catch fatal errors during AJAX requests add_action( 'shutdown', array( $this, 'catch_ajax_fatal_errors' ) ); // Add memory limit checks for AJAX requests add_action( 'admin_init', array( $this, 'increase_memory_limit_for_ajax' ) ); } public function register_frontend_assets() { // Register PDF generation assets wp_register_style( 'aiohm-pdf-generation', AIOHM_KB_PLUGIN_URL . 'assets/css/aiohm-pdf-generation.css', array(), AIOHM_KB_VERSION ); // Use modern WordPress 6.3+ script registration with async/defer support if ( version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) ) { // Modern registration with strategy parameter wp_register_script( 'aiohm-pdf-generation', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-pdf-generation.js', array(), AIOHM_KB_VERSION, array( 'in_footer' => true, 'strategy' => 'defer', ) ); wp_register_script( 'aiohm-chat', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-chat.js', array( 'jquery' ), AIOHM_KB_VERSION, array( 'in_footer' => true, 'strategy' => 'defer', ) ); wp_register_script( 'aiohm-chat-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-chat-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, array( 'in_footer' => true, 'strategy' => 'defer', ) ); wp_register_script( 'aiohm-qa-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-qa-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, array( 'in_footer' => true, 'strategy' => 'defer', ) ); wp_register_script( 'aiohm-search-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-search-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, array( 'in_footer' => true, 'strategy' => 'defer', ) ); } else { // Fallback for WordPress < 6.3 wp_register_script( 'aiohm-pdf-generation', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-pdf-generation.js', array(), AIOHM_KB_VERSION, true ); wp_register_script( 'aiohm-chat', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-chat.js', array( 'jquery' ), AIOHM_KB_VERSION, true ); wp_register_script( 'aiohm-chat-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-chat-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, true ); wp_register_script( 'aiohm-qa-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-qa-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, true ); wp_register_script( 'aiohm-search-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/js/aiohm-search-shortcode.js', array( 'jquery' ), AIOHM_KB_VERSION, true ); // Add defer attribute using wp_script_add_data for backward compatibility if ( function_exists( 'wp_script_add_data' ) ) { wp_script_add_data( 'aiohm-pdf-generation', 'strategy', 'defer' ); wp_script_add_data( 'aiohm-chat', 'strategy', 'defer' ); wp_script_add_data( 'aiohm-chat-shortcode', 'strategy', 'defer' ); wp_script_add_data( 'aiohm-qa-shortcode', 'strategy', 'defer' ); wp_script_add_data( 'aiohm-search-shortcode', 'strategy', 'defer' ); } } // Register styles (no changes needed) wp_register_style( 'aiohm-chat', AIOHM_KB_PLUGIN_URL . 'assets/css/aiohm-chat.css', array(), AIOHM_KB_VERSION ); wp_register_style( 'aiohm-qa-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/css/aiohm-qa-shortcode.css', array(), AIOHM_KB_VERSION ); wp_register_style( 'aiohm-search-shortcode', AIOHM_KB_PLUGIN_URL . 'assets/css/aiohm-search-shortcode.css', array(), AIOHM_KB_VERSION ); } public function load_dependencies() { $base_files = array( 'db-optimizer.php', 'parsers/abstract-parser.php', 'parsers/parser-factory.php', 'parsers/text-parser.php', 'parsers/data-parser.php', 'parsers/clean-pdf-parser.php', 'parsers/html-parser.php', 'parsers/spreadsheet-parser.php', 'parsers/ai-fallback-parser.php', 'pdf-library-loader.php', 'rag-engine.php', 'ai-gpt-client.php', 'user-functions.php', 'crawler-site.php', 'crawler-uploads.php', 'aiohm-kb-manager.php', 'chat-box.php', 'core-init.php', 'settings-page.php', 'shortcode-chat.php', 'shortcode-search.php', 'shortcode-qa.php', 'shortcode-private-assistant.php', 'frontend-widget.php', ); $premium_files = array(); if ( function_exists('aiohm_kb_fs') ) { $fs = aiohm_kb_fs(); // Use comprehensive license checking that matches license-integration.php logic if ( $fs->can_use_premium_code() || $fs->is_paying() || $fs->is_premium() || $fs->is_trial() ) { $premium_files = array( 'mcp-handler.php', 'license-integration.php', ); } } $files = array_merge( $base_files, $premium_files ); foreach ( $files as $file ) { $file_path = AIOHM_KB_INCLUDES_DIR . $file; if ( file_exists( $file_path ) ) { require_once $file_path; } } } public function init_plugin() { AIOHM_KB_Core_Init::init(); AIOHM_KB_Settings_Page::init(); AIOHM_KB_Shortcode_Chat::init(); AIOHM_KB_Shortcode_Search::init(); AIOHM_KB_Shortcode_QA::init(); AIOHM_KB_Frontend_Widget::init(); // Initialize private assistant (FREE feature) if ( class_exists('AIOHM_KB_Shortcode_Private_Assistant') ) { AIOHM_KB_Shortcode_Private_Assistant::init(); } // Initialize premium features only if the license allows it if ( function_exists('aiohm_kb_fs') ) { $fs = aiohm_kb_fs(); // Use comprehensive license checking that matches license-integration.php logic if ( $fs->can_use_premium_code() || $fs->is_paying() || $fs->is_premium() || $fs->is_trial() ) { // Initialize MCP handler for API access if ( class_exists( 'AIOHM_KB_MCP_Handler' ) ) { AIOHM_KB_MCP_Handler::get_instance(); } } } } public function activate() { global $wp_version; if ( version_compare( $wp_version, '6.2', '<' ) ) { deactivate_plugins( plugin_basename( __FILE__ ) ); wp_die( esc_html__( 'AIOHM Knowledge Assistant requires WordPress 6.2 or higher.', 'aiohm-knowledge-assistant' ), esc_html__( 'Plugin Activation Error', 'aiohm-knowledge-assistant' ), array( 'back_link' => true ) ); } require_once AIOHM_KB_INCLUDES_DIR . 'rag-engine.php'; $this->create_tables(); $this->maybe_update_content_type_column(); // Force migration on activation $this->create_project_tables(); // Moved this before conversations $this->create_conversation_tables(); if ( function_exists('aiohm_kb_fs') ) { $fs = aiohm_kb_fs(); if ( $fs->can_use_premium_code() || $fs->is_paying() || $fs->is_premium() || $fs->is_trial() ) { $this->create_mcp_tables(); // Create MCP API tables } } $this->maybe_update_messages_table(); // Update messages table schema $this->maybe_add_conversation_index(); // Add performance index for conversations $this->maybe_migrate_soul_signature(); // Migrate soul signature formatting $this->maybe_standardize_content_types(); // Standardize content type naming $this->set_default_options(); flush_rewrite_rules(); } public function deactivate() { flush_rewrite_rules(); } /** * Encrypt API key for secure storage * * @param string $api_key The API key to encrypt * @return string The encrypted API key */ public static function encrypt_api_key( $api_key ) { if ( empty( $api_key ) ) { return ''; } $key = wp_hash( SECURE_AUTH_KEY . LOGGED_IN_KEY ); try { $iv = random_bytes( 16 ); } catch ( Exception $e ) { $iv = openssl_random_pseudo_bytes( 16 ); if ( $iv === false ) { return ''; } } $encrypted = openssl_encrypt( $api_key, 'AES-256-CBC', $key, 0, $iv ); if ( $encrypted === false ) { return ''; } return base64_encode( $iv . $encrypted ); } /** * Decrypt API key for use * * @param string $encrypted_key The encrypted API key * @return string The decrypted API key */ public static function decrypt_api_key( $encrypted_key, $depth = 0 ) { // Prevent infinite recursion - maximum 2 decryption attempts if ( $depth > 1 ) { return ''; } if ( empty( $encrypted_key ) ) { return ''; } $data = base64_decode( $encrypted_key ); if ( $data === false || strlen( $data ) < 16 ) { return ''; } $key = wp_hash( SECURE_AUTH_KEY . LOGGED_IN_KEY ); $iv = substr( $data, 0, 16 ); $encrypted = substr( $data, 16 ); $decrypted = openssl_decrypt( $encrypted, 'AES-256-CBC', $key, 0, $iv ); if ( $decrypted === false ) { return ''; } // Check if result looks like it might be double-encrypted (base64-like pattern) if ( $depth === 0 && strlen( $decrypted ) > 50 && preg_match( '/^[A-Za-z0-9+\/]+=*$/', $decrypted ) ) { $second_decrypted = self::decrypt_api_key( $decrypted, $depth + 1 ); if ( ! empty( $second_decrypted ) && strlen( $second_decrypted ) < 100 ) { return $second_decrypted; } } return $decrypted; } public static function get_settings() { $default_settings = array( 'aiohm_app_email' => '', 'external_api_consent' => true, 'openai_api_key' => '', 'gemini_api_key' => '', 'claude_api_key' => '', 'shareai_api_key' => '', 'shareai_model' => 'llama4:17b-scout-16e-instruct-fp16', 'groq_api_key' => '', 'groq_model' => 'llama-3.3-70b-versatile', 'private_llm_server_url' => '', 'private_llm_model' => 'llama2', 'default_ai_provider' => 'ollama', 'pro_features' => array( 'ollama_enabled' => true, 'ollama_server_url' => 'http://localhost:11434', 'ollama_model' => 'llama3.2:latest', 'mcp_enabled' => false, ), 'chat_enabled' => true, 'show_floating_chat' => false, 'chunk_size' => 1000, 'chunk_overlap' => 200, 'mirror_mode' => array( 'qa_system_message' => "You are the official AIOHM Knowledge Assistant for \"%site_name%\" - where your brand voice becomes your AI.\n\nYour mission is to embody AIOHM's 'your brand voice... your AI' philosophy by reflecting the authentic personality and expertise of this brand. Today is %day_of_week%, %current_date%.\n\n## BRAND VOICE PHILOSOPHY\nYou represent the AIOHM principle: every AI interaction should authentically reflect the unique voice, values, and character of the brand you serve. You're not generic - you're the digital embodiment of this brand's wisdom.\n\n## RESPONSE EXCELLENCE\n\n**Primary Directive:**\nAnswer using the context below as your authoritative knowledge source - this contains the brand's approved messaging and expertise.\n\n**Brand Voice Expression:**\n• Respond with genuine understanding that reflects this brand's unique personality\n• Maintain professional warmth that matches the brand's character\n• Be comprehensive yet concise - every word should add authentic value\n• Let the brand's expertise and authority shine through your responses\n\n**Knowledge Boundaries:**\nWhen context is insufficient: \"I want to give you accurate information that truly reflects our expertise. For this specific question, connecting directly with our team would serve you best - they can provide the personalized insight you're looking for.\"\n\n**Technical Standards:**\nUse basic HTML formatting only (, ,