Component initialization queue */ private SplQueue $initQueue; /** * @var ArrayObject Component initialization status tracker */ private ArrayObject $componentStatus; /** * @var array Plugin configuration cache */ private array $configCache = []; /** * @var bool Whether plugin is fully initialized */ private bool $isInitialized = false; /** * Constructor with property initialization * * Initializes SPL data structures and begins plugin bootstrap process. * Uses lazy loading to prevent Safari cascade overload issues. */ private function __construct() { $this->initQueue = new SplQueue(); $this->componentStatus = new ArrayObject([], ArrayObject::ARRAY_AS_PROPS); $this->defineConstants(); $this->includeFiles(); $this->initializeHooks(); } /** * Get singleton instance * * @return static The singleton instance */ public static function instance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Prevent cloning of singleton */ private function __clone(): void {} /** * Prevent unserialization of singleton * * @throws Exception Always throws to prevent unserialization */ public function __wakeup(): never { throw new Exception('Cannot unserialize singleton instance'); } /** * Define plugin constants with type safety * * Defines all plugin constants with proper validation and fallbacks. * Uses PHP 8+ match expression for cleaner constant definition logic. */ private function defineConstants(): void { if (!defined('HVAC_PLUGIN_VERSION')) { define('HVAC_PLUGIN_VERSION', '2.0.0'); } if (!defined('HVAC_VERSION')) { define('HVAC_VERSION', '2.0.0'); } if (!defined('HVAC_PLUGIN_FILE')) { define('HVAC_PLUGIN_FILE', dirname(__DIR__) . '/hvac-community-events.php'); } if (!defined('HVAC_PLUGIN_DIR')) { define('HVAC_PLUGIN_DIR', plugin_dir_path(HVAC_PLUGIN_FILE)); } if (!defined('HVAC_PLUGIN_URL')) { define('HVAC_PLUGIN_URL', plugin_dir_url(HVAC_PLUGIN_FILE)); } if (!defined('HVAC_PLUGIN_BASENAME')) { define('HVAC_PLUGIN_BASENAME', plugin_basename(HVAC_PLUGIN_FILE)); } } /** * Include required files using generator for memory efficiency * * Uses generator-based file inclusion to minimize memory footprint * and prevent Safari browser cascade loading issues. * * @throws RuntimeException If critical files are missing */ private function includeFiles(): void { // Safari request debugger - load first to catch all requests require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-safari-request-debugger.php'; // Safari script blocker - RE-ENABLED with improved lightweight approach require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-safari-script-blocker.php'; // Theme-agnostic layout manager require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-layout-manager.php'; // Load critical core files first $coreFiles = [ 'includes/class-hvac-logger.php', 'includes/class-hvac-activator.php', 'includes/class-hvac-deactivator.php', 'includes/class-hvac-page-manager.php', 'includes/class-hvac-template-loader.php', 'includes/class-hvac-secure-storage.php' ]; foreach ($this->loadCoreFiles($coreFiles) as $file => $loaded) { if (!$loaded) { throw new RuntimeException("Critical file missing: {$file}"); } } // Check which roles manager exists if (file_exists(HVAC_PLUGIN_DIR . 'includes/class-hvac-roles-manager.php')) { require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-roles-manager.php'; } elseif (file_exists(HVAC_PLUGIN_DIR . 'includes/class-hvac-roles.php')) { require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-roles.php'; } // Core architecture includes require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-browser-detection.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-find-trainer-assets.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-safari-debugger.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-shortcodes.php'; // DISABLED - Using TEC Community Events 5.x instead // require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-edit-event-shortcode.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-scripts-styles.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-bundled-assets.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-route-manager.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-menu-system.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-master-menu-system.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-master-content-injector.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-role-consolidator.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-welcome-popup.php'; require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-query-monitor.php'; // TEC Integration - Load early for proper routing require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-tec-integration.php'; // Unified Event Management System (replaces 8+ fragmented implementations) require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-event-manager.php'; // Load feature files using generator for memory efficiency $featureFiles = [ 'class-hvac-ajax-security.php', 'class-hvac-ajax-handlers.php', 'class-hvac-trainer-status.php', 'class-hvac-access-control.php', 'class-hvac-registration.php', 'class-hvac-security-helpers.php', 'class-hvac-venues.php', 'class-hvac-trainer-profile-manager.php', 'class-hvac-profile-sync-handler.php', 'class-hvac-geocoding-service.php', 'class-hvac-trainer-profile-settings.php', 'class-hvac-geocoding-ajax.php', 'class-hvac-qr-generator.php', 'class-hvac-organizers.php', 'class-hvac-trainer-navigation.php', 'class-hvac-breadcrumbs.php', 'class-hvac-template-integration.php', 'class-hvac-training-leads.php', 'class-hvac-trainer-communication-templates.php', 'class-hvac-import-export-manager.php', // DISABLED - Using TEC Community Events 5.x instead // REMOVED: Consolidated into HVAC_Event_Manager // 'class-hvac-manage-event.php', // 'class-hvac-event-edit-fix.php', // 'class-hvac-event-edit-comprehensive.php', // 'class-hvac-custom-event-edit.php', // 'class-hvac-edit-event-shortcode.php', // 'class-event-form-handler.php', // 'class-hvac-event-summary.php', 'class-hvac-trainer-profile.php', 'class-hvac-master-dashboard.php', 'class-hvac-master-dashboard-data.php', 'class-hvac-settings.php', 'class-hvac-dashboard.php', 'class-hvac-dashboard-data.php', 'class-hvac-approval-workflow.php', 'class-hvac-master-pending-approvals.php', 'class-hvac-master-events-overview.php', 'class-hvac-master-trainers-overview.php', 'class-hvac-announcements-manager.php', 'class-hvac-announcements-display.php', 'class-hvac-master-pages-fixer.php', 'class-hvac-master-layout-standardizer.php', 'class-hvac-master-content-injector.php', 'class-hvac-event-navigation.php', 'class-hvac-event-manage-header.php', 'class-hvac-help-system.php', 'class-hvac-documentation-content.php', 'class-event-form-handler.php', 'class-event-author-fixer.php', 'class-attendee-profile.php', 'class-hvac-page-content-fixer.php', 'class-hvac-page-content-manager.php', ]; // Find a Trainer feature files $findTrainerFiles = [ 'database/class-hvac-contact-submissions-table.php', 'find-trainer/class-hvac-find-trainer-page.php', 'find-trainer/class-hvac-mapgeo-integration.php', 'find-trainer/class-hvac-contact-form-handler.php', 'find-trainer/class-hvac-trainer-directory-query.php', 'class-hvac-mapgeo-safety.php', // MapGeo safety wrapper ]; // Load feature files with memory-efficient generator foreach ($this->loadFeatureFiles($featureFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus[$file] = true; } } // Load Find a Trainer feature files foreach ($this->loadFeatureFiles($findTrainerFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["find_trainer_{$file}"] = true; } } // Load community system files $communityFiles = [ 'community/class-login-handler.php', 'community/class-event-handler.php' ]; foreach ($this->loadFeatureFiles($communityFiles) as $file => $status) { if ($status === 'loaded' && $file === 'community/class-login-handler.php') { // Initialize Login_Handler to register shortcode if (class_exists('\HVAC_Community_Events\Community\Login_Handler')) { new \HVAC_Community_Events\Community\Login_Handler(); } } } // Certificate system files $certificateFiles = [ 'certificates/class-certificate-security.php', 'certificates/class-certificate-installer.php', 'certificates/class-certificate-manager.php', 'certificates/class-certificate-generator.php', 'certificates/class-certificate-url-handler.php', ]; // Trainer certification system files $trainerCertificationFiles = [ 'certifications/class-hvac-trainer-certification-manager.php', 'certifications/class-hvac-certification-migrator.php', 'certifications/class-hvac-certification-admin.php', ]; // Load certificate and certification files foreach ($this->loadFeatureFiles($certificateFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["certificate_{$file}"] = true; } } foreach ($this->loadFeatureFiles($trainerCertificationFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["trainer_cert_{$file}"] = true; } } // Initialize announcements system $announcementsFile = 'class-hvac-announcements-manager.php'; foreach ($this->loadFeatureFiles([$announcementsFile]) as $file => $status) { if ($status === 'loaded' && class_exists('HVAC_Announcements_Manager')) { HVAC_Announcements_Manager::init(); } } // Admin system files $adminFiles = [ 'admin/class-zoho-admin.php', 'admin/class-admin-dashboard.php', 'admin/class-hvac-enhanced-settings.php', ]; // Load admin files conditionally if (is_admin() || wp_doing_ajax()) { foreach ($this->loadFeatureFiles($adminFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["admin_{$file}"] = true; } } } // Google Sheets integration files $googleFiles = [ 'google-sheets/class-google-sheets-auth.php', 'google-sheets/class-google-sheets-admin.php', ]; // Load Google integration files foreach ($this->loadFeatureFiles($googleFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["google_{$file}"] = true; } } // Communication system files $communicationFiles = [ 'communication/class-communication-installer.php', 'communication/class-communication-scheduler.php', 'communication/class-communication-templates.php', ]; // Load communication files foreach ($this->loadFeatureFiles($communicationFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["comm_{$file}"] = true; } } // Load helper files $helperFiles = ['helpers/attendee-profile-link.php']; foreach ($this->loadFeatureFiles($helperFiles) as $file => $status) { if ($status === 'loaded') { $this->componentStatus["helper_{$file}"] = true; } } // Load legacy files for backward compatibility $this->includeLegacyFiles(); } /** * Initialize WordPress hooks with proper typing * * Registers all WordPress actions and filters with type-safe callbacks. * Uses modern array syntax and proper hook priorities. */ private function initializeHooks(): void { // Lifecycle hooks register_activation_hook(HVAC_PLUGIN_FILE, [$this, 'activate']); register_deactivation_hook(HVAC_PLUGIN_FILE, [$this, 'deactivate']); // Core initialization hooks add_action('init', [$this, 'initialize'], 0); add_action('plugins_loaded', [$this, 'pluginsLoaded']); // Admin hooks add_action('admin_init', [$this, 'adminInit']); add_action('admin_menu', [$this, 'addAdminMenus']); // Feature initialization add_action('init', [$this, 'initializeFindTrainer'], 20); // AJAX handlers with proper naming add_action('wp_ajax_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']); add_action('wp_ajax_hvac_safari_debug', [$this, 'ajaxSafariDebug']); add_action('wp_ajax_nopriv_hvac_safari_debug', [$this, 'ajaxSafariDebug']); // Theme integration add_filter('body_class', [$this, 'add_hvac_body_classes']); add_filter('post_class', [$this, 'addHvacPostClasses']); // Astra theme layout overrides add_filter('astra_page_layout', [$this, 'force_full_width_layout']); add_filter('astra_get_content_layout', [$this, 'force_full_width_layout']); // Page management hooks add_action('admin_init', [$this, 'checkForPageUpdates']); add_action('init', [$this, 'checkPageUpdateFlag'], 99); } /** * Plugin activation handler * * Delegates to activator class with proper error handling. * * @throws RuntimeException If activation fails */ public function activate(): void { try { HVAC_Activator::activate(); } catch (Exception $e) { error_log('HVAC Plugin activation failed: ' . $e->getMessage()); throw new RuntimeException('Plugin activation failed', 0, $e); } } /** * Plugin deactivation handler * * Delegates to deactivator class with proper cleanup. */ public function deactivate(): void { try { HVAC_Deactivator::deactivate(); } catch (Exception $e) { error_log('HVAC Plugin deactivation error: ' . $e->getMessage()); // Don't throw on deactivation - log and continue } } /** * Main initialization method * * Initializes core components with lazy loading to prevent memory issues. * Uses SPL queue to manage component initialization order. */ public function initialize(): void { if ($this->isInitialized) { return; } // Initialize core architecture components with type safety $coreComponents = [ 'HVAC_Shortcodes' => fn() => HVAC_Shortcodes::instance(), 'HVAC_Scripts_Styles' => fn() => HVAC_Scripts_Styles::instance(), 'HVAC_Bundled_Assets' => fn() => HVAC_Bundled_Assets::instance(), 'HVAC_Find_Trainer_Assets' => fn() => HVAC_Find_Trainer_Assets::instance(), 'HVAC_Safari_Debugger' => fn() => HVAC_Safari_Debugger::instance(), 'HVAC_Route_Manager' => fn() => HVAC_Route_Manager::instance() ]; // Initialize core components with error handling foreach ($coreComponents as $name => $initializer) { try { if (class_exists($name)) { $initializer(); $this->componentStatus[$name] = true; } } catch (Exception $e) { error_log("Failed to initialize {$name}: " . $e->getMessage()); } } // Initialize template loader HVAC_Template_Loader::init(); // Initialize access control with error handling if (class_exists('HVAC_Access_Control')) { new HVAC_Access_Control(); } // Initialize optional components $this->initializeOptionalComponents(); // Initialize secondary components $this->initializeComponents(); // Registration access hook add_action('template_redirect', [$this, 'ensure_registration_access'], 5); $this->isInitialized = true; } /** * Initialize optional components with proper error handling * * Components are initialized only if their classes exist to prevent fatal errors. */ private function initializeOptionalComponents(): void { // Trainer certification system if (class_exists('HVAC_Trainer_Certification_Manager')) { try { HVAC_Trainer_Certification_Manager::instance(); $this->componentStatus['trainer_certification'] = true; } catch (Exception $e) { error_log('Trainer certification initialization failed: ' . $e->getMessage()); } } // Query monitoring if (class_exists('HVAC_Query_Monitor')) { HVAC_Query_Monitor::init(); $this->componentStatus['query_monitor'] = true; } } /** * Initialize plugin components with lazy loading * * Prevents Safari cascade overload by using deferred initialization. */ private function initializeComponents(): void { // Initialize only critical core components immediately if (class_exists('HVAC_Community_Events')) { // DISABLED - Using TEC Community Events 5.x instead // HVAC_Community_Events::instance(); } // Schedule non-critical components for lazy loading add_action('wp_loaded', [$this, 'initializeSecondaryComponents'], 5); add_action('admin_init', [$this, 'initializeAdminComponents'], 5); } /** * Initialize secondary components with lazy loading * * Spreads initialization across multiple hooks to prevent browser overload. * Uses memory-efficient component loading patterns. */ public function initializeSecondaryComponents(): void { // Initialize registration if class exists if (class_exists('HVAC_Registration')) { new HVAC_Registration(); } // Initialize venues management if (class_exists('HVAC_Venues')) { HVAC_Venues::instance(); } // Initialize trainer profile manager if (class_exists('HVAC_Trainer_Profile_Manager')) { HVAC_Trainer_Profile_Manager::get_instance(); } // Initialize profile sync handler if (class_exists('HVAC_Profile_Sync_Handler')) { HVAC_Profile_Sync_Handler::get_instance(); } // Initialize geocoding service if (class_exists('HVAC_Geocoding_Service')) { HVAC_Geocoding_Service::get_instance(); } // Initialize trainer profile settings if (class_exists('HVAC_Trainer_Profile_Settings')) { HVAC_Trainer_Profile_Settings::get_instance(); } // Initialize organizers management if (class_exists('HVAC_Organizers')) { HVAC_Organizers::instance(); } // Initialize training leads management if (class_exists('HVAC_Training_Leads')) { HVAC_Training_Leads::instance(); } // Initialize menu systems if (class_exists('HVAC_Menu_System')) { HVAC_Menu_System::instance(); } if (class_exists('HVAC_Master_Menu_System')) { HVAC_Master_Menu_System::instance(); } // Initialize breadcrumbs if (class_exists('HVAC_Breadcrumbs')) { HVAC_Breadcrumbs::instance(); } // Initialize unified event management system (replaces 8+ fragmented implementations) if (class_exists('HVAC_Event_Manager')) { HVAC_Event_Manager::instance(); } // Legacy event summary (if still needed) if (class_exists('HVAC_Event_Summary')) { new HVAC_Event_Summary(); } // Initialize trainer profile if (class_exists('HVAC_Trainer_Profile')) { new HVAC_Trainer_Profile(); } // Initialize dashboards if (class_exists('HVAC_Dashboard')) { new HVAC_Dashboard(); } if (class_exists('HVAC_Master_Dashboard')) { new HVAC_Master_Dashboard(); } // Initialize settings if (class_exists('HVAC_Settings')) { new HVAC_Settings(); } // Initialize approval workflow if (class_exists('HVAC_Approval_Workflow')) { new HVAC_Approval_Workflow(); } // Initialize event navigation if (class_exists('HVAC_Event_Navigation')) { new HVAC_Event_Navigation(); } // Initialize help system if (class_exists('HVAC_Help_System')) { HVAC_Help_System::instance(); } // Initialize Master Layout Standardizer if (class_exists('HVAC_Master_Layout_Standardizer')) { HVAC_Master_Layout_Standardizer::instance(); } // Initialize Master Content Injector if (class_exists('HVAC_Master_Content_Injector')) { HVAC_Master_Content_Injector::instance(); } // Initialize Page Content Manager if (class_exists('HVAC_Page_Content_Manager')) { HVAC_Page_Content_Manager::instance(); } // Initialize certificate security if (class_exists('HVAC_Certificate_Security')) { HVAC_Certificate_Security::instance(); } // Initialize certificate URL handler if (class_exists('HVAC_Certificate_URL_Handler')) { HVAC_Certificate_URL_Handler::instance(); } // Initialize attendee profile if (class_exists('HVAC_Attendee_Profile')) { HVAC_Attendee_Profile::instance(); } // Initialize Google Sheets if (class_exists('HVAC_Google_Sheets_Auth')) { new HVAC_Google_Sheets_Auth(); } if (class_exists('HVAC_Google_Sheets_Admin')) { new HVAC_Google_Sheets_Admin(); } // Initialize communication system if (class_exists('HVAC_Communication_Installer')) { HVAC_Communication_Installer::maybe_update(); } if (class_exists('HVAC_Communication_Scheduler')) { hvac_communication_scheduler(); } // Initialize Master Trainer manager classes (fix for missing shortcode registrations) if (class_exists('HVAC_Master_Events_Overview')) { HVAC_Master_Events_Overview::instance(); } if (class_exists('HVAC_Master_Pending_Approvals')) { HVAC_Master_Pending_Approvals::instance(); } if (class_exists('HVAC_Master_Trainers_Overview')) { HVAC_Master_Trainers_Overview::instance(); } if (class_exists('HVAC_Announcements_Display')) { HVAC_Announcements_Display::get_instance(); } if (class_exists('HVAC_Announcements_Admin')) { HVAC_Announcements_Admin::get_instance(); } } /** * Initialize admin components with conditional loading * * Only loads admin components in admin context to improve frontend performance. */ public function initializeAdminComponents(): void { // Initialize admin components only when needed if (class_exists('HVAC_Zoho_Admin')) { HVAC_Zoho_Admin::instance(); } if (class_exists('HVAC_Admin_Dashboard')) { new HVAC_Admin_Dashboard(); } if (class_exists('HVAC_Enhanced_Settings')) { HVAC_Enhanced_Settings::instance(); } // Initialize trainer certification admin interface if (class_exists('HVAC_Certification_Admin') && current_user_can('manage_hvac_certifications')) { HVAC_Certification_Admin::instance(); } } /** * Initialize Find a Trainer feature components * * Loads trainer directory functionality with proper error handling. */ public function initializeFindTrainer(): void { // Initialize Find a Trainer page if (class_exists('HVAC_Find_Trainer_Page')) { HVAC_Find_Trainer_Page::get_instance(); } // Initialize MapGeo integration if (class_exists('HVAC_MapGeo_Integration')) { HVAC_MapGeo_Integration::get_instance(); } // Initialize contact form handler if (class_exists('HVAC_Contact_Form_Handler')) { HVAC_Contact_Form_Handler::get_instance(); } // Initialize trainer directory query if (class_exists('HVAC_Trainer_Directory_Query')) { HVAC_Trainer_Directory_Query::get_instance(); } // Initialize master trainer manager components if (class_exists('HVAC_Master_Trainers_Overview')) { HVAC_Master_Trainers_Overview::instance(); } if (class_exists('HVAC_Announcements_Manager')) { HVAC_Announcements_Manager::get_instance(); } if (class_exists('HVAC_Master_Pending_Approvals')) { HVAC_Master_Pending_Approvals::instance(); } if (class_exists('HVAC_Master_Events_Overview')) { HVAC_Master_Events_Overview::instance(); } // Fix master trainer pages if needed if (class_exists('HVAC_Master_Pages_Fixer')) { // Run the fix immediately on plugin activation if (defined('WP_INSTALLING_PLUGIN') || (isset($_GET['action']) && $_GET['action'] === 'activate')) { HVAC_Master_Pages_Fixer::fix_pages(); } // Also check periodically add_action('init', array('HVAC_Master_Pages_Fixer', 'check_pages'), 999); } } /** * Plugins loaded hook - internationalization setup * * Loads plugin text domain for translations with proper path resolution. */ public function pluginsLoaded(): void { $textDomainPath = dirname(HVAC_PLUGIN_BASENAME) . '/languages'; $loaded = load_plugin_textdomain( 'hvac-community-events', false, $textDomainPath ); if (!$loaded && defined('WP_DEBUG') && WP_DEBUG) { error_log("Failed to load text domain from: {$textDomainPath}"); } } /** * Admin initialization hook * * Handles admin-specific initialization including version management. */ public function adminInit(): void { $this->checkPluginUpdates(); } /** * Add admin menu items with proper capability checking * * Registers admin menu pages with appropriate capabilities and callbacks. */ public function addAdminMenus(): void { // Add event seeder page add_submenu_page( 'tools.php', // Parent menu 'HVAC Event Seeder', // Page title 'HVAC Event Seeder', // Menu title 'manage_options', // Capability 'hvac-seed-events', // Menu slug [$this, 'renderSeedEventsPage'] // Callback ); } /** * Render the seed events admin page * * Includes the seeding interface with proper security checks. * * @throws RuntimeException If admin file is missing */ public function renderSeedEventsPage(): void { $adminFile = HVAC_PLUGIN_DIR . 'admin/seed-events-direct.php'; if (!file_exists($adminFile)) { throw new RuntimeException('Seed events admin file not found'); } require_once $adminFile; } /** * Check for plugin updates with semantic versioning * * Compares current version with installed version and triggers upgrade. */ private function checkPluginUpdates(): void { $currentVersion = get_option('hvac_plugin_version', '0.0.0'); if (version_compare($currentVersion, HVAC_PLUGIN_VERSION, '<')) { try { $this->runUpgradeRoutines($currentVersion); update_option('hvac_plugin_version', HVAC_PLUGIN_VERSION); } catch (Exception $e) { error_log('Plugin upgrade failed: ' . $e->getMessage()); } } } /** * Run version-specific upgrade routines * * @param string $fromVersion Version upgrading from * @throws Exception If upgrade fails */ private function runUpgradeRoutines(string $fromVersion): void { if (class_exists('HVAC_Logger')) { HVAC_Logger::info( "Upgrading from version {$fromVersion} to " . HVAC_PLUGIN_VERSION, 'Upgrade' ); } // Version-specific upgrade logic can be added here using match expression $upgradeActions = match (true) { version_compare($fromVersion, '2.0.0', '<') => $this->upgradeTo200(), default => null }; } /** * Upgrade to version 2.0.0 * * Handles specific upgrade tasks for version 2.0.0. */ private function upgradeTo200(): void { // Version 2.0.0 specific upgrade tasks // This method can be expanded as needed } /** * Ensure trainer registration page is publicly accessible * * @return void */ public function ensure_registration_access() { // If we're on the trainer registration page, don't apply any authentication checks $current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); if ($current_path === 'trainer/registration' || is_page('registration') || is_page('trainer-registration')) { // Remove any potential authentication hooks that might be added by other code remove_all_actions('template_redirect', 10); } } /** * Handle AJAX request for master dashboard events table * * @return void */ public function ajax_master_dashboard_events() { // Check authentication first if (!is_user_logged_in()) { wp_send_json_error('Authentication required', 401); } // Verify nonce if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'hvac_master_dashboard_nonce')) { wp_die('Security check failed'); } // Check permissions if (!current_user_can('view_master_dashboard') && !current_user_can('view_all_trainer_data') && !current_user_can('manage_options')) { wp_send_json_error('Insufficient permissions'); } // Load master dashboard data class if needed if (!class_exists('HVAC_Master_Dashboard_Data')) { $data_file = HVAC_PLUGIN_DIR . 'includes/class-hvac-master-dashboard-data.php'; if (file_exists($data_file)) { require_once $data_file; } } if (!class_exists('HVAC_Master_Dashboard_Data')) { wp_send_json_error('Master dashboard data class not found'); } // Initialize data handler $master_data = new HVAC_Master_Dashboard_Data(); // Get table data with filters $args = array( 'status' => sanitize_text_field($_POST['status'] ?? 'all'), 'search' => sanitize_text_field($_POST['search'] ?? ''), 'orderby' => sanitize_text_field($_POST['orderby'] ?? 'date'), 'order' => sanitize_text_field($_POST['order'] ?? 'DESC'), 'page' => absint($_POST['page'] ?? 1), 'per_page' => absint($_POST['per_page'] ?? 10), 'date_from' => sanitize_text_field($_POST['date_from'] ?? ''), 'date_to' => sanitize_text_field($_POST['date_to'] ?? ''), 'trainer_id' => absint($_POST['trainer_id'] ?? 0), ); $table_data = $master_data->get_events_table_data($args); wp_send_json_success($table_data); } /** * Handle AJAX request for Safari debugging logs * * Processes browser debug data with proper sanitization and logging. */ public function ajaxSafariDebug(): void { // Validate log data $logData = $_POST['log'] ?? ''; if (empty($logData)) { wp_send_json_error('No log data provided'); return; } // Decode and validate JSON data $logEntry = json_decode(stripslashes($logData), true); if (!is_array($logEntry)) { wp_send_json_error('Invalid log data format'); return; } // Create sanitized log entry $safeLogEntry = [ 'timestamp' => $logEntry['time'] ? sanitize_text_field($logEntry['time']) : current_time('Y-m-d H:i:s'), 'message' => sanitize_text_field($logEntry['message'] ?? ''), 'user_agent' => sanitize_text_field($logEntry['userAgent'] ?? ''), 'data' => isset($logEntry['data']) ? wp_json_encode($logEntry['data']) : null, 'error_info' => sanitize_text_field($logEntry['error'] ?? ''), 'stack_trace' => sanitize_textarea_field($logEntry['stack'] ?? '') ]; // Log to WordPress debug log with structured format if (defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) { $logParts = [ '[SAFARI-DEBUG]', $safeLogEntry['timestamp'], $safeLogEntry['message'], 'UA: ' . $safeLogEntry['user_agent'] ]; if (!empty($safeLogEntry['data'])) { $logParts[] = 'Data: ' . $safeLogEntry['data']; } if (!empty($safeLogEntry['error_info'])) { $logParts[] = 'Error: ' . $safeLogEntry['error_info']; } error_log(implode(' | ', $logParts)); } // Store in database with size limits to prevent bloat $logOptionKey = 'hvac_safari_debug_logs_' . date('Y_m_d'); $existingLogs = get_option($logOptionKey, []); $existingLogs[] = $safeLogEntry; // Keep only last 100 entries per day if (count($existingLogs) > 100) { $existingLogs = array_slice($existingLogs, -100); } update_option($logOptionKey, $existingLogs, false); wp_send_json_success(['logged' => true]); } /** * Add HVAC-specific body classes * * @param array $classes Body classes * @return array Modified body classes */ public function add_hvac_body_classes($classes) { // Check if we're on a plugin page if (defined('HVAC_IN_PAGE_TEMPLATE') && HVAC_IN_PAGE_TEMPLATE) { $classes[] = 'hvac-page'; $classes[] = 'hvac-plugin-active'; // Add solid header class to prevent transparency $classes[] = 'ast-header-without-transparency'; $classes[] = 'header-main-layout-standard'; // Add Astra full-width layout classes $classes[] = 'ast-no-sidebar'; $classes[] = 'single'; $classes[] = 'ast-single-post'; $classes[] = 'ast-full-width-layout'; } // Check URL for plugin pages $current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); if (strpos($current_path, 'trainer/') !== false || strpos($current_path, 'master-trainer/') !== false) { $classes[] = 'hvac-trainer-page'; $classes[] = 'ast-header-without-transparency'; // Ensure full-width for all trainer pages $classes[] = 'ast-no-sidebar'; $classes[] = 'ast-full-width-layout'; } // Add specific page classes if (is_page_template('page-trainer-dashboard.php')) { $classes[] = 'hvac-trainer-dashboard'; } if (is_page_template('page-certificate-reports.php')) { $classes[] = 'hvac-certificate-reports'; } if (is_page_template('page-trainer-profile.php')) { $classes[] = 'hvac-trainer-profile'; } // Remove sidebar classes using modern array syntax $sidebarClasses = [ 'ast-right-sidebar', 'ast-left-sidebar', 'ast-separate-container' ]; $classes = array_diff($classes, $sidebarClasses); return $classes; } /** * Add HVAC-specific post classes with type safety * * @param string[] $classes Post classes array * @return string[] Modified post classes */ public function addHvacPostClasses(array $classes): array { global $post; if ($post instanceof WP_Post && str_contains($post->post_name, 'trainer')) { $classes[] = 'hvac-post'; } return $classes; } /** * Force full-width layout for HVAC pages in Astra theme * * @param string $layout Current layout * @return string Modified layout */ public function force_full_width_layout($layout) { // Check if we're on a plugin page if (defined('HVAC_IN_PAGE_TEMPLATE') && HVAC_IN_PAGE_TEMPLATE) { return 'no-sidebar'; } // Check URL for plugin pages $current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'); if (strpos($current_path, 'trainer/') !== false || strpos($current_path, 'master-trainer/') !== false) { return 'no-sidebar'; } // Check by page template with modern string functions if (is_page_template()) { $template = get_page_template_slug(); $hvacTemplates = [ 'page-trainer', 'page-master', 'page-certificate', 'page-generate', 'page-manage-event' ]; foreach ($hvacTemplates as $hvacTemplate) { if (str_contains($template, $hvacTemplate)) { return 'no-sidebar'; } } } return $layout; } /** * Check for manual page update request with security validation * * Handles admin-triggered page updates with proper authorization. */ public function checkForPageUpdates(): void { // Only allow admins to trigger updates if (!current_user_can('manage_options')) { return; } // Check for update trigger with type safety $updateRequest = $_GET['hvac_update_pages'] ?? ''; if ($updateRequest === 'true') { $pageManagerFile = HVAC_PLUGIN_DIR . 'includes/class-hvac-page-manager.php'; if (!file_exists($pageManagerFile)) { error_log('HVAC Page Manager file not found'); return; } require_once $pageManagerFile; try { // Update all page layouts and templates HVAC_Page_Manager::update_existing_page_layouts(); // Add admin notice using anonymous function add_action('admin_notices', static function(): void { echo '
'; echo '

HVAC pages have been updated with correct templates and layouts.

'; echo '
'; }); // Clean redirect $redirectUrl = remove_query_arg('hvac_update_pages'); wp_safe_redirect($redirectUrl); exit; } catch (Exception $e) { error_log('Page update failed: ' . $e->getMessage()); } } } /** * Check if pages need update after activation * * Handles post-activation page updates with proper error handling. */ public function checkPageUpdateFlag(): void { if (!get_option('hvac_pages_need_update', false)) { return; } $pageManagerFile = HVAC_PLUGIN_DIR . 'includes/class-hvac-page-manager.php'; if (!file_exists($pageManagerFile)) { error_log('HVAC Page Manager file not found during activation update'); return; } require_once $pageManagerFile; try { // Update all page layouts and templates HVAC_Page_Manager::update_existing_page_layouts(); // Remove the update flag delete_option('hvac_pages_need_update'); // Log successful update if (class_exists('HVAC_Logger')) { HVAC_Logger::info('Pages updated after activation', 'HVAC Plugin'); } } catch (Exception $e) { error_log('Post-activation page update failed: ' . $e->getMessage()); } } /** * Load core files with error handling * * @param string[] $files Array of file paths relative to plugin directory * @return Generator File path => loaded status */ private function loadCoreFiles(array $files): Generator { foreach ($files as $file) { $filePath = HVAC_PLUGIN_DIR . $file; if (file_exists($filePath)) { require_once $filePath; yield $file => true; } else { yield $file => false; } } } /** * Load feature files with memory-efficient generator * * @param string[] $files Array of file paths * @return Generator File path => load status */ private function loadFeatureFiles(array $files): Generator { foreach ($files as $file) { $filePath = HVAC_PLUGIN_DIR . 'includes/' . $file; if (file_exists($filePath)) { try { require_once $filePath; yield $file => 'loaded'; } catch (Exception $e) { error_log("Failed to load feature file {$file}: " . $e->getMessage()); yield $file => 'error'; } } else { yield $file => 'missing'; } } } /** * Include legacy files for backward compatibility * * Loads legacy function files with proper error handling. */ private function includeLegacyFiles(): void { $legacyFiles = [ 'includes/hvac-ce-functions.php', 'includes/hvac-ce-admin.php', 'includes/hvac-ce-certificates.php' ]; foreach ($legacyFiles as $file) { $filePath = HVAC_PLUGIN_DIR . $file; if (file_exists($filePath)) { try { require_once $filePath; $this->componentStatus["legacy_{$file}"] = true; } catch (Exception $e) { error_log("Failed to load legacy file {$file}: " . $e->getMessage()); } } } } /** * Get component initialization status * * @return ArrayObject Component status map */ public function getComponentStatus(): ArrayObject { return $this->componentStatus; } /** * Check if plugin is fully initialized */ public function isInitialized(): bool { return $this->isInitialized; } }