- Add explicit role setting in update_user() method for existing users - Fix issue where existing users kept their old roles during import - Update both CLI and web-based import scripts - Update documentation to reflect role handling improvement 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			415 lines
		
	
	
		
			No EOL
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			No EOL
		
	
	
		
			15 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | ||
| /**
 | ||
|  * HVAC Trainer Import - WordPress Admin Page
 | ||
|  * 
 | ||
|  * Upload this file to your WordPress root directory and access via:
 | ||
|  * https://yoursite.com/admin-import-trainers.php
 | ||
|  * 
 | ||
|  * IMPORTANT: Remove this file after import is complete for security!
 | ||
|  */
 | ||
| 
 | ||
| // WordPress environment setup
 | ||
| if (!defined('ABSPATH')) {
 | ||
|     require_once __DIR__ . '/wp-load.php';
 | ||
| }
 | ||
| 
 | ||
| // Security check - only allow admin users
 | ||
| if (!current_user_can('manage_options')) {
 | ||
|     wp_die('You do not have sufficient permissions to access this page.');
 | ||
| }
 | ||
| 
 | ||
| // Handle CSV upload and processing
 | ||
| if ($_POST && isset($_POST['action']) && $_POST['action'] === 'import_csv') {
 | ||
|     if (!wp_verify_nonce($_POST['import_nonce'], 'trainer_import')) {
 | ||
|         wp_die('Security check failed');
 | ||
|     }
 | ||
|     
 | ||
|     $dry_run = isset($_POST['dry_run']) && $_POST['dry_run'] === '1';
 | ||
|     
 | ||
|     if (isset($_FILES['csv_file']) && $_FILES['csv_file']['error'] === 0) {
 | ||
|         $csv_file = $_FILES['csv_file']['tmp_name'];
 | ||
|         $importer = new TrainerCSVImporter($dry_run);
 | ||
|         
 | ||
|         // Capture output
 | ||
|         ob_start();
 | ||
|         $importer->import_csv($csv_file);
 | ||
|         $output = ob_get_clean();
 | ||
|         
 | ||
|         echo '<div class="notice notice-success"><p>Import completed!</p></div>';
 | ||
|         echo '<pre>' . esc_html($output) . '</pre>';
 | ||
|     } else {
 | ||
|         echo '<div class="notice notice-error"><p>Error uploading file.</p></div>';
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| class TrainerCSVImporter {
 | ||
|     
 | ||
|     private $dry_run = false;
 | ||
|     private $stats = [
 | ||
|         'processed' => 0,
 | ||
|         'created' => 0,
 | ||
|         'updated' => 0,
 | ||
|         'skipped' => 0,
 | ||
|         'errors' => 0
 | ||
|     ];
 | ||
|     
 | ||
|     public function __construct($dry_run = false) {
 | ||
|         $this->dry_run = $dry_run;
 | ||
|     }
 | ||
|     
 | ||
|     public function import_csv($csv_file) {
 | ||
|         echo "Starting import from uploaded CSV\n";
 | ||
|         if ($this->dry_run) {
 | ||
|             echo "DRY RUN MODE - No data will be modified\n";
 | ||
|         }
 | ||
|         echo "----------------------------------------\n";
 | ||
|         
 | ||
|         $handle = fopen($csv_file, 'r');
 | ||
|         if (!$handle) {
 | ||
|             die("Could not open CSV file\n");
 | ||
|         }
 | ||
|         
 | ||
|         // Read header row
 | ||
|         $headers = fgetcsv($handle);
 | ||
|         if (!$headers) {
 | ||
|             die("Could not read CSV headers\n");
 | ||
|         }
 | ||
|         
 | ||
|         // Process each row
 | ||
|         while (($row = fgetcsv($handle)) !== false) {
 | ||
|             $data = array_combine($headers, $row);
 | ||
|             $this->process_trainer_record($data);
 | ||
|             $this->stats['processed']++;
 | ||
|         }
 | ||
|         
 | ||
|         fclose($handle);
 | ||
|         $this->print_stats();
 | ||
|     }
 | ||
|     
 | ||
|     private function process_trainer_record($data) {
 | ||
|         // Skip if no email
 | ||
|         if (empty($data['Work Email'])) {
 | ||
|             echo "Skipping record with no email\n";
 | ||
|             $this->stats['skipped']++;
 | ||
|             return;
 | ||
|         }
 | ||
|         
 | ||
|         $email = sanitize_email($data['Work Email']);
 | ||
|         $existing_user = get_user_by('email', $email);
 | ||
|         
 | ||
|         if ($existing_user) {
 | ||
|             echo "Updating existing user: $email\n";
 | ||
|             $this->update_user($existing_user, $data);
 | ||
|             $this->stats['updated']++;
 | ||
|         } else {
 | ||
|             echo "Creating new user: $email\n";
 | ||
|             $this->create_user($data);
 | ||
|             $this->stats['created']++;
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     private function create_user($data) {
 | ||
|         if ($this->dry_run) return;
 | ||
|         
 | ||
|         $email = sanitize_email($data['Work Email']);
 | ||
|         $username = $this->generate_username($email);
 | ||
|         
 | ||
|         // Generate random password - user will need to reset
 | ||
|         $password = wp_generate_password(12, true, true);
 | ||
|         
 | ||
|         $user_data = [
 | ||
|             'user_login' => $username,
 | ||
|             'user_email' => $email,
 | ||
|             'user_pass' => $password,
 | ||
|             'first_name' => sanitize_text_field($data['Name'] ?? ''),
 | ||
|             'last_name' => sanitize_text_field($data['Last Name'] ?? ''),
 | ||
|             'display_name' => trim(($data['Name'] ?? '') . ' ' . ($data['Last Name'] ?? '')),
 | ||
|             'role' => 'hvac_trainer'
 | ||
|         ];
 | ||
|         
 | ||
|         $user_id = wp_insert_user($user_data);
 | ||
|         
 | ||
|         if (is_wp_error($user_id)) {
 | ||
|             echo "Error creating user $email: " . $user_id->get_error_message() . "\n";
 | ||
|             $this->stats['errors']++;
 | ||
|             return;
 | ||
|         }
 | ||
|         
 | ||
|         $this->set_user_meta($user_id, $data);
 | ||
|         
 | ||
|         // Send password reset email
 | ||
|         $this->send_welcome_email($user_id);
 | ||
|     }
 | ||
|     
 | ||
|     private function update_user($user, $data) {
 | ||
|         if ($this->dry_run) return;
 | ||
|         
 | ||
|         // Update basic user data
 | ||
|         $user_data = [
 | ||
|             'ID' => $user->ID,
 | ||
|             'first_name' => sanitize_text_field($data['Name'] ?? ''),
 | ||
|             'last_name' => sanitize_text_field($data['Last Name'] ?? ''),
 | ||
|             'display_name' => trim(($data['Name'] ?? '') . ' ' . ($data['Last Name'] ?? ''))
 | ||
|         ];
 | ||
|         
 | ||
|         wp_update_user($user_data);
 | ||
|         
 | ||
|         // Ensure user has correct role
 | ||
|         $user_obj = new WP_User($user->ID);
 | ||
|         $user_obj->set_role('hvac_trainer');
 | ||
|         
 | ||
|         $this->set_user_meta($user->ID, $data);
 | ||
|     }
 | ||
|     
 | ||
|     private function set_user_meta($user_id, $data) {
 | ||
|         // Personal Information
 | ||
|         update_user_meta($user_id, 'personal_accreditation', sanitize_textarea_field($data['Personal Accreditations'] ?? ''));
 | ||
|         
 | ||
|         // Business Information
 | ||
|         update_user_meta($user_id, 'business_name', sanitize_text_field($data['Company Name'] ?? ''));
 | ||
|         update_user_meta($user_id, 'business_phone', sanitize_text_field($data['Phone Number'] ?? ''));
 | ||
|         update_user_meta($user_id, 'business_email', sanitize_email($data['Work Email'] ?? ''));
 | ||
|         update_user_meta($user_id, 'business_website', esc_url_raw($data['Company Website'] ?? ''));
 | ||
|         update_user_meta($user_id, 'business_type', $this->map_organization_type($data['Organization Type'] ?? ''));
 | ||
|         
 | ||
|         // Address Information
 | ||
|         update_user_meta($user_id, 'user_country', sanitize_text_field($data['Country'] ?? ''));
 | ||
|         update_user_meta($user_id, 'user_state', sanitize_text_field($data['State'] ?? ''));
 | ||
|         
 | ||
|         // Training Information
 | ||
|         $training_audience = $this->map_training_audience($data['Training Target'] ?? '');
 | ||
|         update_user_meta($user_id, 'training_audience', $training_audience);
 | ||
|         
 | ||
|         // Application Data
 | ||
|         update_user_meta($user_id, 'application_details', sanitize_textarea_field($data['Trainer Details'] ?? ''));
 | ||
|         
 | ||
|         // Profile Image - Map staging URL to production
 | ||
|         $profile_image_id = $this->map_profile_image($data['Profile Picture'] ?? '');
 | ||
|         if ($profile_image_id) {
 | ||
|             update_user_meta($user_id, 'profile_image_id', $profile_image_id);
 | ||
|         }
 | ||
|         
 | ||
|         // Default settings for imported users
 | ||
|         update_user_meta($user_id, 'account_status', 'approved'); // Pre-approved users
 | ||
|         update_user_meta($user_id, 'create_venue', 'Yes');
 | ||
|         
 | ||
|         // Create organizer and venue profiles
 | ||
|         $this->create_organizer_venue($user_id, $data);
 | ||
|     }
 | ||
|     
 | ||
|     private function map_organization_type($org_type) {
 | ||
|         $mapping = [
 | ||
|             'Training Organization' => 'Manufacturer',
 | ||
|             'Service Company' => 'Contractor',
 | ||
|             '' => 'Other'
 | ||
|         ];
 | ||
|         
 | ||
|         return $mapping[$org_type] ?? 'Other';
 | ||
|     }
 | ||
|     
 | ||
|     private function map_training_audience($target) {
 | ||
|         if (empty($target)) {
 | ||
|             return ['Anyone'];
 | ||
|         }
 | ||
|         
 | ||
|         // Convert single string to array format expected by system
 | ||
|         $mapping = [
 | ||
|             'Industry professionals' => ['Industry professionals'],
 | ||
|             'Internal staff' => ['Internal staff'],
 | ||
|             'Anyone' => ['Anyone']
 | ||
|         ];
 | ||
|         
 | ||
|         return $mapping[$target] ?? ['Anyone'];
 | ||
|     }
 | ||
|     
 | ||
|     private function map_profile_image($staging_url) {
 | ||
|         if (empty($staging_url)) {
 | ||
|             return null;
 | ||
|         }
 | ||
|         
 | ||
|         // Convert staging URL to production URL pattern  
 | ||
|         $production_url = str_replace(
 | ||
|             'upskill-staging.measurequick.com',
 | ||
|             $_SERVER['HTTP_HOST'], // Use current domain
 | ||
|             $staging_url
 | ||
|         );
 | ||
|         
 | ||
|         // Try to find existing attachment by URL
 | ||
|         $attachment_id = attachment_url_to_postid($production_url);
 | ||
|         
 | ||
|         if (!$attachment_id) {
 | ||
|             // Try with staging URL in case it's already in the system
 | ||
|             $attachment_id = attachment_url_to_postid($staging_url);
 | ||
|         }
 | ||
|         
 | ||
|         if (!$attachment_id) {
 | ||
|             echo "Warning: Could not find attachment for URL: $staging_url\n";
 | ||
|             return null;
 | ||
|         }
 | ||
|         
 | ||
|         return $attachment_id;
 | ||
|     }
 | ||
|     
 | ||
|     private function create_organizer_venue($user_id, $data) {
 | ||
|         if ($this->dry_run) return;
 | ||
|         
 | ||
|         // Check if organizer already exists
 | ||
|         $existing_organizer = get_user_meta($user_id, 'hvac_organizer_id', true);
 | ||
|         if ($existing_organizer) {
 | ||
|             return; // Already has organizer
 | ||
|         }
 | ||
|         
 | ||
|         // Only create if Events Calendar is active
 | ||
|         if (!class_exists('Tribe__Events__Main')) {
 | ||
|             return;
 | ||
|         }
 | ||
|         
 | ||
|         // Create Events Calendar organizer
 | ||
|         $organizer_data = [
 | ||
|             'post_title' => trim(($data['Name'] ?? '') . ' ' . ($data['Last Name'] ?? '')),
 | ||
|             'post_type' => 'tribe_organizer',
 | ||
|             'post_status' => 'publish',
 | ||
|             'meta_input' => [
 | ||
|                 '_OrganizerEmail' => sanitize_email($data['Work Email'] ?? ''),
 | ||
|                 '_OrganizerPhone' => sanitize_text_field($data['Phone Number'] ?? ''),
 | ||
|                 '_OrganizerWebsite' => esc_url_raw($data['Company Website'] ?? '')
 | ||
|             ]
 | ||
|         ];
 | ||
|         
 | ||
|         $organizer_id = wp_insert_post($organizer_data);
 | ||
|         if ($organizer_id) {
 | ||
|             update_user_meta($user_id, 'hvac_organizer_id', $organizer_id);
 | ||
|         }
 | ||
|         
 | ||
|         // Create venue if business name exists
 | ||
|         if (!empty($data['Company Name'])) {
 | ||
|             $venue_data = [
 | ||
|                 'post_title' => sanitize_text_field($data['Company Name']),
 | ||
|                 'post_type' => 'tribe_venue',
 | ||
|                 'post_status' => 'publish',
 | ||
|                 'meta_input' => [
 | ||
|                     '_VenueCountry' => sanitize_text_field($data['Country'] ?? ''),
 | ||
|                     '_VenueStateProvince' => sanitize_text_field($data['State'] ?? ''),
 | ||
|                     '_VenueURL' => esc_url_raw($data['Company Website'] ?? '')
 | ||
|                 ]
 | ||
|             ];
 | ||
|             
 | ||
|             $venue_id = wp_insert_post($venue_data);
 | ||
|             if ($venue_id) {
 | ||
|                 update_user_meta($user_id, 'hvac_venue_id', $venue_id);
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     private function generate_username($email) {
 | ||
|         $username = sanitize_user(substr($email, 0, strpos($email, '@')));
 | ||
|         
 | ||
|         // Ensure username is unique
 | ||
|         $base_username = $username;
 | ||
|         $counter = 1;
 | ||
|         
 | ||
|         while (username_exists($username)) {
 | ||
|             $username = $base_username . $counter;
 | ||
|             $counter++;
 | ||
|         }
 | ||
|         
 | ||
|         return $username;
 | ||
|     }
 | ||
|     
 | ||
|     private function send_welcome_email($user_id) {
 | ||
|         $user = get_user_by('id', $user_id);
 | ||
|         if (!$user) return;
 | ||
|         
 | ||
|         // Send password reset email
 | ||
|         $reset_key = get_password_reset_key($user);
 | ||
|         if (!is_wp_error($reset_key)) {
 | ||
|             $reset_url = network_site_url("wp-login.php?action=rp&key=$reset_key&login=" . rawurlencode($user->user_login), 'login');
 | ||
|             
 | ||
|             $subject = 'Welcome to HVAC Upskill Platform';
 | ||
|             $message = "Hello {$user->first_name},\n\n";
 | ||
|             $message .= "Your trainer account has been created on the HVAC Upskill Platform.\n\n";
 | ||
|             $message .= "Please set your password by clicking this link:\n";
 | ||
|             $message .= $reset_url . "\n\n";
 | ||
|             $message .= "After setting your password, you can login at: " . wp_login_url() . "\n\n";
 | ||
|             $message .= "Welcome to the platform!\n";
 | ||
|             
 | ||
|             wp_mail($user->user_email, $subject, $message);
 | ||
|         }
 | ||
|     }
 | ||
|     
 | ||
|     private function print_stats() {
 | ||
|         echo "\n========================================\n";
 | ||
|         echo "Import Complete!\n";
 | ||
|         echo "========================================\n";
 | ||
|         echo "Processed: {$this->stats['processed']}\n";
 | ||
|         echo "Created: {$this->stats['created']}\n";
 | ||
|         echo "Updated: {$this->stats['updated']}\n";
 | ||
|         echo "Skipped: {$this->stats['skipped']}\n";
 | ||
|         echo "Errors: {$this->stats['errors']}\n";
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // Only show form if not processing
 | ||
| if (!$_POST || !isset($_POST['action']) || $_POST['action'] !== 'import_csv') {
 | ||
| ?>
 | ||
| <!DOCTYPE html>
 | ||
| <html>
 | ||
| <head>
 | ||
|     <title>HVAC Trainer Import</title>
 | ||
|     <style>
 | ||
|         body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 20px; }
 | ||
|         .container { max-width: 600px; margin: 0 auto; }
 | ||
|         .form-group { margin-bottom: 20px; }
 | ||
|         label { display: block; margin-bottom: 5px; font-weight: bold; }
 | ||
|         input[type="file"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }
 | ||
|         input[type="checkbox"] { margin-right: 10px; }
 | ||
|         input[type="submit"] { background: #0073aa; color: white; padding: 12px 24px; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; }
 | ||
|         input[type="submit"]:hover { background: #005a87; }
 | ||
|         .warning { background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
 | ||
|         .info { background: #d1ecf1; border: 1px solid #bee5eb; color: #0c5460; padding: 15px; border-radius: 4px; margin-bottom: 20px; }
 | ||
|     </style>
 | ||
| </head>
 | ||
| <body>
 | ||
|     <div class="container">
 | ||
|         <h1>HVAC Trainer CSV Import</h1>
 | ||
|         
 | ||
|         <div class="warning">
 | ||
|             <strong>⚠️ IMPORTANT:</strong> This tool imports trainer data into your WordPress database. 
 | ||
|             Always run a dry-run first and backup your database before importing.
 | ||
|             <strong>Remove this file after import for security!</strong>
 | ||
|         </div>
 | ||
|         
 | ||
|         <div class="info">
 | ||
|             <strong>ℹ️ Instructions:</strong><br>
 | ||
|             1. Download the CSV file: <a href="https://upskill-staging.measurequick.com/wp-content/uploads/2025/06/250618120131_user-registration_formidable_entries.csv" target="_blank">Trainer Registration CSV</a><br>
 | ||
|             2. Upload the CSV file below<br>
 | ||
|             3. Run a dry-run first to preview changes<br>
 | ||
|             4. Run actual import if dry-run looks good
 | ||
|         </div>
 | ||
|         
 | ||
|         <form method="post" enctype="multipart/form-data">
 | ||
|             <?php wp_nonce_field('trainer_import', 'import_nonce'); ?>
 | ||
|             <input type="hidden" name="action" value="import_csv">
 | ||
|             
 | ||
|             <div class="form-group">
 | ||
|                 <label for="csv_file">Select CSV File:</label>
 | ||
|                 <input type="file" name="csv_file" id="csv_file" accept=".csv" required>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="form-group">
 | ||
|                 <label>
 | ||
|                     <input type="checkbox" name="dry_run" value="1" checked>
 | ||
|                     Dry Run (preview changes without importing)
 | ||
|                 </label>
 | ||
|             </div>
 | ||
|             
 | ||
|             <div class="form-group">
 | ||
|                 <input type="submit" value="Import Trainers">
 | ||
|             </div>
 | ||
|         </form>
 | ||
|     </div>
 | ||
| </body>
 | ||
| </html>
 | ||
| <?php
 | ||
| }
 | ||
| ?>
 |