## Major Enhancements ### 🏗️ Architecture & Infrastructure - Implement comprehensive Docker testing infrastructure with hermetic environment - Add Forgejo Actions CI/CD pipeline for automated deployments - Create Page Object Model (POM) testing architecture reducing test duplication by 90% - Establish security-first development patterns with input validation and output escaping ### 🧪 Testing Framework Modernization - Migrate 146+ tests from 80 duplicate files to centralized architecture - Add comprehensive E2E test suites for all user roles and workflows - Implement WordPress error detection with automatic site health monitoring - Create robust browser lifecycle management with proper cleanup ### 📚 Documentation & Guides - Add comprehensive development best practices guide - Create detailed administrator setup documentation - Establish user guides for trainers and master trainers - Document security incident reports and migration guides ### 🔧 Core Plugin Features - Enhance trainer profile management with certification system - Improve find trainer functionality with advanced filtering - Strengthen master trainer area with content management - Add comprehensive venue and organizer management ### 🛡️ Security & Reliability - Implement security-first patterns throughout codebase - Add comprehensive input validation and output escaping - Create secure credential management system - Establish proper WordPress role-based access control ### 🎯 WordPress Integration - Strengthen singleton pattern implementation across all classes - Enhance template hierarchy with proper WordPress integration - Improve page manager with hierarchical URL structure - Add comprehensive shortcode and menu system ### 🔍 Developer Experience - Add extensive debugging and troubleshooting tools - Create comprehensive test data seeding scripts - Implement proper error handling and logging - Establish consistent code patterns and standards ### 📊 Performance & Optimization - Optimize database queries and caching strategies - Improve asset loading and script management - Enhance template rendering performance - Streamline user experience across all interfaces 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1236 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			1236 lines
		
	
	
	
		
			50 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| if (!defined('ABSPATH')) {
 | |
|     exit;
 | |
| }
 | |
| 
 | |
| class HVAC_Trainer_Profile_Manager {
 | |
|     
 | |
|     private static $instance = null;
 | |
|     
 | |
|     public static function get_instance() {
 | |
|         if (null === self::$instance) {
 | |
|             self::$instance = new self();
 | |
|         }
 | |
|         return self::$instance;
 | |
|     }
 | |
|     
 | |
|     private function __construct() {
 | |
|         add_action('init', [$this, 'register_post_type']);
 | |
|         add_action('init', [$this, 'register_taxonomies']);
 | |
|         add_action('add_user_role', [$this, 'maybe_create_trainer_profile'], 10, 2);
 | |
|         add_action('set_user_role', [$this, 'maybe_create_trainer_profile'], 10, 3);
 | |
|         add_filter('map_meta_cap', [$this, 'trainer_profile_edit_permissions'], 10, 4);
 | |
|         
 | |
|         // Register shortcodes for backwards compatibility
 | |
|         add_shortcode('hvac_trainer_profile_view', [$this, 'render_profile_view']);
 | |
|         add_shortcode('hvac_trainer_profile_edit', [$this, 'render_profile_edit']);
 | |
|         
 | |
|         // AJAX handlers
 | |
|         add_action('wp_ajax_hvac_save_trainer_profile', [$this, 'ajax_save_trainer_profile']);
 | |
|         add_action('wp_ajax_hvac_auto_save_profile', [$this, 'ajax_auto_save_profile']);
 | |
|         
 | |
|         // Hook into plugin activation to create profiles for existing trainers
 | |
|         add_action('hvac_plugin_activated', [$this, 'create_profiles_for_existing_trainers']);
 | |
|         
 | |
|         // Add migration hook to update certification colors for existing profiles
 | |
|         add_action('hvac_plugin_activated', [$this, 'migrate_certification_colors']);
 | |
|         add_action('init', [$this, 'maybe_migrate_certification_colors'], 20);
 | |
|     }
 | |
|     
 | |
|     public function register_post_type() {
 | |
|         register_post_type('trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Trainer Profiles',
 | |
|                 'singular_name' => 'Trainer Profile',
 | |
|                 'edit_item' => 'Edit Trainer Profile',
 | |
|                 'add_new_item' => 'Add New Trainer Profile',
 | |
|                 'view_item' => 'View Trainer Profile',
 | |
|                 'search_items' => 'Search Trainer Profiles',
 | |
|                 'not_found' => 'No trainer profiles found',
 | |
|                 'not_found_in_trash' => 'No trainer profiles found in trash'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'publicly_queryable' => true,
 | |
|             'show_ui' => true,
 | |
|             'show_in_menu' => 'hvac-settings',
 | |
|             'show_in_rest' => true,
 | |
|             'rest_base' => 'trainer-profiles',
 | |
|             'capability_type' => 'post',
 | |
|             'supports' => ['title', 'editor', 'custom-fields', 'thumbnail'],
 | |
|             'has_archive' => true,
 | |
|             'rewrite' => ['slug' => 'trainers'],
 | |
|             'menu_icon' => 'dashicons-groups'
 | |
|         ]);
 | |
|     }
 | |
|     
 | |
|     public function register_taxonomies() {
 | |
|         // Business Type taxonomy
 | |
|         register_taxonomy('business_type', 'trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Business Types',
 | |
|                 'singular_name' => 'Business Type',
 | |
|                 'add_new_item' => 'Add New Business Type',
 | |
|                 'edit_item' => 'Edit Business Type'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'hierarchical' => false,
 | |
|             'show_in_rest' => true,
 | |
|             'rewrite' => ['slug' => 'business-type']
 | |
|         ]);
 | |
|         
 | |
|         // Training Audience taxonomy
 | |
|         register_taxonomy('training_audience', 'trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Training Audiences',
 | |
|                 'singular_name' => 'Training Audience',
 | |
|                 'add_new_item' => 'Add New Training Audience',
 | |
|                 'edit_item' => 'Edit Training Audience'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'hierarchical' => false,
 | |
|             'show_in_rest' => true,
 | |
|             'rewrite' => ['slug' => 'training-audience']
 | |
|         ]);
 | |
|         
 | |
|         // Training Formats taxonomy
 | |
|         register_taxonomy('training_formats', 'trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Training Formats',
 | |
|                 'singular_name' => 'Training Format',
 | |
|                 'add_new_item' => 'Add New Training Format',
 | |
|                 'edit_item' => 'Edit Training Format'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'hierarchical' => false,
 | |
|             'show_in_rest' => true,
 | |
|             'rewrite' => ['slug' => 'training-formats']
 | |
|         ]);
 | |
|         
 | |
|         // Training Locations taxonomy
 | |
|         register_taxonomy('training_locations', 'trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Training Locations',
 | |
|                 'singular_name' => 'Training Location',
 | |
|                 'add_new_item' => 'Add New Training Location',
 | |
|                 'edit_item' => 'Edit Training Location'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'hierarchical' => false,
 | |
|             'show_in_rest' => true,
 | |
|             'rewrite' => ['slug' => 'training-locations']
 | |
|         ]);
 | |
|         
 | |
|         // Training Resources taxonomy
 | |
|         register_taxonomy('training_resources', 'trainer_profile', [
 | |
|             'labels' => [
 | |
|                 'name' => 'Training Resources',
 | |
|                 'singular_name' => 'Training Resource',
 | |
|                 'add_new_item' => 'Add New Training Resource',
 | |
|                 'edit_item' => 'Edit Training Resource'
 | |
|             ],
 | |
|             'public' => true,
 | |
|             'hierarchical' => false,
 | |
|             'show_in_rest' => true,
 | |
|             'rewrite' => ['slug' => 'training-resources']
 | |
|         ]);
 | |
|         
 | |
|         // Populate default terms for all taxonomies
 | |
|         $this->create_default_taxonomy_terms();
 | |
|     }
 | |
|     
 | |
|     private function create_default_taxonomy_terms() {
 | |
|         // Business Types (updated to match user requirements)
 | |
|         $business_types = [
 | |
|             'Manufacturer',
 | |
|             'Distributor',
 | |
|             'Contractor',
 | |
|             'Consultant',
 | |
|             'Educator',
 | |
|             'Government',
 | |
|             'Other'
 | |
|         ];
 | |
|         
 | |
|         foreach ($business_types as $type) {
 | |
|             if (!term_exists($type, 'business_type')) {
 | |
|                 wp_insert_term($type, 'business_type');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Training Audiences
 | |
|         $training_audiences = [
 | |
|             'Anyone (open to the public)',
 | |
|             'Industry professionals',
 | |
|             'Internal staff in my company',
 | |
|             'Registered students/members of my org/institution'
 | |
|         ];
 | |
|         
 | |
|         foreach ($training_audiences as $audience) {
 | |
|             if (!term_exists($audience, 'training_audience')) {
 | |
|                 wp_insert_term($audience, 'training_audience');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Training Formats
 | |
|         $training_formats = [
 | |
|             'In-person',
 | |
|             'Virtual',
 | |
|             'Hybrid',
 | |
|             'On-demand'
 | |
|         ];
 | |
|         
 | |
|         foreach ($training_formats as $format) {
 | |
|             if (!term_exists($format, 'training_formats')) {
 | |
|                 wp_insert_term($format, 'training_formats');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Training Locations
 | |
|         $training_locations = [
 | |
|             'Online',
 | |
|             'Local',
 | |
|             'Regional Travel',
 | |
|             'National Travel',
 | |
|             'International Travel'
 | |
|         ];
 | |
|         
 | |
|         foreach ($training_locations as $location) {
 | |
|             if (!term_exists($location, 'training_locations')) {
 | |
|                 wp_insert_term($location, 'training_locations');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Training Resources
 | |
|         $training_resources = [
 | |
|             'Classroom',
 | |
|             'Training Lab',
 | |
|             'Ducted Furnace(s)',
 | |
|             'Ducted Air Handler(s)',
 | |
|             'Ducted Air Conditioner(s)',
 | |
|             'Ducted Heat Pump(s)',
 | |
|             'Ductless Heat Pump(s)',
 | |
|             'Training Manuals',
 | |
|             'Presentation Slides',
 | |
|             'LMS Platform / SCORM Files',
 | |
|             'Custom Curriculum',
 | |
|             'Other'
 | |
|         ];
 | |
|         
 | |
|         foreach ($training_resources as $resource) {
 | |
|             if (!term_exists($resource, 'training_resources')) {
 | |
|                 wp_insert_term($resource, 'training_resources');
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function maybe_create_trainer_profile($user_id, $role, $old_roles = null) {
 | |
|         $trainer_roles = ['hvac_trainer', 'hvac_master_trainer'];
 | |
|         
 | |
|         if (in_array($role, $trainer_roles)) {
 | |
|             $this->create_trainer_profile($user_id);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function create_trainer_profile($user_id, $csv_data = null) {
 | |
|         // Check if profile already exists
 | |
|         $existing_profile_id = get_user_meta($user_id, 'trainer_profile_id', true);
 | |
|         if ($existing_profile_id && get_post($existing_profile_id)) {
 | |
|             return $existing_profile_id;
 | |
|         }
 | |
|         
 | |
|         $user = get_userdata($user_id);
 | |
|         if (!$user) {
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         // Create trainer profile post
 | |
|         $profile_data = [
 | |
|             'post_type' => 'trainer_profile',
 | |
|             'post_title' => $user->display_name . ' - Trainer Profile',
 | |
|             'post_status' => 'publish',
 | |
|             'post_author' => $user_id,
 | |
|             'post_content' => get_user_meta($user_id, 'biographical_info', true) ?: ''
 | |
|         ];
 | |
|         
 | |
|         $profile_id = wp_insert_post($profile_data);
 | |
|         
 | |
|         if (is_wp_error($profile_id)) {
 | |
|             error_log('HVAC Trainer Profile: Failed to create profile for user ' . $user_id . ': ' . $profile_id->get_error_message());
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         // Establish relationships
 | |
|         update_post_meta($profile_id, 'user_id', $user_id);
 | |
|         update_user_meta($user_id, 'trainer_profile_id', $profile_id);
 | |
|         
 | |
|         // Migrate user meta to profile meta
 | |
|         $this->migrate_user_meta_to_profile($user_id, $profile_id, $csv_data);
 | |
|         
 | |
|         // Set default visibility
 | |
|         update_post_meta($profile_id, 'is_public_profile', '1');
 | |
|         
 | |
|         return $profile_id;
 | |
|     }
 | |
|     
 | |
|     private function migrate_user_meta_to_profile($user_id, $profile_id, $csv_data = null) {
 | |
|         $user = get_userdata($user_id);
 | |
|         
 | |
|         // Synchronized fields (user ↔ profile)
 | |
|         $sync_fields = [
 | |
|             'first_name' => 'trainer_first_name',
 | |
|             'last_name' => 'trainer_last_name',
 | |
|             'display_name' => 'trainer_display_name'
 | |
|         ];
 | |
|         
 | |
|         foreach ($sync_fields as $user_field => $profile_field) {
 | |
|             $value = $user->$user_field;
 | |
|             if ($csv_data && isset($csv_data[$user_field])) {
 | |
|                 $value = $csv_data[$user_field];
 | |
|             }
 | |
|             update_post_meta($profile_id, $profile_field, $value);
 | |
|         }
 | |
|         
 | |
|         // Profile-exclusive fields (non-taxonomy)
 | |
|         $profile_fields = [
 | |
|             'linkedin_profile_url',
 | |
|             'personal_accreditation', 
 | |
|             'annual_revenue_target',
 | |
|             'application_details',
 | |
|             'date_certified',
 | |
|             'certification_type',
 | |
|             'certification_status'
 | |
|         ];
 | |
|         
 | |
|         foreach ($profile_fields as $field) {
 | |
|             $value = '';
 | |
|             
 | |
|             // Use CSV data if available
 | |
|             if ($csv_data && isset($csv_data[$field])) {
 | |
|                 $value = $csv_data[$field];
 | |
|             } else {
 | |
|                 // Migrate from user meta
 | |
|                 $value = get_user_meta($user_id, $field, true);
 | |
|             }
 | |
|             
 | |
|             if ($value) {
 | |
|                 update_post_meta($profile_id, $field, $value);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle location fields (map from org_headquarters_* to trainer_*)
 | |
|         $location_mapping = [
 | |
|             'org_headquarters_city' => 'trainer_city',
 | |
|             'org_headquarters_state' => 'trainer_state', 
 | |
|             'org_headquarters_country' => 'trainer_country'
 | |
|         ];
 | |
|         
 | |
|         foreach ($location_mapping as $user_field => $profile_field) {
 | |
|             $value = '';
 | |
|             
 | |
|             if ($csv_data && isset($csv_data[$profile_field])) {
 | |
|                 $value = $csv_data[$profile_field];
 | |
|             } else {
 | |
|                 // Get from user meta using the registration field name
 | |
|                 $value = get_user_meta($user_id, $user_field, true);
 | |
|             }
 | |
|             
 | |
|             if ($value) {
 | |
|                 update_post_meta($profile_id, $profile_field, $value);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle training fields (deserialize arrays and convert to strings)
 | |
|         $training_fields = [
 | |
|             'training_formats',
 | |
|             'training_locations',
 | |
|             'training_audience',
 | |
|             'training_resources'
 | |
|         ];
 | |
|         
 | |
|         foreach ($training_fields as $field) {
 | |
|             $value = '';
 | |
|             
 | |
|             if ($csv_data && isset($csv_data[$field])) {
 | |
|                 $value = $csv_data[$field];
 | |
|             } else {
 | |
|                 // Get serialized data from user meta and convert to string
 | |
|                 $serialized_data = get_user_meta($user_id, $field, true);
 | |
|                 if ($serialized_data && is_array($serialized_data)) {
 | |
|                     $value = implode(', ', $serialized_data);
 | |
|                 } elseif ($serialized_data && is_string($serialized_data)) {
 | |
|                     // Already a string, use as-is
 | |
|                     $value = $serialized_data;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             if ($value) {
 | |
|                 update_post_meta($profile_id, $field, $value);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Set certification color based on certification type
 | |
|         $certification_type = get_post_meta($profile_id, 'certification_type', true);
 | |
|         if ($certification_type) {
 | |
|             $certification_color = $this->get_certification_color($certification_type);
 | |
|             update_post_meta($profile_id, 'certification_color', $certification_color);
 | |
|         } else {
 | |
|             // Set default color for profiles without certification type
 | |
|             update_post_meta($profile_id, 'certification_color', $this->get_certification_color(''));
 | |
|         }
 | |
|         
 | |
|         // Handle taxonomy fields
 | |
|         $this->migrate_taxonomy_fields($user_id, $profile_id, $csv_data);
 | |
|         
 | |
|         // Set default certification status if not provided
 | |
|         if (!get_post_meta($profile_id, 'certification_status', true)) {
 | |
|             update_post_meta($profile_id, 'certification_status', 'Active');
 | |
|         }
 | |
|         
 | |
|         // Clean up old user meta fields to prevent confusion
 | |
|         foreach ($profile_fields as $field) {
 | |
|             delete_user_meta($user_id, $field);
 | |
|         }
 | |
|         
 | |
|         // Clean up old taxonomy fields from user meta
 | |
|         $taxonomy_fields = ['business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources'];
 | |
|         foreach ($taxonomy_fields as $field) {
 | |
|             delete_user_meta($user_id, $field);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     private function migrate_taxonomy_fields($user_id, $profile_id, $csv_data = null) {
 | |
|         $taxonomy_mappings = [
 | |
|             'business_type' => 'business_type',
 | |
|             'training_audience' => 'training_audience', 
 | |
|             'training_formats' => 'training_formats',
 | |
|             'training_locations' => 'training_locations',
 | |
|             'training_resources' => 'training_resources'
 | |
|         ];
 | |
|         
 | |
|         foreach ($taxonomy_mappings as $field => $taxonomy) {
 | |
|             $terms_to_assign = [];
 | |
|             
 | |
|             // Get data from CSV first, then user meta
 | |
|             if ($csv_data && !empty($csv_data[$field])) {
 | |
|                 $value = $csv_data[$field];
 | |
|             } else {
 | |
|                 $value = get_user_meta($user_id, $field, true);
 | |
|             }
 | |
|             
 | |
|             if (empty($value)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Handle different data formats
 | |
|             if (is_array($value)) {
 | |
|                 // Array of terms
 | |
|                 $term_names = $value;
 | |
|             } elseif (is_string($value)) {
 | |
|                 // String - could be comma-separated or single value
 | |
|                 $term_names = array_map('trim', explode(',', $value));
 | |
|             } else {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Find or create terms and collect their IDs
 | |
|             foreach ($term_names as $term_name) {
 | |
|                 if (empty($term_name)) {
 | |
|                     continue;
 | |
|                 }
 | |
|                 
 | |
|                 $term = get_term_by('name', $term_name, $taxonomy);
 | |
|                 if (!$term) {
 | |
|                     // Create the term if it doesn't exist
 | |
|                     $result = wp_insert_term($term_name, $taxonomy);
 | |
|                     if (!is_wp_error($result)) {
 | |
|                         $terms_to_assign[] = $result['term_id'];
 | |
|                     }
 | |
|                 } else {
 | |
|                     $terms_to_assign[] = $term->term_id;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Assign terms to the profile
 | |
|             if (!empty($terms_to_assign)) {
 | |
|                 wp_set_post_terms($profile_id, $terms_to_assign, $taxonomy, false);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function trainer_profile_edit_permissions($caps, $cap, $user_id, $args) {
 | |
|         if (!in_array($cap, ['edit_post', 'delete_post'])) {
 | |
|             return $caps;
 | |
|         }
 | |
|         
 | |
|         if (empty($args[0])) {
 | |
|             return $caps;
 | |
|         }
 | |
|         
 | |
|         $post_id = $args[0];
 | |
|         $post = get_post($post_id);
 | |
|         
 | |
|         if (!$post || $post->post_type !== 'trainer_profile') {
 | |
|             return $caps;
 | |
|         }
 | |
|         
 | |
|         $profile_user_id = get_post_meta($post_id, 'user_id', true);
 | |
|         
 | |
|         // Allow profile owner, master trainers, or administrators
 | |
|         if ($user_id == $profile_user_id || 
 | |
|             user_can($user_id, 'hvac_master_trainer') || 
 | |
|             user_can($user_id, 'administrator')) {
 | |
|             return ['exist'];
 | |
|         }
 | |
|         
 | |
|         return $caps;
 | |
|     }
 | |
|     
 | |
|     public function get_trainer_profile($user_id) {
 | |
|         $profile_id = get_user_meta($user_id, 'trainer_profile_id', true);
 | |
|         
 | |
|         if (!$profile_id) {
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         $profile = get_post($profile_id);
 | |
|         if (!$profile || $profile->post_type !== 'trainer_profile') {
 | |
|             return false;
 | |
|         }
 | |
|         
 | |
|         return $profile;
 | |
|     }
 | |
|     
 | |
|     public function get_profile_meta($profile_id, $key = null) {
 | |
|         if ($key) {
 | |
|             return get_post_meta($profile_id, $key, true);
 | |
|         }
 | |
|         
 | |
|         // Return all profile meta
 | |
|         $meta = get_post_meta($profile_id);
 | |
|         $clean_meta = [];
 | |
|         
 | |
|         foreach ($meta as $key => $value) {
 | |
|             $clean_meta[$key] = is_array($value) && count($value) === 1 ? $value[0] : $value;
 | |
|         }
 | |
|         
 | |
|         return $clean_meta;
 | |
|     }
 | |
|     
 | |
|     public function update_profile($profile_id, $data, $user_id = null) {
 | |
|         $profile = get_post($profile_id);
 | |
|         if (!$profile || $profile->post_type !== 'trainer_profile') {
 | |
|             return new WP_Error('invalid_profile', 'Invalid trainer profile');
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if ($user_id) {
 | |
|             $profile_user_id = get_post_meta($profile_id, 'user_id', true);
 | |
|             if (!($user_id == $profile_user_id || 
 | |
|                   user_can($user_id, 'hvac_master_trainer') || 
 | |
|                   user_can($user_id, 'administrator'))) {
 | |
|                 return new WP_Error('insufficient_permissions', 'Insufficient permissions to edit this profile');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Update post content if biographical_info is provided
 | |
|         if (isset($data['biographical_info'])) {
 | |
|             wp_update_post([
 | |
|                 'ID' => $profile_id,
 | |
|                 'post_content' => wp_kses_post($data['biographical_info'])
 | |
|             ]);
 | |
|             unset($data['biographical_info']);
 | |
|         }
 | |
|         
 | |
|         // Define taxonomies for special handling
 | |
|         $taxonomy_fields = ['business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources'];
 | |
|         
 | |
|         // Update meta fields and taxonomies
 | |
|         foreach ($data as $key => $value) {
 | |
|             if (in_array($key, $taxonomy_fields)) {
 | |
|                 // Handle taxonomy fields
 | |
|                 $this->update_profile_taxonomy($profile_id, $key, $value);
 | |
|             } else {
 | |
|                 // Handle regular meta fields
 | |
|                 update_post_meta($profile_id, $key, sanitize_text_field($value));
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Automatically set certification_color based on certification_type
 | |
|         if (isset($data['certification_type'])) {
 | |
|             $certification_color = $this->get_certification_color($data['certification_type']);
 | |
|             update_post_meta($profile_id, 'certification_color', $certification_color);
 | |
|         }
 | |
|         
 | |
|         // Trigger geocoding if address fields changed
 | |
|         $address_fields = ['trainer_city', 'trainer_state', 'trainer_country'];
 | |
|         if (array_intersect_key($data, array_flip($address_fields))) {
 | |
|             do_action('hvac_profile_address_updated', $profile_id);
 | |
|         }
 | |
|         
 | |
|         return true;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get certification color based on certification type
 | |
|      * 
 | |
|      * @param string $certification_type
 | |
|      * @return string HEX color code
 | |
|      */
 | |
|     private function get_certification_color($certification_type) {
 | |
|         switch ($certification_type) {
 | |
|             case 'Certified measureQuick Champion':
 | |
|                 return '#f19a42';
 | |
|             case 'Certified measureQuick Trainer':
 | |
|                 return '#5077bb';
 | |
|             default:
 | |
|                 return '#f0f7e8';
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     private function update_profile_taxonomy($profile_id, $taxonomy_field, $value) {
 | |
|         if (empty($value)) {
 | |
|             // Remove all terms if value is empty
 | |
|             wp_set_post_terms($profile_id, [], $taxonomy_field, false);
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         $terms_to_assign = [];
 | |
|         
 | |
|         // Handle different value formats
 | |
|         if (is_array($value)) {
 | |
|             // Array of term names or IDs
 | |
|             $term_identifiers = $value;
 | |
|         } elseif (is_string($value)) {
 | |
|             // String - could be comma-separated or single value
 | |
|             $term_identifiers = array_map('trim', explode(',', $value));
 | |
|         } else {
 | |
|             return;
 | |
|         }
 | |
|         
 | |
|         // Process each term identifier
 | |
|         foreach ($term_identifiers as $identifier) {
 | |
|             if (empty($identifier)) {
 | |
|                 continue;
 | |
|             }
 | |
|             
 | |
|             // Check if it's a term ID (numeric) or term name (string)
 | |
|             if (is_numeric($identifier)) {
 | |
|                 $term = get_term($identifier, $taxonomy_field);
 | |
|                 if ($term && !is_wp_error($term)) {
 | |
|                     $terms_to_assign[] = (int)$identifier;
 | |
|                 }
 | |
|             } else {
 | |
|                 // Find term by name
 | |
|                 $term = get_term_by('name', $identifier, $taxonomy_field);
 | |
|                 if ($term) {
 | |
|                     $terms_to_assign[] = $term->term_id;
 | |
|                 } else {
 | |
|                     // Create new term if it doesn't exist
 | |
|                     $result = wp_insert_term($identifier, $taxonomy_field);
 | |
|                     if (!is_wp_error($result)) {
 | |
|                         $terms_to_assign[] = $result['term_id'];
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Assign terms to the profile (replace existing terms)
 | |
|         if (!empty($terms_to_assign)) {
 | |
|             wp_set_post_terms($profile_id, $terms_to_assign, $taxonomy_field, false);
 | |
|         } else {
 | |
|             // Remove all terms if no valid terms found
 | |
|             wp_set_post_terms($profile_id, [], $taxonomy_field, false);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function create_profiles_for_existing_trainers() {
 | |
|         $trainers = get_users([
 | |
|             'role__in' => ['hvac_trainer', 'hvac_master_trainer'],
 | |
|             'meta_query' => [
 | |
|                 [
 | |
|                     'key' => 'trainer_profile_id',
 | |
|                     'compare' => 'NOT EXISTS'
 | |
|                 ]
 | |
|             ]
 | |
|         ]);
 | |
|         
 | |
|         foreach ($trainers as $trainer) {
 | |
|             $this->create_trainer_profile($trainer->ID);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     public function get_all_trainer_profiles($args = []) {
 | |
|         $default_args = [
 | |
|             'post_type' => 'trainer_profile',
 | |
|             'post_status' => 'publish',
 | |
|             'posts_per_page' => -1,
 | |
|             'meta_query' => []
 | |
|         ];
 | |
|         
 | |
|         // Filter public profiles only for non-privileged users
 | |
|         if (!current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             $default_args['meta_query'][] = [
 | |
|                 'key' => 'is_public_profile',
 | |
|                 'value' => '1',
 | |
|                 'compare' => '='
 | |
|             ];
 | |
|         }
 | |
|         
 | |
|         $args = wp_parse_args($args, $default_args);
 | |
|         return get_posts($args);
 | |
|     }
 | |
|     
 | |
|     public function delete_trainer_profile($profile_id, $user_id = null) {
 | |
|         $profile = get_post($profile_id);
 | |
|         if (!$profile || $profile->post_type !== 'trainer_profile') {
 | |
|             return new WP_Error('invalid_profile', 'Invalid trainer profile');
 | |
|         }
 | |
|         
 | |
|         // Check permissions
 | |
|         if ($user_id) {
 | |
|             $profile_user_id = get_post_meta($profile_id, 'user_id', true);
 | |
|             if (!($user_id == $profile_user_id || 
 | |
|                   user_can($user_id, 'administrator'))) {
 | |
|                 return new WP_Error('insufficient_permissions', 'Insufficient permissions to delete this profile');
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Clean up relationships
 | |
|         $profile_user_id = get_post_meta($profile_id, 'user_id', true);
 | |
|         if ($profile_user_id) {
 | |
|             delete_user_meta($profile_user_id, 'trainer_profile_id');
 | |
|         }
 | |
|         
 | |
|         // Delete the post
 | |
|         return wp_delete_post($profile_id, true);
 | |
|     }
 | |
|     
 | |
|     public function ajax_save_trainer_profile() {
 | |
|         check_ajax_referer('hvac_profile_nonce', 'nonce');
 | |
|         
 | |
|         if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|         }
 | |
|         
 | |
|         $user_id = get_current_user_id();
 | |
|         $profile_id = get_user_meta($user_id, 'trainer_profile_id', true);
 | |
|         
 | |
|         if (!$profile_id) {
 | |
|             wp_send_json_error('No trainer profile found');
 | |
|         }
 | |
|         
 | |
|         // Collect form data
 | |
|         $profile_data = [];
 | |
|         $user_data = ['ID' => $user_id];
 | |
|         
 | |
|         // Handle synchronized fields
 | |
|         if (isset($_POST['trainer_first_name'])) {
 | |
|             $profile_data['trainer_first_name'] = sanitize_text_field($_POST['trainer_first_name']);
 | |
|             $user_data['first_name'] = $profile_data['trainer_first_name'];
 | |
|         }
 | |
|         if (isset($_POST['trainer_last_name'])) {
 | |
|             $profile_data['trainer_last_name'] = sanitize_text_field($_POST['trainer_last_name']);
 | |
|             $user_data['last_name'] = $profile_data['trainer_last_name'];
 | |
|         }
 | |
|         if (isset($_POST['trainer_display_name'])) {
 | |
|             $profile_data['trainer_display_name'] = sanitize_text_field($_POST['trainer_display_name']);
 | |
|             $user_data['display_name'] = $profile_data['trainer_display_name'];
 | |
|         }
 | |
|         
 | |
|         // Handle profile-exclusive fields (non-taxonomy)
 | |
|         $profile_fields = [
 | |
|             'linkedin_profile_url', 'personal_accreditation',
 | |
|             'annual_revenue_target', 'application_details', 'trainer_city',
 | |
|             'trainer_state', 'trainer_country'
 | |
|         ];
 | |
|         
 | |
|         foreach ($profile_fields as $field) {
 | |
|             if (isset($_POST[$field])) {
 | |
|                 $profile_data[$field] = sanitize_text_field($_POST[$field]);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle taxonomy fields
 | |
|         $taxonomy_fields = ['business_type', 'training_audience', 'training_formats', 'training_locations', 'training_resources'];
 | |
|         
 | |
|         foreach ($taxonomy_fields as $field) {
 | |
|             if (isset($_POST[$field])) {
 | |
|                 // Handle array values (checkboxes) or single values (radio/select)
 | |
|                 $value = $_POST[$field];
 | |
|                 if (is_array($value)) {
 | |
|                     $profile_data[$field] = array_map('sanitize_text_field', $value);
 | |
|                 } else {
 | |
|                     $profile_data[$field] = sanitize_text_field($value);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle biographical info (rich text)
 | |
|         if (isset($_POST['biographical_info'])) {
 | |
|             $profile_data['biographical_info'] = wp_kses_post($_POST['biographical_info']);
 | |
|         }
 | |
|         
 | |
|         // Handle certification fields (restricted access)
 | |
|         if (current_user_can('hvac_master_trainer') || current_user_can('administrator')) {
 | |
|             $cert_fields = ['date_certified', 'certification_type', 'certification_status'];
 | |
|             foreach ($cert_fields as $field) {
 | |
|                 if (isset($_POST[$field])) {
 | |
|                     $profile_data[$field] = sanitize_text_field($_POST[$field]);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Update user data
 | |
|         if (count($user_data) > 1) {
 | |
|             wp_update_user($user_data);
 | |
|         }
 | |
|         
 | |
|         // Update profile
 | |
|         $result = $this->update_profile($profile_id, $profile_data, $user_id);
 | |
|         
 | |
|         if (is_wp_error($result)) {
 | |
|             wp_send_json_error($result->get_error_message());
 | |
|         }
 | |
|         
 | |
|         $response_data = ['message' => 'Profile updated successfully'];
 | |
|         
 | |
|         // Check if geocoding was triggered
 | |
|         $address_fields = ['trainer_city', 'trainer_state', 'trainer_country'];
 | |
|         if (array_intersect_key($_POST, array_flip($address_fields))) {
 | |
|             $response_data['geocoding_triggered'] = true;
 | |
|         }
 | |
|         
 | |
|         wp_send_json_success($response_data);
 | |
|     }
 | |
|     
 | |
|     public function ajax_auto_save_profile() {
 | |
|         check_ajax_referer('hvac_profile_nonce', 'nonce');
 | |
|         
 | |
|         if (!current_user_can('hvac_trainer') && !current_user_can('hvac_master_trainer') && !current_user_can('administrator')) {
 | |
|             wp_send_json_error('Insufficient permissions');
 | |
|         }
 | |
|         
 | |
|         $user_id = get_current_user_id();
 | |
|         $profile_id = get_user_meta($user_id, 'trainer_profile_id', true);
 | |
|         
 | |
|         if (!$profile_id) {
 | |
|             wp_send_json_error('No trainer profile found');
 | |
|         }
 | |
|         
 | |
|         // Auto-save only basic fields to prevent conflicts
 | |
|         $safe_fields = [
 | |
|             'linkedin_profile_url', 'application_details', 'trainer_city', 'trainer_state', 'trainer_country'
 | |
|         ];
 | |
|         
 | |
|         // Safe taxonomy fields for auto-save
 | |
|         $safe_taxonomy_fields = ['training_locations', 'training_resources'];
 | |
|         
 | |
|         $profile_data = [];
 | |
|         
 | |
|         // Handle regular fields
 | |
|         foreach ($safe_fields as $field) {
 | |
|             if (isset($_POST[$field])) {
 | |
|                 $profile_data[$field] = sanitize_text_field($_POST[$field]);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // Handle taxonomy fields
 | |
|         foreach ($safe_taxonomy_fields as $field) {
 | |
|             if (isset($_POST[$field])) {
 | |
|                 $value = $_POST[$field];
 | |
|                 if (is_array($value)) {
 | |
|                     $profile_data[$field] = array_map('sanitize_text_field', $value);
 | |
|                 } else {
 | |
|                     $profile_data[$field] = sanitize_text_field($value);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         if (!empty($profile_data)) {
 | |
|             $this->update_profile($profile_id, $profile_data, $user_id);
 | |
|         }
 | |
|         
 | |
|         wp_send_json_success('Auto-saved successfully');
 | |
|     }
 | |
|     
 | |
|     public function render_profile_view() {
 | |
|         if (!is_user_logged_in()) {
 | |
|             return '<p>You must be logged in to view this page.</p>';
 | |
|         }
 | |
|         
 | |
|         $user_id = get_current_user_id();
 | |
|         $profile = $this->get_trainer_profile($user_id);
 | |
|         
 | |
|         if (!$profile) {
 | |
|             return '<p>No trainer profile found. Please contact an administrator.</p>';
 | |
|         }
 | |
|         
 | |
|         // Get profile metadata and user data
 | |
|         $profile_meta = $this->get_profile_meta($profile->ID);
 | |
|         $user = get_userdata($user_id);
 | |
|         
 | |
|         // Get coordinates if available
 | |
|         $geocoding_service = class_exists('HVAC_Geocoding_Service') ? HVAC_Geocoding_Service::get_instance() : null;
 | |
|         $coordinates = $geocoding_service ? $geocoding_service->get_coordinates($profile->ID) : null;
 | |
|         
 | |
|         ob_start();
 | |
|         ?>
 | |
|         <div class="hvac-trainer-profile-view">
 | |
|             <div class="hvac-page-header">
 | |
|                 <h1>Trainer Profile</h1>
 | |
|                 <a href="/trainer/profile/edit/" class="hvac-button hvac-button-primary">Edit Profile</a>
 | |
|             </div>
 | |
|             
 | |
|             <div class="hvac-profile-content">
 | |
|                 <div class="hvac-profile-sidebar">
 | |
|                     <div class="hvac-profile-photo">
 | |
|                         <?php if (has_post_thumbnail($profile->ID)): ?>
 | |
|                             <?php echo get_the_post_thumbnail($profile->ID, 'medium', ['alt' => $user->display_name]); ?>
 | |
|                         <?php else: ?>
 | |
|                             <div class="hvac-profile-photo-placeholder">
 | |
|                                 <span><?php echo esc_html(substr($user->first_name ?: 'U', 0, 1) . substr($user->last_name ?: 'U', 0, 1)); ?></span>
 | |
|                             </div>
 | |
|                         <?php endif; ?>
 | |
|                     </div>
 | |
|                     
 | |
|                     <div class="hvac-profile-stats">
 | |
|                         <div class="hvac-stat-item">
 | |
|                             <span class="hvac-stat-value"><?php echo count_user_posts($user_id, 'tribe_events'); ?></span>
 | |
|                             <span class="hvac-stat-label">Events Created</span>
 | |
|                         </div>
 | |
|                         <?php if (!empty($profile_meta['years_experience'])): ?>
 | |
|                         <div class="hvac-stat-item">
 | |
|                             <span class="hvac-stat-value"><?php echo esc_html($profile_meta['years_experience']); ?></span>
 | |
|                             <span class="hvac-stat-label">Years Experience</span>
 | |
|                         </div>
 | |
|                         <?php endif; ?>
 | |
|                         <?php if ($coordinates): ?>
 | |
|                         <div class="hvac-stat-item">
 | |
|                             <span class="hvac-stat-value">📍</span>
 | |
|                             <span class="hvac-stat-label">Location Verified</span>
 | |
|                         </div>
 | |
|                         <?php endif; ?>
 | |
|                     </div>
 | |
|                 </div>
 | |
|             
 | |
|                 <div class="hvac-profile-main">
 | |
|                     <?php 
 | |
|                     // Get certifications from new system first, fallback to legacy
 | |
|                     $trainer_certifications = $this->get_trainer_certifications($user_id);
 | |
|                     $has_legacy_cert = !empty($profile_meta['certification_status']) || !empty($profile_meta['certification_type']) || !empty($profile_meta['date_certified']);
 | |
|                     
 | |
|                     if (!empty($trainer_certifications) || $has_legacy_cert): ?>
 | |
|                     <div class="hvac-profile-section hvac-certification-section">
 | |
|                         <h2>Certification Information</h2>
 | |
|                         
 | |
|                         <?php if (!empty($trainer_certifications)): ?>
 | |
|                             <div class="hvac-certifications-grid">
 | |
|                                 <?php foreach ($trainer_certifications as $cert): ?>
 | |
|                                     <div class="hvac-certification-card hvac-cert-status-<?php echo esc_attr($cert['status']); ?>">
 | |
|                                         <div class="hvac-cert-header">
 | |
|                                             <h3 class="hvac-cert-type"><?php echo esc_html($cert['certification_type']); ?></h3>
 | |
|                                             <span class="hvac-cert-status hvac-status-badge hvac-status-<?php echo esc_attr($cert['status']); ?>">
 | |
|                                                 <?php echo esc_html(ucfirst($cert['status'])); ?>
 | |
|                                             </span>
 | |
|                                         </div>
 | |
|                                         
 | |
|                                         <div class="hvac-cert-details">
 | |
|                                             <?php if (!empty($cert['certification_number'])): ?>
 | |
|                                                 <div class="hvac-cert-number">
 | |
|                                                     <strong>Certificate #:</strong> <?php echo esc_html($cert['certification_number']); ?>
 | |
|                                                 </div>
 | |
|                                             <?php endif; ?>
 | |
|                                             
 | |
|                                             <?php if (!empty($cert['issue_date'])): ?>
 | |
|                                                 <div class="hvac-cert-issued">
 | |
|                                                     <strong>Issued:</strong> <?php echo esc_html(date('F j, Y', strtotime($cert['issue_date']))); ?>
 | |
|                                                 </div>
 | |
|                                             <?php endif; ?>
 | |
|                                             
 | |
|                                             <?php if (!empty($cert['expiration_date'])): ?>
 | |
|                                                 <div class="hvac-cert-expires">
 | |
|                                                     <strong>Expires:</strong> 
 | |
|                                                     <?php 
 | |
|                                                     $exp_date = strtotime($cert['expiration_date']);
 | |
|                                                     $today = time();
 | |
|                                                     $days_until = ceil(($exp_date - $today) / (60 * 60 * 24));
 | |
|                                                     $exp_class = '';
 | |
|                                                     
 | |
|                                                     if ($exp_date < $today) {
 | |
|                                                         $exp_class = 'expired';
 | |
|                                                     } elseif ($days_until <= 30) {
 | |
|                                                         $exp_class = 'expiring-soon';
 | |
|                                                     }
 | |
|                                                     ?>
 | |
|                                                     <span class="hvac-cert-expiration <?php echo esc_attr($exp_class); ?>">
 | |
|                                                         <?php echo esc_html(date('F j, Y', $exp_date)); ?>
 | |
|                                                         <?php if ($days_until > 0 && $days_until <= 90): ?>
 | |
|                                                             (<?php echo esc_html($days_until); ?> days)
 | |
|                                                         <?php elseif ($exp_date < $today): ?>
 | |
|                                                             (Expired)
 | |
|                                                         <?php endif; ?>
 | |
|                                                     </span>
 | |
|                                                 </div>
 | |
|                                             <?php endif; ?>
 | |
|                                         </div>
 | |
|                                     </div>
 | |
|                                 <?php endforeach; ?>
 | |
|                             </div>
 | |
|                             
 | |
|                         <?php elseif ($has_legacy_cert): ?>
 | |
|                             <!-- Legacy certification display for backward compatibility -->
 | |
|                             <div class="hvac-legacy-certification">
 | |
|                                 <div class="hvac-profile-details">
 | |
|                                     <?php if (!empty($profile_meta['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($profile_meta['certification_status'])); ?>">
 | |
|                                             <?php echo esc_html($profile_meta['certification_status']); ?>
 | |
|                                         </span>
 | |
|                                     </div>
 | |
|                                     <?php endif; ?>
 | |
|                                     <?php if (!empty($profile_meta['certification_type'])): ?>
 | |
|                                     <div class="hvac-detail-row">
 | |
|                                         <span class="hvac-detail-label">Certification Type:</span>
 | |
|                                         <span class="hvac-detail-value"><?php echo esc_html($profile_meta['certification_type']); ?></span>
 | |
|                                     </div>
 | |
|                                     <?php endif; ?>
 | |
|                                     <?php if (!empty($profile_meta['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($profile_meta['date_certified']))); ?></span>
 | |
|                                     </div>
 | |
|                                     <?php endif; ?>
 | |
|                                 </div>
 | |
|                             </div>
 | |
|                         <?php endif; ?>
 | |
|                     </div>
 | |
|                     <?php endif; ?>
 | |
|                     
 | |
|                     <div class="hvac-profile-section">
 | |
|                         <h2>Personal Information</h2>
 | |
|                         <div class="hvac-profile-details">
 | |
|                             <div class="hvac-detail-row">
 | |
|                                 <span class="hvac-detail-label">Name:</span>
 | |
|                                 <span class="hvac-detail-value"><?php echo esc_html(($profile_meta['trainer_first_name'] ?? $user->first_name) . ' ' . ($profile_meta['trainer_last_name'] ?? $user->last_name)); ?></span>
 | |
|                             </div>
 | |
|                             <div class="hvac-detail-row">
 | |
|                                 <span class="hvac-detail-label">Email:</span>
 | |
|                                 <span class="hvac-detail-value"><?php echo esc_html($user->user_email); ?></span>
 | |
|                             </div>
 | |
|                             <?php 
 | |
|                             $location_parts = array_filter([
 | |
|                                 $profile_meta['trainer_city'] ?? '',
 | |
|                                 $profile_meta['trainer_state'] ?? '',
 | |
|                                 $profile_meta['trainer_country'] ?? ''
 | |
|                             ]);
 | |
|                             if (!empty($location_parts)):
 | |
|                             ?>
 | |
|                             <div class="hvac-detail-row">
 | |
|                                 <span class="hvac-detail-label">Location:</span>
 | |
|                                 <span class="hvac-detail-value"><?php echo esc_html(implode(', ', $location_parts)); ?></span>
 | |
|                             </div>
 | |
|                             <?php endif; ?>
 | |
|                             <?php if (!empty($profile_meta['linkedin_profile_url'])): ?>
 | |
|                             <div class="hvac-detail-row">
 | |
|                                 <span class="hvac-detail-label">LinkedIn:</span>
 | |
|                                 <span class="hvac-detail-value">
 | |
|                                     <a href="<?php echo esc_url($profile_meta['linkedin_profile_url']); ?>" target="_blank">View Profile</a>
 | |
|                                 </span>
 | |
|                             </div>
 | |
|                             <?php endif; ?>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     
 | |
|                     <?php if (!empty($profile->post_content)): ?>
 | |
|                     <div class="hvac-profile-section">
 | |
|                         <h2>About</h2>
 | |
|                         <div class="hvac-profile-bio">
 | |
|                             <?php echo wp_kses_post(wpautop($profile->post_content)); ?>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     <?php endif; ?>
 | |
|                     
 | |
|                     <?php
 | |
|                     // Get business type
 | |
|                     $business_terms = get_the_terms($profile->ID, 'business_type');
 | |
|                     if ($business_terms && !is_wp_error($business_terms)):
 | |
|                     ?>
 | |
|                     <div class="hvac-profile-section">
 | |
|                         <h2>Business Information</h2>
 | |
|                         <div class="hvac-profile-details">
 | |
|                             <div class="hvac-detail-row">
 | |
|                                 <span class="hvac-detail-label">Business Type:</span>
 | |
|                                 <span class="hvac-detail-value"><?php echo esc_html($business_terms[0]->name); ?></span>
 | |
|                             </div>
 | |
|                         </div>
 | |
|                     </div>
 | |
|                     <?php endif; ?>
 | |
|                 </div>
 | |
|             </div>
 | |
|         </div>
 | |
|         <?php
 | |
|         return ob_get_clean();
 | |
|     }
 | |
|     
 | |
|     public function render_profile_edit() {
 | |
|         if (!is_user_logged_in()) {
 | |
|             return '<p>You must be logged in to view this page.</p>';
 | |
|         }
 | |
|         
 | |
|         $user_id = get_current_user_id();
 | |
|         $profile = $this->get_trainer_profile($user_id);
 | |
|         
 | |
|         if (!$profile) {
 | |
|             return '<p>No trainer profile found. Please contact an administrator.</p>';
 | |
|         }
 | |
|         
 | |
|         // Use the existing, working profile edit form from HVAC_Registration
 | |
|         if (class_exists('HVAC_Registration')) {
 | |
|             $registration = new HVAC_Registration();
 | |
|             return $registration->render_edit_profile_form();
 | |
|         }
 | |
|         
 | |
|         // Fallback if registration class not available
 | |
|         return '<p>Profile editing functionality is currently unavailable. Please contact an administrator.</p>';
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Migration function to set certification colors for existing profiles
 | |
|      */
 | |
|     public function migrate_certification_colors() {
 | |
|         $profiles = get_posts([
 | |
|             'post_type' => 'trainer_profile',
 | |
|             'posts_per_page' => -1,
 | |
|             'post_status' => 'publish',
 | |
|             'meta_query' => [
 | |
|                 [
 | |
|                     'key' => 'certification_color',
 | |
|                     'compare' => 'NOT EXISTS'
 | |
|                 ]
 | |
|             ]
 | |
|         ]);
 | |
|         
 | |
|         foreach ($profiles as $profile) {
 | |
|             $certification_type = get_post_meta($profile->ID, 'certification_type', true);
 | |
|             $certification_color = $this->get_certification_color($certification_type);
 | |
|             update_post_meta($profile->ID, 'certification_color', $certification_color);
 | |
|         }
 | |
|         
 | |
|         if (count($profiles) > 0) {
 | |
|             error_log('HVAC: Updated certification colors for ' . count($profiles) . ' trainer profiles');
 | |
|         }
 | |
|         
 | |
|         // Set flag to prevent running multiple times
 | |
|         update_option('hvac_certification_colors_migrated', '1');
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Check if migration is needed and run it once
 | |
|      */
 | |
|     public function maybe_migrate_certification_colors() {
 | |
|         if (!get_option('hvac_certification_colors_migrated')) {
 | |
|             $this->migrate_certification_colors();
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get trainer certifications using the new certification system.
 | |
|      *
 | |
|      * @param int $user_id The trainer user ID
 | |
|      * @return array Array of certification data
 | |
|      */
 | |
|     public function get_trainer_certifications($user_id) {
 | |
|         // Check if the new certification manager exists
 | |
|         if (!class_exists('HVAC_Trainer_Certification_Manager')) {
 | |
|             return [];
 | |
|         }
 | |
|         
 | |
|         // Get certifications from the new system
 | |
|         $cert_manager = HVAC_Trainer_Certification_Manager::instance();
 | |
|         $certifications = $cert_manager->get_trainer_certifications($user_id);
 | |
|         
 | |
|         // Format certifications for display
 | |
|         $formatted_certifications = [];
 | |
|         
 | |
|         foreach ($certifications as $certification) {
 | |
|             // Get certification meta
 | |
|             $cert_type = get_post_meta($certification->ID, 'certification_type', true);
 | |
|             $status = get_post_meta($certification->ID, 'status', true) ?: 'active';
 | |
|             $issue_date = get_post_meta($certification->ID, 'issue_date', true);
 | |
|             $expiration_date = get_post_meta($certification->ID, 'expiration_date', true);
 | |
|             $certification_number = get_post_meta($certification->ID, 'certification_number', true);
 | |
|             $notes = get_post_meta($certification->ID, 'notes', true);
 | |
|             
 | |
|             // Calculate expiration status
 | |
|             $expiration_status = '';
 | |
|             $days_until_expiration = null;
 | |
|             if ($expiration_date) {
 | |
|                 $exp_timestamp = strtotime($expiration_date);
 | |
|                 $current_timestamp = current_time('timestamp');
 | |
|                 $days_until_expiration = floor(($exp_timestamp - $current_timestamp) / (60 * 60 * 24));
 | |
|                 
 | |
|                 if ($days_until_expiration < 0) {
 | |
|                     $expiration_status = 'expired';
 | |
|                 } elseif ($days_until_expiration <= 30) {
 | |
|                     $expiration_status = 'expiring_soon';
 | |
|                 } else {
 | |
|                     $expiration_status = 'valid';
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             // Format certification data
 | |
|             $formatted_certifications[] = [
 | |
|                 'id' => $certification->ID,
 | |
|                 'type' => $cert_type,
 | |
|                 'title' => $certification->post_title,
 | |
|                 'status' => $status,
 | |
|                 'issue_date' => $issue_date,
 | |
|                 'expiration_date' => $expiration_date,
 | |
|                 'certification_number' => $certification_number,
 | |
|                 'notes' => $notes,
 | |
|                 'expiration_status' => $expiration_status,
 | |
|                 'days_until_expiration' => $days_until_expiration,
 | |
|                 'display_color' => $this->get_certification_display_color($cert_type, $status, $expiration_status)
 | |
|             ];
 | |
|         }
 | |
|         
 | |
|         return $formatted_certifications;
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * Get display color for certification based on type, status, and expiration.
 | |
|      *
 | |
|      * @param string $cert_type Certification type
 | |
|      * @param string $status Certification status
 | |
|      * @param string $expiration_status Expiration status
 | |
|      * @return string CSS class or color code
 | |
|      */
 | |
|     private function get_certification_display_color($cert_type, $status, $expiration_status) {
 | |
|         // Priority: expiration status > certification status > type
 | |
|         if ($expiration_status === 'expired') {
 | |
|             return 'hvac-cert-expired';
 | |
|         }
 | |
|         
 | |
|         if ($expiration_status === 'expiring_soon') {
 | |
|             return 'hvac-cert-expiring';
 | |
|         }
 | |
|         
 | |
|         if ($status !== 'active') {
 | |
|             return 'hvac-cert-inactive';
 | |
|         }
 | |
|         
 | |
|         // Default colors based on certification type
 | |
|         switch (strtolower($cert_type)) {
 | |
|             case 'measurequick certified trainer':
 | |
|                 return 'hvac-cert-trainer';
 | |
|             case 'measurequick certified champion':
 | |
|                 return 'hvac-cert-champion';
 | |
|             default:
 | |
|                 return 'hvac-cert-default';
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| // Initialize the manager
 | |
| HVAC_Trainer_Profile_Manager::get_instance();
 |