Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dwanjuki/1e45d9e718440c6c57e59110b456fbc4 to your computer and use it in GitHub Desktop.

Select an option

Save dwanjuki/1e45d9e718440c6c57e59110b456fbc4 to your computer and use it in GitHub Desktop.
Admin queryable/Non WP-CLI Migration script: Basic User Avatars → PMPro Avatars
<?php
/**
* Migration script: Basic User Avatars → PMPro Avatars
*
* To trigger, visit one of:
* - /wp-admin/?pmpro_avatar_migrate=1&dry_run=1 (dry run)
* - /wp-admin/?pmpro_avatar_migrate=1&dry_run=0 (real migration)
*
* Check PHP error log for progress output.
*
* You can add this recipe to your site by creating a custom plugin
* or using the Code Snippets plugin available for free in the WordPress repository.
* Read this companion article for step-by-step directions on either method.
* https://www.paidmembershipspro.com/create-a-plugin-for-pmpro-customizations/
*/
function pmpro_avatar_migration_admin_trigger() {
if ( ! isset( $_GET['pmpro_avatar_migrate'] ) ) {
return;
}
// Safety: admins only.
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Unauthorized.' );
}
$dry_run = true;
if ( isset( $_GET['dry_run'] ) ) {
$dry_run = $_GET['dry_run'] !== '0';
}
pmpro_migrate_basic_user_avatars( $dry_run );
wp_die(
$dry_run
? 'PMPro avatar migration dry run complete. Check PHP error log.'
: 'PMPro avatar migration complete. Check PHP error log.'
);
}
add_action( 'admin_init', 'pmpro_avatar_migration_admin_trigger' );
/**
* 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 ) {
log_msg( '--- 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 error_log.
*
* @param string $message
* @param string $type 'log' or 'error'
*/
function log_msg( $message, $type = 'log' ) {
error_log( '[PMPro Avatar Migration] ' . $message );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment