# Integrate Discourse with MemberMouse

**URL:** https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636
**Category:** Administrators
**Tags:** wordpress, how-to
**Created:** [February 2, 2018, 3:10pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636 "2018-02-02T15:10:00Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 2, 2018, 3:10pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/1 "2018-02-02T15:10:00Z")

</div>

I maintain Discourse and MemberMouse running on 2 sites. Hopefully this guide will be helpful to people. Your exact specifications may differ from what my desired results were. This guide assumes that you are familiar with MemberMouse [hooks](http://support.membermouse.com/support/solutions/articles/9000020294-membermouse-wordpress-hooks), [filters](http://support.membermouse.com/support/solutions/articles/9000020317-membermouse-wordpress-filters) and MemberMouse [PHP interface](http://support.membermouse.com/support/solutions/articles/9000020212-using-the-php-interface). And also assumes you can comfortably add custom code to WordPress via functions.php or your own custom plugin.

This guide below is what we added in order to:

- Activate/deactivate the user in Discourse depending on their membership status in MemberMouse
- Set Discourse groups that represent MemberMouse membership levels
- Sync up username/email changes instantaneously
- And misc other helpful little tweaks

**Step 1: Install Discourse and WordPress and wp-discourse WordPress plugin**

Get the WordPress and Discourse and wp-discourse WordPress plugin up and running and configured properly with WordPress as the SSO provider. There are lots of threads about this here.

**Step 2: Check the box to allow the wp-discourse plugin to create a new user in Discourse when user is created in WordPress**

I found that in order for the wp-discourse to actually create a user in Discourse when a user is created in WordPress, I needed to make a code change in the plugin. This is because the plugin relies on the “wp\_login” action but it behaves differently in MemberMouse than regular WordPress behavior. So you need add this line to the file /lib/discourse-sso.php in public function \_\_construct( $wordpress\_email\_verifier ):

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

And in functions.php or your own plugin add:

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

```

**Step 3: If you want, make it so new users don’t need to click a Discourse email activation link**

By default Discourse will send an activation email to the new user but I chose to turn this off since the user has already jumped through a satisfactory number of hoops in WordPress to join. If your WordPress site has a low barrier to joining, you may not want to skip the activation email. In our case, you have to pay to join so. Add this to functions.php or to a special plugin you create.

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

```

**Step 4: Whenever an account change happens to a MemberMouse user:  
Map MemberMouse membership levels to Discourse groups  
Sync the email address/username  
Activate/deactivate user in Discourse as appropriate**

You can add this to functions.php or your own 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');

```

In the function run\_discourse\_sync\_based\_on\_mm\_acct\_change you want to:

(1) Use the Discourse API to get this user’s Discourse username (may be slightly different than their WordPress one due to Discourse’s own username rules) and Discourse ID number. [(documentation)](https://meta.discourse.org/t/api-request-to-users-by-external-on-private-forum/60609)

(2) Map their MemberMouse membership level ID to the equivalent Discourse group ID and then set their group in Discourse. First you need to delete their old group ID. [(documentation)](http://docs.discourse.org/#tag/Admin%2Fpaths%2F~1admin~1groups~1%7Bgroup_id%7D.json%2Fdelete). Then you can set the new group. [(documentation)](http://docs.discourse.org/#tag/Admin%2Fpaths%2F~1admin~1users~1%7Bid%7D~1groups%2Fpost)

(3) Sync up their username and email if they have been changed in WordPress. We only allow these to be changed in WordPress. [I got help with this part here.](https://meta.discourse.org/t/if-someone-changes-their-email-in-wordpress-change-it-in-discourse-sso-sync/76572)

(4) Activate/deactivate the user in Discourse depending on their status in MemberMouse. Activate [(documentation)](http://docs.discourse.org/#tag/Admin%2Fpaths%2F~1admin~1users~1%7Bid%7D~1activate%2Fput). Deactivate seems to be missing from the API docs. $url = $url\_base.‘admin/users/’.$discourse\_userid.‘/deactivate.json?’.$api\_auth;

**Step 5: Auto-redirect back to Discourse when appropriate**

(I highly recommend holding off on this part of things until you get a really good feel for how WordPress and Discourse work together.)

If a user is NOT logged into Discourse and NOT logged into WordPress. And they come to a url in Discourse and click the blue Login button, they are taken to WordPress to login but then MemberMouse directs the user to whatever page you have it set to redirect to in MemberMouse’s settings. The user doesn’t get redirected back to Discourse unfortunately. So here is how I solved this. You can add this to functions.php or your own plugin. [(Thread for more info.)](https://meta.discourse.org/t/if-someone-changes-their-email-in-wordpress-change-it-in-discourse-sso-sync/76572/)

```
// If the person came from the discourse forum, push them to exactly where they were after logging in
function my_mm_login_redirect( $infoObj ) {
	if ( @$_COOKIE['detected_forum_referal'] != '' ) { // You need take care of setting this temporary cookie if the user just arrived via discourse
		$current_user = $infoObj->user;
		$user_id = $current_user->ID;
		// Payload and signature.
		$payload = @$_COOKIE['mm_cookie_sso'];
		$sig = @$_COOKIE['mm_cookie_sig'];
		// Change %0B back to %0A.
		$payload = rawurldecode( str_replace( '%0B', '%0A', rawurlencode( $payload ) ) );
		// Validate signature.
		$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 );
		// Redirect back to Discourse.
		return('YOUR-FORUM-BASE-URL' . '/session/sso_login?' . $q);
	}
	return('');
}
add_filter( 'mm_login_redirect', 'my_mm_login_redirect', 10, 1 );

```

---

<div class="post-metadata">

### Author: ![shim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/shim/32/377650_2.png) [@shim](https://meta.discourse.org/u/shim)
#### Post date: [February 2, 2018, 3:23pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/2 "2018-02-02T15:23:40Z")

</div>

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

---

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 2, 2018, 3:25pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/3 "2018-02-02T15:25:27Z")

</div>

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

---

<div class="post-metadata">

### Author: ![shim](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/shim/32/377650_2.png) [@shim](https://meta.discourse.org/u/shim)
#### Post date: [February 2, 2018, 3:27pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/4 "2018-02-02T15:27:13Z")

</div>

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

---

<div class="post-metadata">

### Author: ![andrec](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/andrec/32/120211_2.png) [@andrec](https://meta.discourse.org/u/andrec)
#### Post date: [February 7, 2018, 2:41pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/5 "2018-02-07T14:41:07Z")

</div>

@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!

---

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 7, 2018, 4:26pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/6 "2018-02-07T16:26:44Z")

</div>

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:

> [@Is it possible to restrict categories to specific groups of vetted users?](https://meta.discourse.org/t/is-it-possible-to-restrict-categories-to-specific-groups-of-vetted-users/30249):
>
> Hello! I would like to use discourse to build a community for information security professionals and novices. Some of this would involve sharing sensitive information, but only with users that have been vetted, and can be trusted, Is there any way to restrict categories to specific users. Apologies if this question has been asked before. I searched, but could not find anything.

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.

---

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 7, 2018, 4:30pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/7 "2018-02-07T16:30:52Z")

</div>

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. 🙂

---

<div class="post-metadata">

### Author: ![riking](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/riking/32/170938_2.png) [@riking](https://meta.discourse.org/u/riking)
#### Post date: [February 7, 2018, 9:01pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/8 "2018-02-07T21:01:05Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![andrec](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/andrec/32/120211_2.png) [@andrec](https://meta.discourse.org/u/andrec)
#### Post date: [February 8, 2018, 9:25am UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/9 "2018-02-08T09:25:18Z")

</div>

Thx @lkramer! (and @riking)

---

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 9, 2018, 2:26pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/10 "2018-02-09T14:26:41Z")

</div>

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

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 21, 2018, 12:58am UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/11 "2018-02-21T00:58:48Z")

</div>

> [@lkramer](#):
>
> add\_action( ‘my\_mm\_account\_added’, array( $this, ‘create\_discourse\_user’ ), 10, 2 );

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.

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [February 21, 2018, 1:18am UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/12 "2018-02-21T01:18:37Z")

</div>

> [@pfaffman](#):
>
> This wasn’t working for me, and I just upgraded, and now there is no create\_discourse\_user, it seems.

It’s still there: [https://github.com/discourse/wp-discourse/blob/master/lib/utilities.php#L306](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](https://github.com/discourse/wp-discourse/blob/master/lib/utilities.php#L545) 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.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 21, 2018, 4:23pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/13 "2018-02-21T16:23:43Z")

</div>

Thanks, @simon!

Here’s what I’m doing now:

```php
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

```plaintext

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. 🙂)

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

```plaintext
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 );
}

```

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [February 21, 2018, 4:44pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/14 "2018-02-21T16:44:48Z")

</div>

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:

```php
$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 );`

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 21, 2018, 4:47pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/15 "2018-02-21T16:47:35Z")

</div>

> [@simon](#):
>
> Assuming you are also adding the user to a group, I think you could just call

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?

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [February 21, 2018, 4:57pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/16 "2018-02-21T16:57:43Z")

</div>

> [@pfaffman](#):
>
> 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.

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 21, 2018, 5:04pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/17 "2018-02-21T17:04:23Z")

</div>

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!

---

<div class="post-metadata">

### Author: ![simon](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/simon/32/339122_2.png) [@simon](https://meta.discourse.org/u/simon)
#### Post date: [February 21, 2018, 7:54pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/18 "2018-02-21T19:54:06Z")

</div>

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.)

---

<div class="post-metadata">

### Author: ![pfaffman](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/pfaffman/32/120154_2.png) [@pfaffman](https://meta.discourse.org/u/pfaffman)
#### Post date: [February 21, 2018, 7:56pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/19 "2018-02-21T19:56:51Z")

</div>

> [@simon](#):
>
> Great! I’ll document the changes to the functions soon. They take the same arguments as before, but behave a little differently.

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!

---

<div class="post-metadata">

### Author: ![lkramer](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/lkramer/32/83897_2.png) [@lkramer](https://meta.discourse.org/u/lkramer)
#### Post date: [February 23, 2018, 2:23pm UTC](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636/20 "2018-02-23T14:23:53Z")

</div>

Maybe you can write a new and improved MemberMouse guide, Jay, when this is all said and done. 🙂 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.

[Next page](https://meta.discourse.org/t/integrate-discourse-with-membermouse/79636.md?page=2)
