0

I have an Advanced Custom Fields field group on Edit User pages (user-edit.php), containing an Image field (avatar). I aim to rename that uploaded filename to match the username of the on-page user.

My current code uses wp_handle_upload_prefilter to catch the upload, and, to ensure the rename does not happen to all WP file uploads, I think I need to check that the upload field in question is my specific ACF Image...

In the below code, (1) I can successfully get the ACF field key and field for this check (check not fully implemented)...

add_filter('wp_handle_upload_prefilter', 'custom_upload_filter' );

function custom_upload_filter( $file ) {

    // 1. GET SOURCE FIELD, COPIED FROM ACF'S MEDIA.PHP

    $field = false;
    // Search for field key within available data.
    // Case 1) Media modal query.
    if ( isset( $_POST['query']['_acfuploader'] ) ) {
        $field_key = (string) $_POST['query']['_acfuploader'];

        // Case 2) Media modal upload.
    } elseif ( isset( $_POST['_acfuploader'] ) ) {
        $field_key = (string) $_POST['_acfuploader'];
    }

    // Attempt to load field.
    // Note the `acf_get_field()` function will return false if not found.
    if ( isset( $field_key ) ) {
        $field = acf_get_field( $field_key );
    }


    // 2. GET USER'S ID AND USERNAME

    // Code here


    // 3. TEST RENAME CODE
    $file['name'] = $user_id . 'and-everything-is-awesome-' . $file['name'];
    return $file;

}

However, I'm struggling to (2) get the ID of the user on the user-edit.php page.

I have tried two methods found here on StackExchange...

1. wp_get_current_user...

This...

$user_id = (int) $user_id;
$current_user = wp_get_current_user();
if ( ! defined( 'IS_PROFILE_PAGE' ) )
    define( 'IS_PROFILE_PAGE', ( $user_id == $current_user->ID ) );

if ( ! $user_id && IS_PROFILE_PAGE )
    $user_id = $current_user->ID;
elseif ( ! $user_id && ! IS_PROFILE_PAGE )
    wp_die(__( 'Invalid user ID.' ) );
elseif ( ! get_userdata( $user_id ) )
    wp_die( __('Invalid user ID.') );

2. $_GET['user_id']

This...

// If is current user's profile (profile.php)
if ( defined('IS_PROFILE_PAGE') && IS_PROFILE_PAGE ) {
    $user_id = get_current_user_id();
// If is another user's profile page
} elseif (! empty($_GET['user_id']) && is_numeric($_GET['user_id']) ) {
    $user_id = $_GET['user_id'];
// Otherwise something is wrong.
} else {
    die( 'No user id defined.' );
}

But neither result in a user_id being located. It is always PHP Notice: Undefined variable: user_id

Is this because, whilst the Edit User page is still at user-edit.php?user_id=2, the Media modal has somehow denied us the ability to get user_id?

TL;DR - I need to get user_id of currently-editing user on user-edit.php whilst in a Media picker initiated by ACF. How can I do this?

Update with full answer

Sally CJ's answer is great. Her option #2 is optimal... use filter plupload_default_params to pass variable to the AJAX Media uploader. Then, when I do the user_avatar_rename() on filter wp_handle_upload_prefilter, the value of user_id should be ready and waiting there...

My full code below. Big thanks to Sally.

/**
 * ==============================================================================
 *             RENAME UPLOADED USER AVATAR IMAGE FILE WITH USERNAME
 *    When an image is uploaded to Edit User form through an ACF field (field_6140a6da30a17),
 *    rename file with the username of said user.
 * ==============================================================================
 */

// 1. PASS USER_ID FROM USER-EDIT.PHP TO MEDIA UPLOADER, TO GET USERNAME FOR 
// cf. https://support.advancedcustomfields.com/forums/topic/force-an-image-file-upload-to-a-particular-directory/
// cf. https://wordpress.stackexchange.com/questions/395730/how-to-get-id-of-edit-user-page-during-wp-handle-upload-prefilter-whilst-in-med/395764?noredirect=1#comment577035_395764
add_filter('plupload_default_params', function($params) {
    if (!function_exists('get_current_screen')) {
        return $params;
    }
    $current_screen = get_current_screen();
    if ($current_screen->id == 'user-edit') {
        $params['user_id'] = $_GET['user_id'];
    } elseif ($current_screen->id == 'profile') {
        $params['user_id'] = get_current_user_id();
    }
    return $params;
    });

