upskill-event-manager/includes/class-hvac-breadcrumbs.php
bengizmo 40274d98ad 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>
2025-08-01 10:52:11 -03:00

414 lines
No EOL
12 KiB
PHP

<?php
/**
* 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
*/
if (!defined('ABSPATH')) {
exit;
}
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() {
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
}
/**
* Enqueue breadcrumb styles
*/
public function enqueue_styles() {
if ($this->is_trainer_page()) {
wp_enqueue_style(
'hvac-breadcrumbs',
HVAC_PLUGIN_URL . 'assets/css/hvac-breadcrumbs.css',
array(),
HVAC_PLUGIN_VERSION
);
}
}
/**
* 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);
}
/**
* 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'
);
$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/'
),
'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/'
)
);
// Build breadcrumb trail
for ($i = 0; $i < count($path_parts); $i++) {
$part = $path_parts[$i];
$current_path .= '/' . $part;
if (isset($breadcrumb_map[$part])) {
$crumb = $breadcrumb_map[$part];
// Handle context-dependent titles
if (isset($crumb['context_dependent']) && $crumb['context_dependent']) {
$crumb['title'] = $this->get_contextual_title($part, $path_parts, $i);
}
// 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;
}
}
// 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
);
}
}
return $breadcrumbs;
}
/**
* 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_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(
'title' => $parent_map[$parent_title]['title'],
'url' => home_url($parent_map[$parent_title]['url']),
'position' => 1
);
}
return null;
}
/**
* Render breadcrumb HTML with Schema.org markup
*
* @param array $breadcrumbs Breadcrumb items
* @param array $options Rendering options
* @return string HTML output
*/
private function render_breadcrumb_html($breadcrumbs, $options) {
if (empty($breadcrumbs)) {
return '';
}
$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);
}
$html .= '<ol class="hvac-breadcrumb-list">';
// 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;
}
/**
* Generate Schema.org JSON-LD markup for breadcrumbs
*
* @param array $breadcrumbs Breadcrumb items
* @return string JSON-LD script tag
*/
private function generate_schema_markup($breadcrumbs) {
$schema_items = array();
$position = 1;
// Add home item
$schema_items[] = array(
'@type' => 'ListItem',
'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'];
}
$schema_items[] = $item;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'BreadcrumbList',
'itemListElement' => $schema_items
);
return '<script type="application/ld+json">' . wp_json_encode($schema, JSON_UNESCAPED_SLASHES) . '</script>';
}
}