fix: master trainer dashboard template loading and navigation restructure
Three critical fixes to resolve dashboard not rendering below navigation: 1. Template Loading Fix (class-hvac-community-events.php:838-840): - Force custom template loading for master dashboard page - Remove WordPress template assignment dependency that was failing 2. Direct Template Inclusion (page-master-dashboard.php:44): - Replace shortcode approach with direct include - Bypass shortcode processing issues preventing content render 3. Navigation Restructure (class-hvac-master-menu-system.php): - Reduce navigation from 17 complex items to 5 essential items - Add capability-based filtering and internationalization - Implement proper WordPress security patterns Successfully addresses user-reported issues: - No content below toolbar (template inclusion fix) - Overly complex UI elements (17→5 navigation items) - Non-functional navigation links (structured menu system) Architecture improvements: - Proper role-based access control (roles vs capabilities) - Plugin hook extensibility with apply_filters - Comprehensive capability filtering system - WordPress i18n compliance with esc_html__() 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
201e507b14
commit
9c2e8cdd3c
3 changed files with 382 additions and 27 deletions
|
|
@ -572,8 +572,9 @@ class HVAC_Community_Events {
|
||||||
return '<p>Please log in to view the master dashboard.</p>';
|
return '<p>Please log in to view the master dashboard.</p>';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user has master dashboard permissions - include administrator
|
// Check if user has master dashboard permissions - check roles instead of capabilities
|
||||||
if (!current_user_can('view_master_dashboard') && !current_user_can('view_all_trainer_data') && !current_user_can('administrator')) {
|
$user = wp_get_current_user();
|
||||||
|
if (!in_array('hvac_master_trainer', $user->roles) && !current_user_can('manage_options')) {
|
||||||
return '<div class="hvac-error">You do not have permission to view the master dashboard. This dashboard is only available to Master Trainers and Administrators.</div>';
|
return '<div class="hvac-error">You do not have permission to view the master dashboard. This dashboard is only available to Master Trainers and Administrators.</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -833,14 +834,9 @@ class HVAC_Community_Events {
|
||||||
$custom_template = HVAC_PLUGIN_DIR . 'templates/page-trainer-dashboard.php';
|
$custom_template = HVAC_PLUGIN_DIR . 'templates/page-trainer-dashboard.php';
|
||||||
}
|
}
|
||||||
|
|
||||||
// For master dashboard, check if page has custom template
|
// For master dashboard, force our template regardless of WordPress template assignment
|
||||||
if (is_page('master-trainer/master-dashboard')) {
|
if (is_page('master-trainer/master-dashboard')) {
|
||||||
global $post;
|
$custom_template = HVAC_PLUGIN_DIR . 'templates/page-master-dashboard.php';
|
||||||
$page_template = get_post_meta($post->ID, '_wp_page_template', true);
|
|
||||||
if ($page_template && $page_template !== 'default') {
|
|
||||||
// Let WordPress handle the page template
|
|
||||||
return $template;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for google-sheets page
|
// Check for google-sheets page
|
||||||
|
|
|
||||||
342
includes/class-hvac-master-menu-system.php
Normal file
342
includes/class-hvac-master-menu-system.php
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* HVAC Master Trainer Menu System
|
||||||
|
*
|
||||||
|
* Handles navigation menus specifically for master trainer pages
|
||||||
|
*
|
||||||
|
* @package HVAC_Community_Events
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('ABSPATH')) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HVAC_Master_Menu_System {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance of this class
|
||||||
|
*
|
||||||
|
* @var HVAC_Master_Menu_System
|
||||||
|
*/
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get instance of this class
|
||||||
|
*
|
||||||
|
* @return HVAC_Master_Menu_System
|
||||||
|
*/
|
||||||
|
public static function instance() {
|
||||||
|
if (self::$instance === null) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
private function __construct() {
|
||||||
|
// No auto-init hooks - will be called manually on master pages
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current page is a master trainer page
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function is_master_trainer_page() {
|
||||||
|
$current_url = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
// List of master trainer page patterns
|
||||||
|
$master_pages = array(
|
||||||
|
'/master-trainer/master-dashboard/',
|
||||||
|
'/master-trainer/announcements/',
|
||||||
|
'/master-trainer/edit-trainer-profile/',
|
||||||
|
'/master-trainer/communication-templates/',
|
||||||
|
'/master-trainer/google-sheets/',
|
||||||
|
'/master-trainer/trainers/',
|
||||||
|
'/master-trainer/pending-approvals/',
|
||||||
|
'/master-trainer/trainer-stats/',
|
||||||
|
'/master-trainer/import-export/'
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($master_pages as $page) {
|
||||||
|
if (strpos($current_url, $page) !== false) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render master trainer navigation menu
|
||||||
|
*/
|
||||||
|
public function render_master_menu() {
|
||||||
|
// Check if user has master trainer permissions
|
||||||
|
$user = wp_get_current_user();
|
||||||
|
if (!in_array('hvac_master_trainer', $user->roles) && !current_user_can('manage_options')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$menu_items = $this->get_master_menu_structure();
|
||||||
|
|
||||||
|
echo '<div class="hvac-master-menu-wrapper">';
|
||||||
|
echo '<nav class="hvac-master-nav" role="navigation">';
|
||||||
|
|
||||||
|
// Add hamburger button for mobile
|
||||||
|
echo '<button class="hvac-hamburger-menu" id="hvac-master-hamburger-menu" aria-label="Toggle menu" aria-expanded="false">';
|
||||||
|
echo '<span class="hvac-hamburger-line"></span>';
|
||||||
|
echo '<span class="hvac-hamburger-line"></span>';
|
||||||
|
echo '<span class="hvac-hamburger-line"></span>';
|
||||||
|
echo '</button>';
|
||||||
|
|
||||||
|
echo '<ul class="hvac-master-menu" id="hvac-master-menu">';
|
||||||
|
|
||||||
|
foreach ($menu_items as $item) {
|
||||||
|
$this->render_menu_item($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</ul>';
|
||||||
|
echo '</nav>';
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get master trainer menu structure
|
||||||
|
* Simplified to 5 essential items following UX best practices
|
||||||
|
*/
|
||||||
|
private function get_master_menu_structure() {
|
||||||
|
// Define simplified menu structure (5 primary items max)
|
||||||
|
$menu = array(
|
||||||
|
// Dashboard - Primary landing page
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Dashboard', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/master-dashboard/'),
|
||||||
|
'icon' => 'dashicons-dashboard',
|
||||||
|
'cap' => 'hvac_master_trainer'
|
||||||
|
),
|
||||||
|
|
||||||
|
// Trainers - Core management function
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Trainers', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/trainers/'),
|
||||||
|
'icon' => 'dashicons-groups',
|
||||||
|
'cap' => 'hvac_master_trainer',
|
||||||
|
'children' => array(
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('All Trainers', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/trainers/'),
|
||||||
|
'icon' => 'dashicons-list-view',
|
||||||
|
'cap' => 'hvac_master_trainer'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Pending Approvals', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/pending-approvals/'),
|
||||||
|
'icon' => 'dashicons-clock',
|
||||||
|
'cap' => 'approve_trainers'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
// Events - Aggregate event management
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Events', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/events/'),
|
||||||
|
'icon' => 'dashicons-calendar-alt',
|
||||||
|
'cap' => 'view_all_events'
|
||||||
|
),
|
||||||
|
|
||||||
|
// Tools - Administrative functions
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Tools', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/communication-templates/'),
|
||||||
|
'icon' => 'dashicons-admin-tools',
|
||||||
|
'cap' => 'manage_communication_templates',
|
||||||
|
'children' => array(
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Communication Templates', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/communication-templates/'),
|
||||||
|
'icon' => 'dashicons-email',
|
||||||
|
'cap' => 'manage_communication_templates'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Announcements', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/announcements/'),
|
||||||
|
'icon' => 'dashicons-megaphone',
|
||||||
|
'cap' => 'manage_announcements'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Import/Export', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/master-trainer/import-export/'),
|
||||||
|
'icon' => 'dashicons-database-import',
|
||||||
|
'cap' => 'import_export_data'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
|
// Help - Documentation
|
||||||
|
array(
|
||||||
|
'title' => esc_html__('Help', 'hvac-community-events'),
|
||||||
|
'url' => home_url('/trainer/documentation/'),
|
||||||
|
'icon' => 'dashicons-editor-help',
|
||||||
|
'cap' => 'read',
|
||||||
|
'class' => 'hvac-help-menu-item'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Allow other plugins to modify menu
|
||||||
|
$menu = apply_filters('hvac_master_menu_items', $menu);
|
||||||
|
|
||||||
|
// Filter menu items by user capabilities
|
||||||
|
$menu = $this->filter_menu_by_capabilities($menu);
|
||||||
|
|
||||||
|
return $menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter menu items by user capabilities
|
||||||
|
* Removes items the current user cannot access
|
||||||
|
*
|
||||||
|
* @param array $menu_items Menu items array
|
||||||
|
* @return array Filtered menu items
|
||||||
|
*/
|
||||||
|
private function filter_menu_by_capabilities($menu_items) {
|
||||||
|
$filtered_menu = array();
|
||||||
|
|
||||||
|
foreach ($menu_items as $item) {
|
||||||
|
// Check if user has capability for this item
|
||||||
|
$required_cap = isset($item['cap']) ? $item['cap'] : 'hvac_master_trainer';
|
||||||
|
|
||||||
|
if (!current_user_can($required_cap)) {
|
||||||
|
continue; // Skip this item
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter children if they exist
|
||||||
|
if (isset($item['children']) && is_array($item['children'])) {
|
||||||
|
$filtered_children = array();
|
||||||
|
|
||||||
|
foreach ($item['children'] as $child) {
|
||||||
|
$child_cap = isset($child['cap']) ? $child['cap'] : 'hvac_master_trainer';
|
||||||
|
|
||||||
|
if (current_user_can($child_cap)) {
|
||||||
|
$filtered_children[] = $child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only include parent if it has accessible children or is directly accessible
|
||||||
|
if (!empty($filtered_children)) {
|
||||||
|
$item['children'] = $filtered_children;
|
||||||
|
$filtered_menu[] = $item;
|
||||||
|
} elseif (!isset($item['children']) || empty($item['children'])) {
|
||||||
|
// Include parent items without children
|
||||||
|
$filtered_menu[] = $item;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Include items without children
|
||||||
|
$filtered_menu[] = $item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filtered_menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a single menu item
|
||||||
|
*
|
||||||
|
* @param array $item Menu item configuration
|
||||||
|
*/
|
||||||
|
private function render_menu_item($item) {
|
||||||
|
$has_children = !empty($item['children']);
|
||||||
|
$current_url = $_SERVER['REQUEST_URI'];
|
||||||
|
|
||||||
|
// Check if this item or any of its children are active
|
||||||
|
$is_active = $this->is_menu_item_active($item, $current_url);
|
||||||
|
|
||||||
|
$li_classes = array('menu-item');
|
||||||
|
if ($is_active) {
|
||||||
|
$li_classes[] = 'current-menu-item';
|
||||||
|
}
|
||||||
|
if ($has_children) {
|
||||||
|
$li_classes[] = 'has-children';
|
||||||
|
}
|
||||||
|
if (!empty($item['class'])) {
|
||||||
|
$li_classes[] = $item['class'];
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<li class="' . implode(' ', $li_classes) . '">';
|
||||||
|
|
||||||
|
// Main link
|
||||||
|
$link_attrs = array(
|
||||||
|
'href="' . esc_url($item['url']) . '"'
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($has_children) {
|
||||||
|
$link_attrs[] = 'aria-haspopup="true"';
|
||||||
|
$link_attrs[] = 'aria-expanded="false"';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<a ' . implode(' ', $link_attrs) . '>';
|
||||||
|
|
||||||
|
if (!empty($item['icon'])) {
|
||||||
|
echo '<span class="dashicons ' . esc_attr($item['icon']) . '"></span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<span class="menu-title">' . esc_html($item['title']) . '</span>';
|
||||||
|
|
||||||
|
if ($has_children) {
|
||||||
|
echo '<span class="menu-toggle" aria-hidden="true"></span>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</a>';
|
||||||
|
|
||||||
|
// Children
|
||||||
|
if ($has_children) {
|
||||||
|
echo '<ul class="sub-menu">';
|
||||||
|
foreach ($item['children'] as $child) {
|
||||||
|
$this->render_menu_item($child);
|
||||||
|
}
|
||||||
|
echo '</ul>';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if menu item is active
|
||||||
|
*
|
||||||
|
* @param array $item Menu item
|
||||||
|
* @param string $current_url Current URL
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function is_menu_item_active($item, $current_url) {
|
||||||
|
// Clean URLs for comparison
|
||||||
|
$item_path = parse_url($item['url'], PHP_URL_PATH);
|
||||||
|
$current_path = parse_url($current_url, PHP_URL_PATH);
|
||||||
|
|
||||||
|
// Check exact match
|
||||||
|
if ($item_path === $current_path) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if current URL starts with item URL (for parent items)
|
||||||
|
if (!empty($item_path) && $item_path !== '/' && strpos($current_path, $item_path) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check children
|
||||||
|
if (!empty($item['children'])) {
|
||||||
|
foreach ($item['children'] as $child) {
|
||||||
|
if ($this->is_menu_item_active($child, $current_url)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the class
|
||||||
|
HVAC_Master_Menu_System::instance();
|
||||||
|
|
@ -9,24 +9,41 @@ define('HVAC_IN_PAGE_TEMPLATE', true);
|
||||||
|
|
||||||
get_header();
|
get_header();
|
||||||
|
|
||||||
// Debug output
|
// Check master trainer permissions FIRST
|
||||||
echo '<!-- page-master-dashboard.php template loaded -->';
|
$user = wp_get_current_user();
|
||||||
echo '<!-- Page ID: ' . get_the_ID() . ' -->';
|
if (!in_array('hvac_master_trainer', $user->roles) && !current_user_can('manage_options')) {
|
||||||
echo '<!-- Page Title: ' . get_the_title() . ' -->';
|
?>
|
||||||
|
<div class="hvac-page-wrapper">
|
||||||
// Force render the master dashboard content directly
|
<div class="container">
|
||||||
if (class_exists('HVAC_Community_Events')) {
|
<h1>Access Denied</h1>
|
||||||
$hvac = HVAC_Community_Events::instance();
|
<p>You do not have permission to access this page.</p>
|
||||||
if (method_exists($hvac, 'render_master_dashboard')) {
|
<p>If you believe this is an error, please contact an administrator.</p>
|
||||||
echo '<!-- Calling render_master_dashboard directly -->';
|
<a href="<?php echo home_url(); ?>" class="button">Return to Home</a>
|
||||||
echo $hvac->render_master_dashboard();
|
</div>
|
||||||
} else {
|
</div>
|
||||||
echo '<!-- render_master_dashboard method not found -->';
|
<?php
|
||||||
echo do_shortcode('[hvac_master_dashboard]');
|
get_footer();
|
||||||
}
|
exit;
|
||||||
} else {
|
|
||||||
echo '<!-- HVAC_Community_Events class not found -->';
|
|
||||||
echo do_shortcode('[hvac_master_dashboard]');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render master trainer navigation
|
||||||
|
if (class_exists('HVAC_Master_Menu_System')) {
|
||||||
|
$master_menu = HVAC_Master_Menu_System::instance();
|
||||||
|
$master_menu->render_master_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render breadcrumbs
|
||||||
|
if (class_exists('HVAC_Breadcrumbs')) {
|
||||||
|
HVAC_Breadcrumbs::render();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="hvac-page-wrapper hvac-master-dashboard-page">';
|
||||||
|
echo '<div class="container">';
|
||||||
|
|
||||||
|
// Render the master dashboard content directly (bypassing shortcode processing)
|
||||||
|
include HVAC_PLUGIN_DIR . 'templates/template-hvac-master-dashboard.php';
|
||||||
|
|
||||||
|
echo '</div>'; // .container
|
||||||
|
echo '</div>'; // .hvac-page-wrapper
|
||||||
|
|
||||||
get_footer();
|
get_footer();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue