Аватары WordPress из Discourse, когда Discourse выступает провайдером SSO

У меня настроен WordPress с Discourse, где Discourse выступает провайдером DiscourseConnect для WP. Я использую плагин WP Discourse, и пока всё работает отлично. Единственная проблема, с которой я столкнулся, — это невозможность отображения аватаров из Discourse на сайте WordPress.

Я пытался реализовать решение из Could you update user avatar for wordpress plugin sso? - #2 by angus, добавив код в файл functions.php моего сайта WordPress. Но это не сработало — я не знаю, какой именно код нужно добавить для настройки аватара.

Интересует, удалось ли кому-то решить эту задачу? Я использую плагин Simple Local Avatar, но готов сменить его, если кто-то добился успеха с другим плагином.

Привет @uffehe

Какой именно код вы добавляете и как плагин Simple Local Avatar сохраняет аватары?

Привет, @angus

Я не разработчик, поэтому здесь лишь предполагаю, но, насколько я понимаю, плагин Simple Local Avatar обычно добавляет изображение в медиатеку. Я не нашёл много документации о том, как он работает, если изображение передаётся через URL. Код, который я пытаюсь использовать, выглядит так:

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 нового аватара
        );
        update_user_meta($updated_user, 'simple_local_avatar', $avatar_data);
    }
}

При попытке входа через SSO у меня возникает необработанное исключение.

Я мог бы нанять разработчика для помощи, но кажется, что это довольно распространённый сценарий использования, который другие уже могли реализовать — даже если не с этим плагином.

Я думаю, что причиной исключения является то, что ваша функция my_wpdc_sso_client_updated_user не возвращает массив $updated_user. Этот массив необходим для продолжения работы функции, которая добавляет фильтр wpdc_sso_client_updated_user.

Вам также нужно установить ID пользователя WordPress в вызове update_user_meta.

Это работает, но отключает функциональность изменения размера плагина Simple Local Avatars:

add_filter( 'simple_local_avatars_dynamic_resize', '__return_false' ); // предотвратить изменение размера аватаров
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 нового изображения аватара
		);
		update_user_meta( $wp_user_id, 'simple_local_avatar', $avatar_data );
	}

	return $updated_user;
}

Причина предотвращения изменения размера аватаров заключается в том, что изменение размера выполняется с помощью кода редактора изображений WordPress. Для его работы изображение должно быть загружено в WordPress. Обратите внимание, что если вы удалите строку add_filter( 'simple_local_avatars_dynamic_resize', '__return_false' ) из приведенного выше кода, плагин Simple Local Avatars попытается изменить размер изображений, выдаст предупреждение PHP, а затем использует изображение полного размера — так что для пользователя ничего не будет выглядеть сломанным.

Я немного сомневаюсь в сочетании использования аватаров Discourse с плагином Simple Local Avatars. Проблема, которую я вижу, заключается в том, что плагин предоставляет пользователям возможность загружать собственный аватар в WordPress. Если они это сделают, а затем снова войдут в WordPress из Discourse, они могут удивиться, куда исчез загруженный ими аватар. Было бы сложнее разработать, но, возможно, лучше добавить возможность намеренно установить ваш аватар Discourse как аватар WordPress на странице профиля пользователя WordPress.

Возможно, так и есть. Раз я уже изучал эту тему, решил попробовать подход без использования плагина Simple Local Avatars.

Ниже приведен код, который перехватывает фильтр 'wpdc_sso_client_updated_user', вызываемый при входе пользователя в WordPress через Discourse. Если пользователь загрузил собственный аватар в Discourse, его custom_avatar_template из Discourse сохраняется как метаданные пользователя в WordPress. Далее код перехватывает фильтр WordPress 'get_avatar_url' и использует custom_avatar_template из Discourse для установки URL аватара. Мне нравится этот подход, так как он использует функциональность изменения размера шаблонов в Discourse.

Обратите внимание: я тестировал это только локально.

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;
	}

    // Проверка наличия шаблона для загруженного аватара пользователя в 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}";
    // Сохранение шаблона аватара. Размер для отображения подставляется в фильтре `get_avatar_url`.
    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 ) {
    // Инициализация $user_id как `null`. Приведенный ниже код может не охватить все случаи.
    $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;
    }

    // Если $user_id не установлен, возвращаем параметр $url.
    if ( empty( $user_id ) ) {

        return $url;
    }

    $discourse_avatar_template = get_user_meta( $user_id, 'discourse_avatar_template', true );
    // Если метаданные шаблона не установлены, возвращаем $url.
    if (empty( $discourse_avatar_template ) ) {

        return $url;
    }

    $size = isset( $args['size']) ? $args['size'] : 128;  // Не уверен, может ли 'size' когда-либо быть пустым. 128 кажется безопасным значением по умолчанию.

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

Привет, @simon

Отлично — всё работает именно так, как описано. И я согласен, что это гораздо чище, без необходимости в плагине для WordPress и путаницы из-за конфликтующих аватаров. Спасибо!