Giving users control over email notifications from bbPress

Giving users control over email notifications from bbPress

What problem are we trying to solve?

bbPress is a forum plugin that is well integrated into WordPress, so it is a natural choice for adding forums to your website. However, out of the box, it is pretty basic. The idea seems to be that you should use plugins to add additional functionality. There are many to choose from that appear to be well-written and maintained.

In my case I wanted to have more control over email notifications when there are new posts on the forums. The bbPress core notification functionality lets you choose to be notified on a per-forum and per-topic basis. This is not convenient for my style of working. I don’t want to have to visit the forum to see what’s new, and I don’t want to have to remember to click on Subscribe for every single forum and topic.

A little searching turned up a popular plugin that is used to solve this problem: bbPress Notify (No Spam). This plugin lets you notify everyone in selected roles anytime that a new topic or reply is posted. The odd name is due to some history that is not important. Let’s just call it bbPNNS for short.

This is very helpful but it’s still a pretty blunt instrument. Some casual conversations with my users revealed that they fall into two categories. Those who want to never by notified and those who want to always be notified. The “never notify” people don’t want any more email. They prefer to visit the forums regularly and catch up on things that way. The “always notify” people (like me!) only want to have to visit one place, their email inbox, and have the notifications prod them into action.

So how can we give everyone what they want?

The solution: let users choose

My solution relies on some other plugins that are useful on my site for other reasons: WP-MembersMultiple Roles, and Capability Manager Enhanced. There may be equivalent solutions that use fewer or different plugins, but these are all working well for me.

bbPNNS lets you select who gets notifications based on their WordPress role. So I created two new roles: notify_on_topic and notify_on_reply. These roles have no capabilities at all, they are just a convenient way to label categories of my users. For example, a given user might have roles Author and notify_on_topic. Being an Author lets them post content. Being a notify_on_topic user indicates their notification preferences. This is a bit of an abuse of the WordPress role system, but it seems less crazy than a lot of other stuff I’ve seen in WordPress sites.

I then configured bbPNNS to notify the role notify_on_topic when a new topic is posted, and notify_on_reply when a new reply is posted.

Then it was over to the WP-Members plugin. I defined two new checkbox fields, using the same name as the roles. These fields show up in the user’s profile and can be selected in the profile editor.

So at this point I have profile fields that the user can toggle and roles that tell bbPNNS who wants what. The remaining step was to connect the profile fields to the roles, and ensure that they stay in sync.

Connecting the profile fields and roles

The connection is made by writing some action hooks to customize the profile editing process. We need to keep the settings for role and the corresponding profile fields in sync. I was concerned that there might be several ways for either of those to get modified independently. So I force consistency when a profile editing session is started and when it is finished.

The code below goes into the functions.php that is in the child theme folder. (You did create a child theme, didn’t you?)

Start of profile editing session

At this stage we check the user’s roles and make sure that the corresponding profile fields are consistent with them. The code below does this in the context of the front-end profile editing page that WP-Members creates, which by default lives at /profile on your site.

// Ensure user roles are correctly displayed in profile editor
function my_pre_init() {
    if ( $_REQUEST['a'] == 'edit') {

        // Get the current user's roles
        $current_user = wp_get_current_user();
        $user_id = $current_user->ID;
        if ($user_id == 0) return;  // no logged in user
        $user_info = get_userdata( $user_id );
        $roles = $user_info->roles;

        // Update metadata for WP-members based on role
        $meta_keys = array( 'notify_on_topic', 'notify_on_reply' );
        foreach ($meta_keys as $meta_key) {
            if (in_array( $meta_key, $roles )) {
                $meta_value = get_user_meta( $user_id, $meta_key, true );
                if ($meta_value != '1') {
                    update_user_meta( $user_id, $meta_key, '1' );
add_action( 'wpmem_pre_init', 'my_pre_init' );

End of profile editing session

If the user has changed either of the profile fields, we need to update their roles to be consistent. The following code does that.

// Update user's roles if profile settings were changed 
// Note: We count on the fact that we used the same names for the profile
// metadata as for the roles
function my_post_update_data( $fields ) {

    // Get the current roles
    $user_id = $fields['ID'];
    $user_info = get_userdata( $user_id );
    $roles = $user_info->roles;

    // Check each profile setting
    $user = new WP_User( $user_id );
    $meta_keys = array( 'notify_on_topic', 'notify_on_reply' );
    foreach ($meta_keys as $meta_key) {
        $meta_value = $fields[$meta_key];
        $prev_value = "";

        // Remove role
        if (in_array($meta_key, $roles) && $meta_value != "1") {
            $user->remove_role( $meta_key );

        // Add role
        if (!in_array($meta_key, $roles) && $meta_value == "1") {
            $user->add_role( $meta_key );
add_action( 'wpmem_post_update_data', 'my_post_update_data' );

Force the use of our modified profile editor

The above works great as long as the user uses our front-end profile editor. If they can get to the admin profile editor though, we’re going to have a problem because we haven’t hooked the code for that. The best approach might be to write those hooks, but we haven’t figured that out yet.

Instead, we’ll just force the user into the front-end editor no matter which profiler they start up. The following code does that.

// Don't let user access admin profile editor
function redirect_profile_access() {
    if (current_user_can('manage_options')) return '';

    if (strpos( $_SERVER ['REQUEST_URI'] , 'wp-admin/profile.php' )) {
        // wp_redirect("/profile");
add_action ('init', 'redirect_profile_access');