Невозможно добавить пользователя в форум Discourse с сайта WordPress, когда пользователь добавлен в членство

Здравствуйте, у меня проблема с форумом Discourse по адресу https://community.over40connect.com/, который подключен к моему сайту https://over40connect.com/staging/ с помощью плагина WP Discourse. Я создал функцию для добавления пользователя в группу Discourse при смене его роли, но пользователь не добавляется в группу. Вот код:

function add_user_to_discourse_group_on_role_change($user_id, $role) {
    // Проверьте, актуальна ли смена роли для вашего случая
    // Например, вы можете захотеть выполнять это только если новая роль 's2member_level1'
    //if ($role === 's2member_level1') {
    // Установите конечную точку API Discourse
    $discourse_endpoint = 'https://community.over40connect.com/groups/2/members.json';

    // Подготовьте данные для отправки
    $data = array(
        'usernames' => array('haseebdeveloper'), // Имя пользователя WordPress, которого нужно добавить
    );

    // Выполните API-запрос
    $response = wp_remote_post($discourse_endpoint, array(
        'method'      => 'POST',
        'timeout'     => 45,
        'redirection' => 5,
        'httpversion' => '1.0',
        'blocking'    => true,
        'headers'     => array(
            'Content-Type' => 'application/json',
            'Api-Key'      => 'REDACTED', // Вставьте сюда ваш ключ API Discourse
        ),
        'body'        => json_encode($data),
        'cookies'     => array(),
    ));

    // Проверьте наличие ошибок и обработайте ответ
    if (!is_wp_error($response)) {
        $response_code = wp_remote_retrieve_response_code($response);

        if ($response_code === 200) {
            $response_body = wp_remote_retrieve_body($response);
            error_log('Ответ API Discourse: ' . $response_body); // Запишите ответ в лог
            // Обработайте ответ по мере необходимости
        } else {
            error_log('Ошибка API Discourse: неожиданный код ответа - ' . $response_code); // Запишите ошибку в лог
            // Обработайте ошибку
        }
    } else {
        $error_message = $response->get_error_message();
        error_log('Ошибка API Discourse: ' . $error_message); // Запишите ошибку в лог
        // Обработайте ошибку
    }
    //}
}

Я удалил из вашего фрагмента кода @Haseeb_Ahmed то, что выглядело как настоящий API-ключ. Возможно, стоит отозвать этот ключ и сгенерировать новый. :+1:

Посетив ваш сайт, я предполагаю, что ваш сайт WordPress работает как провайдер аутентификации DiscourseConnect для вашего сайта Discourse. Если это так, вы можете использовать вспомогательную функцию add_user_to_discourse_group для добавления пользователей WordPress в группы Discourse: https://github.com/discourse/wp-discourse/blob/main/lib/utilities.php#L278-L326. Подробная информация об использовании функции доступна здесь:

Обратите внимание на оператор use в верхней части примера кода:

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

И то, как он затем используется для вызова (статического) метода:

$result = DiscourseUtilities::add_user_to_discourse_group( $user_id, $group_name );

Привет @simon, сейчас я использую только этот код

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

// Хук для выполнения при смене роли пользователя
add_action('set_user_role', 'add_user_to_discourse_group_on_role_change', 10, 2);

function add_user_to_discourse_group_on_role_change($user_id, $role) {

  
   $group_names = array("volunteers", "moderators");
   $result = DiscourseUtilities::add_user_to_discourse_group( $user_id, $group_names );

    
}

но пользователь всё равно не добавляется в группу Discourse при смене его роли.

Кажется, это должно быть строка, разделенная запятыми, а не массив:

@Firepup650 прав. Параметр group_names должен быть строкой. Если вы добавляете пользователей в несколько групп, разделяйте имена групп запятой (без пробела после запятой.)

$group_names = "volunteers,moderators";

Если это не работает, или даже если работает, попробуйте вывести значение $result в лог, чтобы увидеть, что именно возвращается.

Привет, @Firepup650, спасибо за помощь. Вот мой код:

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

// Хук для выполнения при изменении роли пользователя
add_action('set_user_role', 'add_user_to_discourse_group_on_role_change', 10, 2);

function add_user_to_discourse_group_on_role_change($user_id, $role) {

  
   $group_names = array("volunteers", "moderators");
	$group_names_string = implode(",", $group_names);

   $result = DiscourseUtilities::add_user_to_discourse_group( $user_id, $group_names_string );

    
}

Пользователь добавляется в группу «volunteers», но не в группу «moderators». Пожалуйста, подскажите, в чём проблема.

С помощью этого вызова функции нельзя добавлять пользователей в группы «moderators», «admin» или «staff». Технически, этим методом нельзя добавлять пользователей ни в какие «автоматические» группы. Вы можете добавлять пользователей только в пользовательские группы, которые вы создали.

Редактирование: если вам нужно добавить пользователей в группу модераторов, обратите внимание на функцию sync_sso_record: https://github.com/discourse/wp-discourse/blob/main/lib/utilities.php#L70-L81. Функция add_user_to_discourse_group — это просто удобная обёртка вокруг sync_sso_record.

Параметр moderator (boolean) принимается в параметрах SSO: discourse/lib/discourse_connect_base.rb at 5098338a9649ee8830c8a0781e0eb538e8206eac · discourse/discourse · GitHub. Вы также можете добавить любые пользовательские группы, в которые хотите включить пользователей, используя параметр add_groups. Это позволит добавить пользователя в группу «moderators» и группу «Volunteer Members» одним вызовом API.

Редактирование 2: Если вы попробуете подход с функцией sync_sso_record, возможно, вам понадобится использовать строку "true" вместо булевого значения true для параметра moderator. PHP интерпретирует true как 1. Похоже, что Discourse принимает строку "true", чтобы обработать этот случай. Обратите внимание, что для установки параметров SSO можно использовать функцию get_sso_params: https://github.com/discourse/wp-discourse/blob/main/lib/utilities.php#L129. Пример того, как это сделать, приведён в коде функции add_user_to_discourse_group.

Привет, @simon! Не мог бы ты привести пример с кодом, показывающий, как, по твоему мнению, нужно добавлять пользователя в группу модераторов?

Вот основная идея. Я предполагаю, что вам нужно добавить какое-то условие в функцию, чтобы все пользователи не добавлялись в группу модераторов при смене их роли.

function add_user_to_discourse_group_on_role_change( $user_id, $role ) {
	$sso_params = array(
		'external_id' => $user_id,
		'moderator'   => 'true', // булевы значения 'true' и 'false' должны задаваться как строки!
		'add_groups'  => 'volunteers' // список через запятую, без пробелов после запятых
	);
    DiscourseUtilities::sync_sso_record( $sso_params );
}

Привет, @simon, у меня возникла проблема. Вот код, который я использую при изменении роли пользователя:

use WPDiscourse\Utilities\Utilities as DiscourseUtilities;

// Хук для выполнения при изменении роли пользователя
add_action('set_user_role', 'add_user_to_discourse_group_on_role_change', 10, 2);

function add_user_to_discourse_group_on_role_change($user_id, $role) {
// 	var_dump($role);
// 	die();
	  if ($role == 's2member_level1') {
            $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
// 		  	$group_names = array("haseeb-group", "moderators");
// 			$s2member_level_1_groups = implode(",", $group_names);
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_1_groups);
// 		  var_dump($result);
// 		  die();
		  	
// 			var_dump($result);
        }
		else if($role == 's2member_level2'){
			   $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
			  $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_2_groups);
		}
		
		else if($role == 's2member_level3'){
			   $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
			  $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_3_groups);
		}
	
		
		else if($role == 's2member_level4'){
			   $s2member_level_4_groups = get_option('s2member_level_4_groups', '');
			  $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_4_groups);
		}
		
		else {
    // Собираем все группы уровней
    $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
    $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
    $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
    $s2member_level_4_groups = get_option('s2member_level_4_groups', '');

    // Объединяем все группы в один массив
    $s2member_level_groups = array_merge(
        explode(',', $s2member_level_1_groups),
        explode(',', $s2member_level_2_groups),
        explode(',', $s2member_level_3_groups),
        explode(',', $s2member_level_4_groups)
    );

    // Удаляем дубликаты
    $s2member_level_groups = array_unique($s2member_level_groups);

    // Удаляем пользователя из всех собранных групп
    foreach ($s2member_level_groups as $group) {
        $result = DiscourseUtilities::remove_user_from_discourse_group($user_id, $group);
        // При необходимости обработайте $result
    }
}


    
}

Смысл кода заключается в добавлении или удалении пользователя в зависимости от условий. Однако в любом случае, независимо от условия, администраторам форума приходит уведомление о необходимости одобрить, отклонить или удалить пользователя. Почему это происходит в любом случае? Например, в блоке else я хочу просто удалить пользователя из конкретной группы, но в этом случае снова появляется уведомление администраторам о проверке нового пользователя.

Ещё один момент: когда пользователь регистрируется на сайте WordPress, он автоматически добавляется в группы trust_level_0 и trust_level_1. Не знаю, почему это происходит.

Пожалуйста, помогите. Я также могу поделиться с вами хуком регистрации пользователя.

