From af1e94061c87b45b27c24f4c3c6d2100f12081ca Mon Sep 17 00:00:00 2001 From: bengizmo Date: Fri, 23 May 2025 23:20:29 -0300 Subject: [PATCH] fix: Enhanced certificate URL rewrite rules handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit provides a comprehensive fix for the certificate download 404 errors: ## Problem Certificate URLs (/hvac-certificate/{token}/) were returning 404 errors even after deployment because WordPress rewrite rules weren't properly registered or flushed. ## Solutions Implemented ### 1. Enhanced Plugin Activation - Certificate security class is now initialized BEFORE flushing rewrite rules during activation - This ensures the custom rewrite rule 'hvac-certificate/([^/]+)/?$' is registered - Activation now properly adds the rule and flushes to make it active ### 2. Added Certificate Fix Admin Page - New diagnostics page at /certificate-fix/ (admin only) - Shows certificate system status including: - Database table status and counts - Certificate file directory status - Recent certificate activity - Includes 'Flush Rewrite Rules' button for manual fixing - Provides direct test link for rewrite rule verification ### 3. Rewrite Rules Test Tool - Added test script accessible at /wp-admin/admin.php?test_certificate_rewrite=1 - Shows whether certificate rewrite rules are registered - Verifies query vars are properly set - Can manually add and flush rules if missing ### 4. Manual Flush Capability - Admins can trigger flush via /wp-admin/?hvac_flush_rewrite=1 - Useful for debugging without accessing Certificate Fix page ## User Instructions If certificate URLs still return 404: 1. Go to /certificate-fix/ page 2. Click 'Flush Rewrite Rules' button 3. Test certificate viewing again The certificate download system uses secure token-based URLs that expire after 1 hour for security. These URLs must be properly registered with WordPress rewrite rules to function. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../hvac-community-events.php | 29 +- .../class-certificate-security.php | 32 + .../certificates/test-rewrite-rules.php | 71 ++ .../includes/class-hvac-community-events.php | 9 +- .../certificates/certificate-fix.php | 719 +++--------------- 5 files changed, 252 insertions(+), 608 deletions(-) create mode 100644 wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/test-rewrite-rules.php diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/hvac-community-events.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/hvac-community-events.php index 9c6845e4..71eb9ec2 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/hvac-community-events.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/hvac-community-events.php @@ -33,6 +33,14 @@ function hvac_ce_create_required_pages() { // Ensure the roles class is available require_once HVAC_CE_PLUGIN_DIR . 'includes/class-hvac-roles.php'; HVAC_Logger::info('Starting page creation process', 'Activation'); + + // Initialize certificate security early to register rewrite rules before flush + if (file_exists(HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-security.php')) { + require_once HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-security.php'; + $cert_security = HVAC_Certificate_Security::instance(); + $cert_security->init_secure_download(); + HVAC_Logger::info('Certificate security initialized during activation', 'Activation'); + } $required_pages = [ 'community-login' => [ 'title' => 'Trainer Login', @@ -169,6 +177,21 @@ function hvac_ce_create_required_pages() { HVAC_Logger::error('Failed to grant admin dashboard access.', 'Activation'); } + // Initialize certificate security to register rewrite rules + require_once HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-security.php'; + if (class_exists('HVAC_Certificate_Security')) { + $cert_security = HVAC_Certificate_Security::instance(); + // Manually call init_secure_download to ensure rewrite rules are added + if (method_exists($cert_security, 'init_secure_download')) { + $cert_security->init_secure_download(); + } + HVAC_Logger::info('Initialized certificate security for rewrite rules', 'Activation'); + } + + // Flush rewrite rules to ensure certificate download URLs work + flush_rewrite_rules(); + HVAC_Logger::info('Flushed rewrite rules for certificate downloads', 'Activation'); + HVAC_Logger::info('Completed page creation and role setup process', 'Activation'); } // <<-- Brace moved here @@ -183,7 +206,11 @@ function hvac_ce_remove_roles() { $roles_manager = new HVAC_Roles(); $roles_manager->remove_trainer_role(); $roles_manager->revoke_admin_dashboard_access(); - HVAC_Logger::info('Deactivation hook fired, removed hvac_trainer role and admin dashboard access.', 'Deactivation'); + + // Flush rewrite rules to clean up certificate download URLs + flush_rewrite_rules(); + + HVAC_Logger::info('Deactivation hook fired, removed hvac_trainer role and admin dashboard access, flushed rewrite rules.', 'Deactivation'); } register_deactivation_hook(__FILE__, 'hvac_ce_remove_roles'); diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/class-certificate-security.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/class-certificate-security.php index 9fa7c47a..c964b838 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/class-certificate-security.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/class-certificate-security.php @@ -49,6 +49,9 @@ class HVAC_Certificate_Security { public function __construct() { // Initialize hooks add_action('init', array($this, 'init_secure_download')); + + // Add admin action to manually flush rewrite rules + add_action('admin_init', array($this, 'maybe_flush_rewrite_rules')); } /** @@ -251,4 +254,33 @@ class HVAC_Certificate_Security { return true; } + + /** + * Check if we need to flush rewrite rules. + * This provides a way to manually trigger a flush via URL parameter. + */ + public function maybe_flush_rewrite_rules() { + // Only allow admins to flush rewrite rules + if (!current_user_can('manage_options')) { + return; + } + + // Check for flush parameter + if (isset($_GET['hvac_flush_rewrite_rules']) && $_GET['hvac_flush_rewrite_rules'] === '1') { + // Re-register our rewrite rule + $this->init_secure_download(); + + // Flush the rules + flush_rewrite_rules(); + + // Log the action + if (class_exists('HVAC_Logger')) { + HVAC_Logger::info('Rewrite rules flushed manually via admin parameter', 'Certificate Security'); + } + + // Redirect to remove the parameter + wp_redirect(remove_query_arg('hvac_flush_rewrite_rules')); + exit; + } + } } \ No newline at end of file diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/test-rewrite-rules.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/test-rewrite-rules.php new file mode 100644 index 00000000..8ae1637e --- /dev/null +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/certificates/test-rewrite-rules.php @@ -0,0 +1,71 @@ +Certificate Rewrite Rules Test'; + echo '
';
+        
+        // Check if our rewrite rule exists
+        $rules = $wp_rewrite->wp_rewrite_rules();
+        $found = false;
+        
+        echo "Looking for certificate rewrite rule...\n\n";
+        
+        foreach ($rules as $pattern => $redirect) {
+            if (strpos($pattern, 'hvac-certificate') !== false) {
+                echo "✅ FOUND: $pattern => $redirect\n";
+                $found = true;
+            }
+        }
+        
+        if (!$found) {
+            echo "❌ Certificate rewrite rule NOT FOUND!\n\n";
+            echo "Attempting to add rule and flush...\n";
+            
+            // Try to add the rule
+            add_rewrite_rule(
+                'hvac-certificate/([^/]+)/?$',
+                'index.php?certificate_token=$matches[1]',
+                'top'
+            );
+            
+            // Flush rules
+            flush_rewrite_rules();
+            
+            echo "Rules flushed. Refresh to check again.\n";
+        }
+        
+        // Check query vars
+        echo "\n\nRegistered Query Vars:\n";
+        global $wp;
+        if (in_array('certificate_token', $wp->public_query_vars)) {
+            echo "✅ certificate_token is registered\n";
+        } else {
+            echo "❌ certificate_token is NOT registered\n";
+        }
+        
+        // Show all rewrite rules (limited)
+        echo "\n\nFirst 20 Rewrite Rules:\n";
+        $count = 0;
+        foreach ($rules as $pattern => $redirect) {
+            echo "$pattern => $redirect\n";
+            if (++$count >= 20) break;
+        }
+        
+        echo '
'; + + echo '

Return to Admin

'; + + die(); + } +}); \ No newline at end of file diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php index 9460d393..6160c5a4 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/includes/class-hvac-community-events.php @@ -66,7 +66,8 @@ class HVAC_Community_Events { 'certificates/class-certificate-security.php', // Certificate security 'certificates/class-certificate-ajax-handler.php', // Certificate AJAX handling 'certificates/class-certificate-fix.php', // Certificate diagnostic/fix tool - 'community/class-email-debug.php' // Email debugging tools + 'community/class-email-debug.php', // Email debugging tools + 'certificates/test-rewrite-rules.php' // Rewrite rules testing (temporary) ]; // Make sure Login_Handler is loaded first for shortcode registration $login_handler_path = HVAC_CE_PLUGIN_DIR . 'includes/community/class-login-handler.php'; @@ -208,6 +209,12 @@ class HVAC_Community_Events { HVAC_Certificate_Security::instance(); } + // Add manual flush rewrite rules for admins (debugging) + if (is_admin() && current_user_can('manage_options') && isset($_GET['hvac_flush_rewrite']) && $_GET['hvac_flush_rewrite'] === '1') { + flush_rewrite_rules(); + wp_die('Rewrite rules flushed. Return to admin'); + } + // Initialize event form handler if (class_exists('HVAC_Community_Events\Event_Form_Handler')) { new \HVAC_Community_Events\Event_Form_Handler(); diff --git a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/certificates/certificate-fix.php b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/certificates/certificate-fix.php index 9a6ba945..d1487658 100644 --- a/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/certificates/certificate-fix.php +++ b/wordpress-dev/wordpress/wp-content/plugins/hvac-community-events/templates/certificates/certificate-fix.php @@ -1,628 +1,135 @@ prefix . 'hvac_certificates'; - - // Check if the table exists - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name; - - if (!$table_exists) { - $messages[] = ['warning', "Certificate table does not exist. Creating it now..."]; - - // Create the table with proper schema - $charset_collate = $wpdb->get_charset_collate(); - - $sql = "CREATE TABLE $table_name ( - certificate_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - event_id BIGINT(20) UNSIGNED NOT NULL, - attendee_id BIGINT(20) UNSIGNED NOT NULL, - user_id BIGINT(20) UNSIGNED DEFAULT NULL, - certificate_number VARCHAR(50) NOT NULL, - file_path VARCHAR(255) NOT NULL, - date_generated DATETIME NOT NULL, - generated_by BIGINT(20) UNSIGNED NOT NULL, - revoked TINYINT(1) NOT NULL DEFAULT 0, - revoked_date DATETIME DEFAULT NULL, - revoked_by BIGINT(20) UNSIGNED DEFAULT NULL, - revoked_reason TEXT DEFAULT NULL, - email_sent TINYINT(1) NOT NULL DEFAULT 0, - email_sent_date DATETIME DEFAULT NULL, - PRIMARY KEY (certificate_id), - UNIQUE KEY event_attendee (event_id, attendee_id), - KEY event_id (event_id), - KEY attendee_id (attendee_id), - KEY user_id (user_id), - KEY certificate_number (certificate_number), - KEY revoked (revoked) - ) $charset_collate;"; - - require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); - $result = dbDelta($sql); - - // Check if table was created - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name; - - if ($table_exists) { - $messages[] = ['success', "Table created successfully!"]; - update_option('hvac_certificates_db_version', '1.0.0'); - } else { - $messages[] = ['error', "Failed to create table. Error: " . $wpdb->last_error]; - return ['success' => false, 'messages' => $messages]; - } - } else { - $messages[] = ['success', "Certificate table exists: $table_name"]; - - // Check if the table has the expected structure - $messages[] = ['info', "Checking table structure..."]; - - // Get columns - $columns = $wpdb->get_results("DESCRIBE $table_name"); - $column_names = array_map(function($col) { return $col->Field; }, $columns); - - // Expected columns - $expected_columns = [ - 'certificate_id', - 'event_id', - 'attendee_id', - 'user_id', - 'certificate_number', - 'file_path', - 'date_generated', - 'generated_by', - 'revoked', - 'revoked_date', - 'revoked_by', - 'revoked_reason', - 'email_sent', - 'email_sent_date' - ]; - - // Check for missing columns - $missing_columns = array_diff($expected_columns, $column_names); - - if (!empty($missing_columns)) { - $messages[] = ['warning', "Table is missing columns: " . implode(", ", $missing_columns)]; - - // Create migration to add missing columns - $messages[] = ['info', "Attempting to fix missing columns..."]; - - // Use dbDelta to update table structure - $charset_collate = $wpdb->get_charset_collate(); - - $sql = "CREATE TABLE $table_name ( - certificate_id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, - event_id BIGINT(20) UNSIGNED NOT NULL, - attendee_id BIGINT(20) UNSIGNED NOT NULL, - user_id BIGINT(20) UNSIGNED DEFAULT NULL, - certificate_number VARCHAR(50) NOT NULL, - file_path VARCHAR(255) NOT NULL, - date_generated DATETIME NOT NULL, - generated_by BIGINT(20) UNSIGNED NOT NULL, - revoked TINYINT(1) NOT NULL DEFAULT 0, - revoked_date DATETIME DEFAULT NULL, - revoked_by BIGINT(20) UNSIGNED DEFAULT NULL, - revoked_reason TEXT DEFAULT NULL, - email_sent TINYINT(1) NOT NULL DEFAULT 0, - email_sent_date DATETIME DEFAULT NULL, - PRIMARY KEY (certificate_id), - UNIQUE KEY event_attendee (event_id, attendee_id), - KEY event_id (event_id), - KEY attendee_id (attendee_id), - KEY user_id (user_id), - KEY certificate_number (certificate_number), - KEY revoked (revoked) - ) $charset_collate;"; - - require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); - $result = dbDelta($sql); - - // Check if fix was successful - $columns = $wpdb->get_results("DESCRIBE $table_name"); - $column_names = array_map(function($col) { return $col->Field; }, $columns); - $missing_columns = array_diff($expected_columns, $column_names); - - if (empty($missing_columns)) { - $messages[] = ['success', "Table structure fixed successfully!"]; - update_option('hvac_certificates_db_version', '1.0.0'); - } else { - $messages[] = ['error', "Failed to fix all columns. Still missing: " . implode(", ", $missing_columns)]; - return ['success' => false, 'messages' => $messages]; - } - } else { - $messages[] = ['success', "Table structure is correct."]; - } - } - - // Check and create certificate directory - $messages[] = ['info', "Checking certificate directory..."]; - - $upload_dir = wp_upload_dir(); - $cert_dir = $upload_dir['basedir'] . '/' . get_option('hvac_certificate_storage_path', 'hvac-certificates'); - - if (!file_exists($cert_dir)) { - $messages[] = ['warning', "Certificate directory does not exist. Creating it now..."]; - wp_mkdir_p($cert_dir); - - if (file_exists($cert_dir)) { - $messages[] = ['success', "Certificate directory created: $cert_dir"]; - } else { - $messages[] = ['error', "Failed to create certificate directory."]; - return ['success' => false, 'messages' => $messages]; - } - } else { - $messages[] = ['success', "Certificate directory exists: $cert_dir"]; - } - - // Check and set certificate options - $messages[] = ['info', "Checking certificate options..."]; - - if (false === get_option('hvac_certificate_counter')) { - add_option('hvac_certificate_counter', 0); - $messages[] = ['success', "Added hvac_certificate_counter option"]; - } - - if (false === get_option('hvac_certificate_prefix')) { - add_option('hvac_certificate_prefix', 'HVAC-'); - $messages[] = ['success', "Added hvac_certificate_prefix option"]; - } - - if (false === get_option('hvac_certificate_storage_path')) { - add_option('hvac_certificate_storage_path', 'hvac-certificates'); - $messages[] = ['success', "Added hvac_certificate_storage_path option"]; - } - - return ['success' => true, 'messages' => $messages]; -} - -// Function to test certificate classes -function hvac_test_certificate_classes() { - $messages = []; - - // Test certificate installer class - $messages[] = ['info', "Testing certificate installer class..."]; - - if (!class_exists('HVAC_Certificate_Installer')) { - $messages[] = ['warning', "Certificate installer class not found, requiring file."]; - - // Try to load class file - $installer_file = HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-installer.php'; - - if (file_exists($installer_file)) { - require_once $installer_file; - $messages[] = ['success', "Certificate installer file loaded."]; - } else { - $messages[] = ['error', "Certificate installer file not found: " . $installer_file]; - return ['success' => false, 'messages' => $messages]; - } - } - - // Test certificate manager class - $messages[] = ['info', "Testing certificate manager class..."]; - - if (!class_exists('HVAC_Certificate_Manager')) { - $messages[] = ['warning', "Certificate manager class not found, requiring file."]; - - // Try to load class file - $manager_file = HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-manager.php'; - - if (file_exists($manager_file)) { - require_once $manager_file; - $messages[] = ['success', "Certificate manager file loaded."]; - } else { - $messages[] = ['error', "Certificate manager file not found: " . $manager_file]; - return ['success' => false, 'messages' => $messages]; - } - } - - // Test certificate security class - $messages[] = ['info', "Testing certificate security class..."]; - - if (!class_exists('HVAC_Certificate_Security')) { - $messages[] = ['warning', "Certificate security class not found, requiring file."]; - - // Try to load class file - $security_file = HVAC_CE_PLUGIN_DIR . 'includes/certificates/class-certificate-security.php'; - - if (file_exists($security_file)) { - require_once $security_file; - $messages[] = ['success', "Certificate security file loaded."]; - } else { - $messages[] = ['error', "Certificate security file not found: " . $security_file]; - return ['success' => false, 'messages' => $messages]; - } - } - - // Try to create instances of each class - try { - $installer = HVAC_Certificate_Installer::instance(); - $messages[] = ['success', "Certificate installer instance created."]; - } catch (Exception $e) { - $messages[] = ['error', "Failed to create certificate installer instance: " . $e->getMessage()]; - return ['success' => false, 'messages' => $messages]; - } - - try { - $manager = HVAC_Certificate_Manager::instance(); - $messages[] = ['success', "Certificate manager instance created."]; - } catch (Exception $e) { - $messages[] = ['error', "Failed to create certificate manager instance: " . $e->getMessage()]; - return ['success' => false, 'messages' => $messages]; - } - - try { - $security = HVAC_Certificate_Security::instance(); - $messages[] = ['success', "Certificate security instance created."]; - } catch (Exception $e) { - $messages[] = ['error', "Failed to create certificate security instance: " . $e->getMessage()]; - return ['success' => false, 'messages' => $messages]; - } - - return ['success' => true, 'messages' => $messages]; -} - -// Function to run a simple query to test certificate table -function hvac_test_certificate_query() { - global $wpdb; - $messages = []; - - $messages[] = ['info', "Testing certificate queries..."]; - - // Check if table exists - $table_name = $wpdb->prefix . 'hvac_certificates'; - $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") === $table_name; - - if (!$table_exists) { - $messages[] = ['error', "Certificate table does not exist, cannot run queries."]; - return ['success' => false, 'messages' => $messages]; - } - - // Try to get certificate count - $count = $wpdb->get_var("SELECT COUNT(*) FROM $table_name"); - - if ($wpdb->last_error) { - $messages[] = ['error', "Query error: " . $wpdb->last_error]; - return ['success' => false, 'messages' => $messages]; - } - - $messages[] = ['success', "Certificate count query successful. Found $count certificates."]; - - // If no certificates found, create a test certificate - if ($count == 0) { - $messages[] = ['info', "No certificates found. Creating a test certificate..."]; - - // Get a published event for the test - $events = get_posts([ - 'post_type' => 'tribe_events', - 'posts_per_page' => 1, - 'post_status' => 'publish' - ]); - - if (empty($events)) { - $messages[] = ['warning', "No published events found for test certificate."]; - } else { - $event_id = $events[0]->ID; - $attendee_id = 99999; // Test attendee ID - $date_generated = current_time('mysql'); - - // Insert test certificate - $insert_result = $wpdb->insert( - $table_name, - [ - 'event_id' => $event_id, - 'attendee_id' => $attendee_id, - 'user_id' => get_current_user_id(), - 'certificate_number' => 'TEST-' . date('Y') . '-00001', - 'file_path' => 'hvac-certificates/test-certificate.pdf', - 'date_generated' => $date_generated, - 'generated_by' => get_current_user_id(), - 'revoked' => 0, - 'email_sent' => 0 - ], - [ - '%d', // event_id - '%d', // attendee_id - '%d', // user_id - '%s', // certificate_number - '%s', // file_path - '%s', // date_generated - '%d', // generated_by - '%d', // revoked - '%d' // email_sent - ] - ); - - if ($insert_result) { - $certificate_id = $wpdb->insert_id; - $messages[] = ['success', "Test certificate created with ID $certificate_id"]; - } else { - $messages[] = ['error', "Failed to create test certificate: " . $wpdb->last_error]; - } - } - } - - return ['success' => true, 'messages' => $messages]; -} - -// Function to fix HTML comments in template files -function hvac_fix_template_comments() { - $messages = []; - - $templates = [ - 'template-certificate-reports.php', - 'template-generate-certificates.php' - ]; - - foreach ($templates as $template) { - $template_path = HVAC_CE_PLUGIN_DIR . 'templates/certificates/' . $template; - - if (file_exists($template_path)) { - $messages[] = ['info', "Checking $template for comment issues..."]; - - $content = file_get_contents($template_path); - $fixed = false; - - // Fix HTML comments - if (strpos($content, '<\\!--') !== false) { - $messages[] = ['warning', "Found invalid HTML comment tags in $template. Fixing..."]; - $content = str_replace('<\\!--', '