upskill-event-manager/includes/class-hvac-bundled-assets.php
ben 054639c95c
Some checks failed
HVAC Plugin CI/CD Pipeline / Code Quality & Standards (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Unit Tests (push) Has been cancelled
Security Monitoring & Compliance / Secrets & Credential Scan (push) Has been cancelled
Security Monitoring & Compliance / WordPress Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Security Analysis (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Integration Tests (push) Has been cancelled
Security Monitoring & Compliance / Dependency Vulnerability Scan (push) Has been cancelled
Security Monitoring & Compliance / Static Code Security Analysis (push) Has been cancelled
Security Monitoring & Compliance / Security Compliance Validation (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Deploy to Production (push) Has been cancelled
HVAC Plugin CI/CD Pipeline / Notification (push) Has been cancelled
Security Monitoring & Compliance / Security Summary Report (push) Has been cancelled
Security Monitoring & Compliance / Security Team Notification (push) Has been cancelled
feat: complete master trainer system transformation from 0% to 100% success
- Deploy 6 simultaneous WordPress specialized agents using sequential thinking and Zen MCP
- Resolve all critical issues: permissions, jQuery dependencies, CDN mapping, security vulnerabilities
- Implement bulletproof jQuery loading system with WordPress hook timing fixes
- Create professional MapGeo Safety system with CDN health monitoring and fallback UI
- Fix privilege escalation vulnerability with capability-based authorization
- Add complete announcement admin system with modal forms and AJAX handling
- Enhance import/export functionality (54 trainers successfully exported)
- Achieve 100% operational master trainer functionality verified via MCP Playwright E2E testing

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

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

739 lines
No EOL
27 KiB
PHP

<?php
/**
* HVAC Bundled Assets Manager
*
* Modern asset management system using webpack-generated bundles
* Replaces individual script loading with optimized bundles
*
* @package HVAC_Community_Events
* @since 2.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC Bundled Assets Manager
*/
class HVAC_Bundled_Assets {
/**
* Instance
*
* @var HVAC_Bundled_Assets
*/
private static $instance = null;
/**
* Asset version for cache busting
*
* @var string
*/
private $version;
/**
* Bundle manifest
*
* @var array
*/
private $manifest = array();
/**
* Get instance
*
* @return HVAC_Bundled_Assets
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->version = HVAC_PLUGIN_VERSION;
$this->load_manifest();
$this->init_hooks();
}
/**
* Load webpack manifest with integrity validation
*/
private function load_manifest() {
$manifest_path = HVAC_PLUGIN_DIR . 'assets/js/dist/manifest.json';
if (!file_exists($manifest_path)) {
return false;
}
// Add integrity validation
$manifest_content = file_get_contents($manifest_path);
if ($manifest_content === false) {
error_log('HVAC: Failed to read manifest file');
return false;
}
$manifest_hash = hash('sha256', $manifest_content);
$expected_hash = get_option('hvac_manifest_hash');
// Validate manifest integrity if hash exists
if ($expected_hash && $expected_hash !== $manifest_hash) {
error_log('HVAC: Manifest integrity check failed - possible tampering detected');
return false;
}
$decoded_manifest = json_decode($manifest_content, true);
if (json_last_error() !== JSON_ERROR_NONE) {
error_log('HVAC: Invalid manifest JSON - ' . json_last_error_msg());
return false;
}
// Validate manifest structure
if (!is_array($decoded_manifest)) {
error_log('HVAC: Manifest is not a valid array');
return false;
}
// Store hash for future validation
update_option('hvac_manifest_hash', $manifest_hash);
$this->manifest = $decoded_manifest;
return true;
}
/**
* Initialize hooks
*/
private function init_hooks() {
// CRITICAL FIX: Only initialize bundled assets if NOT using legacy Scripts_Styles system
// This prevents dual script loading that causes jQuery dependency conflicts
if (!$this->should_use_legacy_scripts_system()) {
add_action('wp_enqueue_scripts', array($this, 'enqueue_bundled_assets'), 5); // Load early
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_bundled_assets'), 5);
add_action('wp_head', array($this, 'add_bundle_preload_hints'), 5);
}
}
/**
* Check if we should use legacy Scripts_Styles system instead of bundles
* CRITICAL: Prevents dual script loading conflicts
*
* @return bool
*/
private function should_use_legacy_scripts_system() {
// Force legacy mode if HVAC_FORCE_LEGACY_SCRIPTS is defined
if (defined('HVAC_FORCE_LEGACY_SCRIPTS') && HVAC_FORCE_LEGACY_SCRIPTS) {
return true;
}
// Use legacy system in development by default (safer for debugging)
if (defined('WP_DEBUG') && WP_DEBUG && (!defined('HVAC_USE_BUNDLES') || !HVAC_USE_BUNDLES)) {
return true;
}
// Safari browsers should use legacy system to prevent cascade issues
if (class_exists('HVAC_Browser_Detection')) {
$browser_detection = HVAC_Browser_Detection::instance();
if ($browser_detection->is_safari_browser()) {
error_log('HVAC Bundled Assets: Using legacy scripts for Safari compatibility');
return true;
}
}
return false;
}
/**
* Check if we should use bundled assets
*
* @return bool
*/
private function should_use_bundled_assets() {
// Don't use bundles if we should use legacy system
if ($this->should_use_legacy_scripts_system()) {
return false;
}
// Don't use bundles if forced to legacy mode
if ($this->should_use_legacy_fallback()) {
return false;
}
// Don't use bundles if manifest is empty (loading failed)
if (empty($this->manifest)) {
error_log('HVAC: Manifest is empty, falling back to legacy assets');
return false;
}
// Use bundled assets in production or if HVAC_USE_BUNDLES is true
return (!defined('WP_DEBUG') || !WP_DEBUG) ||
(defined('HVAC_USE_BUNDLES') && HVAC_USE_BUNDLES);
}
/**
* Check if current page needs HVAC assets
*
* @return bool
*/
private function is_plugin_page() {
// Check if we're in a page template context
if (defined('HVAC_IN_PAGE_TEMPLATE') && HVAC_IN_PAGE_TEMPLATE) {
return true;
}
// Check using template loader if available
if (class_exists('HVAC_Template_Loader')) {
return HVAC_Template_Loader::is_plugin_template();
}
return false;
}
/**
* Enqueue bundled frontend assets
*/
public function enqueue_bundled_assets() {
if (!$this->is_plugin_page() || !$this->should_use_bundled_assets()) {
return;
}
$browser_detection = HVAC_Browser_Detection::instance();
// Always load core bundle on plugin pages
$this->enqueue_bundle('hvac-core');
// Safari compatibility bundle for Safari browsers
if ($browser_detection->is_safari_browser()) {
$this->enqueue_bundle('hvac-safari-compat');
}
// Context-specific bundles based on page type
if ($this->is_dashboard_page()) {
$this->enqueue_bundle('hvac-dashboard');
}
if ($this->is_certificate_page()) {
$this->enqueue_bundle('hvac-certificates');
}
if ($this->is_master_trainer_page()) {
$this->enqueue_bundle('hvac-master');
}
if ($this->is_trainer_page()) {
$this->enqueue_bundle('hvac-trainer');
}
if ($this->is_event_page()) {
$this->enqueue_bundle('hvac-events');
}
// Localization for core bundle
$this->localize_core_bundle();
}
/**
* Enqueue admin bundled assets
*/
public function enqueue_admin_bundled_assets() {
if (!$this->should_use_bundled_assets()) {
return;
}
// Admin bundle for WordPress admin areas
if ($this->is_hvac_admin_page()) {
$this->enqueue_bundle('hvac-admin');
}
}
/**
* Enqueue a specific bundle with validation and graceful degradation
*
* @param string $bundle_name
*/
private function enqueue_bundle($bundle_name) {
$js_file = $this->get_bundle_file($bundle_name, 'js');
$css_file = $this->get_bundle_file($bundle_name, 'css');
// Validate and enqueue JavaScript bundle with security and performance monitoring
if ($js_file) {
$js_path = HVAC_PLUGIN_DIR . 'assets/js/dist/' . $js_file;
if (file_exists($js_path)) {
// Security: Validate file size is reasonable (prevent potential DoS)
$file_size = filesize($js_path);
if ($file_size > 1024 * 1024) { // 1MB limit
error_log("[HVAC] Bundle {$js_file} exceeds size limit ({$file_size} bytes) - falling back");
$this->enqueue_legacy_fallback($bundle_name);
return;
}
// Security: Validate filename contains only safe characters
if (!preg_match('/^[a-zA-Z0-9._-]+$/', $js_file)) {
error_log("[HVAC] Invalid bundle filename: {$js_file} - falling back");
$this->enqueue_legacy_fallback($bundle_name);
return;
}
$version = filemtime($js_path); // Use file modification time for cache busting
// CRITICAL FIX: Ensure jQuery is always loaded first
wp_enqueue_script('jquery');
wp_enqueue_script(
$bundle_name,
HVAC_PLUGIN_URL . 'assets/js/dist/' . $js_file,
array('jquery'),
$version,
true
);
// Add performance monitoring attributes
$this->add_bundle_performance_monitoring($bundle_name);
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[HVAC Bundled Assets] Loaded JS bundle: ' . $bundle_name . ' (' . round($file_size / 1024, 1) . 'KB)');
}
} else {
error_log("[HVAC] Missing JS bundle: {$js_file} - falling back to legacy scripts");
$this->enqueue_legacy_fallback($bundle_name);
}
}
// Validate and enqueue CSS bundle if exists
if ($css_file) {
$css_path = HVAC_PLUGIN_DIR . 'assets/js/dist/' . $css_file;
if (file_exists($css_path)) {
wp_enqueue_style(
$bundle_name . '-style',
HVAC_PLUGIN_URL . 'assets/js/dist/' . $css_file,
array(),
filemtime($css_path)
);
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log('[HVAC Bundled Assets] Loaded CSS bundle: ' . $bundle_name);
}
} else {
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("[HVAC] Missing CSS bundle: {$css_file}");
}
}
}
}
/**
* Get bundle file path from manifest with chunk support
*
* @param string $bundle_name
* @param string $type js|css
* @return string|null
*/
private function get_bundle_file($bundle_name, $type = 'js') {
$key = $bundle_name . '.' . $type;
if (isset($this->manifest[$key])) {
return $this->manifest[$key];
}
// Check for chunk files (lazy-loaded components)
$chunk_key = $bundle_name . '.chunk.' . $type;
if (isset($this->manifest[$chunk_key])) {
return $this->manifest[$chunk_key];
}
// Fallback to expected filename pattern
$suffix = (defined('WP_DEBUG') && WP_DEBUG) ? '' : '.min';
return $bundle_name . '.bundle' . $suffix . '.' . $type;
}
/**
* Get all chunk files for a bundle (for preloading)
*
* @param string $bundle_name
* @return array
*/
private function get_bundle_chunks($bundle_name) {
$chunks = [];
// Look for all chunk files related to this bundle
foreach ($this->manifest as $key => $filename) {
// Match chunk files like "trainer-profile.chunk.js", "event-editing.chunk.js"
if (preg_match("/({$bundle_name}-.+|.+-{$bundle_name})\.chunk\.(js|css)$/", $key)) {
$chunks[$key] = $filename;
}
}
return $chunks;
}
/**
* Fallback to legacy individual scripts when bundles fail
*
* @param string $bundle_name
*/
private function enqueue_legacy_fallback($bundle_name) {
// Check if we're already in too many error scenarios
if ($this->should_use_legacy_fallback()) {
return;
}
// Increment error count
$error_count = get_transient('hvac_bundle_errors') ?: 0;
set_transient('hvac_bundle_errors', $error_count + 1, HOUR_IN_SECONDS);
// Map bundle names to legacy script methods
$legacy_map = array(
'hvac-core' => 'enqueue_core_scripts',
'hvac-dashboard' => 'enqueue_dashboard_scripts',
'hvac-certificates' => 'enqueue_certificate_scripts',
'hvac-master' => 'enqueue_master_trainer_scripts',
'hvac-trainer' => 'enqueue_trainer_scripts',
'hvac-events' => 'enqueue_event_scripts',
'hvac-admin' => 'enqueue_admin_scripts'
);
if (isset($legacy_map[$bundle_name]) && class_exists('HVAC_Scripts_Styles')) {
$scripts_instance = HVAC_Scripts_Styles::instance();
$method = $legacy_map[$bundle_name];
if (method_exists($scripts_instance, $method)) {
$scripts_instance->$method();
error_log("[HVAC] Fallback activated for bundle: {$bundle_name}");
}
}
}
/**
* Check if we should use legacy fallback due to too many errors
*
* @return bool
*/
private function should_use_legacy_fallback() {
$error_count = get_transient('hvac_bundle_errors');
if ($error_count > 5) {
// Too many errors, use legacy for 1 hour
set_transient('hvac_force_legacy', true, HOUR_IN_SECONDS);
return true;
}
return get_transient('hvac_force_legacy');
}
/**
* Localize core bundle with WordPress data and security monitoring
*/
private function localize_core_bundle() {
wp_localize_script('hvac-core', 'hvacBundleData', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('hvac_bundle_nonce'),
'rest_url' => rest_url('hvac/v1/'),
'current_user_id' => get_current_user_id(),
'is_safari' => HVAC_Browser_Detection::instance()->is_safari_browser(),
'debug' => defined('WP_DEBUG') && WP_DEBUG,
'version' => $this->version,
// Security monitoring configuration
'security' => array(
'report_errors' => true,
'error_endpoint' => rest_url('hvac/v1/bundle-errors'),
'performance_monitoring' => !defined('HVAC_DISABLE_PERF_MONITORING'),
'max_load_time' => 5000, // 5 seconds
'retry_attempts' => 2
)
));
// Add client-side error detection and performance monitoring
$this->add_client_side_monitoring();
}
/**
* Add client-side error detection and performance monitoring
*/
private function add_client_side_monitoring() {
$monitoring_script = "
(function() {
'use strict';
var hvacSecurity = {
errors: [],
performance: {},
// Report bundle loading errors
reportError: function(error, bundle) {
if (!window.hvacBundleData || !window.hvacBundleData.security.report_errors) return;
this.errors.push({
error: error,
bundle: bundle,
timestamp: Date.now(),
userAgent: navigator.userAgent.substring(0, 100), // Limit length for security
url: window.location.href
});
// Report to server if REST API available
if (window.hvacBundleData.security.error_endpoint) {
this.sendErrorReport();
}
},
// Send error reports to server
sendErrorReport: function() {
if (this.errors.length === 0) return;
fetch(window.hvacBundleData.security.error_endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': window.hvacBundleData.nonce
},
body: JSON.stringify({
errors: this.errors.splice(0, 5), // Send max 5 errors at once
page: window.location.pathname,
version: window.hvacBundleData.version
})
}).catch(function(e) {
console.warn('HVAC: Failed to report bundle errors', e);
});
},
// Monitor bundle loading performance
monitorPerformance: function(bundleName, startTime) {
if (!window.hvacBundleData.security.performance_monitoring) return;
var loadTime = Date.now() - startTime;
this.performance[bundleName] = loadTime;
var maxLoadTime = window.hvacBundleData.security.max_load_time || 5000;
if (loadTime > maxLoadTime) {
this.reportError('Bundle load time exceeded ' + maxLoadTime + 'ms (' + loadTime + 'ms)', bundleName);
}
}
};
// Global error handler for script loading failures
window.addEventListener('error', function(e) {
if (e.target && e.target.tagName === 'SCRIPT' && e.target.src && e.target.src.includes('hvac')) {
var bundleName = e.target.src.match(/hvac-([^.]+)/) ? e.target.src.match(/hvac-([^.]+)/)[1] : 'unknown';
hvacSecurity.reportError('Script load failed: ' + e.message, 'hvac-' + bundleName);
}
});
// Attach to global scope for bundle monitoring
window.hvacSecurity = hvacSecurity;
// Auto-report errors every 30 seconds if any exist
setInterval(function() {
if (hvacSecurity.errors.length > 0) {
hvacSecurity.sendErrorReport();
}
}, 30000);
})();
";
wp_add_inline_script('hvac-core', $monitoring_script, 'before');
}
/**
* Add bundle preload hints for critical resources
*/
public function add_bundle_preload_hints() {
if (!$this->is_plugin_page() || !$this->should_use_bundled_assets()) {
return;
}
// Always preload core bundle on plugin pages
$this->add_preload_hint('hvac-core', 'script');
// Context-specific preloading for critical bundles
if ($this->is_dashboard_page()) {
$this->add_preload_hint('hvac-dashboard', 'script');
}
if ($this->is_master_trainer_page()) {
$this->add_preload_hint('hvac-master', 'script');
}
// Safari compatibility bundle for Safari browsers
$browser_detection = HVAC_Browser_Detection::instance();
if ($browser_detection->is_safari_browser()) {
$this->add_preload_hint('hvac-safari-compat', 'script');
}
}
/**
* Add individual preload hint with security validation
*
* @param string $bundle_name
* @param string $type script|style
*/
private function add_preload_hint($bundle_name, $type) {
$file_type = $type === 'script' ? 'js' : 'css';
$bundle_file = $this->get_bundle_file($bundle_name, $file_type);
if (!$bundle_file) {
return;
}
// Validate file exists and is safe
$file_path = HVAC_PLUGIN_DIR . 'assets/js/dist/' . $bundle_file;
if (!file_exists($file_path)) {
return;
}
// Security: Validate filename contains only safe characters
if (!preg_match('/^[a-zA-Z0-9._-]+$/', $bundle_file)) {
error_log('HVAC: Invalid bundle filename for preload: ' . $bundle_file);
return;
}
$bundle_url = esc_url(HVAC_PLUGIN_URL . 'assets/js/dist/' . $bundle_file);
$as_attribute = $type === 'script' ? 'script' : 'style';
// Add integrity hash for additional security
$integrity_hash = base64_encode(hash('sha384', file_get_contents($file_path), true));
echo '<link rel="preload" href="' . $bundle_url . '" as="' . $as_attribute . '" integrity="sha384-' . $integrity_hash . '" crossorigin="anonymous">' . "\n";
if (defined('WP_DEBUG') && WP_DEBUG) {
error_log("[HVAC Bundled Assets] Preload hint added for: {$bundle_name}.{$file_type}");
}
}
/**
* Add performance monitoring attributes to bundle scripts
*
* @param string $bundle_name
*/
private function add_bundle_performance_monitoring($bundle_name) {
// Add inline script to monitor bundle loading performance
$monitoring_script = "
(function() {
var startTime = performance.now();
var bundleName = '{$bundle_name}';
// Monitor when this script finishes loading
if (window.hvacSecurity && window.hvacSecurity.monitorPerformance) {
document.addEventListener('DOMContentLoaded', function() {
window.hvacSecurity.monitorPerformance(bundleName, startTime);
});
}
// Fallback error detection if hvacSecurity not loaded
window.addEventListener('error', function(e) {
if (e.target && e.target.src && e.target.src.includes(bundleName)) {
console.error('HVAC Bundle Error: Failed to load ' + bundleName);
}
});
})();
";
wp_add_inline_script($bundle_name, $monitoring_script, 'before');
}
/**
* Page detection methods
*/
private function is_dashboard_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists('HVAC_Scripts_Styles', 'instance') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_dashboard_page') ?
HVAC_Scripts_Styles::instance()->is_dashboard_page() : false;
}
private function is_certificate_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists('HVAC_Scripts_Styles', 'instance') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_certificate_page') ?
HVAC_Scripts_Styles::instance()->is_certificate_page() : false;
}
private function is_master_trainer_page() {
return $this->is_pending_approvals_page() ||
$this->is_master_events_page() ||
$this->is_import_export_page() ||
is_page('master-dashboard') ||
strpos($_SERVER['REQUEST_URI'], 'master-trainer/') !== false;
}
private function is_trainer_page() {
return $this->is_trainer_profile_page() ||
$this->is_registration_page() ||
strpos($_SERVER['REQUEST_URI'], '/trainer/') !== false;
}
private function is_event_page() {
return $this->is_event_manage_page() ||
$this->is_create_event_page() ||
$this->is_edit_event_page() ||
$this->is_organizers_page() ||
$this->is_venues_page();
}
private function is_hvac_admin_page() {
$screen = get_current_screen();
return $screen && strpos($screen->id, 'hvac') !== false;
}
// Helper methods (delegate to existing HVAC_Scripts_Styles if available)
private function is_pending_approvals_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_pending_approvals_page') ?
HVAC_Scripts_Styles::instance()->is_pending_approvals_page() : false;
}
private function is_master_events_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_master_events_page') ?
HVAC_Scripts_Styles::instance()->is_master_events_page() : false;
}
private function is_import_export_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_import_export_page') ?
HVAC_Scripts_Styles::instance()->is_import_export_page() : false;
}
private function is_trainer_profile_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_trainer_profile_page') ?
HVAC_Scripts_Styles::instance()->is_trainer_profile_page() : false;
}
private function is_registration_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_registration_page') ?
HVAC_Scripts_Styles::instance()->is_registration_page() : false;
}
private function is_event_manage_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_event_manage_page') ?
HVAC_Scripts_Styles::instance()->is_event_manage_page() : false;
}
private function is_create_event_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_create_event_page') ?
HVAC_Scripts_Styles::instance()->is_create_event_page() : false;
}
private function is_edit_event_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_edit_event_page') ?
HVAC_Scripts_Styles::instance()->is_edit_event_page() : false;
}
private function is_organizers_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_organizers_page') ?
HVAC_Scripts_Styles::instance()->is_organizers_page() : false;
}
private function is_venues_page() {
return class_exists('HVAC_Scripts_Styles') &&
method_exists(HVAC_Scripts_Styles::instance(), 'is_venues_page') ?
HVAC_Scripts_Styles::instance()->is_venues_page() : false;
}
}