init_hooks(); } private function init_hooks() { register_activation_hook(AIOHM_KB_PLUGIN_FILE, [$this, "activate"]); register_deactivation_hook(AIOHM_KB_PLUGIN_FILE, [$this, "deactivate"]); // Ensure HTTPS URLs in admin add_filter("upload_dir", [$this, "force_https_upload_dir"]); add_action("plugins_loaded", [$this, "load_dependencies"]); add_action("init", [$this, "init_plugin"]); add_action("wp_enqueue_scripts", [$this, "register_frontend_assets"]); add_filter( "plugin_action_links_" . plugin_basename(AIOHM_KB_PLUGIN_FILE), [$this, "add_settings_link"], ); add_action("aiohm_process_file_queue", [ $this, "handle_background_file_processing", ]); add_action("aiohm_cleanup_completed_queue", [ $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", [ $this, "add_script_security_attributes", ]); } // Reduce heartbeat frequency to prevent 503 errors add_filter("heartbeat_settings", [$this, "modify_heartbeat_settings"]); // Disable heartbeat on admin pages to prevent conflicts add_action("admin_enqueue_scripts", [ $this, "disable_heartbeat_on_admin", ]); // Add AJAX error handling to prevent 503 errors add_action("wp_ajax_nopriv_handle_ajax_error", [ $this, "handle_ajax_error", ]); add_action("wp_ajax_handle_ajax_error", [$this, "handle_ajax_error"]); // Add shutdown function to catch fatal errors during AJAX requests add_action("shutdown", [$this, "catch_ajax_fatal_errors"]); // Add memory limit checks for AJAX requests add_action("admin_init", [$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", [], 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", [], AIOHM_KB_VERSION, [ "in_footer" => true, "strategy" => "defer", ], ); wp_register_script( "aiohm-chat", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-chat.js", ["jquery"], AIOHM_KB_VERSION, [ "in_footer" => true, "strategy" => "defer", ], ); wp_register_script( "aiohm-chat-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-chat-shortcode.js", ["jquery"], AIOHM_KB_VERSION, [ "in_footer" => true, "strategy" => "defer", ], ); wp_register_script( "aiohm-qa-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-qa-shortcode.js", ["jquery"], AIOHM_KB_VERSION, [ "in_footer" => true, "strategy" => "defer", ], ); wp_register_script( "aiohm-search-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-search-shortcode.js", ["jquery"], AIOHM_KB_VERSION, [ "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", [], AIOHM_KB_VERSION, true, ); wp_register_script( "aiohm-chat", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-chat.js", ["jquery"], AIOHM_KB_VERSION, true, ); wp_register_script( "aiohm-chat-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-chat-shortcode.js", ["jquery"], AIOHM_KB_VERSION, true, ); wp_register_script( "aiohm-qa-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-qa-shortcode.js", ["jquery"], AIOHM_KB_VERSION, true, ); wp_register_script( "aiohm-search-shortcode", AIOHM_KB_PLUGIN_URL . "assets/js/aiohm-search-shortcode.js", ["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", [], AIOHM_KB_VERSION, ); wp_register_style( "aiohm-qa-shortcode", AIOHM_KB_PLUGIN_URL . "assets/css/aiohm-qa-shortcode.css", [], AIOHM_KB_VERSION, ); wp_register_style( "aiohm-search-shortcode", AIOHM_KB_PLUGIN_URL . "assets/css/aiohm-search-shortcode.css", [], AIOHM_KB_VERSION, ); } public function load_dependencies() { $base_files = [ "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 = []; 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 = ["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", ), ["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 = [ "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" => [ "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" => [ "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 (, ,