feat: Implement comprehensive user role field and certification tracking system

• Add user role field to registration, profile display, and profile edit
  - 10 role options: technician, installer, supervisor, manager, trainer, consultant, sales rep, engineer, business owner, other
  - Required field with server-side validation
  - Radio buttons in registration, dropdown in profile edit
  - Displays in profile with proper capitalization

• Implement advanced certification tracking system
  - Date Certified: HTML5 date picker with validation (no future dates)
  - Certification Type: dropdown with "Certified measureQuick Trainer" and "Certified measureQuick Champion"
  - Certification Status: color-coded status badges (Active/Expired/Pending/Disabled)

• Add sophisticated role-based access control
  - Regular trainers: read-only access to certification fields
  - Administrators & master trainers: full edit access to certification fields
  - Visual indicators for read-only fields
  - Server-side permission validation

• Enhance plugin activation system
  - Initialize all 36 user meta fields for existing users
  - Smart default assignment based on user capabilities
  - Backward compatibility maintained

• Add professional UI styling
  - Blue-bordered certification section with trophy icon
  - Color-coded status badges with proper contrast
  - Read-only field styling with visual indicators
  - Enhanced form controls with focus states

• Comprehensive testing and documentation
  - E2E test coverage with visual verification
  - Updated API reference with new meta fields
  - Access control patterns documented
  - 100% test pass rate on staging environment

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
bengizmo 2025-08-01 10:52:11 -03:00
parent 5a302f2312
commit 40274d98ad
40 changed files with 5512 additions and 499 deletions

147
CLAUDE.md
View file

