How to replace email notifications with custom layout?
Before using this filter, consider the built-in Email Template Customization options. You can customize colors, logo, text, and even use a custom HTML template directly from the plugin settings without writing code.
Use this filter only when you need programmatic control over email content, such as dynamic templates based on user roles or conditional content.
A conversation can contain messages from guests and AI chat bots, which have negative user IDs, and system messages with a sender ID of 0. None of them have a WordPress user record, so get_userdata() returns false for all three and any $sender->display_name on the result is a fatal error. Resolve senders with Better_Messages()->functions->rest_user_item( $sender_id, false ), as the example below does.
To be able to implement this guide, you need to learn how to insert PHP snippets to your website.
You can find guide here: WP Beginner
<?php
add_filter('bp_better_messages_overwrite_email', 'override_messages_emailer', 10, 4);
function override_messages_emailer( $bool, $user_id, $thread_id, $messages ){
$gmt_offset = get_option('gmt_offset') * 3600;
/**
* Get user which will receive email
*/
$user = get_userdata( $user_id );
/** Getting subject */
$subject = $messages[0]->subject;
$email_subject = '[' . get_bloginfo('name') . '] You have unread messages:' . $subject;
$thread_url = esc_url( Better_Messages()->functions->add_hash_arg('conversation/' . $thread_id, [], Better_Messages()->functions->get_link($user_id) ) );
/**
* Composing html messages list
*/
$messageHtml = '<table style="margin: 0 0 20px!important;width: 100%;"><tbody>';
$last_id = null;
foreach ($messages as $message) {
$sender_id = (int) $message->sender_id;
$timestamp = strtotime($message->date_sent) + $gmt_offset;
$time_format = get_option('time_format');
if (gmdate('Ymd') != gmdate('Ymd', $timestamp)) {
$time_format .= ' ' . get_option('date_format');
}
$time = wp_strip_all_tags(stripslashes(date_i18n($time_format, $timestamp)));
$text = wp_strip_all_tags(stripslashes($message->message));
// sender_id 0 is a system message ("X joined the conversation", call events)
if ($sender_id === 0) {
$messageHtml .= '<tr><td colspan="2" style="text-align:center;"><i>' . $text . '</i></td></tr>';
$last_id = null;
continue;
}
// Senders can be guests or AI bots, which have NEGATIVE ids and no WP user record.
// rest_user_item() resolves all three kinds; get_userdata() would return false here.
$sender = Better_Messages()->functions->rest_user_item( $sender_id, false );
$author = wp_strip_all_tags(stripslashes(sprintf(__('%s wrote:', 'bp-better-messages'), $sender['name'])));
if ($last_id !== $sender_id) {
$messageHtml .= '<tr><td colspan="2"><b>' . $author . '</b></td></tr>';
}
$messageHtml .= '<tr>';
$messageHtml .= '<td style="padding-right: 10px;">' . $text . '</td>';
$messageHtml .= '<td style="width: 1px;white-space: nowrap;vertical-align: top;"><i>' . $time . '</i></td>';
$messageHtml .= '</tr>';
$last_id = $sender_id;
}
$messageHtml .= '</tbody></table>';
/**
* Composing Email HTML
*/
ob_start(); ?>
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Simple Transactional Email</title>
<style>
/* -------------------------------------
INLINED WITH htmlemail.io/inline
------------------------------------- */
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class=body] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class=body] p,
table[class=body] ul,
table[class=body] ol,
table[class=body] td,
table[class=body] span,
table[class=body] a {
font-size: 16px !important;
}
table[class=body] .wrapper,
table[class=body] .article {
padding: 10px !important;
}
table[class=body] .content {
padding: 0 !important;
}
table[class=body] .container {
padding: 0 !important;
width: 100% !important;
}
table[class=body] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class=body] .btn table {
width: 100% !important;
}
table[class=body] .btn a {
width: 100% !important;
}
table[class=body] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
#MessageViewBody a {
color: inherit;
text-decoration: none;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
line-height: inherit;
}
.btn-primary table td:hover {
background-color: #34495e !important;
}
.btn-primary a:hover {
background-color: #34495e !important;
border-color: #34495e !important;
}
}
</style>
</head>
<body class="" style="background-color: #f6f6f6; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
<table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f6f6f6;">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
<td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto; max-width: 580px; padding: 10px; width: 580px;">
<div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto; max-width: 580px; padding: 10px;">
<!-- START CENTERED WHITE CONTAINER -->
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 3px;">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;">
<p style="font-family: sans-serif; font-size: 16px; font-weight: bold; margin: 0; Margin-bottom: 15px;">Hi <?php echo $user->display_name; ?>,</p>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;">You have unread messages: "<?php echo esc_attr($subject); ?>"</p>
<?php echo $messageHtml; ?>
<p style="font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0; Margin-bottom: 15px;"><a href="<?php echo $thread_url; ?>">Go to the discussion</a> to reply or catch up on the conversation.</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
<!-- START FOOTER -->
<div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
<tr>
<td class="content-block" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: 12px; color: #999999; text-align: center;">
<span class="apple-link" style="color: #999999; font-size: 12px; text-align: center;"><a href="<?php echo home_url(); ?>"><?php echo get_bloginfo('name'); ?></a></span>
</td>
</tr>
</table>
</div>
<!-- END FOOTER -->
<!-- END CENTERED WHITE CONTAINER -->
</div>
</td>
<td style="font-family: sans-serif; font-size: 14px; vertical-align: top;"> </td>
</tr>
</table>
</body>
</html>
<?php
$content = ob_get_clean();
/*
* Configure wp_mail to send as html and sending
*/
$content_type = function() { return 'text/html'; };
add_filter( 'wp_mail_content_type', $content_type );
wp_mail( $user->user_email, $email_subject, $content );
remove_filter( 'wp_mail_content_type', $content_type );
// If true is returned the plugin will not send email itself, if false is returned it will send.
return true;
}