// 2. ON UPLOAD, DO THE RENAME
// Filter, cf. https://wordpress.stackexchange.com/questions/168790/how-to-get-profile-user-id-when-uploading-image-via-media-uploader-on-profile-pa
// p1: filter, p2: function to execute, p3: priority eg 10, p4: number of arguments eg 2
add_filter('wp_handle_upload_prefilter', 'user_avatar_rename' );
function user_avatar_rename( $file ) {
    // Working with $POST contents of AJAX Media uploader
    $theuserid = $_POST['user_id'];         // Passed from user-edit.php via plupload_default_params function
    $acffield  = $_POST['_acfuploader'];    // ACF field key, inherent in $_POST
    // If user_id was present AND ACF field is for avatar Image
    if ( ($theuserid) && ($acffield=='field_6140a6da30a17') ) {
        // Get ID's username and rename file accordingly, cf. https://stackoverflow.com/a/3261107/1375163
        $user = get_userdata( $theuserid );
        $info = pathinfo($file['name']);
        $ext  = empty($info['extension']) ? '' : '.' . $info['extension'];
        $name = basename($file['name'], $ext);
        $file['name'] = $user->user_login . $ext;
        // Carry on
        return $file;
    // Else, just use original filename
    } else {
        return $file;
    }
}
0

1 Answer 1

1

Is this because, whilst the Edit User page is still at user-edit.php?user_id=2, the Media modal has somehow denied us the ability to get user_id?

Getting the user ID on that page is easy, e.g. you could simply use get_current_user_id() to get the ID of the currently logged-in user, or use $_GET['user_id'] to get the ID of the user for the profile that's being edited.

But the thing is, the default WordPress media uploader will upload the file via AJAX to wp-admin/async-upload.php and despite on that page, you could still get the ID of the currently logged-in user, you wouldn't be able to get the ID of the user being edited, i.e. the value of the above user_id parameter, because the media uploader doesn't "forward" the user_id value to async-upload.php.

So by default, there's no way to get that value from the wp_handle_upload_prefilter hook which runs on the async-upload.php page.

However, you could do one of these:

  1. Use wp_get_referer() to get the referring URL and then read the user_id from the URL's query string.

  2. Manually "forward" the user ID to async-upload.php, which can be done via the plupload_default_params filter.

Working Examples

  1. For the first option above:

    // Note: This is a simplified example. In actual implementation, you'd want to
    // check if the referring page is wp-admin/user-edit.php or wp-admin/profile.php
    
    parse_str( parse_url( wp_get_referer(), PHP_URL_QUERY ), $args );
    $user_id = $args['user_id'] ?? get_current_user_id();
    
  2. For the second option above:

    add_filter( 'plupload_default_params', 'my_plupload_default_params' );
    function my_plupload_default_params( $params ) {
        // the admin page's file name, without .php
        $screens = array( 'user-edit', 'profile' );
    
        if ( is_admin() && in_array( get_current_screen()->id, $screens ) ) {
            $params['user_id'] = ( defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE ) ?
                get_current_user_id() : $_REQUEST['user_id'];
        }
    
        return $params;
    }
    

    And then in your function, just use $_POST['user_id'] to get the user ID.

5
  • 1
    Thanks for clearly laying out the two options. With #2 ("forward" the user ID to async-upload.php), would my substantive file-renaming stuff continue to be done in my existing function custom_upload_filter upon filter wp_handle_upload_prefilter as before, now that the AJAX module should have been passed the value? Or should it be executed within my_plupload_default_params? Sep 17, 2021 at 13:37
  • The purpose of using the my_plupload_default_params is to include the user_id in the request body (POST data) of the AJAX upload, and that filter runs on the user/profile edit page and not async-upload.php, so no, don't run the file renaming stuff there. Do it in the function that you hook on wp_handle_upload_prefilter.
    – Sally CJ
    Sep 17, 2021 at 13:54
  • But just try the code and you'll know for sure whether it works or not.. :)
    – Sally CJ
    Sep 17, 2021 at 13:57
  • Nearly there... If the function fired by plupload_default_params sets $params['user_id'], how do you then access those $params (user_id) over in the function fired by wp_handle_upload_prefilter (custom_upload_filter())? wp_handle_upload_prefilter seems to have a single parameter, $file - does it automatically go to $file['user_id'] ? Sep 17, 2021 at 14:11
  • No it doesn't. But all of those params will be stored in the $_POST superglobal/array on the upload endpoint (async-upload.php), so just use $_POST['user_id'] like you would when accessing the same parameter coming from a standard <input> in a form with method="post".
    – Sally CJ
    Sep 17, 2021 at 14:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.