Intégrer Discourse avec MemberMouse

Je maintiens Discourse et MemberMouse sur 2 sites. J’espère que ce guide sera utile aux autres. Vos spécifications exactes peuvent différer de mes objectifs. Ce guide suppose que vous êtes familier avec les hooks, les filtres de MemberMouse et l’interface PHP de MemberMouse. Il suppose également que vous pouvez ajouter facilement du code personnalisé à WordPress via functions.php ou votre propre plugin personnalisé.

Ce guide ci-dessous décrit ce que nous avons ajouté afin de :

  • Activer/désactiver l’utilisateur dans Discourse en fonction de son statut d’adhésion dans MemberMouse
  • Définir des groupes Discourse représentant les niveaux d’adhésion MemberMouse
  • Synchroniser instantanément les modifications de nom d’utilisateur/email
  • Et diverses autres petites améliorations utiles

Étape 1 : Installer Discourse, WordPress et le plugin WordPress wp-discourse

Installez et configurez correctement WordPress, Discourse et le plugin WordPress wp-discourse, en utilisant WordPress comme fournisseur SSO. Il existe de nombreux fils de discussion à ce sujet ici.

Étape 2 : Cocher la case pour permettre au plugin wp-discourse de créer un nouvel utilisateur dans Discourse lorsqu’un utilisateur est créé dans WordPress

J’ai constaté que pour que wp-discourse crée réellement un utilisateur dans Discourse lorsqu’un utilisateur est créé dans WordPress, je devais apporter une modification de code dans le plugin. En effet, le plugin repose sur l’action “wp_login”, mais son comportement diffère dans MemberMouse par rapport au comportement standard de WordPress. Vous devez donc ajouter cette ligne au fichier /lib/discourse-sso.php dans la fonction publique __construct( $wordpress_email_verifier ) :

add_action( 'my_mm_account_added', array( $this, 'create_discourse_user' ), 10, 2 );

Et dans functions.php ou votre propre plugin, ajoutez :

function add_user_to_discourse($data) {
    do_action( 'my_mm_account_added', $data["username"], get_user_by('ID',$data["member_id"]) );	
}
add_action('mm_member_add', 'add_user_to_discourse');

Étape 3 : Si vous le souhaitez, empêcher les nouveaux utilisateurs de devoir cliquer sur un lien d’activation par email Discourse

Par défaut, Discourse envoie un email d’activation au nouvel utilisateur, mais j’ai choisi de désactiver cette option car l’utilisateur a déjà franchi un nombre suffisant d’étapes dans WordPress pour s’inscrire. Si votre site WordPress a un seuil d’inscription faible, vous ne voudrez peut-être pas sauter l’email d’activation. Dans notre cas, il faut payer pour s’inscrire. Ajoutez ceci à functions.php ou à un plugin spécial que vous créez.

add_filter( 'wpdc_auto_create_user_require_activation', 'my_wpdc_auto_create_user_require_activation' );
function my_wpdc_auto_create_user_require_activation( $require_activation ) {
    return false;
}

Étape 4 : Chaque fois qu’une modification de compte survient pour un utilisateur MemberMouse :

  • Mapper les niveaux d’adhésion MemberMouse aux groupes Discourse
  • Synchroniser l’adresse email/nom d’utilisateur
  • Activer/désactiver l’utilisateur dans Discourse selon le cas

Vous pouvez ajouter ceci à functions.php ou à votre propre plugin.

add_action('mm_member_membership_change', 'run_discourse_sync_based_on_mm_acct_change');
add_action('mm_member_status_change', 'run_discourse_sync_based_on_mm_acct_change');
add_action('mm_member_account_update', 'run_discourse_sync_based_on_mm_acct_change');

Dans la fonction run_discourse_sync_based_on_mm_acct_change, vous devez :

(1) Utiliser l’API Discourse pour obtenir le nom d’utilisateur Discourse de cet utilisateur (qui peut différer légèrement de celui de WordPress en raison des règles de nommage propres à Discourse) et son identifiant Discourse. (documentation)

(2) Mapper l’ID de niveau d’adhésion MemberMouse à l’ID de groupe Discourse équivalent, puis définir son groupe dans Discourse. Vous devez d’abord supprimer son ancien ID de groupe. (documentation). Ensuite, vous pouvez définir le nouveau groupe. (documentation)

(3) Synchroniser son nom d’utilisateur et son email s’ils ont été modifiés dans WordPress. Nous autorisons uniquement ces modifications dans WordPress. J’ai obtenu de l’aide pour cette partie ici.

(4) Activer/désactiver l’utilisateur dans Discourse en fonction de son statut dans MemberMouse. Activer (documentation). La désactivation semble absente de la documentation de l’API. $url = $url_base.‘admin/users/’.$discourse_userid.‘/deactivate.json?’.$api_auth;

Étape 5 : Redirection automatique vers Discourse lorsque cela est approprié

(Je recommande vivement d’attendre cette partie jusqu’à ce que vous ayez une très bonne compréhension du fonctionnement conjoint de WordPress et Discourse.)

