Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

So, I am new to PHP, so I'm hoping to do this with a plugin, if possible.

We are hosting on Heroku, this means uploading a local image to /uploads isn't going to work for this. We need to be able to reference the individual user's avatar from our hosting at Amazon s3 (they don't need to be able to upload it themselves, we have a limited number of authors). We need a way to set the avatar to look to our s3 URL.

Is there a plugin that asks for a URL for an avatar instead of a local file?

Thank you.

share|improve this question

2 Answers 2

up vote 2 down vote accepted

The function that does all the heavy lifting for avatars is get_avatar. It's a pluggable function, meaning you can replace it with a plugin. Get avatar also has a handy filter you can use to override things programatically. So, a few options: (1) completely replace get_avatar which might have unintended side effects and break things like avatars in comments, (2) Role your own function just for author avatars, (3) Filter get_avatar for authors and leave it alone for everyone else.

Either way, it's starts with adding a field to user profiles so you can specify an avatar to do that.

Let's start by wrapping everything in a class.

<?php
WPSE67312_Avatars::init();

class WPSE67312_Avatars
{
    // we'll need a nonce key
    const NONCE = 'wpse67312_nonce';

    // The meta key where the avatar URL is stored.
    const META_KEY = 'wpse67312_avatar';

    /***** Singleton pattern to add hooks and such *****/

    private static $ins = null;

    public static function init()
    {
        add_action('plugins_loaded', array(__CLASS__, 'instance'));
    }

    public static function instance()
    {
        is_null(self::$ins) && self::$ins = new self;
        return self::$ins;
    }
}

You'll need to hook into show_user_profile and edit_user_profile. The first shows fields on your own profile (and authors will be able to see the field as well) and edit_user_profile will show the field when you're editing other users.

This also adds a little helper function to fetch the user avatar url. Pretty self explanatory: stick in a nonce, add a header, and echo out the field.

<?php
class WPSE67312_Avatars
{
    // snip snip

    /**
     * Helper to fetch avatar urls.
     *
     * @param   int $user_id The user ID for which to fetch the avatar
     * @uses    get_user_meta To fetch the avatar URL.
     * @return  string The avatar url
     */
    public static function get_avatar($user_id)
    {
        return get_user_meta($user_id, self::META_KEY, true);
    }

    /**
     * Constructor.  Where all the real actions get added.
     *
     * @uses    add_action
     * @uses    add_filter
     * @return  void
     */
    protected function __construct()
    {
        add_action('edit_user_profile', array($this, 'field'));
        add_action('show_user_profile', array($this, 'field'));
    }

    public function field($user)
    {
        // Might want to hide this from authors?
        // if(!current_user_can('manage_options'))
        //      return;

        wp_nonce_field(self::NONCE . $user->ID, self::NONCE, false);
        echo '<h4>', esc_html__('Avatar URL', 'wpse'), '</h4>';
        printf(
            '<input type="text" class="regular-text" id="%1$s" name="%1$s" value="%2$s" />',
            esc_attr(self::META_KEY),
            esc_attr(self::get_avatar($user->ID))
        );
    }
}

Adding the field doesn't actually take care of saving it, however. You need to hook into edit_user_profile_update (fires when saving profiles other than your own) and person_options_update (fires when you save your own profile). In the hooked function we check our nonce, check to make sure the current user can edit, and save the data with update_user_meta or delete it with delete_user_meta.

<?php
class WPSE67312_Avatars
{
    // snip snip

    /**
     * Constructor.  Where all the real actions get added.
     *
     * @uses    add_action
     * @uses    add_filter
     * @return  void
     */
    protected function __construct()
    {
        add_action('edit_user_profile', array($this, 'field'));
        add_action('show_user_profile', array($this, 'field'));
        add_action('edit_user_profile_update', array($this, 'save'));
        add_action('personal_options_update', array($this, 'save'));
    }

    // snip snip

    public function save($user_id)
    {
        if(
            !isset($_POST[self::NONCE]) ||
            !wp_verify_nonce($_POST[self::NONCE], self::NONCE . $user_id)
        ) return; // nonce is no good, bail

        if(!current_user_can('edit_user', $user_id))
            return; // current user can't edit this user, bail

        if(!empty($_POST[self::META_KEY]))
        {
            // we have data! save it!
            update_user_meta(
                $user_id, 
                self::META_KEY,
                esc_url_raw($_POST[self::META_KEY])
            );
        }
        else
        {
            // empty field, delete the old value
            delete_user_meta($user_id, self::META_KEY);
        }
    }
}

You could use the above in your template directly (somewhere in the loop):

<?php
if($avt = WPSEWPSE67312_Avatars::get_avatar(get_the_author_meta('ID')))
{
    printf(
        '<img src="%1s$" alt="%2$s" title="%4$s" />', 
        esc_url($avt), 
        esc_attr(get_the_author_meta('display_name'))
    );
}

Which is kind of messy. Or we can try filtering the avatar with get_avatar:

<?php
class WPSE67312_Avatars
{
    // snip snip

    /**
     * Constructor.  Where all the real actions get added.
     *
     * @uses    add_action
     * @uses    add_filter
     * @return  void
     */
    protected function __construct()
    {
        add_action('edit_user_profile', array($this, 'field'));
        add_action('show_user_profile', array($this, 'field'));
        add_action('edit_user_profile_update', array($this, 'save'));
        add_action('personal_options_update', array($this, 'save'));
        add_filter('get_avatar', array($this, 'filter_avatar'), 10, 5);
    }

    // snip snip

    public function filter_avatar($avatar, $id_or_email, $size, $default, $alt)
    {
        // do the dance to get a user
        $id = false;
        if(is_numeric($id_or_email))
        {
            $id = $id_or_email;
        }
        elseif(is_object($id_or_email))
        {
            if(!empty($id_or_email->user_id))
                $id = $id_or_email->user_id; // comment
        }
        elseif($user = get_user_by('email', $id_or_email))
        {
            $id = $user->ID;
        }

        if($id && ($avt = self::get_avatar($id)))
        {
            $avatar = sprintf(
                '<img src="%1$s" alt="%2$s" title="%2$s" width="%3$s" />',
                esc_url($avt),
                esc_attr($alt),
                esc_attr($size)
            );
        }

        return $avatar;
    }
}

Theoretically the above should leave normal avatars alone, and use your custom avatars when they're available. It probably needs to be tested more than I was able to.

Here is all that, wrapped in a plugin.

share|improve this answer

Chipps!

You can try filtering the get_avatar function. Let's try shall we?

Step 1

    function custom_avatar($gravatar, $id_or_email, $size, $default, $alt) {

        $avatar = get_the_author_meta('custom_avatar_url');
        $alt = get_the_author_meta('display_name'); 

        if ($avatar) {

        //retrieve avatar from URL found in 'custom_avatar_url' user_meta field.
        $image = '<img src="'.$avatar.'" width="'.$size.'" height="'.$size.'" alt="'.$alt.'" />';

        } elseif ($gravatar) {

        //if no custom $avatar is set then return a [gravatar] if found
        $image = $gravatar; 

        } else {

        //if no $gravatar found, revert to default placeholder for user avatar
        $image = '<img src="'.$default.'" width="'.$size.'" height="'.$size.'" alt="'.$alt.'" />';

        }

        return $image;
    }
    add_filter('get_avatar', 'custom_avatar', 10, 5);

Place the above into your functions.php file.

How this works

First our function custom_avatar

  • checks for the existence of a URL in our meta_key named custom_avatar_url the result of which is stored in our variable $avatar
  • if FALSE we proceed to check for the existence of a gravatar which is what WordPress will do by default
  • if this is also FALSE then we fallback to a default image placeholder held within our WordPress installation. (you can alter this to something entirely custom as well)

If you don't want to check for a Gravatar you don't have to! Just remove that conditional check altogether.

Step 2

The above function handles the retrieval of the custom image URL you specify, but you need somewhere to specify that URL too.

$avatar = get_the_author_meta('custom_avatar_url');

Indicates that we are storing the custom image URL as user_meta, much like post_meta, users can also have meta information attached to them for all kinds of purposes, such as this one.

We need to add a form input field on the User Profile page, so place this into your functions.php file as well;

function custom_avatar( $user ) {

    $html  = '<table class="form-table"><tbody>';
    $html .= '<th><label>External Avatar URL<label></th>';
    $html .= '<td><input type="text" name="custom_avatar_url" class="custom_avatar_url regular-text" value="' . esc_attr( get_the_author_meta( 'custom_avatar_url', $user->ID ) ) . '" /></td>';
    $html .= '</tbody></table>';
    echo $html;

}
add_action( 'show_user_profile', 'custom_avatar' );
add_action( 'edit_user_profile', 'custom_avatar' );

function save_custom_avatar_url( $user_id ) {

    update_usermeta( $user_id, 'custom_avatar_url', $_POST['custom_avatar_url'] );

}
add_action( 'personal_options_update', 'save_custom_avatar_url' );
add_action( 'edit_user_profile_update', 'save_custom_avatar_url' );



Result

enter image description here


If you've not been copying and pasting as you read along then,

The entire script as a whole (paste into your functions.php file)

function custom_avatar($gravatar, $id_or_email, $size, $default, $alt) {

    $avatar = get_the_author_meta('custom_avatar_url');
    $alt = get_the_author_meta('display_name'); 

    if ($avatar) {

    //retrieve avatar from URL found in 'custom_avatar_url' user_meta field.
    $image = '<img src="'.$avatar.'" width="'.$size.'" height="'.$size.'" alt="'.$alt.'" />';

    } elseif ($gravatar) {

    //if no custom $avatar is set then return a [gravatar] if found
    $image = $gravatar; 

    } else {

    //if no $gravatar found, revert to default placeholder for user avatar
    $image = '<img src="'.$default.'" width="'.$size.'" height="'.$size.'" alt="'.$alt.'" />';

    }

    return $image;
}
add_filter('get_avatar', 'custom_avatar', 10, 5);

function custom_avatar_input( $user ) {

    $html  = '<table class="form-table"><tbody>';
    $html .= '<th><label>External Avatar URL<label></th>';
    $html .= '<td><input type="text" name="custom_avatar_url" class="custom_avatar_url regular-text" value="' . esc_attr( get_the_author_meta( 'custom_avatar_url', $user->ID ) ) . '" /></td>';
    $html .= '</tbody></table>';
    echo $html;

}
add_action( 'show_user_profile', 'custom_avatar_input' );
add_action( 'edit_user_profile', 'custom_avatar_input' );

function save_custom_avatar_url( $user_id ) {

    update_usermeta( $user_id, 'custom_avatar_url', $_POST['custom_avatar_url'] );

}
add_action( 'personal_options_update', 'save_custom_avatar_url' );
add_action( 'edit_user_profile_update', 'save_custom_avatar_url' );

Notes

The variables,

  • $size
  • $default
  • $alt

...are all optional. So if you decide to remove any of those from the function arguements defined in,

function custom_avatar($arg1, $arg2, $arg3, $arg4, $arg5) { ...

Such as,

function custom_avatar($arg1, $arg2) { ...

Then you need to also remove $arg3, $arg4, $arg5 from the body of the function itself and also change the required arguments of the filter from,

add_filter('get_avatar', 'custom_avatar', 10, 5); //5 represents 5 function arguments

to

add_filter('get_avatar', 'custom_avatar', 10, 2); //2 represents 2 function arguments
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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