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.4.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
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. Sent at approval, also for a post approved with a future date: then it says when the post goes live, and nothing more is sent at go-live. 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 site’s administrators, at most 20. Editors and other roles that can review are not emailed by default; add them with the bpmb_review_request_recipients filter, which replaces the list. With Pro, the administrators picked under Select Admin User(s) receive it (anyone picked there who is no longer an administrator is ignored), and Pro’s Admin Notifications and Member Notification switches silence the review request and the approved email. Nobody is emailed about their own action.

Comments, claps, new followers, view milestones, co-author invitations and replies, and scheduled posts send no email. The co-author actions bpmb_coauthor_invited, bpmb_coauthor_accepted and bpmb_coauthor_declined still fire, and bpmb_scheduled_post_publish_blocked still fires when a scheduled post is returned to draft. None of them sends mail.

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

Every built-in subject follows one pattern, {Site name}: Sentence, with the post title where there is one:

ID Built-in subject
admin_pending_review Site: "Title" needs your approval
post_published Site: Your post "Title" is published (... is approved when it goes live later)
post_rejected Site: Your post "Title" was sent back
follow_new_post Site: Priya Sharma published "Title"
digest Site: 3 new posts from the writers you follow

The site name is added in one place, EmailRegistry::site_subject(), so a sender passes only the sentence. A subject the owner wrote on Notification Emails (Pro) is sent exactly as written, with its placeholders filled in and no prefix added.

The link carries a signed token for one member and one preference, and works without signing in. Opening it (a GET) changes nothing: it shows an Unsubscribe button that POSTs the same link, so inbox link scanners (Safe Links, Mimecast) cannot unsubscribe anyone. Every email with an unsubscribe link also carries List-Unsubscribe and List-Unsubscribe-Post: List-Unsubscribe=One-Click headers (RFC 8058), so mail clients can unsubscribe with a POST to the same link and no browser.

On WordPress’s default PHP mail() transport, PHPMailer wraps any header longer than 63 characters, which mangles List-Unsubscribe. Over SMTP (any SMTP plugin) it is sent as written.

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. Otherwise the built-in subject gets the Site name: prefix.
  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, with the List-Unsubscribe headers.
  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 'follow_new_post' === $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 ( 'post_published' !== $id ) {
return $message;
}
return array(
'heading' => __( 'Your post is live', '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.