Skip to content

Emails

WB Member Blog and WB Member Blog Pro send 17 emails between them. Every one works out of the box with no configuration — subjects, headings, the site name and every link are built from your site’s own settings — and every one can be changed without forking a plugin.

There is no email settings screen. Everything here is done in code, from a theme or a small plugin.

Each has a stable id. That id is what you filter on, and what you name a template file after.

ID Sent to When
digest Follower A periodic round-up of new posts from writers and topics they follow
follow_new_post Follower A writer they follow publishes
clap Author A reader claps for their post
ID Sent to When
admin_pending_review Administrators A member submits a post for review
post_published Author Their post is approved and published
post_rejected Author A reviewer sends the post back
ID Sent to When
new_comment Author Someone comments on their post
post_reaction Author Someone reacts (with the Reactions plugin active)
post_milestone Author A post passes a view threshold
ID Sent to When
coauthor_invited Invitee An author invites them onto a post
coauthor_accepted Inviter The invitee accepts
coauthor_declined Inviter The invitee declines
coauthor_approved Co-authors The post is approved
coauthor_revision Co-authors A reviewer asks for changes
coauthor_published Co-authors The post goes live
ID Sent to When
scheduled_published Author A scheduled post publishes
scheduled_blocked Author A scheduled post is blocked at publish time

To read the list in code, including labels and the placeholders each supports:

$emails = \Wbcom\MemberBlog\Services\EmailRegistry::all();
add_filter( 'bpmb_email_enabled', function ( $enabled, $id ) {
// Silence clap notifications, leave the other sixteen alone.
return 'clap' === $id ? false : $enabled;
}, 10, 2 );

This is the plugin-level switch. Members still have their own notification preferences, which are checked separately — turning an email off here overrides both.

add_filter( 'bpmb_email_subject', function ( $subject, $id ) {
if ( 'post_rejected' === $id ) {
return sprintf( __( '%s: your draft needs another look', 'your-textdomain' ), get_bloginfo( 'name' ) );
}
return $subject;
}, 10, 2 );
add_filter( 'bpmb_email_body', function ( $message, $id ) {
if ( 'clap' !== $id ) {
return $message;
}
// $message is either a string, or the template's argument array.
return array(
'heading' => __( 'Someone liked your writing', 'your-textdomain' ),
'body' => array( __( 'Keep going — people are reading.', 'your-textdomain' ) ),
'button' => array(
'label' => __( 'See your posts', 'your-textdomain' ),
'url' => home_url( '/my-blog-dashboard/' ),
),
);
}, 10, 2 );

Supported keys: preheader, greeting, heading, body (string or array of paragraphs), button (label, url), note, footer.

Replacing a whole message with a template file

Section titled “Replacing a whole message with a template file”

Drop a file in your theme, named after the email id:

yourtheme/buddypress-member-blog/emails/post_rejected.php

It is looked up in the child theme first, then the parent, then the plugin’s own templates/emails/. Whatever it echoes becomes the message body, still wrapped in the shared frame.

yourtheme/buddypress-member-blog/emails/post_rejected.php
<?php
defined( 'ABSPATH' ) || exit;
// $bpmb_email holds: to, subject, message
printf(
'<p>%s</p>',
esc_html__( 'Your draft needs a small change before we can publish it.', 'your-textdomain' )
);

To find the resolved path (useful when debugging which file is winning):

\Wbcom\MemberBlog\Services\EmailRegistry::locate_template( 'post_rejected' );

The shared frame draws its colours from one filterable palette, so a single filter rebrands all 17:

add_filter( 'bpmb_email_palette', function ( $palette ) {
$palette['accent'] = '#8b5cf6';
$palette['button'] = '#8b5cf6';
return $palette;
} );

Keys: accent, ink, muted, rule, paper, canvas, button, on_dark.

The frame uses tables and inline styles on purpose — Outlook’s rendering engine has neither flexbox nor grid, and no email client can be relied on for CSS custom properties. That is why the palette is resolved to literal hex here rather than reusing the --bpmb-* variables the screen styles use.

bpmb_mail()::dispatch(
$user->user_email,
__( 'Something happened', 'your-textdomain' ),
array(
'heading' => __( 'Something happened', 'your-textdomain' ),
'body' => array( __( 'Here are the details.', 'your-textdomain' ) ),
),
array(),
'your_email_id' // Optional. Register it via `bpmb_emails` to make it filterable.
);

One recipient sends immediately. For many, use dispatch_bulk() (one body, many recipients) or dispatch_each() (a personalised body per recipient) — both queue and drain on cron, so a fan-out never blocks the request the member is waiting on.

The plain-text alternative is generated from the same content automatically. You do not write it twice.