Wordpress avatars from Discourse when Discourse is the SSO provider

I have Wordpress with Discourse setup where Discourse is the DiscourseConnect provider for WP. I’m using the WP Discourse plugin and it’s working great so far. The only issue I have encountered so far is that I can’t get the avatar images from Discourse to display in WP.

I have tried to implement the solution in Could you update user avatar for wordpress plugin sso? - #2 by angus - adding the code to my WP site’s function.php file. But it’s not working for me - I don’t know what code to add to set the avatar.

I’m wondering if anyone has gotten this to work? I’m using the Simple Local Avatar plugin, but I’m happy to change it if anyone has been successful with another plugin.

Hey @uffehe

What code are you adding and how does the Simple Local Avatar plugin save avatars?

Hi @angus

I’m not a developer so I’m kind of guessing here, but as I understand it the Simple Local Avatar plugin normally adds the image to the media library. I couldn’t find much documentation on how it works if it’s supplied via a URL. The code I’m trying looks like this:

add_filter( 'wpdc_sso_client_updated_user', 'my_wpdc_sso_client_updated_user', 10, 2 );
function my_wpdc_sso_client_updated_user( $updated_user, $query ) {
    if (isset($query['avatar_url'])) {
        $new_avatar_url = $query['avatar_url'];

        $avatar_data = array(
            'full' => $new_avatar_url,  // URL of the new avatar image
        );
        update_user_meta($updated_user, 'simple_local_avatar', $avatar_data);
    }
}

This gives me an unhandled exception when I try to log in using SSO.

I could probably hire a developer to help me, but it seems that this would be a pretty common use case that others might have already implemented - even if not with this plugin.

I think the cause of the exception is that your function my_wpdc_sso_client_updated_user isn’t returning the $updated_user array. That array is needed for the function that adds the wpdc_sso_client_updated_user filter to continue.

You also need to set the user’s WordPress user ID in the call to update_user_meta.

This works, but disables the Simple Local Avatars plugin’s rescaling functionality:

add_filter( 'simple_local_avatars_dynamic_resize', '__return_false' ); // prevent resizing of avatars
add_filter( 'wpdc_sso_client_updated_user', 'my_wpdc_sso_client_updated_user', 10, 2 );
function my_wpdc_sso_client_updated_user( $updated_user, $query ) {
	if ( isset( $query['avatar_url'] ) ) {
		$new_avatar_url = $query['avatar_url'];
		$wp_user_id = $updated_user['ID'];

		$avatar_data = array(
			'full' => $new_avatar_url,  // URL of the new avatar image
		);
		update_user_meta( $wp_user_id, 'simple_local_avatar', $avatar_data );
	}

	return $updated_user;
}

The reason for preventing the resizing of avatars is because the resizing is done with the WordPress image editor code. For it to work, the image needs to be downloaded to WordPress. Note that if you leave off the add_filter( 'simple_local_avatars_dynamic_resize', '__return_false' ) line from the code above, the Simple Local Avatars plugin will attempt to resize the images, throw a PHP warning, then use the full size image - so nothing would seem broken from a user’s point of view.

I’m somewhat unsure about combining the use of Discourse avatars with the Simple Local Avatars plugin. The issue I see is that the plugin gives users the option to upload a custom avatar on WordPress. If they do that, then log back into WordPress from Discourse, they might wonder what has happened to the avatar they uploaded. It would be more complex to develop, but it might be better to add the ability to deliberately set your Discourse avatar as your WordPress avatar on the user’s WordPress profile page.

2 Likes

Maybe it is. Since I was looking at it anyway, I figured I might as well try an approach that doesn’t use the Simple Local Avatars plugin.

The code below hooks into the 'wpdc_sso_client_updated_user' filter that’s called when a user logs into WordPress via Discourse. If the user has uploaded a custom avatar to Discourse, their Discourse custom_avatar_template is saved as user metadata on WordPress. It then hooks into the WordPress 'get_avatar_url' filter and uses the Discourse custom_avatar_template to set the avatar’s URL. I like this approach because it makes use of Discourse’s template resizing functionality.

Note that I’ve only tested this locally.

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;
add_filter( 'wpdc_sso_client_updated_user', 'my_wpdc_sso_client_updated_user', 10, 2 );
function my_wpdc_sso_client_updated_user( $updated_user, $query ) {
	$options  = DiscourseUtilities::get_options();
	$base_url = $options['url'];
	$discourse_username    = $query['username'];
	$discourse_profile_url = "{$base_url}/u/{$discourse_username}.json";
	$wp_user_id             = $updated_user['ID'];
	$profile_data           = DiscourseUtilities::discourse_request( $discourse_profile_url );
	if ( is_wp_error( $profile_data ) ) {

		return $updated_user;
	}

    // Check to see if the user has a template for an uploaded avatar on Discourse.
	$user_data = $profile_data->user;
	if ( ! property_exists( $user_data, 'custom_avatar_template' ) ) {

		return $updated_user;
	}

	$custom_avatar_template_path = $profile_data->user->custom_avatar_template;
    $custom_avatar_template = "{$base_url}{$custom_avatar_template_path}";
    // Save the avatar template. The size to display gets substituted in the `get_avatar_url` filter.
    update_user_meta( $wp_user_id, 'discourse_avatar_template', $custom_avatar_template );

    return $updated_user;
}

add_filter('get_avatar_url', 'my_get_avatar_url', 10, 3 );
function my_get_avatar_url($url, $id_or_email, $args ) {
    // Start with $user_id set to `null`. The code below may miss some cases.
    $user_id = null;
    if ( is_numeric( $id_or_email ) ) {
        $user_id = $id_or_email;
    } elseif ( $id_or_email instanceof WP_User ) {
        $user_id = $id_or_email->ID;
    } elseif ( $id_or_email instanceof WP_Post ) {
        $user_id = $id_or_email->user_id;
    } elseif ( $id_or_email instanceof WP_Comment ) {
        $user_id = $id_or_email->user_id;
    }

    // If $user_id hasn't been set, return the $url parameter.
    if ( empty( $user_id ) ) {

        return $url;
    }

    $discourse_avatar_template = get_user_meta( $user_id, 'discourse_avatar_template', true );
    // If the template metadata hasn't been set, return $url.
    if (empty( $discourse_avatar_template ) ) {

        return $url;
    }

    $size = isset( $args['size']) ? $args['size'] : 128;  // I'm not sure if 'size' could ever be empty. 128 seems like a safe fallback.

    return str_replace( '{size}', $size, $discourse_avatar_template );
}
3 Likes

Hi @simon

Perfect - it works exactly as described. And I agree, it’s much cleaner without needing a WP plugin and the confusion around conflicting avatars. Thanks!

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.