fix: Ensure import script sets correct hvac_trainer role for all users
- 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>
This commit is contained in:
parent
ec883f5c03
commit
e608a15c74
3 changed files with 884 additions and 0 deletions
415
admin-import-trainers.php
Normal file
415
admin-import-trainers.php
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
<?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
|
||||
}
|
||||
?>
|
||||
118
scripts/README-import.md
Normal file
118
scripts/README-import.md
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# HVAC Trainer CSV Import
|
||||
|
||||
This script imports trainer data from the Formidable Forms CSV export into the current HVAC Upskill platform user database.
|
||||
|
||||
## Usage
|
||||
|
||||
### Download the CSV File First
|
||||
```bash
|
||||
# Download the CSV file to your local machine
|
||||
curl -O "https://upskill-staging.measurequick.com/wp-content/uploads/2025/06/250618120131_user-registration_formidable_entries.csv"
|
||||
```
|
||||
|
||||
### Run Import Script
|
||||
```bash
|
||||
# Dry run (shows what would be imported without making changes)
|
||||
php scripts/import-trainer-csv.php 250618120131_user-registration_formidable_entries.csv --dry-run
|
||||
|
||||
# Actual import
|
||||
php scripts/import-trainer-csv.php 250618120131_user-registration_formidable_entries.csv
|
||||
```
|
||||
|
||||
## What the Script Does
|
||||
|
||||
### User Creation
|
||||
- Creates new WordPress users with `hvac_trainer` role
|
||||
- Updates existing users and ensures they have `hvac_trainer` role
|
||||
- Maps CSV fields to current database schema
|
||||
- Sets users as `approved` (pre-approved from previous system)
|
||||
- Generates secure random passwords
|
||||
- Sends welcome emails with password reset links
|
||||
|
||||
### Field Mappings
|
||||
| CSV Field | Database Field | Notes |
|
||||
|-----------|----------------|-------|
|
||||
| Name | first_name | Direct mapping |
|
||||
| Last Name | last_name | Direct mapping |
|
||||
| Work Email | user_email, user_login | Primary identifier |
|
||||
| Country | user_country | Direct mapping |
|
||||
| State | user_state | Direct mapping |
|
||||
| Personal Accreditations | personal_accreditation | Direct mapping |
|
||||
| Company Name | business_name | Direct mapping |
|
||||
| Company Website | business_website | Direct mapping |
|
||||
| Phone Number | business_phone | Direct mapping |
|
||||
| Trainer Details | application_details | Background info |
|
||||
| Training Target | training_audience | Converted to array |
|
||||
| Organization Type | business_type | Mapped values |
|
||||
| Profile Picture | profile_image_id | URL mapping only |
|
||||
|
||||
### Image Handling
|
||||
- **Does NOT download images** from staging URLs
|
||||
- Maps staging URLs (`upskill-staging.measurequick.com`) to production URLs
|
||||
- Searches for existing attachments by URL
|
||||
- Sets `profile_image_id` if attachment found
|
||||
- Logs warnings for missing images
|
||||
|
||||
### Additional Features
|
||||
- Creates Events Calendar organizer profiles
|
||||
- Creates venue profiles for businesses
|
||||
- Handles duplicate users (updates existing)
|
||||
- Comprehensive error handling and logging
|
||||
- Import statistics summary
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Before Running
|
||||
1. **Backup your database** - This script creates/modifies user data
|
||||
2. **Run dry-run first** - Always test with `--dry-run` flag
|
||||
3. **Check production domain** - Update the domain mapping in `map_profile_image()` method if needed
|
||||
|
||||
### Image URL Mapping
|
||||
The script expects images to already exist on your current instance but with staging URLs in the CSV. It converts:
|
||||
```
|
||||
upskill-staging.measurequick.com → upskill.measurequick.com
|
||||
```
|
||||
|
||||
Update line 169 in the script if your production domain is different.
|
||||
|
||||
### Generated Passwords
|
||||
- Users get randomly generated secure passwords
|
||||
- Welcome emails include password reset links
|
||||
- Users must reset passwords on first login
|
||||
|
||||
### Organizer/Venue Creation
|
||||
- Creates Events Calendar organizer profiles for each trainer
|
||||
- Creates venue profiles for trainers with company names
|
||||
- Links profiles to user accounts via meta fields
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
1. **Missing images**: Check if images exist on current instance
|
||||
2. **Duplicate usernames**: Script auto-handles with numbered suffixes
|
||||
3. **Email sending**: Ensure WordPress mail is configured
|
||||
4. **WordPress not found**: Run from WordPress root directory
|
||||
|
||||
### Error Messages
|
||||
- `Could not load WordPress`: Run from correct directory
|
||||
- `CSV file not found`: Check file path
|
||||
- `Could not find attachment`: Image doesn't exist on current instance
|
||||
|
||||
## Output Example
|
||||
```
|
||||
Starting import from: trainers.csv
|
||||
----------------------------------------
|
||||
Creating new user: trainer1@example.com
|
||||
Creating new user: trainer2@example.com
|
||||
Updating existing user: existing@example.com
|
||||
Warning: Could not find attachment for URL: https://...
|
||||
|
||||
========================================
|
||||
Import Complete!
|
||||
========================================
|
||||
Processed: 25
|
||||
Created: 23
|
||||
Updated: 2
|
||||
Skipped: 0
|
||||
Errors: 0
|
||||
```
|
||||
351
scripts/import-trainer-csv.php
Normal file
351
scripts/import-trainer-csv.php
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
<?php
|
||||
/**
|
||||
* Import HVAC Trainers from CSV export
|
||||
*
|
||||
* Usage: php scripts/import-trainer-csv.php /path/to/csv/file.csv
|
||||
*/
|
||||
|
||||
// WordPress environment setup
|
||||
if (!defined('WP_CLI') && !defined('ABSPATH')) {
|
||||
// Try to load WordPress
|
||||
$wp_load_paths = [
|
||||
__DIR__ . '/../wordpress/wp-load.php',
|
||||
__DIR__ . '/../wp-load.php',
|
||||
__DIR__ . '/../../../../wp-load.php',
|
||||
'/var/www/html/wp-load.php'
|
||||
];
|
||||
|
||||
foreach ($wp_load_paths as $path) {
|
||||
if (file_exists($path)) {
|
||||
require_once $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
die("Could not load WordPress. Please run from WordPress root or adjust path.\n");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!file_exists($csv_file)) {
|
||||
die("CSV file not found: $csv_file\n");
|
||||
}
|
||||
|
||||
echo "Starting import from: $csv_file\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',
|
||||
'upskill.measurequick.com', // Adjust this to your production 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
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
}
|
||||
|
||||
// Command line execution
|
||||
if (php_sapi_name() === 'cli') {
|
||||
$csv_file = $argv[1] ?? '';
|
||||
$dry_run = in_array('--dry-run', $argv);
|
||||
|
||||
if (empty($csv_file)) {
|
||||
echo "Usage: php import-trainer-csv.php <csv_file> [--dry-run]\n";
|
||||
echo "Example: php import-trainer-csv.php /path/to/trainers.csv\n";
|
||||
echo "Options:\n";
|
||||
echo " --dry-run Show what would be imported without making changes\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$importer = new TrainerCSVImporter($dry_run);
|
||||
$importer->import_csv($csv_file);
|
||||
}
|
||||
Loading…
Reference in a new issue