Si un utilisateur n’est PAS connecté à Discourse et n’est PAS connecté à WordPress. Et qu’il accède à une URL dans Discourse et clique sur le bouton bleu de connexion, il est redirigé vers WordPress pour se connecter, mais MemberMouse redirige ensuite l’utilisateur vers la page configurée dans les paramètres de MemberMouse. Malheureusement, l’utilisateur n’est pas redirigé vers Discourse. Voici comment j’ai résolu ce problème. Vous pouvez ajouter ceci à functions.php ou à votre propre plugin. (Fil de discussion pour plus d’informations.)

// Si la personne venait du forum Discourse, la rediriger exactement là où elle se trouvait après la connexion
function my_mm_login_redirect( $infoObj ) {
    if ( @$_COOKIE['detected_forum_referal'] != '' ) { // Vous devez gérer la définition de ce cookie temporaire si l'utilisateur vient tout juste d'arriver via Discourse
        $current_user       = $infoObj->user;
        $user_id            = $current_user->ID;
        // Charge utile et signature.
        $payload = @$_COOKIE['mm_cookie_sso'];
        $sig     = @$_COOKIE['mm_cookie_sig'];
        // Remplacer %0B par %0A.
        $payload = rawurldecode( str_replace( '%0B', '%0A', rawurlencode( $payload ) ) );
        // Valider la signature.
        $sso_secret = 'VOTRE-SECRET-SSO';
        $sso        = new \WPDiscourse\SSO\SSO( $sso_secret );
        if ( ! ( $sso->validate( $payload, $sig ) ) ) {
            return '';
        }
        $nonce  = $sso->get_nonce( $payload );
        $params = array(
            'nonce'               => $nonce,
            'username'            => $current_user->user_login,
            'email'               => $current_user->user_email,
            'external_id'         => $user_id,
        );
        $params = apply_filters( 'wpdc_sso_params', $params, $current_user );
        $q = $sso->build_login_string( $params );
        do_action( 'wpdc_sso_provider_before_sso_redirect', $user_id, $current_user );
        // Rediriger vers Discourse.
        return('VOTRE-URL-DEBASE-DU-FORUM' . '/session/sso_login?' . $q);
    }
    return('');
}
add_filter( 'mm_login_redirect', 'my_mm_login_redirect', 10, 1 );
17 « J'aime »

if i May ask do you need to install the Discourse on its own or … you just need the discourse plugin in your wordpress

You need to install Discourse on it’s own. The plugin just helps WordPress and Discourse talk to each other.

4 « J'aime »

thank you m new here and i like this community so im in a process on launching it on my Google cloude…

1 « J'aime »

@lkramer - how does this workflow need to change to use MemberMouse Bundles instead of the MM membership status?

(For us MM memberships are too limited, as a customer can only be in one at a time (free or paid). We have many products, so we use Bundles which allows us infinite flexibility.)

Our use case is as follows:

We sell multiple courses. We control course access using Bundles. “Product 1 Bundle” and “Product 2 Bundle,” etc. Any one customer can have access to one or many (or all) of course courses.

Within Discourse we have a separate course forum (category/group) for each product…

We want customers of “Product 1 Bundle” to only be able to see the Discourse category/group for that course, and so on.

Any idea how your workflow would need to change to allow for our use case?

Thx a ton in advance, Leah!

3 « J'aime »

In order to rely on bundles rather than membership levels – anywhere I mentioned membership status/level, you’d just check the person’s bundle status via a MemberMouse php function. So check whether they have bundle X and whether it’s currently ‘active’ and if so, put them in Discourse group X or else remove them from group X. I believe the MemberMouse php function you want is something like this:

if ( mm_member_decision(array("hasBundle"=>"1")) )

Now, regarding only making certain Discourse categories accessible to certain Discourse groups, I’ve never tried it but according to this thread here you can restrict categories to certain groups:

So as long as you can successfully set/unset a person’s Discourse group based on their bundle (which should be do-able), then you can achieve your goal of only having access to certain categories.

4 « J'aime »

Another tidbit of potentially helpful info – To learn to use the Discourse API, write some small isolated scripts to get small pieces working as a proof of concept and to know exactly what code works. There are often several ways to interact with an API even within one language. So, e.g, write a little script that just tests the concept of setting and unsetting someone’s Discourse group. This is what I did and then I knew it was safe/reliable to add them into the MemberMouse eco-system where approprirate. :slight_smile:

6 « J'aime »

The best way to sync group membership is, of course, the sync_sso and during SSO login (which should go through the same function!! you don’t want to add someone from a group and take them back out when they log in) — because this is way more efficient, only 1 API call to change as many groups as you want.

5 « J'aime »

Thx @lkramer! (and @riking)

Thx @riking. So are you saying you can set/unset multiple groups in sync_sso. That’s good to know.

1 « J'aime »

This wasn’t working for me, and I just upgraded, and now there is no create_discourse_user, it seems.

@simon, have you got a suggestion here?

