Created
March 6, 2026 16:56
-
-
Save kimcoleman/f031b36f4a685eff315390ccc19889b1 to your computer and use it in GitHub Desktop.
Migration script: Basic User Avatars → PMPro Avatars
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 | |
| /** | |
| * Migration script: Basic User Avatars → PMPro Avatars | |
| * | |
| * Run this as a WP-CLI command. | |
| * Save this code to a file migrate-avatars.php. | |
| * Put the file in your WordPress root directory (the same folder where wp-config.php lives). | |
| * | |
| * WP-CLI usage: wp eval-file migrate-avatars.php | |
| * Update the $dry_run value to "false" to run the actual migration. | |
| * | |
| * Place this file in your WordPress root and run via WP-CLI, | |
| * or call pmpro_migrate_basic_user_avatars() from a temporary admin hook. | |
| */ | |
| // WP-CLI dry run? | |
| $dry_run = true; | |
| pmpro_migrate_basic_user_avatars( $dry_run ); | |
| /** | |
| * Migrate all users from Basic User Avatars to PMPro avatar storage. | |
| * | |
| * @param bool $dry_run If true, log actions without making changes. | |
| */ | |
| function pmpro_migrate_basic_user_avatars( $dry_run = false ) { | |
| if ( $dry_run ) { | |
| WP_CLI::log( '--- DRY RUN MODE — no changes will be made ---' ); | |
| } | |
| // Get all users who have a Basic User Avatar set. | |
| $users = get_users( array( | |
| 'meta_key' => 'basic_user_avatar', | |
| 'meta_compare' => 'EXISTS', | |
| 'fields' => 'ids', | |
| 'number' => -1, | |
| ) ); | |
| if ( empty( $users ) ) { | |
| log_msg( 'No users found with basic_user_avatar meta. Nothing to migrate.' ); | |
| return; | |
| } | |
| log_msg( sprintf( 'Found %d user(s) to migrate.', count( $users ) ) ); | |
| $success = 0; | |
| $skipped = 0; | |
| $failed = 0; | |
| foreach ( $users as $user_id ) { | |
| $result = pmpro_migrate_single_user_avatar( $user_id, $dry_run ); | |
| if ( true === $result ) { | |
| $success++; | |
| } elseif ( 'skipped' === $result ) { | |
| $skipped++; | |
| } else { | |
| $failed++; | |
| log_msg( sprintf( 'FAILED user %d: %s', $user_id, $result ), 'error' ); | |
| } | |
| } | |
| log_msg( sprintf( | |
| 'Migration complete. Success: %d | Skipped: %d | Failed: %d', | |
| $success, $skipped, $failed | |
| ) ); | |
| } | |
| /** | |
| * Migrate a single user's avatar from Basic User Avatars to PMPro. | |
| * | |
| * @param int $user_id The user ID. | |
| * @param bool $dry_run If true, log actions without making changes. | |
| * @return true|string True on success, 'skipped' if nothing to do, error string on failure. | |
| */ | |
| function pmpro_migrate_single_user_avatar( $user_id, $dry_run = false ) { | |
| // Get Basic User Avatars meta. | |
| $basic_avatar = get_user_meta( $user_id, 'basic_user_avatar', true ); | |
| if ( empty( $basic_avatar ) || ! is_array( $basic_avatar ) || empty( $basic_avatar['full'] ) ) { | |
| log_msg( sprintf( 'User %d: no valid basic_user_avatar meta found, skipping.', $user_id ) ); | |
| return 'skipped'; | |
| } | |
| // Skip if the user already has a PMPro avatar. | |
| $existing_pmpro = get_user_meta( $user_id, 'pmpro_avatar', true ); | |
| if ( ! empty( $existing_pmpro ) ) { | |
| log_msg( sprintf( 'User %d: already has pmpro_avatar, skipping.', $user_id ) ); | |
| return 'skipped'; | |
| } | |
| $source_url = $basic_avatar['full']; | |
| log_msg( sprintf( 'User %d: migrating from %s', $user_id, $source_url ) ); | |
| if ( $dry_run ) { | |
| return true; | |
| } | |
| // Resolve source URL to a local filesystem path if possible, | |
| // otherwise download it to a temp file. | |
| $source_path = pmpro_avatar_url_to_local_path( $source_url ); | |
| $is_temp = false; | |
| if ( ! $source_path || ! file_exists( $source_path ) ) { | |
| // Fall back to downloading the file. | |
| $source_path = pmpro_avatar_download_to_temp( $source_url ); | |
| if ( is_wp_error( $source_path ) ) { | |
| return $source_path->get_error_message(); | |
| } | |
| $is_temp = true; | |
| } | |
| // Determine extension from the source file. | |
| $filetype = wp_check_filetype( $source_path ); | |
| $ext = ! empty( $filetype['ext'] ) ? $filetype['ext'] : 'jpg'; | |
| $save_ext = pmpro_avatar_get_save_extension( $ext ); | |
| // Set up the PMPro user avatar directory. | |
| pmpro_avatar_setup_directory(); | |
| $user_dir = pmpro_avatar_get_upload_dir( $user_id ); | |
| if ( ! file_exists( $user_dir ) ) { | |
| wp_mkdir_p( $user_dir ); | |
| } | |
| // Process the image — crop to square, resize to max dimension. | |
| $max_dimension = pmpro_avatar_get_max_dimension(); | |
| $image = wp_get_image_editor( $source_path ); | |
| if ( is_wp_error( $image ) ) { | |
| if ( $is_temp ) { | |
| @unlink( $source_path ); | |
| } | |
| return 'Could not open image editor: ' . $image->get_error_message(); | |
| } | |
| $size = $image->get_size(); | |
| $orig_width = $size['width']; | |
| $orig_height = $size['height']; | |
| $min_dim = min( $orig_width, $orig_height ); | |
| $crop_x = ( $orig_width - $min_dim ) / 2; | |
| $crop_y = ( $orig_height - $min_dim ) / 2; | |
| $image->crop( $crop_x, $crop_y, $min_dim, $min_dim ); | |
| if ( $min_dim > $max_dimension ) { | |
| $image->resize( $max_dimension, $max_dimension, true ); | |
| } | |
| $image->set_quality( 90 ); | |
| $base_filename = 'avatar.' . $save_ext; | |
| $base_path = $user_dir . $base_filename; | |
| $saved = $image->save( $base_path ); | |
| if ( $is_temp ) { | |
| @unlink( $source_path ); | |
| } | |
| if ( is_wp_error( $saved ) ) { | |
| return 'Failed to save processed image: ' . $saved->get_error_message(); | |
| } | |
| // Pre-generate bucket sizes. | |
| $bucket_sizes = pmpro_avatar_get_bucket_sizes(); | |
| $base_size = $image->get_size(); | |
| $base_dim = $base_size['width']; // Square, so width = height. | |
| foreach ( $bucket_sizes as $bucket ) { | |
| if ( $bucket < $base_dim ) { | |
| $bucket_image = wp_get_image_editor( $base_path ); | |
| if ( ! is_wp_error( $bucket_image ) ) { | |
| $bucket_image->resize( $bucket, $bucket, true ); | |
| $bucket_image->set_quality( 90 ); | |
| $bucket_filename = sprintf( 'avatar-%dx%d.%s', $bucket, $bucket, $save_ext ); | |
| $bucket_image->save( $user_dir . $bucket_filename ); | |
| } | |
| } | |
| } | |
| // Save PMPro avatar meta. | |
| $avatar_data = array( | |
| 'extension' => $save_ext, | |
| 'uploaded' => time(), | |
| ); | |
| update_user_meta( $user_id, 'pmpro_avatar', $avatar_data ); | |
| log_msg( sprintf( 'User %d: migrated successfully.', $user_id ) ); | |
| return true; | |
| } | |
| /** | |
| * Attempt to convert an upload URL to a local filesystem path. | |
| * | |
| * Returns false if the URL doesn't map to the local uploads directory. | |
| * | |
| * @param string $url | |
| * @return string|false | |
| */ | |
| function pmpro_avatar_url_to_local_path( $url ) { | |
| $upload_dir = wp_upload_dir(); | |
| // Strip query strings. | |
| $url = strtok( $url, '?' ); | |
| // Handle SSL/non-SSL mismatches. | |
| $base_url = str_replace( 'https://', 'http://', $upload_dir['baseurl'] ); | |
| $clean_url = str_replace( 'https://', 'http://', $url ); | |
| if ( strpos( $clean_url, $base_url ) !== 0 ) { | |
| return false; | |
| } | |
| $relative_path = substr( $clean_url, strlen( $base_url ) ); | |
| return $upload_dir['basedir'] . $relative_path; | |
| } | |
| /** | |
| * Download a remote file to a temp location. | |
| * | |
| * @param string $url | |
| * @return string|WP_Error Local temp path or WP_Error. | |
| */ | |
| function pmpro_avatar_download_to_temp( $url ) { | |
| if ( ! function_exists( 'download_url' ) ) { | |
| require_once ABSPATH . 'wp-admin/includes/file.php'; | |
| } | |
| $tmp = download_url( $url, 30 ); | |
| if ( is_wp_error( $tmp ) ) { | |
| return $tmp; | |
| } | |
| return $tmp; | |
| } | |
| /** | |
| * Simple logger — uses WP-CLI if available, otherwise error_log. | |
| * | |
| * @param string $message | |
| * @param string $type 'log' or 'error' | |
| */ | |
| function log_msg( $message, $type = 'log' ) { | |
| if ( defined( 'WP_CLI' ) && WP_CLI ) { | |
| if ( $type === 'error' ) { | |
| WP_CLI::warning( $message ); | |
| } else { | |
| WP_CLI::log( $message ); | |
| } | |
| } else { | |
| error_log( '[PMPro Avatar Migration] ' . $message ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment