将Discourse与MemberMouse集成

我在两个站点上维护 Discourse 和 MemberMouse。希望本指南能对大家有所帮助。您的具体需求可能与我的预期结果有所不同。本指南假设您熟悉 MemberMouse 的 hooksfilters 以及 MemberMouse 的 PHP 接口。同时也假设您能够通过 functions.php 文件或您自己的自定义插件,轻松地向 WordPress 添加自定义代码。

以下指南是我们为了实现以下目标而添加的内容:

  • 根据用户在 MemberMouse 中的会员状态,在 Discourse 中激活/停用用户
  • 设置代表 MemberMouse 会员等级的 Discourse 用户组
  • 即时同步用户名/电子邮件的更改
  • 以及其他一些有用的微调

第 1 步:安装 Discourse、WordPress 和 wp-discourse WordPress 插件

安装并配置好 WordPress、Discourse 以及 wp-discourse WordPress 插件,确保 WordPress 作为 SSO 提供者正确运行。关于此问题,这里有很多相关讨论帖。

第 2 步:勾选允许 wp-discourse 插件在 WordPress 中创建用户时也在 Discourse 中创建新用户的选项

我发现,为了让 wp-discourse 插件在 WordPress 中创建用户时真正在 Discourse 中创建用户,我需要对插件进行代码修改。这是因为该插件依赖于 “wp_login” 操作,但其在 MemberMouse 中的行为与标准的 WordPress 行为不同。因此,您需要在文件 /lib/discourse-sso.phppublic function __construct( $wordpress_email_verifier ) 函数中添加以下代码行:

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

并在 functions.php 或您自己的插件中添加:

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');

第 3 步:如果您愿意,可以设置新用户无需点击 Discourse 的电子邮件激活链接

默认情况下,Discourse 会向新用户发送激活邮件,但我选择关闭此功能,因为用户已经在 WordPress 中通过了足够多的验证步骤才能加入。如果您的 WordPress 站点加入门槛较低,您可能不希望跳过激活邮件。在我们的案例中,加入需要付费。请将以下代码添加到 functions.php 或您创建的专用插件中。

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

第 4 步:每当 MemberMouse 用户的账户发生变更时:
将 MemberMouse 会员等级映射到 Discourse 用户组
同步电子邮件地址/用户名
根据情况在 Discourse 中激活/停用用户

您可以将以下代码添加到 functions.php 或您自己的插件中。

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');

在函数 run_discourse_sync_based_on_mm_acct_change 中,您需要:

(1) 使用 Discourse API 获取该用户的 Discourse 用户名(由于 Discourse 自身的用户名规则,可能与 WordPress 中的略有不同)和 Discourse ID 编号。(文档)

(2) 将其 MemberMouse 会员等级 ID 映射到等效的 Discourse 用户组 ID,然后在 Discourse 中设置其用户组。首先,您需要删除其旧的用户组 ID。(文档)。然后您可以设置新用户组。(文档)

(3) 如果他们在 WordPress 中更改了用户名和电子邮件,请同步这些信息。我们只允许在 WordPress 中更改这些信息。我在此处获得了此部分的帮助。

(4) 根据其在 MemberMouse 中的状态,在 Discourse 中激活/停用用户。激活 (文档)。停用功能似乎未包含在 API 文档中。$url = $url_base.‘admin/users/’.$discourse_userid.‘/deactivate.json?’.$api_auth;

第 5 步:在适当时自动重定向回 Discourse

(我强烈建议您在完全掌握 WordPress 和 Discourse 如何协同工作之前,先暂缓执行此部分。)

如果用户未登录 Discourse 且未登录 WordPress,当他们访问 Discourse 的某个 URL 并点击蓝色的“登录”按钮时,会被带到 WordPress 进行登录,但随后 MemberMouse 会将用户重定向到 MemberMouse 设置中指定的页面。不幸的是,用户无法被重定向回 Discourse。以下是我的解决方案。您可以将此代码添加到 functions.php 或您自己的插件中。(更多信息请见此讨论帖。)

// 如果用户来自 Discourse 论坛,登录后将他们重定向到确切的位置
function my_mm_login_redirect( $infoObj ) {
	if ( @$_COOKIE['detected_forum_referal'] != '' ) { // 如果用户刚通过 Discourse 到达,您需要设置此临时 Cookie
		$current_user       = $infoObj->user;
		$user_id            = $current_user->ID;
		// 载荷和签名。
		$payload = @$_COOKIE['mm_cookie_sso'];
		$sig     = @$_COOKIE['mm_cookie_sig'];
		// 将 %0B 转换回 %0A。
		$payload = rawurldecode( str_replace( '%0B', '%0A', rawurlencode( $payload ) ) );
		// 验证签名。
		$sso_secret = 'YOUR-SSO-SECRET';
		$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 );
		// 重定向回 Discourse。
		return('YOUR-FORUM-BASE-URL' . '/session/sso_login?' . $q);
	}
	return('');
}
add_filter( 'mm_login_redirect', 'my_mm_login_redirect', 10, 1 );
17 个赞

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 个赞

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

1 个赞

@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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞

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 个赞