• Resolved critical MapGeo marker correlation issue where all clicks showed "William Ramsey" • Replaced complex 600+ line console interception with streamlined 150-line solution • Implemented simplified MapGeo custom action system using direct profile ID correlation • Added Champions detection to prevent modal popups for measureQuick Champions • Implemented certification_color field with automatic hex color assignment: - Certified measureQuick Champion: #f19a42 - Certified measureQuick Trainer: #5077bb - Others/Default: #f0f7e8 • Added automatic color migration for existing trainer profiles • Enhanced AJAX handler to return both certification_type and certification_color • Deployed complete solution to staging with 295 markers detected • System now shows correct trainer modals with perfect correlation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Ben Reed <ben@tealmaker.com>
1026 lines
40 KiB
PHP
1026 lines
40 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',
|
|
'trainer_city',
|
|
'trainer_state',
|
|
'trainer_country'
|
|
];
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// 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 if (!empty($profile_meta['certification_status']) || !empty($profile_meta['certification_type']) || !empty($profile_meta['date_certified'])): ?>
|
|
<div class="hvac-profile-section hvac-certification-section">
|
|
<h2>Certification Information</h2>
|
|
<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 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize the manager
|
|
HVAC_Trainer_Profile_Manager::get_instance();
|