[ '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 '

You must be logged in to view this page.

'; } $user_id = get_current_user_id(); $profile = $this->get_trainer_profile($user_id); if (!$profile) { return '

No trainer profile found. Please contact an administrator.

'; } // 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(); ?>

Trainer Profile

Edit Profile
ID)): ?> ID, 'medium', ['alt' => $user->display_name]); ?>
first_name ?: 'U', 0, 1) . substr($user->last_name ?: 'U', 0, 1)); ?>
Events Created
Years Experience
📍 Location Verified

Certification Information

Certification Status:
Certification Type:
Date Certified:

Personal Information

Name: first_name) . ' ' . ($profile_meta['trainer_last_name'] ?? $user->last_name)); ?>
Email: user_email); ?>
Location:
LinkedIn: View Profile
post_content)): ?>

About

post_content)); ?>
ID, 'business_type'); if ($business_terms && !is_wp_error($business_terms)): ?>

Business Information

Business Type: name); ?>
You must be logged in to view this page.

'; } $user_id = get_current_user_id(); $profile = $this->get_trainer_profile($user_id); if (!$profile) { return '

No trainer profile found. Please contact an administrator.

'; } // 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 '

Profile editing functionality is currently unavailable. Please contact an administrator.

'; } /** * 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(); } } } // Initialize the manager HVAC_Trainer_Profile_Manager::get_instance();