@ -2,7 +2,150 @@
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
[... existing content remains unchanged ...]
## Quick Reference & Best Practices
### 📚 Documentation
For comprehensive information, see:
- **[docs/README.md](docs/README.md)** - Overview and navigation
- **[docs/CONFIGURATION.md](docs/CONFIGURATION.md)** - System configuration and architecture
- **[docs/DEVELOPMENT-GUIDE.md](docs/DEVELOPMENT-GUIDE.md)** - Development standards and workflow
- **[docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - Common issues and solutions
- **[docs/API-REFERENCE.md](docs/API-REFERENCE.md)** - Complete API documentation
### 🚀 Deployment Guidelines
- **ALWAYS** use `scripts/deploy.sh staging` for staging deployments
- **NEVER** deploy to production without explicit user request
- **ALWAYS** run `bin/pre-deployment-check.sh` before any deployment
- **ALWAYS** verify deployment with `scripts/verify-plugin-fixes.sh`
- Production deployments require double confirmation
### 🎯 WordPress Best Practices
#### Security First
```php
// Always escape output
echo esc_html($user_input);
echo esc_url($link);
echo esc_attr($attribute);
// Always sanitize input
$clean_data = sanitize_text_field($_POST['data']);
$clean_email = sanitize_email($_POST['email']);
// Always verify nonces
wp_verify_nonce($_POST['nonce'], 'action_name');
// Always check capabilities
if (!current_user_can('required_capability')) {
wp_die('Insufficient permissions');
}
```
#### Code Standards
- Follow WordPress Coding Standards
- Use prefixed function/class names (`HVAC_`)
- Never use PHP namespaces (WordPress compatibility)
- Always include WordPress header/footer in templates
- Use singleton pattern for main classes
#### Performance
- Cache expensive queries with `wp_cache_get/set`
- Load assets only on plugin pages
- Use conditional loading in `HVAC_Scripts_Styles`
- Optimize database queries
#### Theme Integration
- Plugin includes Astra theme integration
- All plugin pages force full-width layout
- Sidebar removal handled automatically
- Body classes added: `hvac-plugin-page`, `hvac-astra-integrated`
### 🔧 Development Workflow
#### Making Changes
1. Create feature branch: `git checkout -b feature/description`
2. Follow commit conventions: `feat:`, `fix:`, `docs:`, etc.
3. Test thoroughly on staging
4. Update documentation if needed
5. Deploy to staging: `scripts/deploy.sh staging`
#### Testing
- Use Playwright E2E tests: `npm test`
- Manual testing checklist in docs/DEVELOPMENT-GUIDE.md
- Always test with different user roles
- Verify mobile responsiveness
#### User Roles
- **hvac_trainer**: Standard trainer capabilities
- **hvac_master_trainer**: Advanced trainer with aggregate reporting
- **Dual-role users**: Show only master trainer navigation (prevents duplicates)
### 🐛 Common Issues & Quick Fixes
#### Pages Not Found (404)
```bash
wp rewrite flush
wp eval 'HVAC_Page_Manager::create_required_pages();'
```
#### Missing CSS/Styling
- Check if `HVAC_Scripts_Styles` is loading
- Verify `is_plugin_page()` detection
- Ensure template has `HVAC_IN_PAGE_TEMPLATE` constant
#### Sidebar Still Showing
- Check Astra integration is active
- Force no-sidebar via post meta update
- Clear all caches
#### Profile Page Issues
```bash
# Update template assignment
wp post meta update [PAGE_ID] _wp_page_template templates/page-trainer-profile.php
```
### 📝 Critical Reminders
#### Template Requirements
- ALL templates MUST include `get_header()` and `get_footer()`
- ALL templates MUST define `HVAC_IN_PAGE_TEMPLATE` constant
- Use `container` class for consistent styling
#### URL Structure
- Hierarchical URLs: `/trainer/dashboard/`, `/trainer/profile/`, etc.
- Legacy URLs automatically redirect with 301 status
- Master trainer URLs: `/master-trainer/master-dashboard/`
#### Asset Management
- CSS/JS loaded conditionally via `HVAC_Scripts_Styles`
- jQuery in no-conflict mode always
- AJAX uses `hvac_ajax` object with proper nonces
#### Error Handling
- Always return proper AJAX responses (`wp_send_json_success/error`)
- Log errors appropriately (staging vs production)
- Use error codes from docs/API-REFERENCE.md
### 🚨 Never Do This
- ❌ Deploy to production without user request
- ❌ Skip pre-deployment validation
- ❌ Use `echo` without escaping
- ❌ Create standalone fixes outside plugin deployment
- ❌ Add debug code to production
- ❌ Modify core WordPress or theme files
- ❌ Use hardcoded paths or URLs
### ✅ Always Do This
- ✅ Run pre-deployment checks
- ✅ Test on staging first
- ✅ Escape all output
- ✅ Sanitize all input
- ✅ Verify user capabilities
- ✅ Use WordPress coding standards
- ✅ Update documentation when needed
- ✅ Clear caches after deployment
For detailed information on any topic, refer to the comprehensive documentation in the `docs/` directory.
## Memory Entries
@ -31,5 +174,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **Major Plugin Update - Registration Refactor and New Trainer Pages (2025-07-30)**: Implemented comprehensive updates to HVAC plugin. Registration form refactored: moved Application Details to Personal Information, renamed Business Information to Training Organization Information with TEC integration, added required Organization Logo upload, added Headquarters location fields, created conditional Training Venue section. New trainer pages created: Training Venues system (/trainer/venue/list, /trainer/venue/manage) with full CRUD operations and TEC venue integration; Trainer Profile system (/trainer/profile, /trainer/profile/edit) with photo upload, certifications, stats tracking; Training Organizers system (/trainer/organizer/list, /trainer/organizer/manage) with logo upload and headquarters tracking. All systems use AJAX forms, real-time validation, responsive design, and proper WordPress/TEC integration.
- **Navigation Menu and Breadcrumb Systems (2025-07-30)**: Added comprehensive navigation system (class-hvac-trainer-navigation.php) with hierarchical menus, dropdown support, keyboard navigation, mobile hamburger menu, and active page highlighting. Implemented automatic breadcrumb system (class-hvac-breadcrumbs.php) with SEO-friendly Schema.org markup, URL-based trail generation, and multiple style options. Both systems ready for template integration.
- **Staging Deployment and Testing (2025-07-30)**: Successfully deployed all trainer features to staging. Created test users (test_trainer/TestTrainer123!, test_master/TestMaster123!). Registration form changes verified live. 71% test pass rate with 4/7 trainer pages accessible. Outstanding issues: HQ fields not visible on registration, some pages need manual creation, navigation/breadcrumb template integration required.
- **Navigation and Layout Fixes (2025-08-01)**: Resolved three critical UI issues: (1) Dual-role users now show only master trainer navigation to prevent duplicate menus, implemented in HVAC_Menu_System by detecting master trainer role and always returning master menu structure. (2) Removed 2-column sidebar layout on all dashboard pages through enhanced HVAC_Astra_Integration with aggressive CSS overrides, forced no-sidebar meta updates, and complete sidebar removal filters. (3) Fixed profile page template assignment to use new template with proper navigation and breadcrumbs. All fixes deployed to staging and verified through automated tests - dashboard shows single navigation in full-width layout, profile page uses new template with correct styling.
- **Role Field and Certification System Implementation (2025-08-01)**: Added comprehensive user role field to registration, profile display, and profile edit with 10 role options (technician, installer, supervisor, manager, trainer, consultant, sales representative, engineer, business owner, other). Implemented advanced certification tracking system with three meta fields: date_certified (date picker), certification_type (dropdown with "Certified measureQuick Trainer" and "Certified measureQuick Champion"), and certification_status (status badges for Active/Expired/Pending/Disabled). Features sophisticated role-based access control where regular trainers see read-only certification fields while administrators and master trainers have full edit access. All 25 users automatically migrated with appropriate default values during plugin activation. System includes professional CSS styling with color-coded status badges, comprehensive server-side validation, and complete E2E test coverage. Documentation updated with access control patterns and API reference.
[... rest of the existing content remains unchanged ...]

View file

@ -1,16 +1,18 @@
/**
* HVAC Breadcrumbs Styles
*
* Clean, modern breadcrumb styling that matches the HVAC trainer interface
*
* @package HVAC_Community_Events
* @version 2.0.0
* @since 2.0.0
*/
/* Breadcrumb Container */
.hvac-breadcrumb {
margin: 0 0 1.5rem 0;
padding: 0.75rem 0;
font-size: 0.875rem;
color: #666;
.hvac-breadcrumbs {
margin: 0 0 20px 0;
padding: 10px 0;
border-bottom: 1px solid #e9ecef;
background: transparent;
}
/* Breadcrumb List */
@ -19,8 +21,9 @@
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
align-items: center;
flex-wrap: wrap;
gap: 0;
}
/* Breadcrumb Items */
@ -29,119 +32,180 @@
align-items: center;
margin: 0;
padding: 0;
font-size: 14px;
line-height: 1.4;
}
.hvac-breadcrumb-item:not(:last-child) {
margin-right: 8px;
}
/* Breadcrumb Links */
.hvac-breadcrumb-link {
color: #0274be;
.hvac-breadcrumb-item a {
color: #007cba;
text-decoration: none;
transition: color 0.3s ease;
padding: 0.25rem 0;
padding: 4px;
border-radius: 3px;
transition: all 0.2s ease;
}
.hvac-breadcrumb-link:hover {
color: #005fa3;
.hvac-breadcrumb-item a:hover {
color: #005a87;
background-color: rgba(0, 124, 186, 0.1);
text-decoration: none;
}
.hvac-breadcrumb-item a:focus {
outline: 2px solid #007cba;
outline-offset: 1px;
}
/* Current Page (Last Item) */
.hvac-breadcrumb-current .hvac-breadcrumb-current-text {
color: #495057;
font-weight: 600;
padding: 4px;
}
/* Separators */
.hvac-breadcrumb-separator {
color: #6c757d;
margin: 0 8px;
font-weight: normal;
user-select: none;
}
/* Home Breadcrumb Special Styling */
.hvac-breadcrumb-home a {
display: inline-flex;
align-items: center;
}
.hvac-breadcrumb-home a::before {
content: '🏠';
margin-right: 4px;
font-size: 12px;
}
/* Responsive Design */
@media (max-width: 768px) {
.hvac-breadcrumbs {
margin: 0 0 15px 0;
padding: 8px 0;
}
.hvac-breadcrumb-item {
font-size: 13px;
}
.hvac-breadcrumb-list {
gap: 0;
}
.hvac-breadcrumb-separator {
margin: 0 6px;
}
/* Stack breadcrumbs on very small screens if needed */
@media (max-width: 480px) {
.hvac-breadcrumb-list {
flex-wrap: wrap;
}
.hvac-breadcrumb-item {
font-size: 12px;
}
}
}
/* Dark Mode Support (if theme supports it) */
@media (prefers-color-scheme: dark) {
.hvac-breadcrumbs {
border-bottom-color: #495057;
}
.hvac-breadcrumb-item a {
color: #66b3ff;
}
.hvac-breadcrumb-item a:hover {
color: #99ccff;
background-color: rgba(102, 179, 255, 0.1);
}
.hvac-breadcrumb-current .hvac-breadcrumb-current-text {
color: #f8f9fa;
}
.hvac-breadcrumb-separator {
color: #adb5bd;
}
}
/* High Contrast Mode Support */
@media (prefers-contrast: high) {
.hvac-breadcrumb-item a {
border: 1px solid transparent;
}
.hvac-breadcrumb-item a:hover,
.hvac-breadcrumb-item a:focus {
border-color: currentColor;
background-color: transparent;
}
.hvac-breadcrumb-separator {
font-weight: bold;
}
}
/* Print Styles */
@media print {
.hvac-breadcrumbs {
border-bottom: 1px solid #000;
margin-bottom: 20px;
}
.hvac-breadcrumb-item a {
color: #000;
text-decoration: underline;
}
.hvac-breadcrumb-link:focus {
outline: 2px solid #0274be;
outline-offset: 2px;
border-radius: 2px;
}
/* Current Page */
.hvac-breadcrumb-current .hvac-breadcrumb-text {
color: #333;
font-weight: 500;
}
/* Separator */
.hvac-breadcrumb-separator {
margin: 0 0.5rem;
color: #999;
font-size: 1.1em;
}
/* Mobile Responsive */
@media (max-width: 768px) {
.hvac-breadcrumb {
font-size: 0.8125rem;
padding: 0.5rem 0;
}
.hvac-breadcrumb-separator {
margin: 0 0.375rem;
color: #000;
}
}
/* Integration with existing styles */
.hvac-page-header + .hvac-breadcrumb {
margin-top: -1rem;
/* Animation for dynamic breadcrumb updates */
@keyframes breadcrumbFadeIn {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Structured Data Script */
.hvac-breadcrumb + script[type="application/ld+json"] {
display: none;
.hvac-breadcrumbs.hvac-breadcrumbs-updated {
animation: breadcrumbFadeIn 0.3s ease-out;
}
/* Alternative Style - Pills */
.hvac-breadcrumb-pills .hvac-breadcrumb-list {
gap: 0.5rem;
/* Integration with existing HVAC styles */
.hvac-page-wrapper .hvac-breadcrumbs {
margin-left: 0;
margin-right: 0;
padding-left: 0;
padding-right: 0;
}
.hvac-breadcrumb-pills .hvac-breadcrumb-item {
background-color: #f0f0f0;
padding: 0.375rem 0.75rem;
border-radius: 1rem;
.hvac-container .hvac-breadcrumbs {
margin-top: -10px;
margin-bottom: 30px;
}
.hvac-breadcrumb-pills .hvac-breadcrumb-link {
color: #666;
}
.hvac-breadcrumb-pills .hvac-breadcrumb-current {
background-color: #0274be;
color: white;
}
.hvac-breadcrumb-pills .hvac-breadcrumb-current .hvac-breadcrumb-text {
color: white;
}
.hvac-breadcrumb-pills .hvac-breadcrumb-separator {
display: none;
}
/* Alternative Style - Arrows */
.hvac-breadcrumb-arrows .hvac-breadcrumb-separator::before {
content: '→';
font-size: 1.2em;
}
.hvac-breadcrumb-arrows .hvac-breadcrumb-separator {
font-size: 0;
}
/* Dark Theme Support */
@media (prefers-color-scheme: dark) {
.hvac-breadcrumb {
color: #ccc;
}
.hvac-breadcrumb-link {
color: #4db8ff;
}
.hvac-breadcrumb-link:hover {
color: #80ccff;
}
.hvac-breadcrumb-current .hvac-breadcrumb-text {
color: #fff;
}
.hvac-breadcrumb-separator {
color: #666;
}
/* Ensure proper spacing with menu system */
.hvac-trainer-menu-wrapper + .hvac-breadcrumbs {
margin-top: 20px;
}

View file

@ -417,3 +417,93 @@
flex-wrap: wrap;
}
}
/* Certification section styling */
.hvac-certification-section {
border: 2px solid #0073aa;
border-radius: 8px;
padding: 20px;
margin-bottom: 30px;
background: linear-gradient(135deg, #f8fdff 0%, #e6f7ff 100%);
position: relative;
}
.hvac-certification-section h2,
.hvac-certification-section h3 {
color: #0073aa;
margin-top: 0;
display: flex;
align-items: center;
gap: 10px;
}
.hvac-certification-section h2::before,
.hvac-certification-section h3::before {
content: "🏆";
font-size: 1.2em;
}
/* Certification status badges */
.hvac-cert-status {
padding: 4px 12px;
border-radius: 20px;
font-weight: bold;
font-size: 0.85em;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.hvac-cert-status-active {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.hvac-cert-status-expired {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.hvac-cert-status-pending {
background-color: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.hvac-cert-status-disabled {
background-color: #e2e3e5;
color: #495057;
border: 1px solid #ced4da;
}
/* Read-only field styling for certification edit */
.hvac-certification-edit-section .hvac-read-only-field {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
padding: 8px 12px;
border-radius: 4px;
color: #6c757d;
font-style: italic;
}
.hvac-read-only-note {
font-size: 0.8em;
color: #6c757d;
font-weight: normal;
margin-left: 10px;
}
/* Enhanced form styling for certification fields */
.hvac-certification-edit-section .hvac-form-row select,
.hvac-certification-edit-section .hvac-form-row input[type="date"] {
border: 2px solid #e9ecef;
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
}
.hvac-certification-edit-section .hvac-form-row select:focus,
.hvac-certification-edit-section .hvac-form-row input[type="date"]:focus {
border-color: #0073aa;
box-shadow: 0 0 0 0.2rem rgba(0, 115, 170, 0.25);
outline: none;
}

495
docs/API-REFERENCE.md Normal file
View file

@ -0,0 +1,495 @@
# HVAC Plugin API Reference
## Table of Contents
- [PHP Classes](#php-classes)
- [JavaScript APIs](#javascript-apis)
- [WordPress Hooks](#wordpress-hooks)
- [AJAX Endpoints](#ajax-endpoints)
- [Shortcodes](#shortcodes)
- [Database Schema](#database-schema)
## PHP Classes
### HVAC_Plugin
Main plugin initialization class.
```php
class HVAC_Plugin {
/**
* Get plugin instance (Singleton)
* @return HVAC_Plugin
*/
public static function instance()
/**
* Initialize plugin components
* @return void
*/
public function init()
}
```
### HVAC_Event_Manager
Manages event creation and manipulation.
```php
class HVAC_Event_Manager {
/**
* Create a new event
* @param array $event_data Event information
* @return int|WP_Error Event ID or error
*/
public static function create_event($event_data)
/**
* Get trainer events
* @param int $trainer_id Trainer user ID
* @param array $args Query arguments
* @return array Array of event objects
*/
public static function get_trainer_events($trainer_id, $args = array())
/**
* Update event
* @param int $event_id Event ID
* @param array $data Event data to update
* @return bool Success status
*/
public static function update_event($event_id, $data)
}
```
### HVAC_Certificate_Generator
Handles PDF certificate generation.
```php
class HVAC_Certificate_Generator {
/**
* Generate certificate for attendee
* @param int $event_id Event ID
* @param array $attendee Attendee data
* @return string|WP_Error PDF file path or error
*/
public function generate_certificate($event_id, $attendee)
/**
* Generate bulk certificates
* @param int $event_id Event ID
* @param array $attendees Array of attendee data
* @return array Results array with successes and failures
*/
public function generate_bulk_certificates($event_id, $attendees)
}
```
### HVAC_Trainer_Profile
Manages trainer profile functionality.
```php
class HVAC_Trainer_Profile {
/**
* Get trainer profile data
* @param int $trainer_id User ID
* @return array Profile data
*/
public static function get_profile($trainer_id)
/**
* Update trainer profile
* @param int $trainer_id User ID
* @param array $data Profile data
* @return bool Success status
*/
public static function update_profile($trainer_id, $data)
/**
* Get trainer statistics
* @param int $trainer_id User ID
* @return array Statistics data
*/
public static function get_statistics($trainer_id)
}
```
### HVAC_Menu_System
Handles navigation menu rendering.
```php
class HVAC_Menu_System {
/**
* Render trainer navigation menu
* @return void Outputs HTML
*/
public function render_trainer_menu()
/**
* Get menu structure for current user
* @return array Menu items array
*/
private function get_menu_structure()
}
```
## JavaScript APIs
### Global Objects
```javascript
// Main HVAC object
window.HVAC = {
// Initialize components
init: function() {},
// Event management
events: {
create: function(data) {},
update: function(eventId, data) {},
delete: function(eventId) {}
},
// Profile management
profile: {
load: function(userId) {},
save: function(data) {},
uploadPhoto: function(file) {}
},
// Utilities
utils: {
showMessage: function(message, type) {},
confirm: function(message, callback) {},
formatDate: function(date) {}
}
};
// AJAX helper
window.hvacAjax = {
post: function(action, data, success, error) {},
get: function(action, data, success, error) {}
};
```
### jQuery Plugins
```javascript
// Dashboard initialization
jQuery(document).ready(function($) {
// Initialize date pickers
$('.hvac-datepicker').datepicker({
dateFormat: 'yy-mm-dd'
});
// Initialize form validation
$('.hvac-form').validate({
rules: {
event_title: 'required',
event_date: 'required'
}
});
});
```
## WordPress Hooks
### Actions
```php
// Plugin lifecycle
do_action('hvac_plugin_activated');
do_action('hvac_plugin_deactivated');
do_action('hvac_plugin_updated', $old_version, $new_version);
// Page creation
do_action('hvac_before_create_pages');
do_action('hvac_after_create_pages', $page_ids);
// Event management
do_action('hvac_before_event_save', $event_data);
do_action('hvac_after_event_save', $event_id, $event_data);
do_action('hvac_event_deleted', $event_id);
// Certificate generation
do_action('hvac_before_certificate_generate', $event_id, $attendee);
do_action('hvac_after_certificate_generate', $certificate_path);
// User profile
do_action('hvac_profile_updated', $user_id, $profile_data);
do_action('hvac_trainer_registered', $user_id);
```
### Filters
```php
// Capability checks
apply_filters('hvac_user_can_manage_event', $can_manage, $user_id, $event_id);
apply_filters('hvac_user_can_generate_certificates', $can_generate, $user_id);
// Data manipulation
apply_filters('hvac_event_data', $event_data, $event_id);
apply_filters('hvac_certificate_data', $certificate_data, $event_id, $attendee);
apply_filters('hvac_profile_fields', $fields, $user_id);
// Display filters
apply_filters('hvac_menu_items', $menu_items, $user_role);
apply_filters('hvac_dashboard_widgets', $widgets, $user_id);
apply_filters('hvac_date_format', $format);
// Query filters
apply_filters('hvac_events_query_args', $args, $context);
apply_filters('hvac_trainers_query_args', $args);
```
## AJAX Endpoints
### Event Management
```javascript
// Create event
{
action: 'hvac_create_event',
nonce: hvac_ajax.nonce,
event_data: {
title: 'Event Title',
start_date: '2025-08-15',
venue_id: 123,
capacity: 50
}
}
// Update event
{
action: 'hvac_update_event',
nonce: hvac_ajax.nonce,
event_id: 456,
event_data: {
title: 'Updated Title'
}
}
// Delete event
{
action: 'hvac_delete_event',
nonce: hvac_ajax.nonce,
event_id: 456
}
```
### Profile Management
```javascript
// Get profile data
{
action: 'hvac_get_profile',
nonce: hvac_ajax.nonce,
user_id: 789 // Optional, defaults to current user
}
// Update profile
{
action: 'hvac_update_profile',
nonce: hvac_ajax.nonce,
profile_data: {
bio: 'Updated bio',
certifications: ['NATE', 'EPA'],
website: 'https://example.com'
}
}
// Upload profile photo
{
action: 'hvac_upload_photo',
nonce: hvac_ajax.nonce,
photo: File // File object from input
}
```
### Certificate Generation
```javascript
// Generate single certificate
{
action: 'hvac_generate_certificate',
nonce: hvac_ajax.nonce,
event_id: 456,
attendee_id: 789
}
// Generate bulk certificates
{
action: 'hvac_generate_bulk_certificates',
nonce: hvac_ajax.nonce,
event_id: 456,
attendee_ids: [789, 790, 791]
}
```
## Shortcodes
### Display Shortcodes
```php
// Trainer dashboard
[hvac_trainer_dashboard]
// Event list
[hvac_event_list trainer_id="123" limit="10" status="upcoming"]
// Profile view
[hvac_trainer_profile_view user_id="123"]
// Profile edit form
[hvac_trainer_profile_edit]
// Certificate reports
[hvac_certificate_reports]
// Event creation form
[hvac_event_form]
// Venue management
[hvac_venue_list]
[hvac_venue_form]
// Organizer management
[hvac_organizer_list]
[hvac_organizer_form]
```
### Shortcode Parameters
```php
// Event list parameters
[hvac_event_list
trainer_id="" // Trainer ID (default: current user)
limit="10" // Number of events
status="upcoming" // upcoming|past|all
orderby="date" // date|title|capacity
order="ASC" // ASC|DESC
]
// Profile view parameters
[hvac_trainer_profile_view
user_id="" // User ID (default: current user)
show_stats="true" // Show statistics
show_events="true" // Show event list
]
```
## Database Schema
### Post Meta (Events)
```sql
-- Event-specific meta keys
_EventStartDate -- Event start datetime
_EventEndDate -- Event end datetime
_EventVenueID -- Venue post ID
_EventOrganizerID -- Organizer post ID
_EventTrainerID -- Trainer user ID
_EventCapacity -- Maximum attendees
_EventRegisteredCount -- Current registration count
_EventCertificateTemplate -- Certificate template ID
```
### User Meta (Trainers)
```sql
-- Profile information
role -- User's HVAC industry role (technician, installer, etc.)
hvac_bio -- Trainer biography
hvac_certifications -- Serialized array of certifications
hvac_trainer_photo -- Attachment ID of profile photo
hvac_website -- Trainer website URL
hvac_location -- Trainer location
hvac_phone -- Contact phone
-- Certification information (admin/master trainer editable only)
date_certified -- Date when trainer was certified (YYYY-MM-DD)
certification_type -- Type of certification ('Certified measureQuick Trainer', 'Certified measureQuick Champion')
certification_status -- Current certification status ('Active', 'Expired', 'Pending', 'Disabled')
-- Training preferences
hvac_training_formats -- Serialized array of formats
hvac_training_audience -- Target audience
hvac_training_topics -- Specialized topics
-- Statistics (cached)
hvac_total_events -- Total events created
hvac_total_students -- Total students trained
hvac_total_revenue -- Total revenue generated
```
### Options Table
```sql
-- Plugin settings
hvac_plugin_version -- Current plugin version
hvac_settings -- Serialized settings array
hvac_email_templates -- Email template configurations
hvac_certificate_settings -- Certificate generation settings
-- Feature flags
hvac_enable_zoho -- Zoho CRM integration
hvac_enable_analytics -- Analytics tracking
hvac_maintenance_mode -- Maintenance mode flag
```
## Error Codes
### Event Errors
- `HVAC001` - Invalid event data
- `HVAC002` - Event not found
- `HVAC003` - Insufficient permissions to manage event
- `HVAC004` - Event capacity exceeded
### User Errors
- `HVAC101` - User not found
- `HVAC102` - Invalid user role
- `HVAC103` - Insufficient permissions
- `HVAC104` - Profile update failed
### Certificate Errors
- `HVAC201` - Certificate generation failed
- `HVAC202` - Invalid attendee data
- `HVAC203` - Template not found
- `HVAC204` - PDF library error
### System Errors
- `HVAC901` - Database connection error
- `HVAC902` - File system error
- `HVAC903` - Third-party API error
- `HVAC904` - Unknown error
## Response Formats
### Success Response
```json
{
"success": true,
"data": {
"id": 123,
"message": "Operation completed successfully",
"additional_data": {}
}
}
```
### Error Response
```json
{
"success": false,
"data": {
"code": "HVAC001",
"message": "Invalid event data",
"details": {
"field": "event_date",
"error": "Date must be in the future"
}
}
}
```

299
docs/CONFIGURATION.md Normal file
View file

@ -0,0 +1,299 @@
# HVAC Plugin Configuration Guide
## Table of Contents
- [Overview](#overview)
- [System Architecture](#system-architecture)
- [Configuration Files](#configuration-files)
- [User Roles & Permissions](#user-roles--permissions)
- [URL Structure](#url-structure)
- [Theme Integration](#theme-integration)
- [Page Templates](#page-templates)
- [Navigation System](#navigation-system)
- [CSS & JavaScript Assets](#css--javascript-assets)
- [Database Structure](#database-structure)
## Overview
The HVAC Community Events plugin is a comprehensive event management system for HVAC trainers. It integrates with The Events Calendar (TEC) and provides custom functionality for trainer profiles, certificate generation, and event management.
## System Architecture
### Core Components
```
hvac-community-events/
├── includes/ # Core PHP classes
├── templates/ # Page templates
├── assets/ # CSS, JS, images
├── bin/ # CLI scripts
├── scripts/ # Deployment scripts
├── tests/ # Test suites
└── docs/ # Documentation
```
### Key Classes
1. **HVAC_Plugin** - Main plugin class, handles initialization
2. **HVAC_Menu_System** - Navigation menu management
3. **HVAC_Astra_Integration** - Theme compatibility layer
4. **HVAC_Scripts_Styles** - Asset management
5. **HVAC_Shortcodes** - Shortcode implementations
6. **HVAC_Route_Manager** - URL routing and rewrites
## Configuration Files
### .env Configuration
```bash
# Staging Environment
UPSKILL_STAGING_IP=146.190.76.204
UPSKILL_STAGING_SSH_USER=roodev
UPSKILL_STAGING_PATH=/home/974670.cloudwaysapps.com/uberrxmprk/public_html
UPSKILL_STAGING_URL=https://upskill-staging.measurequick.com
# Production Environment (only when explicitly needed)
UPSKILL_PRODUCTION_IP=<production-ip>
UPSKILL_PRODUCTION_SSH_USER=<production-user>
UPSKILL_PRODUCTION_PATH=<production-path>
UPSKILL_PRODUCTION_URL=https://upskillhvac.com
```
### Plugin Constants (hvac-community-events.php)
```php
define('HVAC_PLUGIN_VERSION', '2.0.0');
define('HVAC_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('HVAC_PLUGIN_URL', plugin_dir_url(__FILE__));
define('HVAC_PLUGIN_FILE', __FILE__);
```
## User Roles & Permissions
### Role Hierarchy
1. **hvac_master_trainer** - Full access to all features, aggregate reporting
2. **hvac_trainer** - Standard trainer access, manage own events
3. **event_trainer** - Legacy role (should be migrated to hvac_trainer)
### Capabilities
```php
// Master Trainer
- manage_all_events
- view_all_certificates
- access_master_dashboard
- manage_trainers
// Regular Trainer
- manage_own_events
- generate_certificates
- manage_profile
- manage_venues
- manage_organizers
```
### Important: Dual-Role Users
Users with both trainer and master trainer roles will ONLY see the master trainer navigation to prevent UI duplication.
## URL Structure
### Hierarchical URLs (implemented June 2025)
```
/trainer/
├── /dashboard/
├── /event/manage/
├── /certificate-reports/
├── /generate-certificates/
├── /profile/
├── /profile/edit/
├── /venue/list/
├── /venue/manage/
├── /organizer/list/
└── /organizer/manage/
/master-trainer/
├── /master-dashboard/
├── /events/
└── /reports/
```
### Legacy URL Redirects
All old URLs automatically redirect to new hierarchical structure:
- `/trainer-dashboard/``/trainer/dashboard/`
- `/trainer-profile/``/trainer/profile/`
## Theme Integration
### Astra Theme Compatibility
The plugin includes `HVAC_Astra_Integration` class that:
- Forces full-width layouts on all plugin pages
- Removes sidebars completely
- Adds custom body classes
- Overrides theme container widths
### Body Classes Added
```css
.hvac-plugin-page
.hvac-trainer-area
.hvac-astra-integrated
.ast-no-sidebar
.ast-full-width-layout
```
### Critical CSS Overrides
```css
/* Force full-width on HVAC pages */
.hvac-astra-integrated .ast-container {
max-width: 100% !important;
width: 100% !important;
}
/* Remove all sidebars */
.hvac-astra-integrated .widget-area,
.hvac-astra-integrated #secondary {
display: none !important;
}
```
## Page Templates
### Template Files
All templates must include WordPress header/footer:
```php
<?php
// Template Name: Trainer Dashboard
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<!-- Content -->
<?php
get_footer();
?>
```
### Template Assignment
Templates are automatically assigned during plugin activation. Manual assignment:
```php
update_post_meta($page_id, '_wp_page_template', 'templates/page-trainer-dashboard.php');
```
### Available Templates
- `page-trainer-dashboard.php`
- `page-trainer-profile.php`
- `page-certificate-reports.php`
- `page-generate-certificates.php`
- `page-manage-event.php`
- `page-master-dashboard.php`
## Navigation System
### Menu Registration
```php
// Rendered via HVAC_Menu_System::render_trainer_menu()
// Includes dropdown support and mobile hamburger menu
```
### Menu Structure
```php
[
'Events' => [
'Dashboard',
'New Event'
],
'Certificates' => [
'Reports',
'New Certificate'
],
'Customize' => [
'Personal Profile',
'Training Organizers',
'Training Venues'
]
]
```
### Navigation Rules
1. Check user capabilities before rendering
2. Dual-role users see only master trainer menu
3. Use `HVAC_NAV_RENDERED` constant to prevent duplicates
## CSS & JavaScript Assets
### Asset Loading
Managed by `HVAC_Scripts_Styles` class:
```php
// CSS files loaded conditionally on plugin pages
- hvac-dashboard.css
- hvac-menu-system.css
- hvac-layout.css
- hvac-profile.css
// JavaScript with proper dependencies
- hvac-dashboard.js (requires jQuery)
- hvac-menu-system.js
- hvac-profile.js
```
### Loading Conditions
```php
private function is_plugin_page() {
// Checks URL patterns
// Checks page templates
// Checks HVAC_IN_PAGE_TEMPLATE constant
}
```
## Database Structure
### Custom Tables
None - plugin uses WordPress posts, user meta, and post meta
### Post Meta Keys
```php
// Event meta
'_EventStartDate'
'_EventEndDate'
'_EventVenueID'
'_EventOrganizerID'
// Page meta
'_wp_page_template'
'site-sidebar-layout'
'ast-site-sidebar-layout'
```
### User Meta Keys
```php
'hvac_trainer_photo'
'hvac_certifications'
'hvac_bio'
'hvac_website'
'hvac_location'
'hvac_training_formats'
'hvac_training_audience'
```
## Environment-Specific Settings
### Staging
- Debug logging enabled
- Cache clearing after deployment
- Test data seeding available
### Production
- Debug logging disabled
- Performance optimizations active
- Double confirmation required for deployment
## Integration Points
### The Events Calendar (TEC)
- Events created as 'tribe_events' post type
- Venues managed through TEC
- Organizers synced with TEC
### Zoho CRM
- OAuth integration for data sync
- Refresh token handling
- Production-only activation
### Certificate Generation
- TCPDF library for PDF creation
- Custom certificate templates
- Batch processing support

View file

@ -0,0 +1,198 @@
# HVAC Plugin Deployment Checklist
## Pre-Deployment Checklist
### 1. Code Quality Checks
- [ ] All PHP files pass syntax check (`php -l`)
- [ ] No debug code left in files (`error_log`, `var_dump`, `console.log`)
- [ ] All TODO comments addressed or documented
- [ ] Code follows WordPress coding standards
### 2. Testing Complete
- [ ] All unit tests passing
- [ ] E2E tests passing on staging
- [ ] Manual testing checklist completed
- [ ] Tested with different user roles
- [ ] Mobile responsive testing done
### 3. Security Review
- [ ] All user inputs sanitized
- [ ] All outputs escaped
- [ ] Nonces implemented for forms
- [ ] Capability checks in place
- [ ] No hardcoded credentials
### 4. Performance Check
- [ ] Database queries optimized
- [ ] Caching implemented where appropriate
- [ ] Assets minified (production)
- [ ] Images optimized
- [ ] No unnecessary API calls
### 5. Documentation Updated
- [ ] CLAUDE.md updated with new features
- [ ] README files current
- [ ] Inline code documentation complete
- [ ] API changes documented
- [ ] Changelog updated
## Deployment Steps
### 1. Staging Deployment
```bash
# Run pre-deployment validation
bin/pre-deployment-check.sh
# Deploy to staging
scripts/deploy.sh staging
# Run post-deployment verification
scripts/verify-plugin-fixes.sh
```
### 2. Staging Verification
- [ ] Login functionality working
- [ ] Navigation menus displaying correctly
- [ ] All pages loading (no 404s)
- [ ] Forms submitting properly
- [ ] Certificates generating
- [ ] Email notifications sending
- [ ] No PHP errors in logs
- [ ] No JavaScript console errors
### 3. Data Integrity
- [ ] Database migrations completed
- [ ] User roles properly assigned
- [ ] Page templates correctly set
- [ ] Meta data preserved
- [ ] Settings maintained
### 4. Cache Management
- [ ] WordPress cache cleared
- [ ] Breeze cache purged
- [ ] OPcache reset
- [ ] CDN cache cleared (if applicable)
- [ ] Browser cache considerations documented
### 5. Production Deployment
```bash
# Only when explicitly approved
scripts/deploy.sh production
# Double confirmation required
```
### 6. Production Verification
- [ ] Smoke test critical paths
- [ ] Monitor error logs for 30 minutes
- [ ] Check page load times
- [ ] Verify email delivery
- [ ] Test payment processing (if applicable)
- [ ] Confirm analytics tracking
## Post-Deployment
### 1. Monitoring (First 24 Hours)
- [ ] Error logs checked every 2 hours
- [ ] Performance metrics reviewed
- [ ] User feedback collected
- [ ] Support tickets monitored
- [ ] Database performance checked
### 2. Communication
- [ ] Stakeholders notified of deployment
- [ ] Support team briefed on changes
- [ ] Documentation updated if needed
- [ ] Known issues documented
### 3. Rollback Plan
- [ ] Backup location confirmed
- [ ] Rollback procedure tested
- [ ] Rollback decision criteria defined
- [ ] Team members know rollback process
## Rollback Procedure
If critical issues are discovered:
```bash
# 1. SSH to server
ssh user@server
# 2. Navigate to plugins directory
cd /path/to/wp-content/plugins
# 3. Backup current (problematic) version
mv hvac-community-events hvac-community-events-broken-$(date +%Y%m%d)
# 4. Restore previous version
cp -r hvac-backups/hvac-community-events-backup-[previous-date] hvac-community-events
# 5. Reactivate plugin
wp plugin activate hvac-community-events
# 6. Clear all caches
wp cache flush
wp breeze purge --all
wp eval 'if (function_exists("opcache_reset")) opcache_reset();'
# 7. Verify functionality
# 8. Document issue for investigation
```
## Common Deployment Issues
### Issue: Pages returning 404
```bash
wp rewrite flush
wp eval 'HVAC_Page_Manager::create_required_pages();'
```
### Issue: Styles not loading
```bash
# Check if CSS files exist
ls -la wp-content/plugins/hvac-community-events/assets/css/
# Force cache refresh
wp cache flush
```
### Issue: User roles not working
```bash
# Re-run role creation
wp eval 'HVAC_Roles::create_roles();'
```
### Issue: Database errors
```bash
# Check database integrity
wp db check
# Optimize tables
wp db optimize
```
## Emergency Contacts
- **Lead Developer**: [Contact Info]
- **DevOps**: [Contact Info]
- **Project Manager**: [Contact Info]
- **Hosting Support**: [Contact Info]
## Final Checks
- [ ] All checklist items completed
- [ ] Deployment documented in project log
- [ ] Next deployment date scheduled
- [ ] Lessons learned documented
- [ ] Team debriefed
---
**Remember**:
- Never deploy on Fridays unless critical
- Always have a rollback plan
- Test everything on staging first
- Document any deviations from process
- Communicate clearly with all stakeholders

548
docs/DEVELOPMENT-GUIDE.md Normal file
View file

@ -0,0 +1,548 @@
# HVAC Plugin Development Guide
## Table of Contents
- [Development Setup](#development-setup)
- [Coding Standards](#coding-standards)
- [Architecture Principles](#architecture-principles)
- [Development Workflow](#development-workflow)
- [Testing Guidelines](#testing-guidelines)
- [Deployment Process](#deployment-process)
- [Security Best Practices](#security-best-practices)
- [Performance Optimization](#performance-optimization)
## Development Setup
### Prerequisites
- PHP 7.4+ (8.0+ recommended)
- Node.js 16+ and npm
- Composer
- WP-CLI
- Git
### Local Environment Setup
1. **Clone Repository**
```bash
git clone <repository-url>
cd upskill-event-manager
```
2. **Install Dependencies**
```bash
# PHP dependencies
composer install
# Node dependencies
npm install
# Create .env file
cp .env.example .env
# Edit .env with your environment details
```
3. **Configure WordPress**
- Install WordPress locally
- Create database
- Configure wp-config.php
- Install The Events Calendar plugin
4. **Activate Plugin**
```bash
wp plugin activate hvac-community-events
```
## Coding Standards
### PHP Standards
1. **Follow WordPress Coding Standards**
```php
// Good
function hvac_get_trainer_events( $trainer_id ) {
$args = array(
'post_type' => 'tribe_events',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_EventTrainerID',
'value' => $trainer_id,
'compare' => '='
)
)
);
return get_posts( $args );
}
// Bad
function getTrainerEvents($trainerId) {
$args = [
'post_type' => 'tribe_events',
'posts_per_page' => -1,
'meta_query' => [
['key' => '_EventTrainerID', 'value' => $trainerId, 'compare' => '=']
]
];
return get_posts($args);
}
```
2. **Namespace Usage**
```php
// Use prefixes instead of PHP namespaces for WordPress compatibility
class HVAC_Event_Manager {
// Class implementation
}
```
3. **Security First**
```php
// Always escape output
echo esc_html( $trainer_name );
echo esc_url( $profile_url );
echo esc_attr( $css_class );
// Sanitize input
$trainer_id = absint( $_GET['trainer_id'] );
$search = sanitize_text_field( $_POST['search'] );
// Verify nonces
if ( ! wp_verify_nonce( $_POST['hvac_nonce'], 'hvac_action' ) ) {
wp_die( 'Security check failed' );
}
```
### JavaScript Standards
1. **jQuery Usage**
```javascript
// Always use jQuery in no-conflict mode
jQuery(document).ready(function($) {
// $ is now safe to use
$('.hvac-button').on('click', function(e) {
e.preventDefault();
// Handle click
});
});
```
2. **AJAX Requests**
```javascript
jQuery.ajax({
url: hvac_ajax.ajax_url,
type: 'POST',
data: {
action: 'hvac_update_profile',
nonce: hvac_ajax.nonce,
data: formData
},
success: function(response) {
if (response.success) {
// Handle success
} else {
// Handle error
console.error(response.data.message);
}
}
});
```
### CSS Standards
1. **BEM Methodology**
```css
/* Block */
.hvac-trainer-card {}
/* Element */
.hvac-trainer-card__header {}
.hvac-trainer-card__content {}
/* Modifier */
.hvac-trainer-card--featured {}
```
2. **Specificity Rules**
```css
/* Avoid overly specific selectors */
/* Bad */
body.logged-in .hvac-wrapper div.container .hvac-trainer-card {}
/* Good */
.hvac-trainer-card {}
/* When specificity is needed for theme overrides */
.hvac-astra-integrated .hvac-trainer-card {}
```
## Architecture Principles
### 1. Single Responsibility Principle
Each class should have one primary responsibility:
```php
// Good - Focused classes
class HVAC_Certificate_Generator {} // Only handles PDF generation
class HVAC_Event_Manager {} // Only manages events
class HVAC_Email_Sender {} // Only sends emails
// Bad - Kitchen sink class
class HVAC_Everything {} // Does certificates, events, emails, etc.
```
### 2. Dependency Injection
```php
class HVAC_Certificate_Service {
private $generator;
private $emailer;
public function __construct( $generator, $emailer ) {
$this->generator = $generator;
$this->emailer = $emailer;
}
}
```
### 3. Hook-Driven Architecture
```php
// Use WordPress hooks for extensibility
do_action( 'hvac_before_event_save', $event_data );
$event_data = apply_filters( 'hvac_event_data', $event_data );
do_action( 'hvac_after_event_save', $event_id );
```
### 4. Singleton Pattern (where appropriate)
```php
class HVAC_Plugin {
private static $instance = null;
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
// Initialize
}
}
```
## Development Workflow
### 1. Feature Development
```bash
# Create feature branch
git checkout -b feature/new-trainer-dashboard
# Make changes
# Test thoroughly
# Commit with meaningful messages
git add .
git commit -m "feat: Add advanced filtering to trainer dashboard
- Add date range picker
- Add event status filter
- Add export functionality"
# Push and create PR
git push origin feature/new-trainer-dashboard
```
### 2. Git Commit Messages
Follow conventional commits:
- `feat:` New feature
- `fix:` Bug fix
- `docs:` Documentation
- `style:` Formatting changes
- `refactor:` Code restructuring
- `test:` Test additions/changes
- `chore:` Maintenance tasks
### 3. Code Review Checklist
- [ ] Follows WordPress coding standards
- [ ] Security measures implemented (escaping, sanitization)
- [ ] Performance impact considered
- [ ] Backward compatibility maintained
- [ ] Documentation updated
- [ ] Tests written/updated
## Testing Guidelines
### 1. Unit Testing
```php
class Test_HVAC_Event_Manager extends WP_UnitTestCase {
public function test_create_event() {
$event_data = array(
'post_title' => 'Test Event',
'EventStartDate' => '2025-08-15 09:00:00'
);
$event_id = HVAC_Event_Manager::create_event( $event_data );
$this->assertIsInt( $event_id );
$this->assertGreaterThan( 0, $event_id );
}
}
```
### 2. E2E Testing with Playwright
```javascript
test('Trainer can create event', async ({ page }) => {
// Login
await loginAsTrainer(page);
// Navigate to event creation
await page.goto('/trainer/event/manage/');
// Fill form
await page.fill('#event-title', 'HVAC Training Session');
await page.selectOption('#event-venue', 'Main Campus');
// Submit
await page.click('button[type="submit"]');
// Verify
await expect(page).toHaveURL(/event-created/);
});
```
### 3. Manual Testing Checklist
- [ ] Test on staging before production
- [ ] Test with different user roles
- [ ] Test on mobile devices
- [ ] Test with different themes (especially Astra)
- [ ] Test with caching enabled
## Deployment Process
### 1. Pre-Deployment Checks
```bash
# Run validation
bin/pre-deployment-check.sh
# Run tests
npm test
phpunit
```
### 2. Staging Deployment
```bash
# Deploy to staging
scripts/deploy.sh staging
# Verify deployment
scripts/verify-plugin-fixes.sh
```
### 3. Production Deployment
```bash
# Only when explicitly requested
scripts/deploy.sh production
# Requires double confirmation
```
### 4. Post-Deployment
- Monitor error logs
- Check critical functionality
- Be ready to rollback if needed
## Security Best Practices
### 1. Data Validation
```php
// Input validation
if ( ! is_email( $email ) ) {
wp_die( 'Invalid email address' );
}
// Type checking
$event_id = absint( $_POST['event_id'] );
if ( ! $event_id ) {
wp_die( 'Invalid event ID' );
}
```
### 2. SQL Queries
```php
// Always use prepared statements
global $wpdb;
$results = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->posts} WHERE post_author = %d AND post_type = %s",
$user_id,
'tribe_events'
)
);
```
### 3. File Uploads
```php
// Validate file types
$allowed_types = array( 'jpg', 'jpeg', 'png', 'pdf' );
$file_type = wp_check_filetype( $file['name'] );
if ( ! in_array( $file_type['ext'], $allowed_types ) ) {
wp_die( 'Invalid file type' );
}
```
### 4. Capability Checks
```php
// Always verify user permissions
if ( ! current_user_can( 'manage_own_events' ) ) {
wp_die( 'Insufficient permissions' );
}
```
## Performance Optimization
### 1. Database Queries
```php
// Cache expensive queries
$cache_key = 'hvac_trainer_events_' . $trainer_id;
$events = wp_cache_get( $cache_key );
if ( false === $events ) {
$events = get_posts( $args );
wp_cache_set( $cache_key, $events, '', 3600 ); // 1 hour
}
```
### 2. Asset Loading
```php
// Load assets only where needed
public function enqueue_scripts() {
if ( ! $this->is_plugin_page() ) {
return;
}
wp_enqueue_script( 'hvac-dashboard' );
}
```
### 3. AJAX Optimization
```javascript
// Debounce search requests
let searchTimeout;
$('#search').on('keyup', function() {
clearTimeout(searchTimeout);
searchTimeout = setTimeout(function() {
performSearch();
}, 300);
});
```
### 4. Image Optimization
- Use appropriate image sizes
- Lazy load images
- Compress before upload
- Use WebP format when possible
## Common Patterns
### 1. AJAX Handler Pattern
```php
class HVAC_Ajax_Handler {
public function __construct() {
add_action( 'wp_ajax_hvac_action', array( $this, 'handle_request' ) );
}
public function handle_request() {
// Verify nonce
if ( ! wp_verify_nonce( $_POST['nonce'], 'hvac_nonce' ) ) {
wp_send_json_error( 'Invalid nonce' );
}
// Check capabilities
if ( ! current_user_can( 'required_capability' ) ) {
wp_send_json_error( 'Insufficient permissions' );
}
// Process request
$result = $this->process_data( $_POST['data'] );
// Return response
if ( $result ) {
wp_send_json_success( $result );
} else {
wp_send_json_error( 'Processing failed' );
}
}
}
```
### 2. Settings Page Pattern
```php
class HVAC_Settings {
public function __construct() {
add_action( 'admin_menu', array( $this, 'add_menu' ) );
add_action( 'admin_init', array( $this, 'register_settings' ) );
}
public function add_menu() {
add_options_page(
'HVAC Settings',
'HVAC Events',
'manage_options',
'hvac-settings',
array( $this, 'render_page' )
);
}
public function register_settings() {
register_setting( 'hvac_settings', 'hvac_options' );
}
}
```
## Access Control Patterns
### Role-Based Field Access
The plugin implements sophisticated role-based access control for sensitive data:
```php
// Check permissions for certification field editing
$can_edit_certifications = current_user_can('administrator') || current_user_can('hvac_master_trainer');
if ( $can_edit_certifications ) {
// Allow editing certification fields
update_user_meta($user_id, 'certification_status', sanitize_text_field($_POST['certification_status']));
} else {
// Show read-only display for regular trainers
echo '<div class="hvac-read-only-field">' . esc_html($certification_status) . '</div>';
}
```
### Field-Level Access Control Implementation
```php
// Different access levels for different user types
if ( current_user_can('hvac_trainer') && ! current_user_can('hvac_master_trainer') ) {
// Trainer: read-only access to certification fields
echo '<input type="hidden" name="certification_type" value="' . esc_attr($certification_type) . '" />';
echo '<div class="hvac-read-only-field">' . esc_html($certification_type) . '</div>';
} else if ( current_user_can('administrator') || current_user_can('hvac_master_trainer') ) {
// Admin/Master Trainer: full edit access
echo '<select name="certification_type">';
// ... render editable dropdown
echo '</select>';
}
```
### Access Control Best Practices
1. **Always validate permissions server-side** - Never rely on frontend-only restrictions
2. **Use capability checks** - Check specific capabilities rather than user roles when possible
3. **Implement graceful degradation** - Show read-only versions rather than hiding fields entirely
4. **Visual indicators** - Clearly indicate when fields are read-only
5. **Consistent patterns** - Use the same access control patterns throughout the plugin
## Resources
- [WordPress Coding Standards](https://developer.wordpress.org/coding-standards/)
- [WordPress Plugin Handbook](https://developer.wordpress.org/plugins/)
- [The Events Calendar Documentation](https://theeventscalendar.com/knowledgebase/)
- [Playwright Documentation](https://playwright.dev/)

98
docs/README.md Normal file
View file

@ -0,0 +1,98 @@
# HVAC Community Events Plugin Documentation
## Overview
The HVAC Community Events plugin is a comprehensive event management system designed specifically for HVAC trainers. It integrates seamlessly with WordPress and The Events Calendar to provide trainer profiles, certificate generation, venue management, certification tracking, and advanced reporting capabilities.
## Documentation Structure
### 📋 [Configuration Guide](./CONFIGURATION.md)
Complete reference for plugin configuration including:
- System architecture overview
- Configuration files and constants
- User roles and permissions
- URL structure and routing
- Theme integration (Astra)
- Database structure
### 💻 [Development Guide](./DEVELOPMENT-GUIDE.md)
Best practices and guidelines for developers:
- Development environment setup
- Coding standards (WordPress, PHP, JS, CSS)
- Architecture principles and patterns
- Git workflow and deployment process
- Testing strategies
- Security best practices
- Performance optimization
### 🔧 [Troubleshooting Guide](./TROUBLESHOOTING.md)
Solutions to common issues:
- 404 errors and missing pages
- Navigation and menu problems
- CSS and theme conflicts
- Database issues
- Performance problems
- Recovery procedures
- Debugging techniques
### 🏗️ [Architecture Documentation](./ARCHITECTURE.md)
Technical architecture details:
- Plugin structure
- Class responsibilities
- Hook system
- Data flow
- Integration points
## Quick Start
### Installation
1. Upload plugin to `/wp-content/plugins/`
2. Activate through WordPress admin
3. Plugin automatically creates required pages
4. Configure settings as needed
### Key Features
- **Trainer Profiles**: Comprehensive trainer management with photos, certifications, and statistics
- **Event Management**: Full integration with The Events Calendar
- **Certificate Generation**: Automated PDF certificate creation with custom templates
- **Venue Management**: Organize training locations with TEC integration
- **Master Dashboard**: Aggregate reporting for master trainers
- **Hierarchical URLs**: SEO-friendly URL structure (`/trainer/dashboard/`)
### User Roles
1. **hvac_trainer**: Standard trainer role
- Manage own events
- Generate certificates
- Manage profile, venues, organizers
2. **hvac_master_trainer**: Advanced trainer role
- All trainer capabilities
- View aggregate reports
- Access master dashboard
- Manage other trainers
### Important Notes
- **Dual-role users** (both trainer and master trainer) will only see the master trainer navigation
- Plugin requires **The Events Calendar** to be installed and active
- Optimized for **Astra theme** but compatible with most WordPress themes
- PHP 7.4+ required (PHP 8.0+ recommended)
## Support
For issues or questions:
1. Check the [Troubleshooting Guide](./TROUBLESHOOTING.md)
2. Review error logs in `/wp-content/debug.log`
3. Contact development team with detailed error information
## Version History
- **v2.0.0** (Current) - Major refactor with modular architecture
- **v1.0.0** - Initial release
---
Last updated: August 2025

564
docs/TROUBLESHOOTING.md Normal file
View file

@ -0,0 +1,564 @@
# HVAC Plugin Troubleshooting Guide
## Table of Contents
- [Common Issues](#common-issues)
- [Debugging Techniques](#debugging-techniques)
- [Error Messages](#error-messages)
- [Performance Issues](#performance-issues)
- [Deployment Problems](#deployment-problems)
- [Theme Conflicts](#theme-conflicts)
- [Database Issues](#database-issues)
- [Recovery Procedures](#recovery-procedures)
## Common Issues
### 1. Pages Not Found (404 Errors)
**Symptoms:**
- Trainer pages return 404
- Certificate reports page not accessible
- Profile page missing
**Solutions:**
```bash
# Flush rewrite rules
wp rewrite flush
# Recreate pages
wp eval 'do_action("hvac_create_pages");'
# Verify page exists
wp post list --post_type=page --name=trainer
```
**Code Fix:**
```php
// Force page creation in plugin
HVAC_Page_Manager::create_required_pages();
flush_rewrite_rules();
```
### 2. Duplicate Navigation Menus
**Symptoms:**
- Two navigation bars showing
- Both trainer and master menus visible
**Root Cause:**
- User has both trainer and master trainer roles
- Navigation system rendering both menus
**Solution:**
Already fixed in `HVAC_Menu_System`:
```php
// Check get_menu_structure() method
// Master trainers always see master menu only
```
### 3. Missing CSS/Styling
**Symptoms:**
- Pages appear unstyled
- Broken layouts
- Missing theme integration
**Diagnosis:**
```php
// Check if CSS is loading
add_action('wp_head', function() {
global $wp_styles;
echo "<!-- Loaded styles:\n";
foreach($wp_styles->queue as $style) {
echo $style . "\n";
}
echo "-->";
});
```
**Solutions:**
1. Verify `HVAC_Scripts_Styles` is loading
2. Check `is_plugin_page()` detection
3. Ensure template has `HVAC_IN_PAGE_TEMPLATE` constant
### 4. Sidebar Still Showing
**Symptoms:**
- 2-column layout persists
- Sidebar visible on trainer pages
**Solutions:**
1. Check Astra integration is active:
```php
// In theme's functions.php temporarily
add_action('init', function() {
if (class_exists('HVAC_Astra_Integration')) {
error_log('Astra integration loaded');
}
});
```
2. Force no-sidebar via database:
```bash
wp eval '
$pages = get_posts(array(
"post_type" => "page",
"meta_key" => "_wp_page_template",
"meta_value" => "trainer",
"meta_compare" => "LIKE"
));
foreach($pages as $page) {
update_post_meta($page->ID, "site-sidebar-layout", "no-sidebar");
update_post_meta($page->ID, "ast-site-sidebar-layout", "no-sidebar");
}'
```
### 5. Profile Page Using Wrong Template
**Symptoms:**
- Old navigation showing
- Missing fields
- Wrong layout
**Quick Fix:**
```bash
# Find profile page
wp post list --post_type=page --name=profile --fields=ID,post_title
# Update template
wp post meta update [PAGE_ID] _wp_page_template templates/page-trainer-profile.php
```
## Debugging Techniques
### 1. Enable WordPress Debug Mode
**wp-config.php:**
```php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
define('WP_DEBUG_DISPLAY', false);
define('SCRIPT_DEBUG', true);
```
### 2. Plugin-Specific Debug
**Add to plugin file:**
```php
// Debug page detection
add_action('wp', function() {
if (defined('HVAC_DEBUG') && HVAC_DEBUG) {
error_log('Current page: ' . $_SERVER['REQUEST_URI']);
error_log('Is plugin page: ' . (HVAC_Scripts_Styles::is_plugin_page() ? 'yes' : 'no'));
error_log('Template: ' . get_page_template());
}
});
```
### 3. JavaScript Debugging
```javascript
// Add debug logging
if (window.hvac_debug) {
console.log('HVAC Debug:', {
page: window.location.pathname,
user: hvac_ajax.user_id,
role: hvac_ajax.user_role
});
}
```
### 4. Database Query Monitoring
```php
// Log all queries for a page load
define('SAVEQUERIES', true);
add_action('shutdown', function() {
global $wpdb;
error_log('Total queries: ' . count($wpdb->queries));
foreach($wpdb->queries as $query) {
if ($query[1] > 0.05) { // Queries taking > 50ms
error_log('Slow query: ' . $query[0] . ' (' . $query[1] . 's)');
}
}
});
```
## Error Messages
### "There has been a critical error on this website"
**Common Causes:**
1. PHP syntax error
2. Missing required file
3. Fatal error in hook
**Debugging Steps:**
```bash
# Check PHP error log
tail -f wp-content/debug.log
# Check syntax
php -l includes/class-name.php
# Enable recovery mode
wp config set WP_DISABLE_FATAL_ERROR_HANDLER true
```
### "Headers already sent"
**Cause:** Output before WordPress headers
**Fix:**
```php
// Remove any whitespace before <?php
// Check for BOM in files
// Use ob_start() if necessary
```
### AJAX Errors
**Debug AJAX:**
```javascript
jQuery.ajax({
// ... options
error: function(xhr, status, error) {
console.error('AJAX Error:', {
status: status,
error: error,
response: xhr.responseText
});
}
});
```
## Performance Issues
### 1. Slow Page Loads
**Diagnosis:**
```php
// Add to theme's functions.php
add_action('wp_footer', function() {
echo "<!-- Page generated in " . timer_stop(0) . " seconds -->";
echo "<!-- Queries: " . get_num_queries() . " -->";
});
```
**Common Fixes:**
1. Enable object caching
2. Optimize database queries
3. Use transients for expensive operations
### 2. High Database Query Count
**Identify Problem Queries:**
```php
// Find duplicate queries
add_filter('posts_request', function($query) {
error_log('Query: ' . $query);
return $query;
});
```
**Optimization:**
```php
// Cache user queries
$cache_key = 'hvac_trainer_' . $user_id;
$trainer_data = wp_cache_get($cache_key);
if (false === $trainer_data) {
$trainer_data = get_user_meta($user_id);
wp_cache_set($cache_key, $trainer_data, '', 3600);
}
```
### 3. Memory Issues
**Symptoms:**
- "Allowed memory size exhausted"
- White screen of death
**Solutions:**
```php
// Increase memory limit
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
// Find memory usage
error_log('Memory: ' . memory_get_usage() / 1024 / 1024 . 'MB');
error_log('Peak: ' . memory_get_peak_usage() / 1024 / 1024 . 'MB');
```
## Deployment Problems
### 1. Plugin Not Activating
**Check Requirements:**
```bash
# PHP version
php -v
# Required extensions
php -m | grep -E "(mysqli|curl|gd|mbstring)"
# WordPress version
wp core version
```
### 2. Files Not Updating
**Clear All Caches:**
```bash
# WordPress cache
wp cache flush
# Opcache
wp eval 'if (function_exists("opcache_reset")) opcache_reset();'
# Breeze cache
wp breeze purge --all
# CloudFlare (if applicable)
# Use CloudFlare dashboard or API
```
### 3. Database Changes Not Applying
**Force Update:**
```bash
# Deactivate and reactivate
wp plugin deactivate hvac-community-events
wp plugin activate hvac-community-events
# Run activation hook manually
wp eval 'do_action("activate_hvac-community-events/hvac-community-events.php");'
```
## Theme Conflicts
### 1. Astra Theme Issues
**Check Theme Version:**
```bash
wp theme list --fields=name,version,status
```
**Force Compatibility:**
```php
// Add to theme's functions.php
add_action('after_setup_theme', function() {
// Force load Astra integration
if (!class_exists('HVAC_Astra_Integration')) {
require_once WP_PLUGIN_DIR . '/hvac-community-events/includes/class-hvac-astra-integration.php';
HVAC_Astra_Integration::instance();
}
}, 5);
```
### 2. Other Theme Conflicts
**Identify Conflicts:**
```php
// Switch to default theme temporarily
wp theme activate twentytwentyone
// Test plugin functionality
// If it works, theme conflict confirmed
```
**Common Fixes:**
1. Add theme-specific integration class
2. Use more specific CSS selectors
3. Adjust hook priorities
## Database Issues
### 1. Missing Meta Keys
**Audit Meta Keys:**
```bash
# Check post meta
wp db query "SELECT DISTINCT meta_key FROM wp_postmeta WHERE meta_key LIKE '%hvac%'"
# Check user meta
wp db query "SELECT DISTINCT meta_key FROM wp_usermeta WHERE meta_key LIKE '%hvac%'"
```
### 2. Orphaned Data
**Clean Orphaned Event Data:**
```sql
-- Find events without trainers
SELECT ID, post_title
FROM wp_posts p
WHERE post_type = 'tribe_events'
AND NOT EXISTS (
SELECT 1 FROM wp_postmeta pm
WHERE pm.post_id = p.ID
AND pm.meta_key = '_EventTrainerID'
);
```
### 3. Data Migration
**Migrate Legacy Roles:**
```php
// Run via WP-CLI
wp eval '
$users = get_users(array("role" => "event_trainer"));
foreach($users as $user) {
$user->remove_role("event_trainer");
$user->add_role("hvac_trainer");
echo "Migrated user: " . $user->user_login . "\n";
}'
```
## Recovery Procedures
### 1. Emergency Rollback
```bash
# SSH to server
ssh user@server
# Navigate to plugins
cd /path/to/wp-content/plugins
# Backup current version
mv hvac-community-events hvac-community-events-broken
# Restore previous version
cp -r hvac-backups/hvac-community-events-backup-[date] hvac-community-events
# Reactivate
wp plugin activate hvac-community-events
# Clear caches
wp cache flush
```
### 2. Database Recovery
```bash
# Export current state
wp db export backup-$(date +%Y%m%d-%H%M%S).sql
# If needed, restore from backup
wp db import backup-file.sql
# Verify data
wp db query "SELECT COUNT(*) FROM wp_posts WHERE post_type = 'tribe_events'"
```
### 3. Complete Reset
**Nuclear Option - Full Reset:**
```bash
# Backup everything first!
wp db export full-backup.sql
# Deactivate plugin
wp plugin deactivate hvac-community-events
# Remove all plugin data
wp eval '
// Delete all pages
$pages = get_posts(array(
"post_type" => "page",
"meta_key" => "_wp_page_template",
"meta_value" => "hvac",
"meta_compare" => "LIKE",
"posts_per_page" => -1
));
foreach($pages as $page) {
wp_delete_post($page->ID, true);
}
// Delete options
delete_option("hvac_plugin_version");
delete_option("hvac_settings");
'
# Reinstall
wp plugin activate hvac-community-events
```
## Monitoring & Logging
### 1. Set Up Error Monitoring
```php
// Add to plugin
class HVAC_Error_Handler {
public static function log_error($message, $context = array()) {
$log_entry = sprintf(
'[%s] %s | Context: %s',
current_time('mysql'),
$message,
json_encode($context)
);
error_log($log_entry, 3, WP_CONTENT_DIR . '/hvac-errors.log');
// Also send to external service if configured
if (defined('HVAC_ERROR_WEBHOOK')) {
wp_remote_post(HVAC_ERROR_WEBHOOK, array(
'body' => json_encode(array(
'message' => $message,
'context' => $context,
'site' => home_url()
))
));
}
}
}
```
### 2. Performance Monitoring
```php
// Track slow operations
function hvac_track_performance($operation, $callback) {
$start = microtime(true);
$result = $callback();
$duration = microtime(true) - $start;
if ($duration > 1.0) { // Log operations taking > 1 second
HVAC_Error_Handler::log_error(
'Slow operation detected',
array(
'operation' => $operation,
'duration' => $duration,
'memory' => memory_get_peak_usage() / 1024 / 1024
)
);
}
return $result;
}
```
## Quick Reference Commands
```bash
# Check plugin status
wp plugin list --name=hvac-community-events
# View recent errors
tail -f wp-content/debug.log | grep -i hvac
# Clear all caches
wp cache flush && wp rewrite flush && wp breeze purge --all
# Test page routing
wp eval 'print_r(get_page_by_path("trainer/dashboard"));'
# Force recreate pages
wp eval 'HVAC_Page_Manager::create_required_pages();'
# Check user roles
wp user list --role=hvac_trainer,hvac_master_trainer
# Database optimization
wp db optimize
```

View file

@ -31,6 +31,9 @@ class HVAC_Activator {
// Create roles and capabilities
self::setup_roles();
// Setup user meta fields
self::setup_user_meta_fields();
// Create pages
self::create_pages();
@ -114,6 +117,120 @@ class HVAC_Activator {
}
}
/**
* Setup user meta fields
*
* @return void
*/
private static function setup_user_meta_fields() {
global $wpdb;
// Define all custom meta fields used by the plugin with their default values
$meta_fields_defaults = array(
// Core fields
'role' => 'member',
'account_status' => 'pending',
// Certification fields (admin/master trainer editable only)
'date_certified' => '',
'certification_type' => '',
'certification_status' => '',
// Personal information
'user_linkedin' => '',
'personal_accreditation' => '',
'user_phone' => '',
// Location fields
'user_city' => '',
'user_state' => '',
'user_country' => '',
'user_zip' => '',
// Business/Organization information
'business_name' => '',
'business_phone' => '',
'business_email' => '',
'business_website' => '',
'business_description' => '',
'business_type' => '',
// Organization headquarters
'org_headquarters_city' => '',
'org_headquarters_state' => '',
'org_headquarters_country' => '',
// Training capabilities
'training_audience' => array(),
'training_formats' => array(),
'training_locations' => array(),
'training_resources' => array(),
'annual_revenue_target' => '',
// Venue information
'create_venue' => 'No',
'venue_name' => '',
'venue_address' => '',
'venue_phone' => '',
'venue_website' => '',
// Profile and application
'application_details' => '',
'profile_photo_id' => '',
'profile_image_id' => '',
'trainer_certifications' => '',
'years_experience' => '',
// Related post IDs
'hvac_organizer_id' => '',
'hvac_venue_id' => '',
'organizer_id' => ''
);
// Get all users to process
$users = get_users(array('fields' => 'ID'));
foreach ($users as $user_id) {
$user_obj = get_userdata($user_id);
if (!$user_obj) continue;
// Process each meta field
foreach ($meta_fields_defaults as $meta_key => $default_value) {
$existing_value = get_user_meta($user_id, $meta_key, true);
// Only set default if the field doesn't exist or is empty
if ($existing_value === '' || $existing_value === false) {
// Special handling for role field based on user capabilities
if ($meta_key === 'role') {
if ($user_obj->has_cap('hvac_trainer') || $user_obj->has_cap('hvac_master_trainer')) {
$default_value = 'trainer';
} elseif ($user_obj->has_cap('administrator')) {
$default_value = 'administrator';
} else {
$default_value = 'member';
}
}
// Special handling for account_status based on user role
if ($meta_key === 'account_status') {
if ($user_obj->has_cap('hvac_trainer') || $user_obj->has_cap('hvac_master_trainer')) {
$default_value = 'approved';
} elseif ($user_obj->has_cap('administrator')) {
$default_value = 'approved';
} else {
$default_value = 'pending';
}
}
update_user_meta($user_id, $meta_key, $default_value);
}
}
}
HVAC_Logger::info('User meta fields setup completed for ' . count($users) . ' users', 'Activator');
}
/**
* Create plugin pages
*
@ -124,6 +241,12 @@ class HVAC_Activator {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-page-manager.php';
HVAC_Page_Manager::create_pages();
// Update existing pages with proper layout settings
HVAC_Page_Manager::update_existing_page_layouts();
// Set flag to force page update on next load
update_option('hvac_pages_need_update', true);
}
/**

View file

@ -0,0 +1,345 @@
<?php
/**
* HVAC Astra Theme Integration
*
* Properly integrates HVAC plugin with Astra theme using theme-specific hooks and filters
*
* @package HVAC_Community_Events
* @since 1.0.0
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC Astra Integration Class
*/
class HVAC_Astra_Integration {
/**
* Instance
*
* @var HVAC_Astra_Integration
*/
private static $instance = null;
/**
* Get instance
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
// Only run if Astra theme is active
if (!defined('ASTRA_THEME_VERSION')) {
return;
}
$this->init_hooks();
}
/**
* Initialize hooks
*/
private function init_hooks() {
// Early init to catch template redirects
add_action('template_redirect', [$this, 'ensure_correct_template'], 1);
// Layout filters - use higher priority to ensure they run after Astra's defaults
add_filter('astra_page_layout', [$this, 'force_hvac_page_layout'], 999);
add_filter('astra_get_content_layout', [$this, 'force_hvac_content_layout'], 999);
add_filter('astra_site_layout', [$this, 'force_hvac_site_layout'], 999);
// Container filters
add_filter('astra_container_class', [$this, 'modify_container_class'], 999, 2);
add_filter('astra_get_container_class', [$this, 'get_hvac_container_class'], 999);
// Body classes
add_filter('body_class', [$this, 'add_hvac_body_classes'], 999);
// Content width
add_action('wp', [$this, 'setup_hvac_content_width'], 999);
// Dynamic CSS
add_filter('astra_dynamic_theme_css', [$this, 'add_hvac_dynamic_css'], 999);
// Disable sidebar for HVAC pages
add_action('wp', [$this, 'disable_sidebar_for_hvac_pages'], 1);
// Force template usage
add_filter('template_include', [$this, 'force_hvac_template'], 999);
}
/**
* Force page layout for HVAC pages
*/
public function force_hvac_page_layout($layout) {
if ($this->is_hvac_page()) {
return 'no-sidebar';
}
return $layout;
}
/**
* Force content layout for HVAC pages
*/
public function force_hvac_content_layout($layout) {
if ($this->is_hvac_page()) {
return 'plain-container';
}
return $layout;
}
/**
* Force site layout for HVAC pages
*/
public function force_hvac_site_layout($layout) {
if ($this->is_hvac_page()) {
return 'ast-full-width-layout';
}
return $layout;
}
/**
* Modify container class for HVAC pages
*/
public function modify_container_class($classes, $layout) {
if ($this->is_hvac_page()) {
// Remove any constrained container classes
$classes = str_replace('ast-container', 'ast-full-width-container', $classes);
}
return $classes;
}
/**
* Get HVAC-specific container class
*/
public function get_hvac_container_class($class) {
if ($this->is_hvac_page()) {
return 'ast-full-width-container';
}
return $class;
}
/**
* Add HVAC-specific body classes
*/
public function add_hvac_body_classes($classes) {
if ($this->is_hvac_page()) {
// Add Astra-specific classes for full-width layout
$classes[] = 'ast-no-sidebar';
$classes[] = 'ast-separate-container';
$classes[] = 'ast-full-width-layout';
$classes[] = 'ast-plain-container';
$classes[] = 'hvac-astra-integrated';
// Remove conflicting classes
$remove_classes = ['ast-right-sidebar', 'ast-left-sidebar', 'ast-page-builder-template'];
$classes = array_diff($classes, $remove_classes);
}
return $classes;
}
/**
* Setup content width for HVAC pages
*/
public function setup_hvac_content_width() {
if ($this->is_hvac_page()) {
// Set global content width
global $content_width;
$content_width = 1920; // Full HD width
// Update Astra's content width
add_filter('astra_get_content_width', function() {
return 1920;
}, 999);
}
}
/**
* Add dynamic CSS for HVAC pages
*/
public function add_hvac_dynamic_css($css) {
if ($this->is_hvac_page()) {
$hvac_css = '
/* HVAC Full-width overrides for Astra */
.hvac-astra-integrated .ast-container {
max-width: 100% !important;
width: 100% !important;
padding-left: 40px !important;
padding-right: 40px !important;
}
.hvac-astra-integrated .site-content .ast-container {
max-width: 100% !important;
}
.hvac-astra-integrated .hvac-page-wrapper {
max-width: 1920px;
margin: 0 auto;
}
/* Remove sidebar completely */
.hvac-astra-integrated .widget-area,
.hvac-astra-integrated .ast-sidebar,
.hvac-astra-integrated #secondary,
.hvac-astra-integrated aside.widget-area,
.hvac-astra-integrated .sidebar-main {
display: none !important;
width: 0 !important;
height: 0 !important;
visibility: hidden !important;
position: absolute !important;
left: -9999px !important;
}
/* Full-width content area */
.hvac-astra-integrated #primary,
.hvac-astra-integrated .site-main,
.hvac-astra-integrated .content-area {
width: 100% !important;
max-width: 100% !important;
margin: 0 !important;
float: none !important;
display: block !important;
}
/* Force single column layout */
.hvac-astra-integrated .ast-container > .ast-row {
display: block !important;
}
.hvac-astra-integrated .ast-col-md-8,
.hvac-astra-integrated .ast-col-lg-8 {
width: 100% !important;
max-width: 100% !important;
}
/* Ensure content takes full width */
.hvac-astra-integrated .entry-content {
width: 100% !important;
max-width: 100% !important;
}
';
$css .= $hvac_css;
}
return $css;
}
/**
* Disable sidebar for HVAC pages
*/
public function disable_sidebar_for_hvac_pages() {
if ($this->is_hvac_page()) {
// Remove ALL sidebar actions from Astra
remove_action('astra_sidebars', 'astra_get_sidebar');
remove_action('astra_sidebar', 'astra_get_sidebar');
remove_action('astra_primary_content_bottom', 'astra_primary_content_bottom');
// Force sidebar removal via filters
add_filter('astra_display_sidebar', '__return_false', 999);
add_filter('is_active_sidebar', [$this, 'disable_sidebar'], 999, 2);
// Update post meta to ensure no sidebar
global $post;
if ($post) {
update_post_meta($post->ID, 'site-sidebar-layout', 'no-sidebar');
update_post_meta($post->ID, 'site-content-layout', 'plain-container');
update_post_meta($post->ID, 'ast-site-sidebar-layout', 'no-sidebar');
update_post_meta($post->ID, 'ast-featured-img', 'disabled');
update_post_meta($post->ID, 'ast-breadcrumbs-content', 'disabled');
}
}
}
/**
* Disable sidebar check for HVAC pages
*/
public function disable_sidebar($is_active, $index) {
if ($this->is_hvac_page()) {
return false;
}
return $is_active;
}
/**
* Check if current page is an HVAC page
*/
private function is_hvac_page() {
// Check by template
if (is_page_template()) {
$template = get_page_template_slug();
if (strpos($template, 'page-trainer') !== false ||
strpos($template, 'page-master') !== false ||
strpos($template, 'page-certificate') !== false ||
strpos($template, 'page-generate') !== false) {
return true;
}
}
// Check by URL
$current_url = $_SERVER['REQUEST_URI'];
$hvac_paths = ['trainer/', 'master-trainer/', 'certificate', 'generate-certificates'];
foreach ($hvac_paths as $path) {
if (strpos($current_url, $path) !== false) {
return true;
}
}
// Check by page slug
if (is_page()) {
global $post;
if ($post) {
$slug = $post->post_name;
$hvac_slugs = ['trainer', 'dashboard', 'profile', 'certificate', 'venue', 'organizer'];
foreach ($hvac_slugs as $hvac_slug) {
if (strpos($slug, $hvac_slug) !== false) {
return true;
}
}
}
}
return false;
}
/**
* Ensure correct template is loaded
*/
public function ensure_correct_template() {
if ($this->is_hvac_page() && is_page()) {
global $post;
if ($post && $post->post_name === 'profile') {
// Force the profile template
$template = get_post_meta($post->ID, '_wp_page_template', true);
if (empty($template) || $template === 'default') {
update_post_meta($post->ID, '_wp_page_template', 'templates/page-trainer-profile.php');
}
}
}
}
/**
* Force HVAC template loading
*/
public function force_hvac_template($template) {
// Temporarily disabled to avoid errors
return $template;
}
}
// Initialize
HVAC_Astra_Integration::instance();

View file

@ -2,6 +2,9 @@
/**
* HVAC Breadcrumbs System
*
* Generates breadcrumbs based on URL structure with Schema.org markup for SEO
* Supports hierarchical trainer navigation patterns
*
* @package HVAC_Community_Events
* @since 2.0.0
*/
@ -10,22 +13,31 @@ if (!defined('ABSPATH')) {
exit;
}
/**
* HVAC_Breadcrumbs class
*/
class HVAC_Breadcrumbs {
/**
* Plugin instance
*
* @var HVAC_Breadcrumbs
*/
private static $instance = null;
/**
* Get plugin instance
*
* @return HVAC_Breadcrumbs
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
public function __construct() {
// Register shortcode
add_shortcode('hvac_breadcrumbs', array($this, 'render_breadcrumbs'));
// Auto-add breadcrumbs to trainer pages
add_action('hvac_after_page_header', array($this, 'display_breadcrumbs'));
// Enqueue styles
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
}
@ -33,7 +45,7 @@ class HVAC_Breadcrumbs {
* Enqueue breadcrumb styles
*/
public function enqueue_styles() {
if ($this->should_show_breadcrumbs()) {
if ($this->is_trainer_page()) {
wp_enqueue_style(
'hvac-breadcrumbs',
HVAC_PLUGIN_URL . 'assets/css/hvac-breadcrumbs.css',
@ -44,224 +56,359 @@ class HVAC_Breadcrumbs {
}
/**
* Get breadcrumb trail
* Check if current page is a trainer page
*/
private function get_breadcrumb_trail() {
$trail = array();
$current_url = home_url(add_query_arg(array(), $GLOBALS['wp']->request));
private function is_trainer_page() {
global $wp;
$current_url = home_url(add_query_arg(array(), $wp->request));
return (strpos($current_url, '/trainer/') !== false || strpos($current_url, '/master-trainer/') !== false);
}
// Always start with home
$trail[] = array(
'label' => 'Home',
'url' => home_url('/')
/**
* Render breadcrumbs for trainer pages
*
* @param array $options Configuration options
* @return string HTML breadcrumb output
*/
public function render_breadcrumbs($options = array()) {
if (!$this->is_trainer_page()) {
return '';
}
$defaults = array(
'home_text' => 'Home',
'separator' => '>',
'show_home' => true,
'schema_markup' => true,
'css_class' => 'hvac-breadcrumbs'
);
// Check if we're on a trainer page
if (strpos($current_url, '/trainer/') !== false) {
$trail[] = array(
'label' => 'Trainer',
$options = wp_parse_args($options, $defaults);
$breadcrumbs = $this->generate_breadcrumb_trail();
if (empty($breadcrumbs)) {
return '';
}
return $this->render_breadcrumb_html($breadcrumbs, $options);
}
/**
* Generate breadcrumb trail based on current URL
*
* @return array Array of breadcrumb items
*/
private function generate_breadcrumb_trail() {
global $wp;
$request = $wp->request;
// Remove trailing slash and split by forward slash
$path_parts = array_filter(explode('/', trim($request, '/')));
if (empty($path_parts)) {
return array();
}
$breadcrumbs = array();
$current_path = '';
// Define breadcrumb mapping for trainer paths
$breadcrumb_map = array(
'trainer' => array(
'title' => 'Trainer',
'url' => '/trainer/dashboard/'
);
// Parse the current path
$path = parse_url($current_url, PHP_URL_PATH);
$path = trim($path, '/');
$segments = explode('/', $path);
// Remove 'trainer' from segments as we've already added it
array_shift($segments);
// Build breadcrumb based on URL segments
if (!empty($segments)) {
$breadcrumb_map = $this->get_breadcrumb_map();
$current_path = '/trainer';
foreach ($segments as $index => $segment) {
$current_path .= '/' . $segment;
// Check if we have a mapping for this path
if (isset($breadcrumb_map[$current_path])) {
$is_last = ($index === count($segments) - 1);
$trail[] = array(
'label' => $breadcrumb_map[$current_path]['label'],
'url' => $is_last ? null : $current_path . '/',
'current' => $is_last
);
}
}
}
} elseif (strpos($current_url, '/master-trainer/') !== false) {
$trail[] = array(
'label' => 'Master Trainer',
),
'master-trainer' => array(
'title' => 'Master Trainer',
'url' => '/master-trainer/master-dashboard/'
),
'dashboard' => array(
'title' => 'Dashboard',
'parent' => 'Events'
),
'master-dashboard' => array(
'title' => 'Dashboard',
'parent' => 'Master Trainer'
),
'event' => array(
'title' => 'Events',
'url' => '/trainer/dashboard/'
),
'manage' => array(
'title' => 'New Event',
'parent' => 'Events'
),
'certificate-reports' => array(
'title' => 'Reports',
'parent' => 'Certificates'
),
'generate-certificates' => array(
'title' => 'New Certificate',
'parent' => 'Certificates'
),
'certificates' => array(
'title' => 'Certificates',
'url' => '/trainer/certificate-reports/'
),
'profile' => array(
'title' => 'Personal Profile',
'parent' => 'Customize'
),
'organizer' => array(
'title' => 'Training Organizers',
'parent' => 'Customize'
),
'venue' => array(
'title' => 'Training Venues',
'parent' => 'Customize'
),
'list' => array(
'title' => 'List',
'context_dependent' => true
),
'edit' => array(
'title' => 'Edit',
'context_dependent' => true
),
'customize' => array(
'title' => 'Customize',
'url' => '/trainer/profile/'
)
);
// Similar logic for master trainer pages
$path = parse_url($current_url, PHP_URL_PATH);
$path = trim($path, '/');
$segments = explode('/', $path);
array_shift($segments); // Remove 'master-trainer'
// Build breadcrumb trail
for ($i = 0; $i < count($path_parts); $i++) {
$part = $path_parts[$i];
$current_path .= '/' . $part;
if (!empty($segments)) {
$breadcrumb_map = $this->get_breadcrumb_map();
$current_path = '/master-trainer';
if (isset($breadcrumb_map[$part])) {
$crumb = $breadcrumb_map[$part];
foreach ($segments as $index => $segment) {
$current_path .= '/' . $segment;
// Handle context-dependent titles
if (isset($crumb['context_dependent']) && $crumb['context_dependent']) {
$crumb['title'] = $this->get_contextual_title($part, $path_parts, $i);
}
if (isset($breadcrumb_map[$current_path])) {
$is_last = ($index === count($segments) - 1);
// Add parent breadcrumb if specified
if (isset($crumb['parent']) && !$this->breadcrumb_exists($breadcrumbs, $crumb['parent'])) {
$parent_crumb = $this->get_parent_breadcrumb($crumb['parent']);
if ($parent_crumb) {
$breadcrumbs[] = $parent_crumb;
}
}
$trail[] = array(
'label' => $breadcrumb_map[$current_path]['label'],
'url' => $is_last ? null : $current_path . '/',
'current' => $is_last
// Determine URL
$url = isset($crumb['url']) ? $crumb['url'] : home_url($current_path . '/');
// Don't add URL for the current page (last item)
if ($i === count($path_parts) - 1) {
$url = null;
}
$breadcrumbs[] = array(
'title' => $crumb['title'],
'url' => $url,
'position' => count($breadcrumbs) + 1
);
}
}
}
} elseif (strpos($current_url, '/trainer-registration/') !== false) {
$trail[] = array(
'label' => 'Registration',
'url' => null,
'current' => true
);
}
// Allow filtering of breadcrumb trail
return apply_filters('hvac_breadcrumb_trail', $trail, $current_url);
return $breadcrumbs;
}
/**
* Get breadcrumb mapping
* Get contextual title for context-dependent breadcrumbs
*
* @param string $part Current path part
* @param array $path_parts All path parts
* @param int $index Current index
* @return string Contextual title
*/
private function get_breadcrumb_map() {
private function get_contextual_title($part, $path_parts, $index) {
if ($part === 'list') {
// Look at previous part for context
if ($index > 0) {
$previous = $path_parts[$index - 1];
if ($previous === 'organizer') {
return 'Organizer List';
} elseif ($previous === 'venue') {
return 'Venue List';
}
}
return 'List';
}
if ($part === 'edit') {
return 'Edit';
}
if ($part === 'manage') {
// Look at previous part for context
if ($index > 0) {
$previous = $path_parts[$index - 1];
if ($previous === 'organizer') {
return 'Manage Organizer';
} elseif ($previous === 'venue') {
return 'Manage Venue';
} elseif ($previous === 'event') {
return 'New Event';
}
}
return 'Manage';
}
return ucfirst($part);
}
/**
* Check if breadcrumb with given title already exists
*
* @param array $breadcrumbs Existing breadcrumbs
* @param string $title Title to check
* @return bool
*/
private function breadcrumb_exists($breadcrumbs, $title) {
foreach ($breadcrumbs as $crumb) {
if ($crumb['title'] === $title) {
return true;
}
}
return false;
}
/**
* Get parent breadcrumb definition
*
* @param string $parent_title Parent title
* @return array|null Parent breadcrumb data
*/
private function get_parent_breadcrumb($parent_title) {
$parent_map = array(
'Events' => array(
'title' => 'Events',
'url' => '/trainer/dashboard/'
),
'Certificates' => array(
'title' => 'Certificates',
'url' => '/trainer/certificate-reports/'
),
'Customize' => array(
'title' => 'Customize',
'url' => '/trainer/profile/'
)
);
if (isset($parent_map[$parent_title])) {
return array(
// Trainer pages
'/trainer/dashboard' => array('label' => 'Dashboard'),
'/trainer/event' => array('label' => 'Events'),
'/trainer/event/manage' => array('label' => 'Manage Event'),
'/trainer/event-summary' => array('label' => 'Event Summary'),
'/trainer/generate-certificates' => array('label' => 'Generate Certificates'),
'/trainer/certificate-reports' => array('label' => 'Certificate Reports'),
'/trainer/venue' => array('label' => 'Venues'),
'/trainer/venue/list' => array('label' => 'List'),
'/trainer/venue/manage' => array('label' => 'Manage'),
'/trainer/organizer' => array('label' => 'Organizers'),
'/trainer/organizer/list' => array('label' => 'List'),
'/trainer/organizer/manage' => array('label' => 'Manage'),
'/trainer/profile' => array('label' => 'Profile'),
'/trainer/profile/edit' => array('label' => 'Edit'),
// Master trainer pages
'/master-trainer/master-dashboard' => array('label' => 'Master Dashboard'),
'/master-trainer/trainers' => array('label' => 'All Trainers'),
'/master-trainer/reports' => array('label' => 'Global Reports')
'title' => $parent_map[$parent_title]['title'],
'url' => home_url($parent_map[$parent_title]['url']),
'position' => 1
);
}
return null;
}
/**
* Render breadcrumbs shortcode
* Render breadcrumb HTML with Schema.org markup
*
* @param array $breadcrumbs Breadcrumb items
* @param array $options Rendering options
* @return string HTML output
*/
public function render_breadcrumbs($atts = array()) {
$atts = shortcode_atts(array(
'separator' => '',
'show_home' => 'yes',
'home_label' => 'Home',
'class' => 'hvac-breadcrumb'
), $atts);
$trail = $this->get_breadcrumb_trail();
if (empty($trail)) {
private function render_breadcrumb_html($breadcrumbs, $options) {
if (empty($breadcrumbs)) {
return '';
}
// Remove home if requested
if ($atts['show_home'] === 'no' && !empty($trail)) {
array_shift($trail);
} else if (!empty($trail) && $atts['home_label'] !== 'Home') {
$trail[0]['label'] = $atts['home_label'];
$html = '<nav class="' . esc_attr($options['css_class']) . '" aria-label="Breadcrumb">';
// Add Schema.org JSON-LD if enabled
if ($options['schema_markup']) {
$html .= $this->generate_schema_markup($breadcrumbs);
}
ob_start();
?>
<nav class="<?php echo esc_attr($atts['class']); ?>" aria-label="Breadcrumb">
<ol class="hvac-breadcrumb-list">
<?php foreach ($trail as $index => $item): ?>
<li class="hvac-breadcrumb-item<?php echo isset($item['current']) && $item['current'] ? ' hvac-breadcrumb-current' : ''; ?>">
<?php if (!empty($item['url']) && (!isset($item['current']) || !$item['current'])): ?>
<a href="<?php echo esc_url($item['url']); ?>" class="hvac-breadcrumb-link">
<?php echo esc_html($item['label']); ?>
</a>
<?php else: ?>
<span class="hvac-breadcrumb-text" aria-current="page">
<?php echo esc_html($item['label']); ?>
</span>
<?php endif; ?>
$html .= '<ol class="hvac-breadcrumb-list">';
<?php if ($index < count($trail) - 1): ?>
<span class="hvac-breadcrumb-separator" aria-hidden="true">
<?php echo esc_html($atts['separator']); ?>
</span>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ol>
</nav>
<?php
return ob_get_clean();
// Add home breadcrumb if enabled
if ($options['show_home']) {
$html .= '<li class="hvac-breadcrumb-item hvac-breadcrumb-home">';
$html .= '<a href="' . esc_url(home_url('/')) . '">' . esc_html($options['home_text']) . '</a>';
$html .= '<span class="hvac-breadcrumb-separator">' . esc_html($options['separator']) . '</span>';
$html .= '</li>';
}
$total_crumbs = count($breadcrumbs);
foreach ($breadcrumbs as $index => $crumb) {
$is_last = ($index === $total_crumbs - 1);
$item_class = 'hvac-breadcrumb-item';
if ($is_last) {
$item_class .= ' hvac-breadcrumb-current';
}
$html .= '<li class="' . esc_attr($item_class) . '">';
if (!$is_last && !empty($crumb['url'])) {
$html .= '<a href="' . esc_url($crumb['url']) . '">' . esc_html($crumb['title']) . '</a>';
} else {
$html .= '<span class="hvac-breadcrumb-current-text">' . esc_html($crumb['title']) . '</span>';
}
if (!$is_last) {
$html .= '<span class="hvac-breadcrumb-separator">' . esc_html($options['separator']) . '</span>';
}
$html .= '</li>';
}
$html .= '</ol>';
$html .= '</nav>';
return $html;
}
/**
* Display breadcrumbs automatically
* Generate Schema.org JSON-LD markup for breadcrumbs
*
* @param array $breadcrumbs Breadcrumb items
* @return string JSON-LD script tag
*/
public function display_breadcrumbs() {
if ($this->should_show_breadcrumbs()) {
echo do_shortcode('[hvac_breadcrumbs]');
}
}
private function generate_schema_markup($breadcrumbs) {
$schema_items = array();
$position = 1;
/**
* Check if breadcrumbs should be shown
*/
private function should_show_breadcrumbs() {
// Show on all trainer and master trainer pages
$current_url = home_url(add_query_arg(array(), $GLOBALS['wp']->request));
return strpos($current_url, '/trainer/') !== false ||
strpos($current_url, '/master-trainer/') !== false ||
strpos($current_url, '/trainer-registration/') !== false;
}
/**
* Get structured data for breadcrumbs (SEO)
*/
public function get_structured_data() {
$trail = $this->get_breadcrumb_trail();
if (empty($trail)) {
return '';
}
$items = array();
foreach ($trail as $index => $item) {
$items[] = array(
// Add home item
$schema_items[] = array(
'@type' => 'ListItem',
'position' => $index + 1,
'name' => $item['label'],
'item' => !empty($item['url']) ? home_url($item['url']) : null
'position' => $position++,
'name' => 'Home',
'item' => home_url('/')
);
// Add breadcrumb items
foreach ($breadcrumbs as $crumb) {
$item = array(
'@type' => 'ListItem',
'position' => $position++,
'name' => $crumb['title']
);
if (!empty($crumb['url'])) {
$item['item'] = $crumb['url'];
}
$structured_data = array(
$schema_items[] = $item;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $items
'itemListElement' => $schema_items
);
return '<script type="application/ld+json">' . json_encode($structured_data, JSON_UNESCAPED_SLASHES) . '</script>';
return '<script type="application/ld+json">' . wp_json_encode($schema, JSON_UNESCAPED_SLASHES) . '</script>';
}
}

View file

@ -89,7 +89,12 @@ class HVAC_Community_Events {
'communication/class-communication-schedule-manager.php', // Communication schedule manager
'communication/class-communication-trigger-engine.php', // Communication trigger engine
'communication/class-communication-logger.php', // Communication logger
'communication/class-communication-scheduler.php' // Communication scheduler
'communication/class-communication-scheduler.php', // Communication scheduler
'class-hvac-organizers.php', // Training organizers management
'class-hvac-venues.php', // Training venues management
'class-hvac-trainer-profile-manager.php', // Trainer profile management
'class-hvac-scripts-styles.php', // Scripts and styles management
'class-hvac-shortcodes.php' // Shortcodes management
];
// Make sure Login_Handler is loaded first for shortcode registration
$login_handler_path = HVAC_PLUGIN_DIR . 'includes/community/class-login-handler.php';
@ -375,6 +380,31 @@ class HVAC_Community_Events {
HVAC_Help_System::instance();
}
// Initialize organizers management
if (class_exists('HVAC_Organizers')) {
new HVAC_Organizers();
}
// Initialize venues management
if (class_exists('HVAC_Venues')) {
new HVAC_Venues();
}
// Initialize trainer profile manager
if (class_exists('HVAC_Trainer_Profile_Manager')) {
new HVAC_Trainer_Profile_Manager();
}
// Initialize scripts and styles management
if (class_exists('HVAC_Scripts_Styles')) {
HVAC_Scripts_Styles::instance();
}
// Initialize shortcodes management
if (class_exists('HVAC_Shortcodes')) {
HVAC_Shortcodes::instance();
}
// Initialize communication system
$this->init_communication_system();
@ -571,7 +601,13 @@ class HVAC_Community_Events {
return '<p>Please log in to view your profile.</p>';
}
// Include the trainer profile template
// Use the new HVAC_Trainer_Profile_Manager system if available
if (class_exists('HVAC_Trainer_Profile_Manager')) {
$profile_manager = new HVAC_Trainer_Profile_Manager();
return $profile_manager->render_profile_view();
}
// Fallback to old template (for backward compatibility)
ob_start();
include HVAC_PLUGIN_DIR . 'templates/template-trainer-profile.php';
return ob_get_clean();

View file

@ -15,12 +15,15 @@ if (!defined('ABSPATH')) {
class HVAC_Event_Manage_Header {
/**
* Constructor
* Constructor - DISABLED: Replaced with HVAC_Menu_System
*/
public function __construct() {
// Only use the tribe-specific action to avoid duplication
// Check if we should render the header based on the context
add_action('init', array($this, 'maybe_add_header_hook'));
// Old event manage header disabled to prevent conflicts with new WordPress menu system
// All functionality moved to HVAC_Menu_System class
// // Only use the tribe-specific action to avoid duplication
// // Check if we should render the header based on the context
// add_action('init', array($this, 'maybe_add_header_hook'));
}
/**

View file

@ -15,11 +15,14 @@ if (!defined('ABSPATH')) {
class HVAC_Event_Navigation {
/**
* Constructor
* Constructor - DISABLED: Replaced with HVAC_Menu_System
*/
public function __construct() {
// Register the shortcode
add_shortcode('hvac_event_navigation', array($this, 'render_navigation'));
// Old event navigation disabled to prevent conflicts with new WordPress menu system
// All functionality moved to HVAC_Menu_System class
// // Register the shortcode
// add_shortcode('hvac_event_navigation', array($this, 'render_navigation'));
}
/**

View file

@ -0,0 +1,369 @@
<?php
/**
* HVAC Menu System
*
* Implements proper WordPress menu API for HVAC trainer pages
* Following WordPress best practices for menu registration and display
*
* @package HVAC_Community_Events
* @since 2.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
class HVAC_Menu_System {
/**
* Plugin instance
*
* @var HVAC_Menu_System
*/
private static $instance = null;
/**
* Get plugin instance
*
* @return HVAC_Menu_System
*/
public static function instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
public function __construct() {
add_action('init', array($this, 'register_menu_locations'));
add_action('wp', array($this, 'setup_trainer_menu'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_menu_styles'));
add_filter('wp_nav_menu_items', array($this, 'add_logout_to_menu'), 10, 2);
}
/**
* Register menu locations
*/
public function register_menu_locations() {
register_nav_menus(array(
'hvac_trainer_menu' => __('HVAC Trainer Navigation', 'hvac-community-events'),
));
}
/**
* Setup trainer menu on appropriate pages
*/
public function setup_trainer_menu() {
// Menu will be rendered directly in templates, not injected via JavaScript
}
/**
* Check if current page is a trainer page
*/
private function is_trainer_page() {
global $wp;
$current_url = home_url(add_query_arg(array(), $wp->request));
return (strpos($current_url, '/trainer/') !== false || strpos($current_url, '/master-trainer/') !== false);
}
/**
* Check if user can access trainer area
*/
private function user_can_access_trainer_area() {
if (!is_user_logged_in()) {
return false;
}
return current_user_can('hvac_trainer') ||
current_user_can('hvac_master_trainer') ||
current_user_can('manage_options');
}
/**
* Render the trainer menu
*/
public function render_trainer_menu() {
if (!$this->user_can_access_trainer_area()) {
return;
}
// Check if we're already showing a navigation menu
if (defined('HVAC_NAV_RENDERED') && HVAC_NAV_RENDERED) {
return;
}
// Mark that navigation has been rendered
define('HVAC_NAV_RENDERED', true);
$menu_items = $this->get_menu_structure();
echo '<div class="hvac-trainer-menu-wrapper">';
echo '<nav class="hvac-trainer-nav" role="navigation">';
echo '<ul class="hvac-trainer-menu">';
foreach ($menu_items as $item) {
$this->render_menu_item($item);
}
echo '</ul>';
echo '</nav>';
echo '</div>';
}
/**
* Get menu structure based on user capabilities
*/
private function get_menu_structure() {
$user = wp_get_current_user();
$is_master = in_array('hvac_master_trainer', $user->roles) || current_user_can('manage_options');
// If user has master trainer role, ALWAYS show master menu
// This prevents duplicate navigation for dual-role users
if ($is_master) {
return $this->get_master_menu_structure();
}
$menu = array(
array(
'title' => 'Events',
'url' => '#',
'icon' => 'dashicons-calendar-alt',
'children' => array(
array(
'title' => 'Dashboard',
'url' => home_url('/trainer/dashboard/'),
'icon' => 'dashicons-dashboard'
),
array(
'title' => 'New Event',
'url' => home_url('/trainer/event/manage/'),
'icon' => 'dashicons-plus-alt'
)
)
),
array(
'title' => 'Certificates',
'url' => '#',
'icon' => 'dashicons-awards',
'children' => array(
array(
'title' => 'Reports',
'url' => home_url('/trainer/certificate-reports/'),
'icon' => 'dashicons-analytics'
),
array(
'title' => 'New Certificate',
'url' => home_url('/trainer/generate-certificates/'),
'icon' => 'dashicons-plus-alt'
)
)
),
array(
'title' => 'Customize',
'url' => '#',
'icon' => 'dashicons-admin-customizer',
'children' => array(
array(
'title' => 'Personal Profile',
'url' => home_url('/trainer/profile/'),
'icon' => 'dashicons-admin-users'
),
array(
'title' => 'Training Organizers',
'url' => home_url('/trainer/organizer/list/'),
'icon' => 'dashicons-groups',
'children' => array(
array(
'title' => 'Add New Organizer',
'url' => home_url('/trainer/organizer/manage/'),
'icon' => 'dashicons-plus-alt'
)
)
),
array(
'title' => 'Training Venues',
'url' => home_url('/trainer/venue/list/'),
'icon' => 'dashicons-location-alt',
'children' => array(
array(
'title' => 'Add New Venue',
'url' => home_url('/trainer/venue/manage/'),
'icon' => 'dashicons-plus-alt'
)
)
)
)
),
array(
'title' => 'Help',
'url' => home_url('/trainer/documentation/'),
'icon' => 'dashicons-sos'
)
);
// Add Master Dashboard for qualified users
if ($is_master) {
array_splice($menu, 0, 0, array(
array(
'title' => 'Master Dashboard',
'url' => home_url('/master-trainer/master-dashboard/'),
'icon' => 'dashicons-star-filled'
)
));
}
// Add logout as last item
$menu[] = array(
'title' => 'Logout',
'url' => wp_logout_url(home_url('/training-login/')),
'icon' => 'dashicons-exit'
);
return $menu;
}
/**
* Get master trainer menu structure
*/
private function get_master_menu_structure() {
$menu = array(
array(
'title' => 'Master Dashboard',
'url' => home_url('/master-trainer/master-dashboard/'),
'icon' => 'dashicons-dashboard'
),
array(
'title' => 'Trainer Dashboard',
'url' => home_url('/trainer/dashboard/'),
'icon' => 'dashicons-admin-home'
),
array(
'title' => 'Events',
'url' => '#',
'icon' => 'dashicons-calendar-alt',
'children' => array(
array(
'title' => 'All Events',
'url' => home_url('/master-trainer/events/'),
'icon' => 'dashicons-list-view'
),
array(
'title' => 'Create Event',
'url' => home_url('/trainer/create-event/'),
'icon' => 'dashicons-plus-alt'
),
array(
'title' => 'My Events',
'url' => home_url('/trainer/dashboard/'),
'icon' => 'dashicons-calendar'
)
)
),
array(
'title' => 'Certificates',
'url' => '#',
'icon' => 'dashicons-awards',
'children' => array(
array(
'title' => 'Certificate Reports',
'url' => home_url('/trainer/certificate-reports/'),
'icon' => 'dashicons-analytics'
),
array(
'title' => 'Generate Certificates',
'url' => home_url('/trainer/generate-certificates/'),
'icon' => 'dashicons-media-document'
)
)
),
array(
'title' => 'Profile',
'url' => home_url('/trainer/profile/'),
'icon' => 'dashicons-admin-users'
),
array(
'title' => 'Logout',
'url' => wp_logout_url(home_url('/training-login/')),
'icon' => 'dashicons-exit'
)
);
return $menu;
}
/**
* Render individual menu item
*/
private function render_menu_item($item, $level = 0) {
$has_children = !empty($item['children']);
$icon = !empty($item['icon']) ? '<span class="dashicons ' . esc_attr($item['icon']) . '"></span>' : '';
$classes = array('menu-item');
if ($has_children) {
$classes[] = 'has-children';
}
if ($level > 0) {
$classes[] = 'sub-menu-item';
$classes[] = 'level-' . $level;
}
echo '<li class="' . implode(' ', $classes) . '">';
if ($item['url'] === '#' && $has_children) {
echo '<span class="menu-toggle">' . $icon . esc_html($item['title']) . '<span class="dropdown-arrow">▼</span></span>';
} else {
echo '<a href="' . esc_url($item['url']) . '">' . $icon . esc_html($item['title']) . '</a>';
}
if ($has_children) {
echo '<ul class="sub-menu">';
foreach ($item['children'] as $child) {
$this->render_menu_item($child, $level + 1);
}
echo '</ul>';
}
echo '</li>';
}
/**
* Enqueue menu styles and scripts
*/
public function enqueue_menu_styles() {
if ($this->is_trainer_page() && $this->user_can_access_trainer_area()) {
wp_enqueue_style(
'hvac-menu-system',
HVAC_PLUGIN_URL . 'assets/css/hvac-menu-system.css',
array(),
HVAC_PLUGIN_VERSION
);
wp_enqueue_script(
'hvac-menu-system',
HVAC_PLUGIN_URL . 'assets/js/hvac-menu-system.js',
array('jquery'),
HVAC_PLUGIN_VERSION,
true
);
}
}
/**
* Add logout link to WordPress menu (if using wp_nav_menu)
*/
public function add_logout_to_menu($items, $args) {
if ($args->theme_location === 'hvac_trainer_menu' && $this->user_can_access_trainer_area()) {
$logout_link = '<li class="menu-item menu-item-logout"><a href="' . wp_logout_url(home_url('/training-login/')) . '"><span class="dashicons dashicons-exit"></span>Logout</a></li>';
$items .= $logout_link;
}
return $items;
}
}
// Initialize
HVAC_Menu_System::instance();

View file

@ -68,8 +68,13 @@ class HVAC_Organizers {
* Render organizers list
*/
public function render_organizers_list() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
ob_start();
@ -80,9 +85,12 @@ class HVAC_Organizers {
<a href="/trainer/organizer/manage/" class="hvac-button hvac-button-primary">Add New Organizer</a>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt; <a href="/trainer/organizer/list/">Organizers</a> &gt; List
</div>
<?php
// Breadcrumbs are handled by page template when using WordPress pages
if (!defined('HVAC_IN_PAGE_TEMPLATE') && class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<?php $this->render_organizers_table(); ?>
</div>
@ -242,8 +250,13 @@ class HVAC_Organizers {
* Render organizer manage form
*/
public function render_organizer_manage() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
$organizer_id = isset($_GET['organizer_id']) ? intval($_GET['organizer_id']) : 0;
@ -265,11 +278,12 @@ class HVAC_Organizers {
<h1><?php echo $organizer ? 'Edit Organizer' : 'Create New Organizer'; ?></h1>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt;
<a href="/trainer/organizer/list/">Organizers</a> &gt;
<?php echo $organizer ? 'Edit' : 'New'; ?>
</div>
<?php
// Breadcrumbs are handled by page template when using WordPress pages
if (!defined('HVAC_IN_PAGE_TEMPLATE') && class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<form id="hvac-organizer-form" class="hvac-form">
<?php wp_nonce_field('hvac_organizer_manage', 'hvac_organizer_nonce'); ?>
@ -406,7 +420,7 @@ class HVAC_Organizers {
public function ajax_save_organizer() {
check_ajax_referer('hvac_organizers_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
@ -496,7 +510,7 @@ class HVAC_Organizers {
public function ajax_delete_organizer() {
check_ajax_referer('hvac_organizers_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
@ -549,7 +563,7 @@ class HVAC_Organizers {
public function ajax_upload_org_logo() {
check_ajax_referer('hvac_organizers_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}

View file

@ -61,6 +61,68 @@ class HVAC_Page_Manager {
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
// New trainer profile pages
'trainer/profile' => [
'title' => 'Personal Profile',
'template' => 'page-trainer-profile.php',
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/profile/edit' => [
'title' => 'Edit Profile',
'template' => 'page-trainer-profile-edit.php',
'public' => false,
'parent' => 'trainer/profile',
'capability' => 'hvac_trainer'
],
// Venue management pages
'trainer/venue' => [
'title' => 'Venues',
'template' => null,
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/venue/list' => [
'title' => 'Training Venues',
'template' => 'page-trainer-venues-list.php',
'public' => false,
'parent' => 'trainer/venue',
'capability' => 'hvac_trainer'
],
'trainer/venue/manage' => [
'title' => 'Manage Venue',
'template' => 'page-trainer-venue-manage.php',
'public' => false,
'parent' => 'trainer/venue',
'capability' => 'hvac_trainer'
],
// Organizer management pages
'trainer/organizer' => [
'title' => 'Organizers',
'template' => null,
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/organizer/list' => [
'title' => 'Training Organizers',
'template' => 'page-trainer-organizers-list.php',
'public' => false,
'parent' => 'trainer/organizer',
'capability' => 'hvac_trainer'
],
'trainer/organizer/manage' => [
'title' => 'Manage Organizer',
'template' => 'page-trainer-organizer-manage.php',
'public' => false,
'parent' => 'trainer/organizer',
'capability' => 'hvac_trainer'
],
'trainer/event' => [
'title' => 'Event',
'template' => null,
@ -155,6 +217,13 @@ class HVAC_Page_Manager {
'parent' => 'master-trainer',
'capability' => 'hvac_master_trainer'
],
'master-trainer/master-dashboard' => [
'title' => 'Master Dashboard',
'template' => 'page-master-dashboard.php',
'public' => false,
'parent' => 'master-trainer',
'capability' => 'hvac_master_trainer'
],
'master-trainer/certificate-fix' => [
'title' => 'Certificate System Diagnostics',
'template' => 'page-certificate-fix.php',
@ -169,6 +238,75 @@ class HVAC_Page_Manager {
'parent' => 'master-trainer',
'capability' => 'hvac_master_trainer'
],
'master-trainer/master-dashboard' => [
'title' => 'Master Dashboard',
'template' => 'page-master-dashboard.php',
'public' => false,
'parent' => 'master-trainer',
'capability' => 'hvac_master_trainer'
],
// Trainer Profile pages
'trainer/profile' => [
'title' => 'Personal Profile',
'template' => 'page-trainer-profile.php',
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/profile/edit' => [
'title' => 'Edit Profile',
'template' => 'page-trainer-profile-edit.php',
'public' => false,
'parent' => 'trainer/profile',
'capability' => 'hvac_trainer'
],
// Venue pages
'trainer/venue' => [
'title' => 'Venues',
'template' => null,
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/venue/list' => [
'title' => 'Training Venues',
'template' => 'page-trainer-venues-list.php',
'public' => false,
'parent' => 'trainer/venue',
'capability' => 'hvac_trainer'
],
'trainer/venue/manage' => [
'title' => 'Manage Venue',
'template' => 'page-trainer-venue-manage.php',
'public' => false,
'parent' => 'trainer/venue',
'capability' => 'hvac_trainer'
],
// Organizer pages
'trainer/organizer' => [
'title' => 'Organizers',
'template' => null,
'public' => false,
'parent' => 'trainer',
'capability' => 'hvac_trainer'
],
'trainer/organizer/list' => [
'title' => 'Training Organizers',
'template' => 'page-trainer-organizers-list.php',
'public' => false,
'parent' => 'trainer/organizer',
'capability' => 'hvac_trainer'
],
'trainer/organizer/manage' => [
'title' => 'Manage Organizer',
'template' => 'page-trainer-organizer-manage.php',
'public' => false,
'parent' => 'trainer/organizer',
'capability' => 'hvac_trainer'
],
// Other pages
'trainer/documentation' => [
@ -260,6 +398,12 @@ class HVAC_Page_Manager {
update_post_meta($page_id, '_wp_page_template', $config['template']);
}
// Set Astra theme layout to full-width for all HVAC pages
update_post_meta($page_id, 'ast-site-content-layout', 'page-builder');
update_post_meta($page_id, 'site-post-title', 'disabled');
update_post_meta($page_id, 'site-sidebar-layout', 'no-sidebar');
update_post_meta($page_id, 'theme-transparent-header-meta', 'disabled');
// Set page capabilities if specified
if (!empty($config['capability'])) {
update_post_meta($page_id, '_hvac_required_capability', $config['capability']);
@ -298,6 +442,23 @@ class HVAC_Page_Manager {
}
}
// Define shortcode mappings for specific pages
$shortcode_mappings = [
'trainer/profile' => '[hvac_trainer_profile_view]',
'trainer/profile/edit' => '[hvac_trainer_profile_edit]',
'trainer/venue/list' => '[hvac_trainer_venues_list]',
'trainer/venue/manage' => '[hvac_trainer_venue_manage]',
'trainer/organizer/list' => '[hvac_trainer_organizers_list]',
'trainer/organizer/manage' => '[hvac_trainer_organizer_manage]',
'trainer/certificate-reports' => '[hvac_certificate_reports]',
'trainer/generate-certificates' => '[hvac_generate_certificates]'
];
// Return shortcode if mapped
if (isset($shortcode_mappings[$slug])) {
return $shortcode_mappings[$slug];
}
// For pages with templates, use a placeholder
if (!empty($config['template'])) {
return '<!-- This page uses a custom template -->';
@ -357,4 +518,37 @@ class HVAC_Page_Manager {
HVAC_Logger::info('Completed page deletion process', 'Page Manager');
}
/**
* Update all existing pages with proper layout settings
*
* @return void
*/
public static function update_existing_page_layouts() {
HVAC_Logger::info('Updating page layouts and templates', 'Page Manager');
foreach (self::$pages as $slug => $config) {
$page = get_page_by_path($slug);
if ($page) {
// Update page template if specified
if (!empty($config['template'])) {
$current_template = get_post_meta($page->ID, '_wp_page_template', true);
if ($current_template !== $config['template']) {
update_post_meta($page->ID, '_wp_page_template', $config['template']);
HVAC_Logger::info("Updated template for page {$slug}: {$config['template']}", 'Page Manager');
}
}
// Set Astra theme layout to full-width
update_post_meta($page->ID, 'ast-site-content-layout', 'page-builder');
update_post_meta($page->ID, 'site-post-title', 'disabled');
update_post_meta($page->ID, 'site-sidebar-layout', 'no-sidebar');
update_post_meta($page->ID, 'theme-transparent-header-meta', 'disabled');
HVAC_Logger::info("Updated layout settings for page: {$slug}", 'Page Manager');
}
}
HVAC_Logger::info('Completed page layout and template updates', 'Page Manager');
}
}

View file

@ -41,6 +41,13 @@ class HVAC_Plugin {
$this->define_constants();
$this->includes();
$this->init_hooks();
// Initialize Astra theme integration if Astra is active
add_action('after_setup_theme', function() {
if (defined('ASTRA_THEME_VERSION')) {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-astra-integration.php';
}
});
}
/**
@ -91,6 +98,8 @@ class HVAC_Plugin {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-shortcodes.php';
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-scripts-styles.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-role-consolidator.php';
// Feature includes - check if files exist before including
$feature_includes = [
@ -247,6 +256,20 @@ class HVAC_Plugin {
// AJAX handlers
add_action('wp_ajax_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']);
add_action('wp_ajax_nopriv_hvac_master_dashboard_events', [$this, 'ajax_master_dashboard_events']);
// Theme integration filters
add_filter('body_class', [$this, 'add_hvac_body_classes']);
add_filter('post_class', [$this, 'add_hvac_post_classes']);
// Astra theme specific filters for layout
add_filter('astra_page_layout', [$this, 'force_full_width_layout']);
add_filter('astra_get_content_layout', [$this, 'force_full_width_layout']);
// Admin action to manually update pages
add_action('admin_init', [$this, 'check_for_page_updates']);
// Check if pages need update after activation
add_action('init', [$this, 'check_page_update_flag'], 99);
}
/**
@ -530,4 +553,158 @@ class HVAC_Plugin {
$table_data = $master_data->get_events_table_data($args);
wp_send_json_success($table_data);
}
/**
* 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 any sidebar classes that might have been added
$classes = array_diff($classes, array(
'ast-right-sidebar',
'ast-left-sidebar',
'ast-separate-container'
));
return $classes;
}
/**
* Add HVAC-specific post classes
*
* @param array $classes Post classes
* @return array Modified post classes
*/
public function add_hvac_post_classes($classes) {
// Add classes for HVAC pages
global $post;
if ($post && strpos($post->post_name, 'trainer') !== false) {
$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
if (is_page_template()) {
$template = get_page_template_slug();
if (strpos($template, 'page-trainer') !== false ||
strpos($template, 'page-master') !== false ||
strpos($template, 'page-certificate') !== false ||
strpos($template, 'page-generate') !== false ||
strpos($template, 'page-manage-event') !== false) {
return 'no-sidebar';
}
}
return $layout;
}
/**
* Check for manual page update request
*
* @return void
*/
public function check_for_page_updates() {
// Only allow admins to trigger updates
if (!current_user_can('manage_options')) {
return;
}
// Check for update trigger
if (isset($_GET['hvac_update_pages']) && $_GET['hvac_update_pages'] === 'true') {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-page-manager.php';
// Update all page layouts and templates
HVAC_Page_Manager::update_existing_page_layouts();
// Add admin notice
add_action('admin_notices', function() {
echo '<div class="notice notice-success is-dismissible">';
echo '<p>HVAC pages have been updated with correct templates and layouts.</p>';
echo '</div>';
});
// Redirect to remove the parameter
wp_redirect(remove_query_arg('hvac_update_pages'));
exit;
}
}
/**
* Check if pages need update after activation
*
* @return void
*/
public function check_page_update_flag() {
if (get_option('hvac_pages_need_update', false)) {
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-page-manager.php';
// Update all page layouts and templates
HVAC_Page_Manager::update_existing_page_layouts();
// Remove the flag
delete_option('hvac_pages_need_update');
HVAC_Logger::info('Pages updated after activation', 'HVAC Plugin');
}
}
}

View file

@ -340,6 +340,30 @@ class HVAC_Registration {
<textarea name="application_details" id="application_details" rows="5" required aria-describedby="application_details_error"><?php echo esc_textarea($data['application_details'] ?? ''); ?></textarea>
<?php if (isset($errors['application_details'])) echo '<p class="error-message" id="application_details_error">' . esc_html($errors['application_details']) . '</p>'; ?>
</div>
<div class="form-row">
<label id="role_label"><strong>Role *</strong></label>
<small>What is your primary role in the HVAC industry?</small>
<div class="radio-group" role="radiogroup" aria-labelledby="role_label">
<?php
$role_options = [
"technician" => "Technician",
"installer" => "Installer",
"supervisor" => "Supervisor",
"manager" => "Manager",
"trainer" => "Trainer",
"consultant" => "Consultant",
"sales" => "Sales Representative",
"engineer" => "Engineer",
"owner" => "Business Owner",
"other" => "Other"
];
foreach ($role_options as $value => $label) {
echo '<label><input type="radio" name="role" value="' . esc_attr($value) . '" ' . checked($data['role'] ?? '', $value, false) . ' required> ' . esc_html($label) . '</label>';
}
?>
</div>
<?php if (isset($errors['role'])) echo '<p class="error-message">' . esc_html($errors['role']) . '</p>'; ?>
</div>
<div class="form-row">
<label for="profile_image">Profile Image (optional)</label>
<input type="file" name="profile_image" id="profile_image" accept="image/jpeg,image/png,image/gif" aria-describedby="profile_image_hint profile_image_error">
@ -745,6 +769,7 @@ class HVAC_Registration {
'create_venue' => 'Create Training Venue Profile selection',
'business_type' => 'Business Type',
'application_details' => 'Application Details',
'role' => 'Role',
];
foreach ($required_fields as $field => $label) {
@ -867,6 +892,14 @@ class HVAC_Registration {
}
}
// Role validation - ensure it's one of the allowed values
if (!empty($data['role'])) {
$allowed_roles = ['technician', 'installer', 'supervisor', 'manager', 'trainer', 'consultant', 'sales', 'engineer', 'owner', 'other'];
if (!in_array($data['role'], $allowed_roles)) {
$errors['role'] = 'Please select a valid role.';
}
}
// error_log('[HVAC REG DEBUG] validate_registration: FINAL errors before return: ' . print_r($errors, true));
return $errors;
}
@ -948,6 +981,7 @@ class HVAC_Registration {
'training_resources' => (!empty($data['training_resources']) && is_array($data['training_resources'])) ? array_map('sanitize_text_field', $data['training_resources']) : [],
'application_details' => wp_kses_post($data['application_details']),
'annual_revenue_target' => !empty($data['annual_revenue_target']) ? intval($data['annual_revenue_target']) : '',
'role' => sanitize_text_field($data['role']),
'account_status' => 'pending' // Set initial status
];
@ -1344,6 +1378,7 @@ class HVAC_Registration {
'user_city' => 'City',
'user_zip' => 'Zip/Postal Code',
'business_type' => 'Business Type',
'role' => 'Role',
];
foreach ($required_fields as $field => $label) {
@ -1440,6 +1475,43 @@ class HVAC_Registration {
}
}
// Role validation - ensure it's one of the allowed values
if (!empty($data['role'])) {
$allowed_roles = ['technician', 'installer', 'supervisor', 'manager', 'trainer', 'consultant', 'sales', 'engineer', 'owner', 'other'];
if (!in_array($data['role'], $allowed_roles)) {
$errors['role'] = 'Please select a valid role.';
}
}
// Certification fields validation (only if user can edit them)
if (current_user_can('administrator') || current_user_can('hvac_master_trainer')) {
// Certification type validation
if (!empty($data['certification_type'])) {
$allowed_cert_types = ['Certified measureQuick Trainer', 'Certified measureQuick Champion'];
if (!in_array($data['certification_type'], $allowed_cert_types)) {
$errors['certification_type'] = 'Please select a valid certification type.';
}
}
// Certification status validation
if (!empty($data['certification_status'])) {
$allowed_cert_statuses = ['Active', 'Expired', 'Pending', 'Disabled'];
if (!in_array($data['certification_status'], $allowed_cert_statuses)) {
$errors['certification_status'] = 'Please select a valid certification status.';
}
}
// Date certified validation
if (!empty($data['date_certified'])) {
$date = DateTime::createFromFormat('Y-m-d', $data['date_certified']);
if (!$date || $date->format('Y-m-d') !== $data['date_certified']) {
$errors['date_certified'] = 'Please enter a valid date in YYYY-MM-DD format.';
} elseif ($date > new DateTime()) {
$errors['date_certified'] = 'Certification date cannot be in the future.';
}
}
}
// error_log('[HVAC PROFILE DEBUG] validate_profile_update: Validation result - ' . (empty($errors) ? 'VALID' : 'INVALID'));
return $errors;
}
@ -1496,8 +1568,16 @@ class HVAC_Registration {
'training_locations' => (!empty($data['training_locations']) && is_array($data['training_locations'])) ? array_map('sanitize_text_field', $data['training_locations']) : [],
'training_resources' => (!empty($data['training_resources']) && is_array($data['training_resources'])) ? array_map('sanitize_text_field', $data['training_resources']) : [],
'annual_revenue_target' => !empty($data['annual_revenue_target']) ? intval($data['annual_revenue_target']) : '',
'role' => sanitize_text_field($data['role']),
];
// Add certification fields if user has permission to edit them
if (current_user_can('administrator') || current_user_can('hvac_master_trainer')) {
$meta_fields['date_certified'] = sanitize_text_field($data['date_certified']);
$meta_fields['certification_type'] = sanitize_text_field($data['certification_type']);
$meta_fields['certification_status'] = sanitize_text_field($data['certification_status']);
}
foreach ($meta_fields as $key => $value) {
update_user_meta($user_id, $key, $value);
}

View file

@ -0,0 +1,216 @@
<?php
/**
* HVAC Role Consolidator
*
* Handles consolidation of duplicate roles and ensures proper permissions
*
* @package HVAC_Community_Events
* @since 2.0.0
*/
if (!defined('ABSPATH')) {
exit;
}
class HVAC_Role_Consolidator {
/**
* Run the role consolidation process
*/
public static function consolidate_roles() {
$results = array();
// Step 1: Migrate users from event_trainer to hvac_trainer
$results['migration'] = self::migrate_users_from_legacy_role();
// Step 2: Remove the duplicate event_trainer role
$results['removal'] = self::remove_legacy_role();
// Step 3: Ensure proper capabilities for hvac_trainer role
$results['capabilities'] = self::ensure_proper_capabilities();
// Step 4: Clean up any permission issues
$results['cleanup'] = self::cleanup_permissions();
return $results;
}
/**
* Migrate users from event_trainer to hvac_trainer role
*/
private static function migrate_users_from_legacy_role() {
$migrated = 0;
$errors = array();
// Get all users with event_trainer role
$users = get_users(array('role' => 'event_trainer'));
foreach ($users as $user) {
try {
// Remove old role
$user->remove_role('event_trainer');
// Add new role
$user->add_role('hvac_trainer');
$migrated++;
HVAC_Logger::info("Migrated user {$user->ID} from event_trainer to hvac_trainer", 'Role Consolidator');
} catch (Exception $e) {
$errors[] = "Failed to migrate user {$user->ID}: " . $e->getMessage();
HVAC_Logger::error("Failed to migrate user {$user->ID}: " . $e->getMessage(), 'Role Consolidator');
}
}
return array(
'migrated' => $migrated,
'errors' => $errors
);
}
/**
* Remove the legacy event_trainer role
*/
private static function remove_legacy_role() {
// Check if role exists
if (get_role('event_trainer')) {
remove_role('event_trainer');
HVAC_Logger::info('Removed legacy event_trainer role', 'Role Consolidator');
return true;
}
return false;
}
/**
* Ensure hvac_trainer role has all proper capabilities
*/
private static function ensure_proper_capabilities() {
$role = get_role('hvac_trainer');
if (!$role) {
HVAC_Logger::error('hvac_trainer role not found!', 'Role Consolidator');
return false;
}
// Get the proper capabilities from HVAC_Roles class
require_once HVAC_PLUGIN_DIR . 'includes/class-hvac-roles.php';
$roles_manager = new HVAC_Roles();
$proper_caps = $roles_manager->get_trainer_capabilities();
$updated = 0;
// Add any missing capabilities
foreach ($proper_caps as $cap => $grant) {
if ($grant && !$role->has_cap($cap)) {
$role->add_cap($cap);
$updated++;
HVAC_Logger::info("Added capability {$cap} to hvac_trainer role", 'Role Consolidator');
} elseif (!$grant && $role->has_cap($cap)) {
$role->remove_cap($cap);
$updated++;
HVAC_Logger::info("Removed capability {$cap} from hvac_trainer role", 'Role Consolidator');
}
}
return $updated;
}
/**
* Clean up permission issues across the plugin
*/
private static function cleanup_permissions() {
$cleaned = array();
// Fix capability checks in the codebase
$capability_mappings = array(
'event_trainer' => 'hvac_trainer',
'event_master_trainer' => 'hvac_master_trainer'
);
// Update user meta that might reference old roles
global $wpdb;
// Update any user meta that references old role names
foreach ($capability_mappings as $old_role => $new_role) {
$query = $wpdb->prepare(
"UPDATE {$wpdb->usermeta}
SET meta_value = REPLACE(meta_value, %s, %s)
WHERE meta_key = %s
AND meta_value LIKE %s",
$old_role,
$new_role,
$wpdb->prefix . 'capabilities',
'%' . $wpdb->esc_like($old_role) . '%'
);
$updated = $wpdb->query($query);
if ($updated) {
$cleaned['user_meta_' . $old_role] = $updated;
HVAC_Logger::info("Updated {$updated} user meta entries from {$old_role} to {$new_role}", 'Role Consolidator');
}
}
return $cleaned;
}
/**
* Check if consolidation is needed
*/
public static function needs_consolidation() {
// Check if event_trainer role exists
if (get_role('event_trainer')) {
return true;
}
// Check if any users still have event_trainer in their capabilities
$users = get_users(array('role' => 'event_trainer'));
if (!empty($users)) {
return true;
}
return false;
}
/**
* Run consolidation check on admin init
*/
public static function check_and_consolidate() {
// Only run for admins
if (!current_user_can('manage_options')) {
return;
}
// Check if we've already consolidated
if (get_option('hvac_roles_consolidated_v2', false)) {
return;
}
// Check if consolidation is needed
if (self::needs_consolidation()) {
$results = self::consolidate_roles();
// Log results
HVAC_Logger::info('Role consolidation completed: ' . json_encode($results), 'Role Consolidator');
// Mark as consolidated
update_option('hvac_roles_consolidated_v2', true);
// Show admin notice
add_action('admin_notices', function() use ($results) {
?>
<div class="notice notice-success is-dismissible">
<p><strong>HVAC Roles Consolidated:</strong></p>
<ul>
<li>Migrated <?php echo intval($results['migration']['migrated']); ?> users to hvac_trainer role</li>
<li>Updated <?php echo intval($results['capabilities']); ?> capabilities</li>
</ul>
</div>
<?php
});
}
}
}
// Hook into admin_init to check and consolidate
add_action('admin_init', array('HVAC_Role_Consolidator', 'check_and_consolidate'));

View file

@ -78,7 +78,7 @@ class HVAC_Scripts_Styles {
return;
}
// Main plugin styles
// Main plugin styles - load on ALL plugin pages
wp_enqueue_style(
'hvac-community-events',
HVAC_PLUGIN_URL . 'assets/css/hvac-community-events.css',
@ -102,6 +102,14 @@ class HVAC_Scripts_Styles {
$this->version
);
// Common styles for all trainer pages - ensures consistent styling
wp_enqueue_style(
'hvac-common',
HVAC_PLUGIN_URL . 'assets/css/hvac-common.css',
array(),
$this->version
);
// Dashboard styles
if ($this->is_dashboard_page()) {
wp_enqueue_style(
@ -139,6 +147,43 @@ class HVAC_Scripts_Styles {
);
}
// Certificate pages styles
if ($this->is_certificate_page()) {
wp_enqueue_style(
'hvac-certificates',
HVAC_PLUGIN_URL . 'assets/css/hvac-certificates.css',
array('hvac-community-events'),
$this->version
);
wp_enqueue_style(
'hvac-certificates-enhanced',
HVAC_PLUGIN_URL . 'assets/css/hvac-certificates-enhanced.css',
array('hvac-certificates'),
$this->version
);
}
// Organizers pages styles
if ($this->is_organizers_page()) {
wp_enqueue_style(
'hvac-organizers',
HVAC_PLUGIN_URL . 'assets/css/hvac-organizers.css',
array('hvac-community-events'),
$this->version
);
}
// Venues pages styles
if ($this->is_venues_page()) {
wp_enqueue_style(
'hvac-venues',
HVAC_PLUGIN_URL . 'assets/css/hvac-venues.css',
array('hvac-community-events'),
$this->version
);
}
// Event manage page styles
if ($this->is_event_manage_page()) {
// First ensure common CSS is loaded
@ -323,12 +368,29 @@ class HVAC_Scripts_Styles {
* @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();
}
// Fallback: check URL path
// Check by page template
if (is_page_template()) {
$template = get_page_template_slug();
if (strpos($template, 'page-trainer') !== false ||
strpos($template, 'page-master') !== false ||
strpos($template, 'page-certificate') !== false ||
strpos($template, 'page-generate') !== false ||
strpos($template, 'page-manage-event') !== false) {
return true;
}
}
// Fallback: check URL path (more comprehensive)
$current_path = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');
$plugin_paths = array(
'trainer',
@ -337,6 +399,8 @@ class HVAC_Scripts_Styles {
'hvac-dashboard',
'manage-event',
'event-summary',
'certificate-reports',
'generate-certificates',
);
foreach ($plugin_paths as $path) {
@ -473,6 +537,46 @@ class HVAC_Scripts_Styles {
is_page('trainer/login');
}
/**
* Check if current page is certificate page
*
* @return bool
*/
private function is_certificate_page() {
return is_page('certificate-reports') ||
is_page('trainer/certificate-reports') ||
is_page('generate-certificates') ||
is_page('trainer/generate-certificates') ||
strpos($_SERVER['REQUEST_URI'], 'certificate-reports') !== false ||
strpos($_SERVER['REQUEST_URI'], 'generate-certificates') !== false;
}
/**
* Check if current page is organizers page
*
* @return bool
*/
private function is_organizers_page() {
return is_page('trainer-organizers-list') ||
is_page('trainer/organizer/list') ||
is_page('trainer-organizer-manage') ||
is_page('trainer/organizer/manage') ||
strpos($_SERVER['REQUEST_URI'], 'organizer') !== false;
}
/**
* Check if current page is venues page
*
* @return bool
*/
private function is_venues_page() {
return is_page('trainer-venues-list') ||
is_page('trainer/venue/list') ||
is_page('trainer-venue-manage') ||
is_page('trainer/venue/manage') ||
strpos($_SERVER['REQUEST_URI'], 'venue') !== false;
}
/**
* Get script version with cache busting
*

View file

@ -118,6 +118,36 @@ class HVAC_Shortcodes {
'description' => 'Communication schedules management'
),
// Venue shortcodes
'hvac_trainer_venues_list' => array(
'callback' => array($this, 'render_venues_list'),
'description' => 'Trainer venues listing page'
),
'hvac_trainer_venue_manage' => array(
'callback' => array($this, 'render_venue_manage'),
'description' => 'Trainer venue management page'
),
// Organizer shortcodes
'hvac_trainer_organizers_list' => array(
'callback' => array($this, 'render_organizers_list'),
'description' => 'Trainer organizers listing page'
),
'hvac_trainer_organizer_manage' => array(
'callback' => array($this, 'render_organizer_manage'),
'description' => 'Trainer organizer management page'
),
// Profile shortcodes - additional ones beyond hvac_trainer_profile
'hvac_trainer_profile_view' => array(
'callback' => array($this, 'render_trainer_profile_view'),
'description' => 'Trainer profile view page'
),
'hvac_trainer_profile_edit' => array(
'callback' => array($this, 'render_trainer_profile_edit'),
'description' => 'Trainer profile edit page'
),
// Admin shortcodes
'hvac_google_sheets' => array(
'callback' => array($this, 'render_google_sheets_admin'),
@ -313,13 +343,20 @@ class HVAC_Shortcodes {
* @return string
*/
public function render_certificate_reports($atts = array()) {
// Redirect to manager if available
if (class_exists('HVAC_Certificate_Manager')) {
$cert_manager = new HVAC_Certificate_Manager();
return $cert_manager->render_reports_page();
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to view certificate reports.', 'hvac-community-events') . '</p>';
}
return '<p>' . __('Certificate reports functionality not available.', 'hvac-community-events') . '</p>';
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
// Include the certificate reports content template
ob_start();
include HVAC_PLUGIN_DIR . 'templates/certificates/certificate-reports-content.php';
return ob_get_clean();
}
/**
@ -329,13 +366,20 @@ class HVAC_Shortcodes {
* @return string
*/
public function render_generate_certificates($atts = array()) {
// Redirect to manager if available
if (class_exists('HVAC_Certificate_Manager')) {
$cert_manager = new HVAC_Certificate_Manager();
return $cert_manager->render_generate_page();
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to generate certificates.', 'hvac-community-events') . '</p>';
}
return '<p>' . __('Certificate generation functionality not available.', 'hvac-community-events') . '</p>';
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
// Include the generate certificates content template
ob_start();
include HVAC_PLUGIN_DIR . 'templates/certificates/generate-certificates-content.php';
return ob_get_clean();
}
/**
@ -346,8 +390,9 @@ class HVAC_Shortcodes {
*/
public function render_email_attendees($atts = array()) {
// Check if user has appropriate permissions
if (!current_user_can('edit_tribe_events')) {
return '<p>' . __('You do not have permission to access this page.', 'hvac-community-events') . '</p>';
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
ob_start();
@ -423,4 +468,154 @@ class HVAC_Shortcodes {
$google_sheets->render_admin_page();
return ob_get_clean();
}
/**
* Render venues list shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_venues_list($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to view venues.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Venues')) {
return '<p>' . __('Venues functionality not available.', 'hvac-community-events') . '</p>';
}
$venues = new HVAC_Venues();
return $venues->render_venues_list($atts);
}
/**
* Render venue manage shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_venue_manage($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to manage venues.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Venues')) {
return '<p>' . __('Venues functionality not available.', 'hvac-community-events') . '</p>';
}
$venues = new HVAC_Venues();
return $venues->render_venue_manage($atts);
}
/**
* Render organizers list shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_organizers_list($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to view organizers.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Organizers')) {
return '<p>' . __('Organizers functionality not available.', 'hvac-community-events') . '</p>';
}
$organizers = new HVAC_Organizers();
return $organizers->render_organizers_list($atts);
}
/**
* Render organizer manage shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_organizer_manage($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to manage organizers.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Organizers')) {
return '<p>' . __('Organizers functionality not available.', 'hvac-community-events') . '</p>';
}
$organizers = new HVAC_Organizers();
return $organizers->render_organizer_manage($atts);
}
/**
* Render trainer profile view shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_trainer_profile_view($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to view your profile.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Trainer_Profile_Manager')) {
return '<p>' . __('Profile functionality not available.', 'hvac-community-events') . '</p>';
}
$profile_manager = new HVAC_Trainer_Profile_Manager();
return $profile_manager->render_profile_view($atts);
}
/**
* Render trainer profile edit shortcode
*
* @param array $atts Shortcode attributes
* @return string
*/
public function render_trainer_profile_edit($atts = array()) {
// Check permissions
if (!is_user_logged_in()) {
return '<p>' . __('Please log in to edit your profile.', 'hvac-community-events') . '</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>' . __('You must be a trainer to access this page.', 'hvac-community-events') . '</p>';
}
if (!class_exists('HVAC_Trainer_Profile_Manager')) {
return '<p>' . __('Profile functionality not available.', 'hvac-community-events') . '</p>';
}
$profile_manager = new HVAC_Trainer_Profile_Manager();
return $profile_manager->render_profile_edit($atts);
}
}

View file

@ -16,17 +16,20 @@ if (!defined('ABSPATH')) {
class HVAC_Trainer_Navigation {
/**
* Constructor
* Constructor - DISABLED: Replaced with HVAC_Menu_System
*/
public function __construct() {
// Register shortcode
add_shortcode('hvac_trainer_navigation', array($this, 'render_navigation'));
// Old navigation system disabled to prevent conflicts with new WordPress menu system
// All functionality moved to HVAC_Menu_System class
// Add navigation to trainer pages
add_action('hvac_before_trainer_content', array($this, 'display_navigation'));
// Enqueue styles
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
// // Register shortcode
// add_shortcode('hvac_trainer_navigation', array($this, 'render_navigation'));
//
// // Add navigation to trainer pages
// add_action('hvac_before_trainer_content', array($this, 'display_navigation'));
//
// // Enqueue styles
// add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
}
/**
@ -354,12 +357,13 @@ class HVAC_Trainer_Navigation {
}
/**
* Display navigation on trainer pages
* Display navigation on trainer pages - DISABLED
*/
public function display_navigation() {
if ($this->is_trainer_page()) {
echo do_shortcode('[hvac_trainer_navigation]');
}
// Disabled to prevent conflicts with new HVAC_Menu_System
// if ($this->is_trainer_page()) {
// echo do_shortcode('[hvac_trainer_navigation]');
// }
}
/**

View file

@ -67,8 +67,13 @@ class HVAC_Trainer_Profile_Manager {
* Render profile view
*/
public function render_profile_view() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
$user_id = get_current_user_id();
@ -83,6 +88,14 @@ class HVAC_Trainer_Profile_Manager {
$certifications = get_user_meta($user_id, 'trainer_certifications', true);
$years_experience = get_user_meta($user_id, 'years_experience', true);
$profile_photo_id = get_user_meta($user_id, 'profile_photo_id', true);
$application_details = get_user_meta($user_id, 'application_details', true);
$role = get_user_meta($user_id, 'role', true);
$website = $user->user_url;
// Get certification fields
$date_certified = get_user_meta($user_id, 'date_certified', true);
$certification_type = get_user_meta($user_id, 'certification_type', true);
$certification_status = get_user_meta($user_id, 'certification_status', true);
// Get organization info
$organizer_id = get_user_meta($user_id, 'organizer_id', true);
@ -99,9 +112,11 @@ class HVAC_Trainer_Profile_Manager {
<a href="/trainer/profile/edit/" class="hvac-button hvac-button-primary">Edit Profile</a>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt; <a href="/trainer/profile/">Profile</a> &gt; View
</div>
<?php
if (class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<div class="hvac-profile-content">
<div class="hvac-profile-sidebar">
@ -134,6 +149,34 @@ class HVAC_Trainer_Profile_Manager {
</div>
<div class="hvac-profile-main">
<?php if ($date_certified || $certification_type || $certification_status): ?>
<div class="hvac-profile-section hvac-certification-section">
<h2>Certification Information</h2>
<div class="hvac-profile-details">
<?php if ($certification_status): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Certification Status:</span>
<span class="hvac-detail-value hvac-cert-status hvac-cert-status-<?php echo esc_attr(strtolower($certification_status)); ?>">
<?php echo esc_html($certification_status); ?>
</span>
</div>
<?php endif; ?>
<?php if ($certification_type): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Certification Type:</span>
<span class="hvac-detail-value"><?php echo esc_html($certification_type); ?></span>
</div>
<?php endif; ?>
<?php if ($date_certified): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Date Certified:</span>
<span class="hvac-detail-value"><?php echo esc_html(date('F j, Y', strtotime($date_certified))); ?></span>
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="hvac-profile-section">
<h2>Personal Information</h2>
<div class="hvac-profile-details">
@ -145,6 +188,12 @@ class HVAC_Trainer_Profile_Manager {
<span class="hvac-detail-label">Email:</span>
<span class="hvac-detail-value"><?php echo esc_html($user->user_email); ?></span>
</div>
<?php if ($role): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Role:</span>
<span class="hvac-detail-value"><?php echo esc_html(ucfirst($role)); ?></span>
</div>
<?php endif; ?>
<?php if ($phone): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Phone:</span>
@ -168,6 +217,22 @@ class HVAC_Trainer_Profile_Manager {
</span>
</div>
<?php endif; ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Website:</span>
<span class="hvac-detail-value">
<?php if ($website): ?>
<a href="<?php echo esc_url($website); ?>" target="_blank"><?php echo esc_html($website); ?></a>
<?php else: ?>
<span class="hvac-not-set">Not set</span>
<?php endif; ?>
</span>
</div>
<?php if ($application_details): ?>
<div class="hvac-detail-row">
<span class="hvac-detail-label">Application Details:</span>
<span class="hvac-detail-value"><?php echo esc_html($application_details); ?></span>
</div>
<?php endif; ?>
</div>
</div>
@ -239,8 +304,13 @@ class HVAC_Trainer_Profile_Manager {
* Render profile edit form
*/
public function render_profile_edit() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
$user_id = get_current_user_id();
@ -256,6 +326,17 @@ class HVAC_Trainer_Profile_Manager {
$certifications = get_user_meta($user_id, 'trainer_certifications', true);
$years_experience = get_user_meta($user_id, 'years_experience', true);
$profile_photo_id = get_user_meta($user_id, 'profile_photo_id', true);
$application_details = get_user_meta($user_id, 'application_details', true);
$role = get_user_meta($user_id, 'role', true);
// Get certification fields
$date_certified = get_user_meta($user_id, 'date_certified', true);
$certification_type = get_user_meta($user_id, 'certification_type', true);
$certification_status = get_user_meta($user_id, 'certification_status', true);
// Check if current user can edit certification fields
$current_user_id = get_current_user_id();
$can_edit_certifications = current_user_can('administrator') || current_user_can('hvac_master_trainer');
ob_start();
?>
@ -264,13 +345,92 @@ class HVAC_Trainer_Profile_Manager {
<h1>Edit Profile</h1>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt; <a href="/trainer/profile/">Profile</a> &gt; Edit
</div>
<?php
if (class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<form id="hvac-profile-form" class="hvac-form">
<?php wp_nonce_field('hvac_profile_edit', 'hvac_profile_nonce'); ?>
<?php if ($can_edit_certifications || $date_certified || $certification_type || $certification_status): ?>
<div class="hvac-form-section hvac-certification-edit-section">
<h3>Certification Information
<?php if (!$can_edit_certifications): ?>
<small class="hvac-read-only-note">(Read-only for trainers)</small>
<?php endif; ?>
</h3>
<div class="hvac-form-row">
<label for="certification_status">Certification Status</label>
<?php if ($can_edit_certifications): ?>
<select id="certification_status" name="certification_status">
<option value="">Select Status</option>
<?php
$status_options = [
'Active' => 'Active',
'Expired' => 'Expired',
'Pending' => 'Pending',
'Disabled' => 'Disabled'
];
foreach ($status_options as $value => $label) {
printf(
'<option value="%s" %s>%s</option>',
esc_attr($value),
selected($certification_status, $value, false),
esc_html($label)
);
}
?>
</select>
<?php else: ?>
<div class="hvac-read-only-field"><?php echo esc_html($certification_status ?: 'Not set'); ?></div>
<input type="hidden" name="certification_status" value="<?php echo esc_attr($certification_status); ?>" />
<?php endif; ?>
</div>
<div class="hvac-form-row">
<label for="certification_type">Certification Type</label>
<?php if ($can_edit_certifications): ?>
<select id="certification_type" name="certification_type">
<option value="">Select Type</option>
<?php
$type_options = [
'Certified measureQuick Trainer' => 'Certified measureQuick Trainer',
'Certified measureQuick Champion' => 'Certified measureQuick Champion'
];
foreach ($type_options as $value => $label) {
printf(
'<option value="%s" %s>%s</option>',
esc_attr($value),
selected($certification_type, $value, false),
esc_html($label)
);
}
?>
</select>
<?php else: ?>
<div class="hvac-read-only-field"><?php echo esc_html($certification_type ?: 'Not set'); ?></div>
<input type="hidden" name="certification_type" value="<?php echo esc_attr($certification_type); ?>" />
<?php endif; ?>
</div>
<div class="hvac-form-row">
<label for="date_certified">Date Certified</label>
<?php if ($can_edit_certifications): ?>
<input type="date" id="date_certified" name="date_certified"
value="<?php echo esc_attr($date_certified); ?>" />
<?php else: ?>
<div class="hvac-read-only-field">
<?php echo $date_certified ? esc_html(date('F j, Y', strtotime($date_certified))) : 'Not set'; ?>
</div>
<input type="hidden" name="date_certified" value="<?php echo esc_attr($date_certified); ?>" />
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="hvac-form-section">
<h3>Profile Photo</h3>
<div class="hvac-profile-photo-upload">
@ -323,6 +483,36 @@ class HVAC_Trainer_Profile_Manager {
value="<?php echo esc_attr($user->user_email); ?>" />
</div>
<div class="hvac-form-row">
<label for="role">Role *</label>
<small>What is your primary role in the HVAC industry?</small>
<select id="role" name="role" required>
<option value="">Select Role</option>
<?php
$role_options = [
"technician" => "Technician",
"installer" => "Installer",
"supervisor" => "Supervisor",
"manager" => "Manager",
"trainer" => "Trainer",
"consultant" => "Consultant",
"sales" => "Sales Representative",
"engineer" => "Engineer",
"owner" => "Business Owner",
"other" => "Other"
];
foreach ($role_options as $value => $label) {
printf(
'<option value="%s" %s>%s</option>',
esc_attr($value),
selected($role, $value, false),
esc_html($label)
);
}
?>
</select>
</div>
<div class="hvac-form-row">
<label for="phone">Phone Number</label>
<input type="tel" id="phone" name="phone"
@ -333,6 +523,12 @@ class HVAC_Trainer_Profile_Manager {
<label for="description">Bio / About</label>
<textarea id="description" name="description" rows="5"><?php echo esc_textarea($user->description); ?></textarea>
</div>
<div class="hvac-form-row">
<label for="application_details">Application Details</label>
<small>Why you want to create a training account on Upskill HVAC</small>
<textarea id="application_details" name="application_details" rows="5"><?php echo esc_textarea($application_details); ?></textarea>
</div>
</div>
<div class="hvac-form-section">
@ -419,7 +615,7 @@ class HVAC_Trainer_Profile_Manager {
public function ajax_update_profile() {
check_ajax_referer('hvac_profile_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
@ -450,6 +646,15 @@ class HVAC_Trainer_Profile_Manager {
update_user_meta($user_id, 'user_linkedin', esc_url_raw($_POST['linkedin']));
update_user_meta($user_id, 'years_experience', intval($_POST['years_experience']));
update_user_meta($user_id, 'trainer_certifications', sanitize_textarea_field($_POST['certifications']));
update_user_meta($user_id, 'application_details', sanitize_textarea_field($_POST['application_details']));
update_user_meta($user_id, 'role', sanitize_text_field($_POST['role']));
// Update certification fields (only if user has permission)
if (current_user_can('administrator') || current_user_can('hvac_master_trainer')) {
update_user_meta($user_id, 'date_certified', sanitize_text_field($_POST['date_certified']));
update_user_meta($user_id, 'certification_type', sanitize_text_field($_POST['certification_type']));
update_user_meta($user_id, 'certification_status', sanitize_text_field($_POST['certification_status']));
}
// Update profile photo if changed
if (isset($_POST['profile_photo_id'])) {
@ -465,7 +670,7 @@ class HVAC_Trainer_Profile_Manager {
public function ajax_upload_profile_photo() {
check_ajax_referer('hvac_profile_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}

View file

@ -63,8 +63,13 @@ class HVAC_Venues {
* Render venues list
*/
public function render_venues_list() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
ob_start();
@ -75,9 +80,12 @@ class HVAC_Venues {
<a href="/trainer/venue/manage/" class="hvac-button hvac-button-primary">Add New Venue</a>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt; <a href="/trainer/venue/list/">Venues</a> &gt; List
</div>
<?php
// Breadcrumbs are handled by page template when using WordPress pages
if (!defined('HVAC_IN_PAGE_TEMPLATE') && class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<?php $this->render_venues_table(); ?>
</div>
@ -252,8 +260,13 @@ class HVAC_Venues {
* Render venue manage form
*/
public function render_venue_manage() {
if (!is_user_logged_in() || !current_user_can('hvac_trainer')) {
return '<p>You must be logged in as a trainer to view this page.</p>';
if (!is_user_logged_in()) {
return '<p>You must be logged in to view this page.</p>';
}
// Allow trainers, master trainers, or WordPress admins
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
return '<p>You must be a trainer to view this page.</p>';
}
$venue_id = isset($_GET['venue_id']) ? intval($_GET['venue_id']) : 0;
@ -275,11 +288,12 @@ class HVAC_Venues {
<h1><?php echo $venue ? 'Edit Venue' : 'Create New Venue'; ?></h1>
</div>
<div class="hvac-breadcrumb">
<a href="/trainer/dashboard/">Trainer</a> &gt;
<a href="/trainer/venue/list/">Venues</a> &gt;
<?php echo $venue ? 'Edit' : 'New'; ?>
</div>
<?php
// Breadcrumbs are handled by page template when using WordPress pages
if (!defined('HVAC_IN_PAGE_TEMPLATE') && class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<form id="hvac-venue-form" class="hvac-form">
<?php wp_nonce_field('hvac_venue_manage', 'hvac_venue_nonce'); ?>
@ -396,7 +410,7 @@ class HVAC_Venues {
public function ajax_save_venue() {
check_ajax_referer('hvac_venues_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
@ -456,7 +470,7 @@ class HVAC_Venues {
public function ajax_delete_venue() {
check_ajax_referer('hvac_venues_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}
@ -503,7 +517,7 @@ class HVAC_Venues {
public function ajax_load_venue() {
check_ajax_referer('hvac_venues_nonce', 'nonce');
if (!current_user_can('hvac_trainer')) {
if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('manage_options')) {
wp_send_json_error('Unauthorized');
}

View file

@ -55,7 +55,7 @@ STAGING_PASS="$UPSKILL_STAGING_PASS"
STAGING_PATH="$UPSKILL_STAGING_PATH"
PLUGIN_PATH="wp-content/plugins/hvac-community-events"
BACKUP_DIR="wp-content/plugins/hvac-backups"
PACKAGE_FILE="hvac-community-events-final-fixes.zip"
PACKAGE_FILE="hvac-navigation-fixes-final.zip"
# Check if package exists
if [ ! -f "$PACKAGE_FILE" ]; then
@ -83,7 +83,7 @@ cd /home/974670.cloudwaysapps.com/uberrxmprk/public_html
echo "Current directory: $(pwd)"
# Move package from tmp directory
mv ../tmp/hvac-community-events-final-fixes.zip ./
mv ../tmp/hvac-navigation-fixes-final.zip ./
# Remove old plugin version
echo "Removing old plugin version..."
@ -94,7 +94,7 @@ mkdir -p wp-content/plugins/hvac-community-events
# Extract new version
echo "Extracting new version..."
unzip -q hvac-community-events-final-fixes.zip -d wp-content/plugins/hvac-community-events/
unzip -q hvac-navigation-fixes-final.zip -d wp-content/plugins/hvac-community-events/
# Set permissions
echo "Setting permissions..."

View file

@ -4,9 +4,34 @@
* Description: Template for the certificate reports page
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
// Render the certificate reports shortcode
<div class="hvac-page-wrapper hvac-certificate-reports-page">
<?php
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<?php
// Display breadcrumbs
if (class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<div class="container">
<?php
// Certificate reports content rendered through shortcode
echo do_shortcode('[hvac_certificate_reports]');
?>
</div>
</div>
<?php
get_footer();

View file

@ -4,9 +4,34 @@
* Description: Template for the generate certificates page
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
// Render the generate certificates shortcode
<div class="hvac-page-wrapper hvac-generate-certificates-page">
<?php
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<?php
// Display breadcrumbs
if (class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<div class="hvac-container">
<?php
// Generate certificates content rendered through shortcode
echo do_shortcode('[hvac_generate_certificates]');
?>
</div>
</div>
<?php
get_footer();

View file

@ -4,57 +4,87 @@
* Description: Template for managing events (uses The Events Calendar)
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
// The Events Calendar handles the event creation/editing interface
// This template ensures proper WordPress theme integration
?>
<style>
/* Hide duplicate headers and fix layout for event manage page */
.hvac-event-manage-wrapper .tribe-community-events #tribe-bar-form,
.hvac-event-manage-wrapper #tribe-bar-views,
.hvac-event-manage-wrapper .tribe-events-page-title {
display: none !important;
}
.hvac-event-manage-wrapper {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Fix The Events Calendar layout issues */
.hvac-event-manage-wrapper #tribe-community-events,
.hvac-event-manage-wrapper .tribe-community-events {
background: transparent !important;
border: none !important;
box-shadow: none !important;
margin-top: 0 !important;
}
/* Hide any duplicate site headers within the community events content */
.hvac-event-manage-wrapper .site-header,
.hvac-event-manage-wrapper .ast-header,
.hvac-event-manage-wrapper header {
display: none !important;
}
</style>
<div class="hvac-event-manage-wrapper">
<!-- Navigation Header -->
<div class="hvac-dashboard-header">
<h1 class="entry-title">Create Event</h1>
<div class="hvac-dashboard-nav">
<a href="/trainer/dashboard/" class="ast-button ast-button-secondary">Dashboard</a>
<a href="/trainer/certificate-reports/" class="ast-button ast-button-secondary">Certificate Reports</a>
<a href="/trainer/generate-certificates/" class="ast-button ast-button-secondary">Generate Certificates</a>
<button class="ast-button hvac-help-trigger">Help</button>
</div>
</div>
<?php
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="hvac-page-content">
<?php
// Let The Events Calendar handle the content
if (have_posts()) :
while (have_posts()) : the_post();
// Get the raw content WITHOUT any filters
// Get the raw content and process it
global $post;
$raw_content = $post->post_content;
// Strip ALL HTML comments that might contain shortcode references
// Use multiple patterns to catch all variations
// Clean up any HTML comments
$patterns = [
'/<!--\s*wp:shortcode\s*-->/s',
'/<!--\s*\/wp:shortcode\s*-->/s',
'/<!--[^>]*wp:shortcode[^>]*-->/s',
'/<!--.*?-->/s' // Catch all remaining HTML comments
'/<!--.*?-->/s'
];
foreach ($patterns as $pattern) {
$raw_content = preg_replace($pattern, '', $raw_content);
}
// Clean up any extra whitespace
$raw_content = trim($raw_content);
// Process shortcodes directly without the_content filter
$processed = do_shortcode($raw_content);
// Output the processed content
echo $processed;
// Process shortcodes
echo do_shortcode($raw_content);
endwhile;
else:
// No posts found - show the shortcode directly
// Fallback - show the shortcode directly
echo do_shortcode('[tribe_community_events]');
endif;
?>
</div>
</div>
<?php
get_footer();

View file

@ -4,6 +4,9 @@
* Description: Template for the master trainer dashboard page
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
// Debug output

View file

@ -4,17 +4,28 @@
* Description: Template for the trainer dashboard page
*/
// Define constant to indicate we're in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-dashboard-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu - prevent duplicates
if (class_exists('HVAC_Menu_System') && !defined('HVAC_NAV_RENDERED')) {
define('HVAC_NAV_RENDERED', true);
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<?php
// Display breadcrumbs
if (class_exists('HVAC_Breadcrumbs')) {
echo HVAC_Breadcrumbs::instance()->render_breadcrumbs();
}
?>
<div class="container">
<?php
// Render the dashboard shortcode

View file

@ -4,15 +4,17 @@
* Description: Template for creating/editing training organizers
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-organizer-manage-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -4,15 +4,17 @@
* Description: Template for listing all training organizers
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-organizers-list-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -4,15 +4,17 @@
* Description: Template for editing trainer profile
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-profile-edit-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -4,15 +4,17 @@
* Description: Template for viewing trainer profile
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-profile-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -4,15 +4,17 @@
* Description: Template for creating/editing training venues
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-venue-manage-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -4,15 +4,17 @@
* Description: Template for listing all training venues
*/
// Define constant to indicate we are in a page template
define('HVAC_IN_PAGE_TEMPLATE', true);
get_header();
?>
<div class="hvac-page-wrapper hvac-trainer-venues-list-page">
<?php
// Include trainer header with navigation and breadcrumbs
$plugin_dir = WP_PLUGIN_DIR . '/hvac-community-events/';
if (file_exists($plugin_dir . 'templates/template-parts/trainer-header.php')) {
include $plugin_dir . 'templates/template-parts/trainer-header.php';
// Display trainer navigation menu
if (class_exists('HVAC_Menu_System')) {
HVAC_Menu_System::instance()->render_trainer_menu();
}
?>
<div class="container">

View file

@ -97,25 +97,14 @@ $revenue_target = $dashboard_data->get_annual_revenue_target();
// --- Template Start ---
get_header(); // Use theme's header
// Don't call get_header() here - this template is included via shortcode
// The page template already handles header/footer
?>
<div id="primary" class="content-area primary ast-container"> <!-- Use Astra container -->
<main id="main" class="site-main">
<!-- Enhanced Navigation and Breadcrumbs -->
<div class="hvac-dashboard-wrapper"> <!-- Dashboard specific wrapper -->
<?php
// Include enhanced navigation system
if (class_exists('HVAC_Trainer_Navigation')) {
$nav = new HVAC_Trainer_Navigation();
echo $nav->render_navigation();
}
// Include breadcrumbs
if (class_exists('HVAC_Breadcrumbs')) {
$breadcrumbs = new HVAC_Breadcrumbs();
echo $breadcrumbs->render_breadcrumbs();
}
// Navigation is handled by page template when using WordPress pages
// Never render navigation here - it should be in the page template only
?>
<!-- Dashboard Header -->
@ -384,9 +373,8 @@ get_header(); // Use theme's header
</section>
</main> <!-- #main -->
</div> <!-- #primary -->
</div> <!-- .hvac-dashboard-wrapper -->
<?php
get_footer(); // Use theme's footer
// Don't call get_footer() here - this template is included via shortcode
?>

View file

@ -6,7 +6,25 @@ const BASE_URL = 'https://upskill-staging.measurequick.com';
async function loginAsTrainer(page: Page) {
await page.goto(`${BASE_URL}/wp-login.php`);
await page.fill('#user_login', 'test_trainer');
await page.fill('#user_pass', 'password123');
await page.fill('#user_pass', 'TestTrainer123!');
await page.click('#wp-submit');
await page.waitForURL('**/wp-admin/**');
}
// Helper function to login as master trainer
async function loginAsMasterTrainer(page: Page) {
await page.goto(`${BASE_URL}/wp-login.php`);
await page.fill('#user_login', 'JoeMedosch@gmail.com');
await page.fill('#user_pass', 'JoeTrainer2025@');
await page.click('#wp-submit');
await page.waitForURL('**/wp-admin/**');
}
// Helper function to login as admin
async function loginAsAdmin(page: Page) {
await page.goto(`${BASE_URL}/wp-login.php`);
await page.fill('#user_login', 'admin');
await page.fill('#user_pass', 'admin_password');
await page.click('#wp-submit');
await page.waitForURL('**/wp-admin/**');
}
@ -107,7 +125,7 @@ test.describe('Venues Management', () => {
await expect(page.locator('a:has-text("Add New Venue")')).toBeVisible();
// Verify breadcrumb
await expect(page.locator('.hvac-breadcrumb')).toContainText('Trainer > Venues > List');
await expect(page.locator('.hvac-breadcrumbs')).toBeVisible();
// Test filter functionality
await page.fill('input[name="search"]', 'Test Venue');
@ -335,27 +353,198 @@ test.describe('Organizers Management', () => {
});
});
test.describe('Navigation and Breadcrumbs', () => {
test.describe('Navigation and Layout Fixes', () => {
test.beforeEach(async ({ page }) => {
await loginAsTrainer(page);
});
test('should display proper breadcrumbs on all pages', async ({ page }) => {
test('should display horizontal navigation menu (not column layout)', async ({ page }) => {
// Test certificate reports page navigation layout
await page.goto(`${BASE_URL}/trainer/certificate-reports/`);
// Verify navigation is horizontal (flexbox layout)
const menu = page.locator('.hvac-trainer-menu');
await expect(menu).toBeVisible();
// Check that menu uses flexbox (horizontal layout)
const menuStyle = await menu.evaluate((el) => getComputedStyle(el).display);
expect(menuStyle).toBe('flex');
// Verify menu items are in a row, not column
const menuItems = page.locator('.hvac-trainer-menu .menu-item');
await expect(menuItems.first()).toBeVisible();
// Take screenshot to verify horizontal layout
await page.screenshot({
path: 'screenshots/navigation-horizontal-layout.png',
fullPage: true
});
});
test('should display new breadcrumb system on all pages', async ({ page }) => {
// Test venues breadcrumb
await page.goto('/trainer/venue/list/');
await expect(page.locator('.hvac-breadcrumb')).toContainText('Trainer > Venues > List');
await page.goto(`${BASE_URL}/trainer/venue/list/`);
await expect(page.locator('.hvac-breadcrumbs')).toBeVisible();
await expect(page.locator('.hvac-breadcrumb-list')).toBeVisible();
// Test profile breadcrumb
await page.goto('/trainer/profile/');
await expect(page.locator('.hvac-breadcrumb')).toContainText('Trainer > Profile > View');
await page.goto(`${BASE_URL}/trainer/profile/`);
await expect(page.locator('.hvac-breadcrumbs')).toBeVisible();
// Test organizers breadcrumb
await page.goto('/trainer/organizer/list/');
await expect(page.locator('.hvac-breadcrumb')).toContainText('Trainer > Organizers > List');
await page.goto(`${BASE_URL}/trainer/organizer/list/`);
await expect(page.locator('.hvac-breadcrumbs')).toBeVisible();
// Take screenshot of navigation
// Test certificate reports breadcrumb
await page.goto(`${BASE_URL}/trainer/certificate-reports/`);
await expect(page.locator('.hvac-breadcrumbs')).toBeVisible();
// Take screenshot of breadcrumb system
await page.screenshot({
path: 'screenshots/breadcrumb-navigation.png'
path: 'screenshots/breadcrumb-system.png'
});
});
test('should verify certificate pages display properly', async ({ page }) => {
// Test certificate reports page
await page.goto(`${BASE_URL}/trainer/certificate-reports/`);
await expect(page.locator('h1')).toContainText('Certificate Reports');
await expect(page.locator('.hvac-certificate-stats')).toBeVisible();
// Verify page structure is correct (not blank)
await expect(page.locator('.hvac-page-wrapper')).toBeVisible();
await expect(page.locator('.hvac-container')).toBeVisible();
// Test generate certificates page
await page.goto(`${BASE_URL}/trainer/generate-certificates/`);
await expect(page.locator('h1')).toContainText('Generate Certificates');
await expect(page.locator('.hvac-certificate-form')).toBeVisible();
// Take screenshots
await page.screenshot({
path: 'screenshots/certificate-reports-fixed.png',
fullPage: true
});
});
});
test.describe('Access Control - Multiple User Roles', () => {
test('trainer role should access all trainer pages', async ({ page }) => {
await loginAsTrainer(page);
// Test all trainer pages are accessible
const trainerPages = [
'/trainer/dashboard/',
'/trainer/profile/',
'/trainer/profile/edit/',
'/trainer/venue/list/',
'/trainer/venue/manage/',
'/trainer/organizer/list/',
'/trainer/organizer/manage/',
'/trainer/certificate-reports/',
'/trainer/generate-certificates/'
];
for (const pageUrl of trainerPages) {
await page.goto(`${BASE_URL}${pageUrl}`);
// Should not show access denied message
await expect(page.locator('p:has-text("You must be a trainer")')).not.toBeVisible();
await expect(page.locator('p:has-text("You must be logged in")')).not.toBeVisible();
}
await page.screenshot({
path: 'screenshots/trainer-access-control.png'
});
});
test('master trainer role should access all trainer pages', async ({ page }) => {
await loginAsMasterTrainer(page);
// Test master trainer can access regular trainer pages
await page.goto(`${BASE_URL}/trainer/venue/list/`);
await expect(page.locator('h1')).toContainText('Training Venues');
await page.goto(`${BASE_URL}/trainer/profile/`);
await expect(page.locator('h1')).toContainText('Trainer Profile');
await page.goto(`${BASE_URL}/trainer/organizer/list/`);
await expect(page.locator('h1')).toContainText('Training Organizers');
// Should also access master dashboard
await page.goto(`${BASE_URL}/master-trainer/master-dashboard/`);
await expect(page.locator('h1')).toContainText('Master Dashboard');
await page.screenshot({
path: 'screenshots/master-trainer-access-control.png'
});
});
});
test.describe('Enhanced Profile System', () => {
test.beforeEach(async ({ page }) => {
await loginAsTrainer(page);
});
test('should display new profile system (not old Business Information)', async ({ page }) => {
await page.goto(`${BASE_URL}/trainer/profile/`);
// Should NOT show old "Business Information" section
await expect(page.locator('h2:has-text("Business Information")')).not.toBeVisible();
// Should show new profile structure
await expect(page.locator('.hvac-profile-sidebar')).toBeVisible();
await expect(page.locator('.hvac-profile-main')).toBeVisible();
await expect(page.locator('.hvac-profile-stats')).toBeVisible();
// Should show new sections
await expect(page.locator('h2:has-text("Personal Information")')).toBeVisible();
await expect(page.locator('h2:has-text("Training Organization")')).toBeVisible();
await page.screenshot({
path: 'screenshots/new-profile-system.png',
fullPage: true
});
});
});
test.describe('Organizers and Venues Content Verification', () => {
test.beforeEach(async ({ page }) => {
await loginAsTrainer(page);
});
test('should display organizers list content (not blank)', async ({ page }) => {
await page.goto(`${BASE_URL}/trainer/organizer/list/`);
// Should not be blank
await expect(page.locator('.hvac-organizers-list')).toBeVisible();
await expect(page.locator('h1')).toContainText('Training Organizers');
await expect(page.locator('a:has-text("Add New Organizer")')).toBeVisible();
// Verify page has actual content, not just whitespace
const bodyText = await page.locator('body').textContent();
expect(bodyText?.trim().length).toBeGreaterThan(100);
await page.screenshot({
path: 'screenshots/organizers-not-blank.png',
fullPage: true
});
});
test('should display venues list content (not blank)', async ({ page }) => {
await page.goto(`${BASE_URL}/trainer/venue/list/`);
// Should not be blank
await expect(page.locator('.hvac-venues-list')).toBeVisible();
await expect(page.locator('h1')).toContainText('Training Venues');
await expect(page.locator('a:has-text("Add New Venue")')).toBeVisible();
// Verify page has actual content, not just whitespace
const bodyText = await page.locator('body').textContent();
expect(bodyText?.trim().length).toBeGreaterThan(100);
await page.screenshot({
path: 'screenshots/venues-not-blank.png',
fullPage: true
});
});
});