Created
April 17, 2025 02:57
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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