Это потому, что это основные группы Discourse, используемые для управления правами доступа и т. д.

Это происходит потому, что у вас включена настройка, требующая одобрения всех новых пользователей сотрудниками, как упоминается на вашем скриншоте:

image

Похоже, вы добавляете новых пользователей, что требует одобрения, как я уже упоминал выше.

В дополнение к тому, что написал @Firepup650, когда ваш код вызывает

DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_2_groups);

он обращается к вспомогательной функции, которая отправляет запрос к маршруту Discourse sync_sso: Sync DiscourseConnect user data with the sync_sso route. Этот запрос используется либо для обновления существующего пользователя, либо для создания нового. Если пользователь с $user_id никогда не входил в ваш сайт Discourse, будет создан новый пользователь. Он всё равно должен быть добавлен в группы без каких-либо проблем, но это вызовет уведомление для модераторов, поскольку на вашем сайте включена настройка сайта «требовать одобрения пользователей»:

Привет, @simon, у меня снова возникла проблема.

Я хочу синхронизировать данные пользователя при регистрации на сайте и добавлении в Discourse, но пользовательские поля в его профиле на Discourse не обновляются. Вот мой код, пожалуйста, проверьте:

add_action( 'user_register', 'user_registered_callback', 10, 1 ); // Изменено для приема только одного аргумента

function user_registered_callback($user_id) {
    // Получаем данные пользователя по ID
    $user_data = get_userdata($user_id);

    // Проверяем, существуют ли данные пользователя
    if ($user_data) {
        // Получаем роли пользователя
        $roles = $user_data->roles;

        // Проверяем, есть ли у пользователя роль s2Member
        if (in_array('s2member_level1', $roles)) {
            $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_1_groups);

            // Получаем пользовательские поля
            $custom_fields = get_user_option('1709753088_wp_s2member_custom_fields', $user_id);
            // Подготавливаем массив параметров SSO
            $sso_params_array = [];
            $sso_params_array = [
				'external_id' => $user_id,
                'custom.user_interests' => $custom_fields['user_interests'],
                'custom.user_age' => $custom_fields['user_age'],
                'custom.user_gender' => $custom_fields['user_gender'],
                'custom.user_sexual' => $custom_fields['user_sexual'],
                'custom.user_facebook' => $custom_fields['user_facebook'],
                'custom.user_linkedin' => $custom_fields['user_linkedin'],
                'custom.users_reasons' => $custom_fields['users_reasons']
            ];

            $fields_updated = DiscourseUtilities::sync_sso_record($sso_params_array, $user_id);
			var_dump($fields_updated);
			die();
        } elseif (in_array('s2member_level2', $roles)) {
            $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_2_groups);
        } elseif (in_array('s2member_level3', $roles)) {
            $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_3_groups);
        } elseif (in_array('s2member_level4', $roles)) {
            $s2member_level_4_groups = get_option('s2member_level_4_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_4_groups);
        } else {
            // Собираем все группы уровней
            $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
            $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
            $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
            $s2member_level_4_groups = get_option('s2member_level_4_groups', '');

            // Объединяем все группы в один массив
            $s2member_level_groups = array_merge(
                explode(',', $s2member_level_1_groups),
                explode(',', $s2member_level_2_groups),
                explode(',', $s2member_level_3_groups),
                explode(',', $s2member_level_4_groups)
            );

            // Удаляем дубликаты
            $s2member_level_groups = array_unique($s2member_level_groups);

            // Удаляем пользователя из всех собранных групп
            foreach ($s2member_level_groups as $group) {
                $result = DiscourseUtilities::remove_user_from_discourse_group($user_id, $group);
                // При необходимости обработайте $result
            }
        }
    } else {
        // Обрабатываем случаи, когда данные пользователя недоступны
        // Логируем сообщение или обрабатываем иначе в зависимости от требований
        error_log("Данные пользователя недоступны в user_registered_callback.");
    }
}

Теперь посмотрите на этот код:

  $sso_params_array = [
				'external_id' => $user_id,
                'custom.user_interests' => $custom_fields['user_interests'],
                'custom.user_age' => $custom_fields['user_age'],
                'custom.user_gender' => $custom_fields['user_gender'],
                'custom.user_sexual' => $custom_fields['user_sexual'],
                'custom.user_facebook' => $custom_fields['user_facebook'],
                'custom.user_linkedin' => $custom_fields['user_linkedin'],
                'custom.users_reasons' => $custom_fields['users_reasons']
            ];

Когда я вызываю var_dump($fields_updated), он возвращает true, но данные не добавляются в профиль пользователя на Discourse. В чём проблема?

Если ничего не изменилось, вам нужно использовать подход, описанный здесь: Map custom User Fields - #7 by simon.

Сначала убедитесь, что пользовательские поля уже созданы в Discourse. Затем на странице вашего сайта Discourse /admin/customize/user_fields добавьте .json к URL, чтобы увидеть JSON-структуру пользовательского поля. Например: http://localhost:4200/admin/customize/user_fields.json.

Используйте значения id из JSON для настройки ключей SSO, которые выглядят так: custom.user_field_<field_id>. Например: custom.user_field_1.

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

Привет, @simon, я использовал ваш подход, о котором вы упоминали, и вот мой код сейчас:

add_action( 'user_register', 'user_registered_callback', 10, 1 ); // Откорректировано для принятия только одного аргумента

function user_registered_callback($user_id) {
    // Получение данных пользователя по ID
    $user_data = get_userdata($user_id);
// 	$user = get_user_by( 'ID', $user_id );
    // Проверка наличия данных пользователя
    if ($user_data) {
        // Получение ролей пользователя
        $roles = $user_data->roles;

        // Проверка наличия роли s2Member
        if (in_array('s2member_level1', $roles)) {
            $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_1_groups);
			 $sso_params = array();
//             // Получение пользовательских полей
//             $custom_fields = get_user_option('1709753088_wp_s2member_custom_fields', $user_id);
//             // Подготовка массива параметров SSO
//             $sso_params_array = [];
//             $sso_params_array = [
// 				'external_id' => $user_id,
//                 'custom.user_field_1' => $custom_fields['user_interests'],
//                 'custom.user_field_2' => $custom_fields['user_age'],
//                 'custom.user_field_3' => $custom_fields['user_gender'],
//                 'custom.user_field_4' => $custom_fields['user_sexual'],
//                 'custom.user_field_5' => $custom_fields['users_reasons']
//             ];

//             $fields_updated = DiscourseUtilities::sync_sso_record($sso_params_array);
			 $sso_params = apply_filters( 'wpdc_sso_params', $sso_params, $user_data );
			var_dump($sso_params);
			die();
        } elseif (in_array('s2member_level2', $roles)) {
            $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_2_groups);
        } elseif (in_array('s2member_level3', $roles)) {
            $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_3_groups);
        } elseif (in_array('s2member_level4', $roles)) {
            $s2member_level_4_groups = get_option('s2member_level_4_groups', '');
            $result = DiscourseUtilities::add_user_to_discourse_group($user_id, $s2member_level_4_groups);
        } else {
            // Сбор всех групп уровней
            $s2member_level_1_groups = get_option('s2member_level_1_groups', '');
            $s2member_level_2_groups = get_option('s2member_level_2_groups', '');
            $s2member_level_3_groups = get_option('s2member_level_3_groups', '');
            $s2member_level_4_groups = get_option('s2member_level_4_groups', '');

            // Объединение всех групп в один массив
            $s2member_level_groups = array_merge(
                explode(',', $s2member_level_1_groups),
                explode(',', $s2member_level_2_groups),
                explode(',', $s2member_level_3_groups),
                explode(',', $s2member_level_4_groups)
            );

            // Удаление дубликатов
            $s2member_level_groups = array_unique($s2member_level_groups);

            // Удаление пользователя из всех собранных групп
            foreach ($s2member_level_groups as $group) {
                $result = DiscourseUtilities::remove_user_from_discourse_group($user_id, $group);
                // Здесь можно обработать $result по необходимости
            }
        }
    } else {
        // Обработка случаев, когда данные пользователя недоступны
        // Логирование сообщения или другая обработка в зависимости от требований
        error_log("Данные пользователя недоступны в user_registered_callback.");
    }
}




add_filter( 'wpdc_sso_params', 'my_namespace_set_discourse_custom_field', 10, 2 );

function my_namespace_set_discourse_custom_field( $sso_params, $user ) {
    $custom_fields = get_user_option('1709753088_wp_s2member_custom_fields', $user->ID);
	
    // Проверка, что $custom_fields является массивом и не пуст
    if (is_array($custom_fields) && !empty($custom_fields)) {
        // Присвоение пользовательских полей соответствующим ключам в параметрах SSO
        $sso_params['custom.user_field_1'] = $custom_fields['user_interests'];
        $sso_params['custom.user_field_2'] = $custom_fields['user_age'];
        $sso_params['custom.user_field_3'] = $custom_fields['user_gender'];
        $sso_params['custom.user_field_4'] = $custom_fields['user_sexual'];
        $sso_params['custom.user_field_5'] = $custom_fields['users_reasons'];
    } else {
        // Логирование или отображение сообщения об ошибке, если $custom_fields не соответствует ожидаемому формату
        error_log('Данные пользовательских полей не соответствуют ожидаемому формату.');
    }

    // Возврат изменённых параметров SSO
    return $sso_params;
}

