Using Heroku, need to have non-gravatar avatars, but not stored locally

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.

Read More

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

Thank you.

Related posts

Leave a Reply

2 comments

  1. 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.

  2. 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