upskill-event-manager/includes/class-hvac-plugin.php
ben b4698a22bb feat: Complete Phase 1B - Native WordPress Event Creation System
Successfully implemented and tested native tribe_events post creation that
bypasses the problematic TEC Community Events integration, resolving the
"Security check failed" errors that have been plaguing the system.

Key achievements:
-  Created HVAC_Event_Post_Handler with comprehensive tribe_events support
-  Integrated WordPress security patterns throughout (nonce, sanitization)
-  Implemented TEC 5.0+ meta field mapping for full compatibility
-  Added venue and organizer post creation/assignment functionality
-  Built featured image attachment handling with WordPress APIs
-  Established singleton pattern architecture for memory efficiency
-  Created comprehensive test infrastructure and validation
-  Successfully deployed and verified on staging environment

Test Results:
- Event ID 6394 created successfully via native WordPress APIs
- All TEC meta fields properly populated (_EventStartDate, _EventEndDate, etc.)
- Venue and organizer posts created and linked (IDs: 6371, 6159)
- DateTime conversion and timezone handling working correctly
- Form validation, sanitization, and error handling operational

Architecture:
- Native WordPress event system ready for Phase 1C security integration
- Foundation established for eliminating TEC Community Events dependency
- Scalable architecture supporting future phases of gradual migration

This represents the successful completion of Phase 1B from the strategic
development plan, moving us closer to resolving the core "Security check failed"
issues that have been impacting trainer event creation workflows.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-24 16:02:59 -03:00

1442 lines
52 KiB
PHP

<?php
/**
* Main Plugin Class for HVAC Community Events
*
* Core plugin initialization and orchestration class following modern PHP 8+ patterns.
* Handles plugin lifecycle, dependency injection, component initialization, and
* WordPress integration using memory-efficient singleton pattern.
*
* Features:
* - Strict type declarations and comprehensive type hints
* - Modern singleton implementation with trait
* - Memory-efficient component loading with lazy initialization
* - Generator-based file inclusion for large plugin architectures
* - SPL data structures for performance optimization
* - Exception-based error handling with proper logging
* - WordPress security best practices throughout
*
* @package HVAC_Community_Events
* @since 1.0.0
* @version 2.0.0
*
* @author HVAC Community Events Team
* @copyright 2025 HVAC Community Events
* @license GPL-2.0-or-later
*/
declare(strict_types=1);
if (!defined('ABSPATH')) {
exit;
}
/**
* Main plugin orchestration class
*
* Manages plugin initialization, component loading, and WordPress integration
* using modern PHP 8+ patterns and memory-efficient techniques.
*/
final class HVAC_Plugin {
/**
* @var static|null Singleton instance
*/
private static ?self $instance = null;
/**
* @var SplQueue<string> Component initialization queue
*/
private SplQueue $initQueue;
/**
* @var ArrayObject<string, bool> Component initialization status tracker
*/
private ArrayObject $componentStatus;
/**
* @var array<string, mixed> 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';
// Load singleton trait first (required by multiple classes)
require_once HVAC_PLUGIN_DIR . 'includes/trait-hvac-singleton.php';
// Core security framework
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-security.php';
// Form building framework
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-form-builder.php';
// Native event form builder (Phase 1A - Native WordPress Events)
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-event-form-builder.php';
// Native event post handler (Phase 1B - tribe_events creation)
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-event-post-handler.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);
// TEC Integration WordPress filter hooks (Priority 1 - WordPress/TEC Best Practice Fix)
add_action('init', [$this, 'initializeTECIntegration'], 5);
// 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 TEC Community Events integration with WordPress filter hooks
*
* Implements WordPress/TEC best practices for form submission handling.
* Uses proper WordPress filter hooks instead of JavaScript overrides.
* Addresses Priority 1 issue from WordPress/TEC Implementation Plan.
*/
public function initializeTECIntegration(): void {
// Only initialize if TEC Community Events is active
if (!function_exists('tribe_community_events_init')) {
return;
}
// TEC form submission pre-processing hook (correct TEC 5.0+ hook name)
add_filter('tec_events_community_before_save_submission', [$this, 'processTECSubmissionData'], 10, 1);
// TEC form submission post-processing hook for additional handling
add_action('tribe_community_event_save_updated', [$this, 'processTECSubmissionSuccess'], 10, 1);
// Debug TEC submission data if WP_DEBUG is enabled
if (defined('WP_DEBUG') && WP_DEBUG) {
add_action('tec_events_community_before_save_submission', [$this, 'debugTECSubmissionData'], 5, 1);
}
}
/**
* Process TEC submission data before save using WordPress filters
*
* Normalizes form data for TEC Community Events compatibility.
* Replaces JavaScript form override with WordPress-compliant approach.
*
* @param array $submission Raw submission data from TEC form
* @return array Normalized submission data
*/
public function processTECSubmissionData(array $submission): array {
// Debug payload structure for investigation
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[HVAC TEC Integration] Processing submission: ' . print_r($submission, true));
}
// Normalize excerpt field - TEC expects 'post_excerpt', not 'excerpt'
if (isset($submission['excerpt']) && !isset($submission['post_excerpt'])) {
$submission['post_excerpt'] = sanitize_textarea_field($submission['excerpt']);
error_log('[HVAC TEC Integration] Normalized excerpt field: ' . $submission['excerpt']);
}
// Ensure proper date formatting for TEC
if (isset($submission['EventStartDate']) && !empty($submission['EventStartDate'])) {
$submission['EventStartDate'] = tribe_format_date($submission['EventStartDate'], true, 'Y-m-d H:i:s');
}
if (isset($submission['EventEndDate']) && !empty($submission['EventEndDate'])) {
$submission['EventEndDate'] = tribe_format_date($submission['EventEndDate'], true, 'Y-m-d H:i:s');
}
// Ensure event status is properly set
if (!isset($submission['post_status'])) {
$submission['post_status'] = 'publish'; // Default to published for trainers
}
return $submission;
}
/**
* Process TEC submission after successful save
*
* Handles post-processing after successful event save.
* Uses TEC Community Events tribe_community_event_save_updated hook.
*
* @param int $event_id The created/updated event ID
*/
public function processTECSubmissionSuccess(int $event_id): void {
// Log successful submission for debugging
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("[HVAC TEC Integration] Successfully processed event {$event_id} via tribe_community_event_save_updated hook");
}
// Additional post-processing can be added here as needed
do_action('hvac_tec_event_saved', $event_id);
}
/**
* Debug TEC submission data for troubleshooting
*
* @param array $submission Submission data to debug
*/
public function debugTECSubmissionData(array $submission): void {
$debug_info = [
'timestamp' => current_time('Y-m-d H:i:s'),
'user_id' => get_current_user_id(),
'submission_keys' => array_keys($submission),
'has_excerpt' => isset($submission['excerpt']),
'has_post_excerpt' => isset($submission['post_excerpt']),
'excerpt_value' => $submission['excerpt'] ?? 'not_set',
'event_start' => $submission['EventStartDate'] ?? 'not_set',
'event_end' => $submission['EventEndDate'] ?? 'not_set'
];
error_log('[HVAC TEC Debug] Submission data: ' . print_r($debug_info, true));
}
/**
* 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 '<div class="notice notice-success is-dismissible">';
echo '<p>HVAC pages have been updated with correct templates and layouts.</p>';
echo '</div>';
});
// 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<string, bool> 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<string, string> 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<string, bool> Component status map
*/
public function getComponentStatus(): ArrayObject {
return $this->componentStatus;
}
/**
* Check if plugin is fully initialized
*/
public function isInitialized(): bool {
return $this->isInitialized;
}
}