It seems that having to edit the plugin to make membermouse work is something of a bummer. I think that I can imagine code that would solve that if I first could get WordPress to trigger creating the account.

1 « J'aime »

It’s still there: https://github.com/discourse/wp-discourse/blob/master/lib/utilities.php#L306

You need to call it with the namespace WPDiscourse\Utilities\Utilities::create_discourse_user( $user )

There is also a sync_sso_record function that would be better to use if you are able to. It takes an array of sso parameters as an argument. You can get them from the get_sso_params function.

I’m in the process of cleaning up this file. I won’t remove any functions that I’ve posted about on meta. If I break anything, let me know.

Edit: I read the OP more closely. I hadn’t realized it was editing the plugin’s code. That part will be broken by the most recent update. It would be better to call either the create_discourse_user or the sync_sso_record function from WPDiscourse\Utilities, and add the my_mm_account_added hook to your functions.php file or a separate plugin.

4 « J'aime »

Thanks, @simon!

Here’s what I’m doing now:

function add_user_to_discourse($data) {
	do_action( 'my_mm_account_added', $data["username"], get_user_by('ID',$data["member_id"]) );
    error_log ("Doing add_user");
}

add_action('mm_member_add', 'add_user_to_discourse');

Also, I have a handful of actions like


add_action('mm_member_membership_change', 'run_discourse_sync_based_on_mm_acct_change');
add_action('mm_member_status_change', 'run_discourse_sync_based_on_mm_acct_change');
add_action('mm_member_account_update', 'run_discourse_sync_based_on_mm_acct_change');

Should all of these call sync_sso_record? And what should go in $user when I call WPDiscourse\Utilities\Utilities::create_discourse_user( $user )? (I’m going to RTFC now, but perhaps your 2 minutes can save me an hour. :slight_smile:)

Edit: OK, should something like this work? I see that it’s getting called, but the user’s not getting created.

function add_user_to_discourse($data) {
    $user['name'] = $data['first_name'] . " " . $data['last_name'];
    $user['user_email'] = $data['email'];
    error_log ("Calling create_discourse_user");
    WPDiscourse\Utilities\Utilities::create_discourse_user( $user );
}

Assuming you are also adding the user to a group, I think you could just call WPDiscourse\Utilities\Utilities::add_user_to_discourse_group( $user_id, 'group,names' ). That gets the sso params for then calls sync_sso_record with the params. The remove_user_from_discourse_group function works in a similar way, except it removes users from a group or groups.

If you just want to create or update a user without dealing with groups, you can do something like this:

$user = get_user_by( 'id', 1 ); // Supply user_id here.
$sso_params = WPDiscourse\Utilities\Utilities::get_sso_params( $user );
WPDiscourse\Utilities\Utilities::sync_sso_record( $sso_params );

If SSO is enabled, I don’t think there would be a reason to prefer the create_discourse_user function over the sync_sso_record function. If you do need to use it, it takes a WordPress user object as the argument: $user = get_user_by( 'id', 1 );

4 « J'aime »

Yeah. Sadly, I started with code that was written before add_user_to_discourse_group existed. I was just wondering whether I should change my working API calls to use that instead.

It’s not obvious to me that add_user_to_discourse_group will create the user. Is that happening somewhere that I don’t see?

Yes, it creates a user by sending the SSO parameters to the Discourse /admin/users/sync_sso route. It actually takes a comma separated list of group names as its argument (no spaces between names), so it should be renamed. You can also call it with an empty string as the group_names argument. You have to at least supply an empty string for the group names, or it will throw an error.

4 « J'aime »

So, this is a zillion times easier than it used to be!

add_user_to_discourse_group( $user_id, $group_names ) wants the WP userid? and the Discourse group name (no fussing in the json to figure out the group_id?!?!)?

Now I just need to find the $user_id and I’ll be golden.

Edit: $user = get_user_by('ID',$data["member_id"])

Edit: I’m all set! The version of the MemberMouse bundle-to-group function that I was working on yesterday was over 200 lines. The working version today is about 40.

Thanks again, @simon!

2 « J'aime »

Great! I’ll document the changes to the functions soon. They take the same arguments as before, but behave a little differently. One thing to note is that Discourse will still consider the add_user_to_discourse_group and remove_user_from_discourse_group functions to be API calls - they are just making fewer API calls than the previous version did.

The functions return the status code that is returned from the Discourse request. You want to be getting a 200 response. If you’re adding a lot of users at the same time, you need to look out for 429 status codes and find some way of dealing with them. (When adding one user at a time it shouldn’t be an issue.)

4 « J'aime »

Well, I’m pretty sure that last summer when I did this before there was a lot more that I had to do with my own darn API calls. This is pretty great. Hooray that this is now your day job!

1 « J'aime »

Maybe you can write a new and improved MemberMouse guide, Jay, when this is all said and done. :slight_smile: Come to think of it, just a general, “hook Discourse up to your membership plugin” guide would be great for ANY membership plugin. I’m guessing they all have similar “hooks” as MemberMouse.

4 « J'aime »