0, 'profiles_created' => 0, 'profiles_updated' => 0, 'errors' => 0, 'skipped' => 0 ]; public static function run_migration($dry_run = false) { $migration_id = uniqid('migration_'); $start_time = microtime(true); self::log_message("Starting trainer profile migration (ID: {$migration_id})", 'info'); if ($dry_run) { self::log_message("Running in DRY RUN mode - no changes will be made", 'info'); } // Initialize migration tracking if (!$dry_run) { update_option('hvac_migration_status', [ 'id' => $migration_id, 'status' => 'in_progress', 'start_time' => time(), 'dry_run' => false ]); } try { // Get all users with trainer roles $trainers = get_users([ 'role__in' => ['hvac_trainer', 'hvac_master_trainer', 'event_trainer'], // Include legacy role 'meta_query' => [ 'relation' => 'OR', [ 'key' => 'trainer_profile_id', 'compare' => 'NOT EXISTS' ], [ 'key' => 'trainer_profile_id', 'value' => '', 'compare' => '=' ] ] ]); self::$migration_stats['total_users'] = count($trainers); self::log_message("Found " . count($trainers) . " trainer users to migrate", 'info'); foreach ($trainers as $user) { try { self::migrate_user_to_profile($user, !$dry_run); } catch (Exception $e) { self::$migration_stats['errors']++; self::log_message("Error migrating user {$user->ID} ({$user->user_email}): " . $e->getMessage(), 'error'); } } // Migrate any existing CSV import data self::migrate_csv_data(!$dry_run); // Complete migration if (!$dry_run) { self::complete_migration($migration_id); } $end_time = microtime(true); $duration = round($end_time - $start_time, 2); self::log_message("Migration completed in {$duration} seconds", 'info'); self::log_message("Statistics: " . json_encode(self::$migration_stats), 'info'); } catch (Exception $e) { if (!$dry_run) { self::fail_migration($migration_id, $e->getMessage()); } self::log_message("Migration failed: " . $e->getMessage(), 'error'); throw $e; } return [ 'success' => true, 'stats' => self::$migration_stats, 'log' => self::$migration_log ]; } private static function migrate_user_to_profile($user, $commit = true) { // Check if user already has a profile $existing_profile_id = get_user_meta($user->ID, 'trainer_profile_id', true); if ($existing_profile_id && get_post($existing_profile_id)) { self::log_message("User {$user->ID} already has profile {$existing_profile_id}, skipping", 'info'); self::$migration_stats['skipped']++; return $existing_profile_id; } self::log_message("Migrating user {$user->ID} ({$user->user_email})", 'info'); if (!$commit) { self::log_message("DRY RUN: Would create trainer profile for user {$user->ID}", 'info'); self::$migration_stats['profiles_created']++; return true; } // 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, 'description', true) ?: get_user_meta($user->ID, 'biographical_info', true) ?: '' ]; $profile_id = wp_insert_post($profile_data); if (is_wp_error($profile_id)) { throw new Exception("Failed to create profile post: " . $profile_id->get_error_message()); } // 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 $migrated_fields = self::migrate_user_meta_fields($user->ID, $profile_id); // Set default visibility update_post_meta($profile_id, 'is_public_profile', '1'); // Trigger geocoding if address data exists $address_fields = ['trainer_city', 'trainer_state', 'trainer_country']; $has_address = false; foreach ($address_fields as $field) { if (get_post_meta($profile_id, $field, true)) { $has_address = true; break; } } if ($has_address) { wp_schedule_single_event(time() + 5, 'hvac_geocode_address', [$profile_id]); self::log_message("Scheduled geocoding for profile {$profile_id}", 'info'); } self::$migration_stats['profiles_created']++; self::log_message("Created profile {$profile_id} for user {$user->ID}, migrated " . count($migrated_fields) . " fields", 'info'); return $profile_id; } private static function migrate_user_meta_fields($user_id, $profile_id) { $user = get_userdata($user_id); $migrated_fields = []; // 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 ($value) { update_post_meta($profile_id, $profile_field, $value); $migrated_fields[] = $profile_field; } } // Profile-exclusive fields with potential user meta mappings $profile_field_mappings = [ 'linkedin_profile_url' => ['user_linkedin', 'linkedin_profile_url', 'linkedin_url'], 'personal_accreditation' => ['personal_accreditation', 'accreditation'], 'biographical_info' => ['biographical_info', 'bio', 'description'], 'training_audience' => ['training_audience', 'target_audience'], 'training_formats' => ['training_formats', 'training_methods'], 'training_locations' => ['training_locations', 'training_areas'], 'training_resources' => ['training_resources', 'resources'], 'annual_revenue_target' => ['annual_revenue_target', 'revenue_target'], 'application_details' => ['application_details', 'application_reason'], 'date_certified' => ['date_certified', 'certification_date'], 'certification_type' => ['certification_type', 'cert_type'], 'certification_status' => ['certification_status', 'cert_status'], 'trainer_city' => ['user_city', 'trainer_city', 'city'], 'trainer_state' => ['user_state', 'trainer_state', 'state'], 'trainer_country' => ['user_country', 'trainer_country', 'country'], 'role' => ['role', 'job_role', 'position'], 'years_experience' => ['years_experience', 'experience_years'] ]; foreach ($profile_field_mappings as $profile_field => $possible_meta_keys) { $value = null; // Try each possible meta key until we find a value foreach ($possible_meta_keys as $meta_key) { $temp_value = get_user_meta($user_id, $meta_key, true); if (!empty($temp_value)) { $value = $temp_value; break; } } if ($value) { update_post_meta($profile_id, $profile_field, $value); $migrated_fields[] = $profile_field; // Clean up old user meta fields to prevent confusion foreach ($possible_meta_keys as $old_key) { if ($old_key !== $profile_field) { // Don't delete if same name delete_user_meta($user_id, $old_key); } } } } // Handle business type (migrate from organizer category if available) $organizer_id = get_user_meta($user_id, 'organizer_id', true); if ($organizer_id) { $organizer = get_post($organizer_id); if ($organizer) { $organizer_category = get_post_meta($organizer_id, '_hvac_organizer_category', true); if ($organizer_category) { // Try to match with existing business type terms $term = get_term_by('name', $organizer_category, 'business_type'); if (!$term) { // Create the term if it doesn't exist $term_result = wp_insert_term($organizer_category, 'business_type'); if (!is_wp_error($term_result)) { $term = get_term($term_result['term_id'], 'business_type'); } } if ($term && !is_wp_error($term)) { wp_set_post_terms($profile_id, [$term->term_id], 'business_type'); $migrated_fields[] = 'business_type'; } } } } // Set default certification status if not provided if (!get_post_meta($profile_id, 'certification_status', true)) { // Determine default status based on user role $user_roles = $user->roles; if (in_array('hvac_master_trainer', $user_roles)) { update_post_meta($profile_id, 'certification_status', 'Active'); update_post_meta($profile_id, 'certification_type', 'Certified measureQuick Champion'); } else { update_post_meta($profile_id, 'certification_status', 'Active'); update_post_meta($profile_id, 'certification_type', 'Certified measureQuick Trainer'); } $migrated_fields[] = 'certification_status'; $migrated_fields[] = 'certification_type'; } return $migrated_fields; } private static function migrate_csv_data($commit = true) { // Check if there's any CSV import data to migrate $csv_import_log = get_option('hvac_csv_import_log', []); if (empty($csv_import_log)) { self::log_message("No CSV import data found to migrate", 'info'); return; } self::log_message("Found CSV import data, processing additional mappings", 'info'); foreach ($csv_import_log as $import_session) { if (isset($import_session['imported_users'])) { foreach ($import_session['imported_users'] as $user_data) { if (isset($user_data['user_id'])) { $user_id = $user_data['user_id']; $profile_id = get_user_meta($user_id, 'trainer_profile_id', true); if ($profile_id && $commit) { // Update any CSV-specific fields that might have been missed if (isset($user_data['csv_data'])) { self::update_profile_from_csv($profile_id, $user_data['csv_data']); } } } } } } } private static function update_profile_from_csv($profile_id, $csv_data) { $csv_field_mappings = [ 'Organization Name' => 'organization_name', 'Organization Logo URL' => 'organization_logo_url', 'Headquarters City' => 'trainer_city', 'Headquarters State' => 'trainer_state', 'Headquarters Country' => 'trainer_country', 'Organizer Category' => 'business_type', 'Training Experience' => 'training_experience', 'Specialization' => 'specialization' ]; foreach ($csv_field_mappings as $csv_key => $profile_field) { if (isset($csv_data[$csv_key]) && !empty($csv_data[$csv_key])) { if ($profile_field === 'business_type') { // Handle taxonomy $term = get_term_by('name', $csv_data[$csv_key], 'business_type'); if ($term) { wp_set_post_terms($profile_id, [$term->term_id], 'business_type'); } } else { update_post_meta($profile_id, $profile_field, sanitize_text_field($csv_data[$csv_key])); } } } } private static function complete_migration($migration_id) { update_option('hvac_migration_status', [ 'id' => $migration_id, 'status' => 'completed', 'end_time' => time(), 'stats' => self::$migration_stats ]); // Clean up any scheduled events that might conflict wp_clear_scheduled_hook('hvac_verify_sync_integrity'); // Schedule sync verification if (!wp_next_scheduled('hvac_verify_sync_integrity')) { wp_schedule_event(time() + 300, 'hourly', 'hvac_verify_sync_integrity'); } } private static function fail_migration($migration_id, $error_message) { update_option('hvac_migration_status', [ 'id' => $migration_id, 'status' => 'failed', 'end_time' => time(), 'error' => $error_message, 'stats' => self::$migration_stats ]); } private static function log_message($message, $level = 'info') { $timestamp = date('Y-m-d H:i:s'); $log_entry = "[{$timestamp}] [{$level}] {$message}"; self::$migration_log[] = $log_entry; // Also log to WordPress debug log if enabled if (defined('WP_DEBUG') && WP_DEBUG) { error_log("HVAC Profile Migration: " . $log_entry); } } public static function get_migration_status() { return get_option('hvac_migration_status', ['status' => 'not_started']); } public static function rollback_migration($migration_id = null) { $migration_status = self::get_migration_status(); if (!$migration_id) { $migration_id = $migration_status['id'] ?? null; } if (!$migration_id) { throw new Exception('No migration ID provided for rollback'); } self::log_message("Starting rollback for migration {$migration_id}", 'info'); // Get all trainer profiles created by this migration $profiles = get_posts([ 'post_type' => 'trainer_profile', 'posts_per_page' => -1, 'fields' => 'ids' ]); $rolled_back = 0; foreach ($profiles as $profile_id) { $user_id = get_post_meta($profile_id, 'user_id', true); if ($user_id) { // Remove the relationship delete_user_meta($user_id, 'trainer_profile_id'); // Delete the profile wp_delete_post($profile_id, true); $rolled_back++; } } // Update migration status update_option('hvac_migration_status', [ 'id' => $migration_id, 'status' => 'rolled_back', 'rollback_time' => time(), 'profiles_removed' => $rolled_back ]); self::log_message("Rollback completed: removed {$rolled_back} profiles", 'info'); return $rolled_back; } } // CLI Command support if (defined('WP_CLI') && WP_CLI) { WP_CLI::add_command('hvac migrate-profiles', function($args, $assoc_args) { $dry_run = isset($assoc_args['dry-run']) && $assoc_args['dry-run']; WP_CLI::line('Starting HVAC Trainer Profile Migration...'); try { $result = HVAC_Trainer_Profile_Migration::run_migration($dry_run); WP_CLI::success('Migration completed successfully!'); WP_CLI::line('Statistics:'); WP_CLI::line(' Total users: ' . $result['stats']['total_users']); WP_CLI::line(' Profiles created: ' . $result['stats']['profiles_created']); WP_CLI::line(' Profiles updated: ' . $result['stats']['profiles_updated']); WP_CLI::line(' Errors: ' . $result['stats']['errors']); WP_CLI::line(' Skipped: ' . $result['stats']['skipped']); if (!empty($result['log'])) { WP_CLI::line("\nDetailed log:"); foreach ($result['log'] as $log_entry) { WP_CLI::line(' ' . $log_entry); } } } catch (Exception $e) { WP_CLI::error('Migration failed: ' . $e->getMessage()); } }); WP_CLI::add_command('hvac rollback-profiles', function($args, $assoc_args) { $migration_id = $assoc_args['migration-id'] ?? null; WP_CLI::line('Starting HVAC Trainer Profile Migration Rollback...'); try { $rolled_back = HVAC_Trainer_Profile_Migration::rollback_migration($migration_id); WP_CLI::success("Rollback completed! Removed {$rolled_back} profiles."); } catch (Exception $e) { WP_CLI::error('Rollback failed: ' . $e->getMessage()); } }); }