Теперь единственная проблема в том, что поле users_reasons не добавляется в custom.user_field_5, а другие данные успешно записываются в профиль пользователя. Например, если я закомментирую строку $sso_params['custom.user_field_5'] = $custom_fields['users_reasons'];, то остальные поля добавляются в профиль пользователя на Discourse. Но если я не закомментирую эту строку, то никакие данные не добавляются в профиль пользователя на Discourse. Хочу уточнить, что поле users_reasons на сайте Discourse и на сайте WordPress является полем с множественным выбором. Может ли это быть причиной? Пожалуйста, подскажите.

Да, проблема в том, что это поле с множественным выбором. Я не уверен, поддерживается ли это пока: Add support for multi-select fields in DiscourseConnect protocol.

Возможно, есть способ заставить это работать уже сейчас, но я не вижу, как это сделать. Код Discourse, который вызывается, находится здесь: discourse/app/models/discourse_connect.rb at main · discourse/discourse · GitHub

Привет, @simon, не мог бы ты написать пример кода, следуя моему коду и ссылаясь на предоставленный тобой пример:

Я не думаю, что это возможно. Насколько я понимаю, код для настройки пользовательских полей из полезной нагрузки SSO был немного изменен с момента написания поста, на который я ссылался, но изменения не позволили обновлять поля с множественным выбором из полезной нагрузки SSO.

В идеале Discourse должен обрабатывать этот случай аналогично тому, как он обрабатывает поле SSO add_groups: discourse/app/models/discourse_connect.rb at main · discourse/discourse · GitHub. Для этого код SSO в Discourse должен знать, является ли конкретное пользовательское полем полем с множественным выбором. Мне кажется, что сейчас код не содержит этой информации.

Опять же, возможно, сейчас есть способ сделать это, но это выходит за рамки моих возможностей.

Привет, @simon! Я хочу синхронизировать аватары пользователей между WordPress и Discourse. Я уже использую плагин WP-Dialog для SSO, и теперь хочу, чтобы при изменении аватара на сайте Discourse он автоматически обновлялся и на сайте WordPress. Как это можно реализовать? Я планирую использовать один плагин для аватаров на сайте WordPress. Вот код, который я использую:

function my_wpdc_sso_client_updated_user( $updated_user, $query ) {
		$avatar_url = $query['avatar_url'];
		$user_id = $updated_user->ID;
		// Это позволяет использовать функции download_url() и wp_handle_sideload()
	require_once( ABSPATH . 'wp-admin/includes/file.php' );

	// Загрузка во временную директорию
	$temp_file = download_url( $avatar_url );

	if( is_wp_error( $temp_file ) ) {
		return false;
	}

	// Перемещение временного файла в директорию загрузок
	$file = array(
		'name'     => basename( $image_url ),
		'type'     => mime_content_type( $temp_file ),
		'tmp_name' => $temp_file,
		'size'     => filesize( $temp_file ),
	);
	$sideload = wp_handle_sideload(
		$file,
		array(
			'test_form'   => false // Нет необходимости проверять параметр 'action'
		)
	);

	if( ! empty( $sideload[ 'error' ] ) ) {
		// При желании можно вернуть сообщение об ошибке
		return false;
	}

	// Теперь нужно добавить загруженное изображение в медиатеку WordPress
	$attachment_id = wp_insert_attachment(
		array(
			'guid'           => $sideload[ 'url' ],
			'post_mime_type' => $sideload[ 'type' ],
			'post_title'     => basename( $sideload[ 'file' ] ),
			'post_content'   => '',
			'post_status'    => 'inherit',
		),
		$sideload[ 'file' ]
	);

	if( is_wp_error( $attachment_id ) || ! $attachment_id ) {
		return false;
	}

	// Обновление метаданных, пересоздание размеров изображения
	require_once( ABSPATH . 'wp-admin/includes/image.php' );

	wp_update_attachment_metadata(
		$attachment_id,
		wp_generate_attachment_metadata( $attachment_id, $sideload[ 'file' ] )
	);

	// Возвращаем ID вложения
	update_user_meta($user_id, '1709753088_wp_user_avatar', $attachment_id);
	update_user_meta($user_id, 'wp_user_avatar', $attachment_id);
}

Как видите, я хочу обновить ключи метаданных пользователя, используя ID вложения, но ID вложения не создаётся.

Буду признателен за вашу помощь.

Спасибо!