Skip to content

Instantly share code, notes, and snippets.

@arenagroove
Created April 17, 2025 02:57
Show Gist options
  • Select an option

  • Save arenagroove/4f778a4667a28cce3fc745116e7f93b6 to your computer and use it in GitHub Desktop.

Select an option

Save arenagroove/4f778a4667a28cce3fc745116e7f93b6 to your computer and use it in GitHub Desktop.
MU Plugin to export and import WordPress users, including metadata, hashed passwords, and custom fields.
<?php
/**
* Plugin Name: LR Migrate Users with Metadata
* Description: Exports and imports WordPress users, including metadata, hashed passwords, and custom fields.
* Version: 2.0
* Author: Luis Martinez
* Author URI: https://www.lessrain.com
*/
if (!defined('WPINC')) {
die;
}
// Add an admin menu for managing export/import
add_action('admin_menu', 'lr_migrate_users_menu');
function lr_migrate_users_menu() {
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.'));
}
add_submenu_page(
'users.php',
'LR Migrate Users',
'LR Migrate Users',
'manage_options',
'lr-migrate-users',
'lr_migrate_users_page'
);
}
// Admin Page UI
function lr_migrate_users_page() {
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.'));
}
$export_fields = [
'ID' => 'User ID',
'Username' => 'Username',
'Email' => 'Email',
'Password Hash' => 'Password Hash',
'Role' => 'Role',
'First Name' => 'First Name',
'Last Name' => 'Last Name',
'Display Name' => 'Display Name',
'Nickname' => 'Nickname',
'Bio' => 'Bio',
'Website' => 'Website',
'Registered' => 'Registered Date',
'Profile Picture' => 'Profile Picture',
];
?>
<div class="wrap">
<h1>LR Migrate Users with Metadata</h1>
<h2>Export Users</h2>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<?php wp_nonce_field('lr_export_users', 'lr_export_nonce'); ?>
<input type="hidden" name="action" value="lr_export_users_csv">
<table class="form-table">
<tr>
<th scope="row">Fields to Export</th>
<td>
<?php foreach ($export_fields as $field => $label): ?>
<label>
<input type="checkbox" name="export_fields[]" value="<?php echo esc_attr($field); ?>" checked>
<?php echo esc_html($label); ?>
</label><br>
<?php endforeach; ?>
</td>
</tr>
</table>
<button type="submit" class="button button-primary">Download CSV</button>
</form>
<hr>
<h2>Import Users</h2>
<form method="post" enctype="multipart/form-data">
<?php wp_nonce_field('lr_import_users', 'lr_import_nonce'); ?>
<input type="file" name="import_file" accept=".csv" required>
<button type="submit" name="lr_import_users" class="button button-primary">Import Users</button>
</form>
</div>
<?php
if (isset($_POST['lr_import_users']) && !empty($_FILES['import_file']['tmp_name'])) {
if (!wp_verify_nonce($_POST['lr_import_nonce'], 'lr_import_users')) {
wp_die('Security check failed.');
}
lr_process_import($_FILES['import_file']['tmp_name']);
}
}
// Handle CSV Export via admin-post.php
add_action('admin_post_lr_export_users_csv', 'lr_export_users_csv_handler');
function lr_export_users_csv_handler() {
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.'));
}
if (!wp_verify_nonce($_POST['lr_export_nonce'], 'lr_export_users')) {
wp_die('Security check failed.');
}
$selected_fields = isset($_POST['export_fields']) ? $_POST['export_fields'] : array_keys([
'ID', 'Username', 'Email', 'Password Hash', 'Role', 'First Name', 'Last Name',
'Display Name', 'Nickname', 'Bio', 'Website', 'Registered', 'Profile Picture'
]);
lr_export_users_csv($selected_fields);
}
// Export Functionality
function lr_export_users_csv($selected_fields) {
global $wpdb;
$users = get_users();
if (!$users) {
wp_die('No users found.');
}
// Clear output buffers to prevent corruption
while (ob_get_level()) {
ob_end_clean();
}
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="wordpress-users.csv"');
header('Pragma: no-cache');
header('Expires: 0');
$output = fopen('php://output', 'w');
fputcsv($output, $selected_fields);
foreach ($users as $user) {
$user_meta = get_user_meta($user->ID);
$profile_picture = get_avatar_url($user->ID);
$userdata = [];
foreach ($selected_fields as $field) {
switch ($field) {
case 'ID':
$userdata[] = $user->ID;
break;
case 'Username':
$userdata[] = $user->user_login;
break;
case 'Email':
$userdata[] = $user->user_email;
break;
case 'Password Hash':
$userdata[] = $user->user_pass;
break;
case 'Role':
$userdata[] = implode(',', $user->roles);
break;
case 'First Name':
$userdata[] = $user_meta['first_name'][0] ?? '';
break;
case 'Last Name':
$userdata[] = $user_meta['last_name'][0] ?? '';
break;
case 'Display Name':
$userdata[] = $user->display_name;
break;
case 'Nickname':
$userdata[] = $user_meta['nickname'][0] ?? '';
break;
case 'Bio':
$userdata[] = $user_meta['description'][0] ?? '';
break;
case 'Website':
$userdata[] = $user->user_url;
break;
case 'Registered':
$userdata[] = $user->user_registered;
break;
case 'Profile Picture':
$userdata[] = $profile_picture;
break;
default:
$userdata[] = '';
break;
}
}
fputcsv($output, $userdata);
}
fclose($output);
exit;
}
// IMPORT FUNCTIONALITY
function lr_process_import($file) {
// Restrict access to administrators
if (!current_user_can('manage_options')) {
wp_die(__('You do not have sufficient permissions to access this page.'));
}
global $wpdb;
$handle = fopen($file, 'r');
if (!$handle) {
echo '<p style="color: red;">Error: Unable to open file.</p>';
return;
}
$row = 0;
$headers = [];
while (($data = fgetcsv($handle, 1000, ",")) !== FALSE) {
if ($row == 0) {
// Read headers from the first row
$headers = $data;
$row++;
continue;
}
// Map data to headers dynamically
$row_data = array_combine($headers, $data);
// Validate required fields
if (empty($row_data['Username']) || empty($row_data['Email']) || empty($row_data['Password Hash'])) {
echo "<p style='color: red;'>Error: Missing required fields (Username, Email, Password Hash) on row $row.</p>";
$row++;
continue;
}
$username = $row_data['Username'];
$email = $row_data['Email'];
$password_hash = $row_data['Password Hash'];
$roles = $row_data['Role'] ?? '';
$first_name = $row_data['First Name'] ?? '';
$last_name = $row_data['Last Name'] ?? '';
$display_name = $row_data['Display Name'] ?? '';
$nickname = $row_data['Nickname'] ?? '';
$bio = $row_data['Bio'] ?? '';
$website = $row_data['Website'] ?? '';
$registered = $row_data['Registered'] ?? '';
$profile_picture = $row_data['Profile Picture'] ?? '';
// Check if user already exists
$existing_user = get_user_by('login', $username) ?: get_user_by('email', $email);
if ($existing_user) {
$existing_user_id = $existing_user->ID;
lr_update_user_metadata(
$existing_user_id,
$first_name,
$last_name,
$nickname,
$bio,
$profile_picture,
$registered
);
echo "<p style='color: orange;'>User <strong>$username</strong> already exists. Metadata updated.</p>";
continue;
}
// Insert new user
$user_data = [
'user_login' => sanitize_user($username),
'user_email' => sanitize_email($email),
'user_pass' => null, // Placeholder (password will be manually inserted)
'role' => explode(',', $roles)[0] ?? 'subscriber',
'first_name' => sanitize_text_field($first_name),
'last_name' => sanitize_text_field($last_name),
'display_name' => sanitize_text_field($display_name),
'nickname' => sanitize_text_field($nickname),
'user_url' => esc_url_raw($website),
];
$new_user_id = wp_insert_user($user_data);
if (is_wp_error($new_user_id)) {
echo "<p style='color: red;'>Error adding user <strong>$username</strong>: " . $new_user_id->get_error_message() . "</p>";
continue;
}
// Update password and metadata
$wpdb->update($wpdb->users, ['user_pass' => $password_hash, 'user_registered' => $registered], ['ID' => $new_user_id]);
lr_update_user_metadata($new_user_id, $first_name, $last_name, $nickname, $bio, $profile_picture, $registered);
// Assign roles
if (!empty($roles)) {
lr_assign_user_roles($new_user_id, $roles);
}
echo "<p style='color: green;'>User <strong>$username</strong> imported successfully with full metadata.</p>";
$row++;
}
fclose($handle);
}
// HELPER FUNCTION: Update user metadata
function lr_update_user_metadata($user_id, $first_name, $last_name, $nickname, $bio, $profile_picture, $registered) {
update_user_meta($user_id, 'first_name', sanitize_text_field($first_name));
update_user_meta($user_id, 'last_name', sanitize_text_field($last_name));
update_user_meta($user_id, 'nickname', sanitize_text_field($nickname)); // Nickname
update_user_meta($user_id, 'description', sanitize_text_field($bio));
update_user_meta($user_id, 'profile_picture', esc_url_raw($profile_picture)); // Profile Picture URL
update_user_meta($user_id, 'registered', sanitize_text_field($registered));
}
// HELPER FUNCTION: Assign multiple roles to a user
function lr_assign_user_roles($user_id, $roles) {
$role_array = explode(',', $roles);
foreach ($role_array as $role) {
$user = new WP_User($user_id);
$user->add_role(trim($role));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment