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.
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="https://wordpress.stackexchange.com/questions/67312/%1$s" name="https://wordpress.stackexchange.com/questions/67312/%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="https://wordpress.stackexchange.com/questions/67312/%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="https://wordpress.stackexchange.com/questions/67312/%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.