Skip to content

Every email WB Member Blog and WB Member Blog Pro send has a stable id, goes through one gateway, and is drawn by one shared template: a heading, a short message, one button and an unsubscribe link, with a plain-text alternative generated from the same content. Each one can be switched off, retitled, rewritten or replaced by id, from code or (with Pro) from a settings screen.

Written against version 4.3.0.

The list lives in Wbcom\MemberBlog\Services\EmailRegistry::all(). Read it in code, including each email’s label, recipient and placeholders:

$emails = \Wbcom\MemberBlog\Services\EmailRegistry::all();
ID Sent to When Member preference
digest Follower A periodic round-up of new posts from the writers and topics they follow. Its own opt-in (off by default)
follow_new_post Follower A writer they follow publishes. followed_post
new_follower Writer Somebody follows them. Once per follower, however often they unfollow and follow again. new_follower
clap Author A reader claps for their post. post_reaction
admin_pending_review Reviewers A member post enters review. Once per entry, not on every edit while it waits. workflow_update
post_published Author A reviewer approves their post and it is published. Not sent for a post the author published themselves. post_published
post_rejected Author A reviewer sends their post back, with the reason and an edit link. post_rejected

“Reviewers” are the users who can review (edit_others_posts), at most 20; the bpmb_review_request_recipients filter replaces the list. With Pro, the administrators picked under Select Admin User(s) receive it, and Pro’s Admin Notifications and Member Notification switches silence the review request and the approved email. Nobody is emailed about their own action.

ID Sent to When Member preference
new_comment Author Someone comments on their post. new_comment
post_reaction Author A reader reacts, where the Reactions plugin is active. post_reaction
post_milestone Author A post passes a view threshold. post_milestone
coauthor_invited Invitee An author invites them onto a post. coauthor_invite
coauthor_accepted Inviter The invitee accepts. coauthor_invite
coauthor_declined Inviter The invitee declines. coauthor_invite
coauthor_revision The author and co-authors A reviewer asks for changes. workflow_update
coauthor_published The author and co-authors The co-authored post is published. post_published
scheduled_published Author A scheduled post publishes. post_published
scheduled_blocked Author A scheduled post could not publish (no credits left, or a posting limit reached). The email names the reason, for a limit also when it resets, and what to do next. None: always sent

The “Member preference” column is the choice a member controls on the Author Dashboard settings screen (Pro) and through the one-click unsubscribe link in the email. When a member has turned that preference off, the email is not sent to them.

Every email goes through EmailDispatcher::dispatch(). For an email with an id, the steps run in this order:

  1. On or off. The site owner’s switch for this id, then the bpmb_email_enabled filter. Off means nothing is sent.
  2. Owner’s wording. If the site owner saved their own subject or message for this id (Pro), it replaces the default, with placeholders filled in.
  3. bpmb_email_subject filter.
  4. Theme template. If yourtheme/buddypress-member-blog/emails/{id}.php exists, its output becomes the message.
  5. bpmb_email_body filter.
  6. Member preference. If the recipient is a member who turned this email off, it is not sent. Otherwise the unsubscribe link for that preference is added.
  7. Shared template. The message is wrapped in the email design and sent as HTML with a plain-text part.

One recipient is sent straight away. Many recipients are queued and sent in the background by cron, so publishing a post never waits on follower emails.

From Pro: Member Blog settings, Notification Emails tab (visible when the Notifications module is on). Every email has an on/off switch. The switches are stored in the bpmb_email_switches option as id => 'yes' | 'no'; an id that is not stored is on.

From code, which wins over the switch:

add_filter( 'bpmb_email_enabled', function ( $enabled, $id ) {
return 'clap' === $id ? false : $enabled;
}, 10, 2 );

On the Notification Emails tab, open an email’s wording to set your own subject and message. Leave a field empty to keep the default. The screen lists the placeholders each email supports, such as {post_title}, {post_url}, {reason} and {site_name}. The digest’s message is its list of posts, so only its subject can be changed.

Saved wording is stored in the bpmb_email_copy option as id => [ 'subject' => ..., 'body' => ... ]. Blank lines in the message become paragraphs. This store replaced the old Pro filters bpmbp_notification_subject and bpmbp_notification_content, which were removed in 4.3.0.

Change a subject:

add_filter( 'bpmb_email_subject', function ( $subject, $id ) {
if ( 'post_rejected' === $id ) {
return __( 'Your post needs another look', 'my-plugin' );
}
return $subject;
}, 10, 2 );

Change the message. $message is either a string or the template’s argument array:

add_filter( 'bpmb_email_body', function ( $message, $id ) {
if ( 'clap' !== $id ) {
return $message;
}
return array(
'heading' => __( 'Someone liked your writing', 'my-plugin' ),
'body' => array( __( 'People are reading. Keep going.', 'my-plugin' ) ),
'button' => array(
'label' => __( 'See your posts', 'my-plugin' ),
'url' => home_url( '/my-blog-dashboard/' ),
),
);
}, 10, 2 );

Template arguments:

Key What it is
preheader The inbox preview line. Hidden in the message.
greeting The opening line, such as “Hi Nora,”.
heading The heading. It should restate the subject.
body A string, or an array of paragraphs.
button array( 'label' => ..., 'url' => ... ). One button.
note Small print under the button.
footer Replaces the default site-name footer.
unsubscribe Added by the gateway from the member’s preference. Do not set it yourself.

Put a file in your theme named after the email id:

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

The lookup checks the child theme, then the parent theme. The plugin ships no email template files of its own, so without a theme file the email is drawn from its arguments as usual. Whatever your file prints becomes the message and is still wrapped in the shared design.

The file receives one variable, $bpmb_email, an array with to, subject and message (the default message: a string or the argument array above).

yourtheme/buddypress-member-blog/emails/post_rejected.php
<?php
defined( 'ABSPATH' ) || exit;
printf(
'<p>%s</p>',
esc_html__( 'Your post needs a small change before we can publish it.', 'my-theme' )
);

Find the file that wins for an id, or change it with the bpmb_email_template_path filter (string $path, string $id):

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

The shared template takes its colours from one palette:

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

Keys and defaults: accent #2563eb, ink #111827, muted #6b7280, rule #e2e8f0, paper #ffffff, canvas #f8fafc, button #2563eb, on_dark #ffffff.

The template uses tables and inline styles because email clients such as Outlook support neither flexbox nor CSS custom properties. That is why the palette is literal hex rather than the --bpmb-* tokens the pages use.

Use the same gateway so your email gets the shared design, the plain-text part and background queuing:

bpmb_mail()::dispatch(
$user->user_email,
__( 'Something happened', 'my-plugin' ),
array(
'heading' => __( 'Something happened', 'my-plugin' ),
'body' => array( __( 'Here are the details.', 'my-plugin' ) ),
),
array(),
'my_email_id'
);

The last argument is optional. To make your id switchable, rewordable and overridable like the built-in emails, register it:

add_filter( 'bpmb_emails', function ( $emails ) {
$emails['my_email_id'] = array(
'plugin' => 'free',
'label' => __( 'Something happened', 'my-plugin' ),
'description' => __( 'Sent when something happens.', 'my-plugin' ),
'recipient' => 'author',
'placeholders' => array( '{post_title}' ),
'pref' => '',
);
return $emails;
} );

For many recipients use dispatch_bulk( $recipients, $subject, $message, $headers, $id ) (one message to many) or dispatch_each( $messages, $id ) (a personal message each). Both queue and send in the background.