Skip to:
Content

bbPress.org

Changeset 6919


Ignore:
Timestamp:
11/06/2019 12:02:53 AM (2 weeks ago)
Author:
johnjamesjacoby
Message:

Emails: chunk notification emails into 40 Bcc'd recipients.

This commit introduces the bbp_mail subfilter, used to target bbPress specific emails in conjunction with bbp_get_email_header() to help identify emails that came specifically from bbPress actions.

The purpose of this change is to help forum owners avoid their outbound emails being marked as spam, due to the high number of users that can be subscribed to any given forum or topic.

This change (combined with r6725) goes a long way towards improving the success of subscription emails reaching their intended recipients.

Fixes #3260. Props danielbachhuber.

Location:
trunk/src/includes
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/includes/common/functions.php

    r6917 r6919  
    11321132    /** Headers ***************************************************************/
    11331133
     1134    // Default bbPress X-header
     1135    $headers    = array( bbp_get_email_header() );
     1136
    11341137    // Get the noreply@ address
    11351138    $no_reply   = bbp_get_do_not_reply_address();
     
    11391142
    11401143    // Setup the From header
    1141     $headers = array( 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>' );
     1144    $headers[]  = 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>';
    11421145
    11431146    // Loop through addresses
     
    11521155    $to_email = apply_filters( 'bbp_subscription_to_email',     $no_reply );
    11531156
     1157    // Before
    11541158    do_action( 'bbp_pre_notify_subscribers', $reply_id, $topic_id, $user_ids );
    11551159
     
    11571161    wp_mail( $to_email, $subject, $message, $headers );
    11581162
     1163    // After
    11591164    do_action( 'bbp_post_notify_subscribers', $reply_id, $topic_id, $user_ids );
    11601165
     
    12941299    /** Headers ***************************************************************/
    12951300
     1301    // Default bbPress X-header
     1302    $headers    = array( bbp_get_email_header() );
     1303
    12961304    // Get the noreply@ address
    12971305    $no_reply   = bbp_get_do_not_reply_address();
     
    13011309
    13021310    // Setup the From header
    1303     $headers = array( 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>' );
     1311    $headers[] = 'From: ' . get_bloginfo( 'name' ) . ' <' . $from_email . '>';
    13041312
    13051313    // Loop through addresses
     
    13141322    $to_email = apply_filters( 'bbp_subscription_to_email',     $no_reply );
    13151323
     1324    // Before
    13161325    do_action( 'bbp_pre_notify_forum_subscribers', $topic_id, $forum_id, $user_ids );
    13171326
     
    13191328    wp_mail( $to_email, $subject, $message, $headers );
    13201329
     1330    // After
    13211331    do_action( 'bbp_post_notify_forum_subscribers', $topic_id, $forum_id, $user_ids );
    13221332
     
    14161426    // Filter & return
    14171427    return apply_filters( 'bbp_get_email_addresses_from_user_ids', $retval, $user_ids, $limit );
     1428}
     1429
     1430/**
     1431 * Automatically splits bbPress emails with many Bcc recipients into chunks.
     1432 *
     1433 * This middleware is useful because topics and forums with many subscribers
     1434 * run into problems with Bcc limits, and many hosting companies & third-party
     1435 * services limit the size of a Bcc audience to prevent spamming.
     1436 *
     1437 * The default "chunk" size is 40 users per iteration, and can be filtered if
     1438 * desired. A future version of bbPress will introduce a setting to more easily
     1439 * tune this.
     1440 *
     1441 * @since 2.6.0 (r6918)
     1442 *
     1443 * @param array $args Original arguments passed to wp_mail().
     1444 * @return array
     1445 */
     1446function bbp_chunk_emails( $args = array() ) {
     1447
     1448    // Get the maximum number of Bcc's per chunk
     1449    $max_num = apply_filters( 'bbp_get_bcc_chunk_limit', 40, $args );
     1450
     1451    // Look for "bcc: " in a case-insensitive way, and split into 2 sets
     1452    $match       = '/^bcc: (\w+)/i';
     1453    $old_headers = preg_grep( $match, $args['headers'], PREG_GREP_INVERT );
     1454    $bcc_headers = preg_grep( $match, $args['headers'] );
     1455
     1456    // Bail if less than $max_num recipients
     1457    if ( empty( $bcc_headers ) || ( count( $bcc_headers ) < $max_num ) ) {
     1458        return $args;
     1459    }
     1460
     1461    // Reindex the headers arrays
     1462    $old_headers = array_values( $old_headers );
     1463    $bcc_headers = array_values( $bcc_headers );
     1464
     1465    // Break the Bcc emails into chunks
     1466    foreach ( array_chunk( $bcc_headers, $max_num ) as $i => $chunk ) {
     1467
     1468        // Skip the first chunk (it will get used in the original wp_mail() call)
     1469        if ( 0 === $i ) {
     1470            $first_chunk = $chunk;
     1471            continue;
     1472        }
     1473
     1474        // Send out the chunk
     1475        $chunk_headers = array_merge( $old_headers, $chunk );
     1476
     1477        // Recursion alert, but should be OK!
     1478        wp_mail(
     1479            $args['to'],
     1480            $args['subject'],
     1481            $args['message'],
     1482            $chunk_headers,
     1483            $args['attachments']
     1484        );
     1485    }
     1486
     1487    // Set headers to old headers + the $first_chunk of Bcc's
     1488    $args['headers'] = array_merge( $old_headers, $first_chunk );
     1489
     1490    // Return the reduced args, with the first chunk of Bcc's
     1491    return $args;
     1492}
     1493
     1494/**
     1495 * Return the string used for the bbPress specific X-header.
     1496 *
     1497 * @since 2.6.0 (r6919)
     1498 *
     1499 * @return string
     1500 */
     1501function bbp_get_email_header() {
     1502    return apply_filters( 'bbp_get_email_header', 'X-bbPress: ' . bbp_get_version() );
    14181503}
    14191504
  • trunk/src/includes/core/filters.php

    r6719 r6919  
    4040add_filter( 'request',                 'bbp_request',            10    );
    4141add_filter( 'template_include',        'bbp_template_include',   10    );
     42add_filter( 'wp_mail',                 'bbp_mail',               10, 3 );
    4243add_filter( 'wp_title',                'bbp_title',              10, 3 );
    4344add_filter( 'body_class',              'bbp_body_class',         10, 2 );
     
    6970
    7071/**
     72 * Emails
     73 *
     74 * bbPress sends emails for a few different reasons, largely related to user
     75 * notifications or account changes. Because the `wp_mail` filter can be a
     76 * crowded space, the `bbp_mail` subfilter should be used in conjunction with
     77 * bbp_get_email_header() to narrow the results to only bbPress emails.
     78 */
     79add_filter( 'bbp_mail', 'bbp_chunk_emails' );
     80
     81/**
    7182 * Feeds
    7283 *
     
    8495 * bbp_template_include() works and do something similar. :)
    8596 */
    86 add_filter( 'bbp_template_include',   'bbp_template_include_theme_supports', 2, 1 );
    87 add_filter( 'bbp_template_include',   'bbp_template_include_theme_compat',   4, 2 );
     97add_filter( 'bbp_template_include', 'bbp_template_include_theme_supports', 2, 1 );
     98add_filter( 'bbp_template_include', 'bbp_template_include_theme_compat',   4, 2 );
    8899
    89100// Filter bbPress template locations
    90 add_filter( 'bbp_get_template_stack', 'bbp_add_template_stack_locations'          );
     101add_filter( 'bbp_get_template_stack', 'bbp_add_template_stack_locations' );
    91102
    92103// Links
     
    97108
    98109// wp_filter_kses on new/edit forum/topic/reply title
    99 add_filter( 'bbp_new_forum_pre_title',  'wp_filter_kses'  );
    100 add_filter( 'bbp_new_reply_pre_title',  'wp_filter_kses'  );
    101 add_filter( 'bbp_new_topic_pre_title',  'wp_filter_kses'  );
    102 add_filter( 'bbp_edit_forum_pre_title', 'wp_filter_kses'  );
    103 add_filter( 'bbp_edit_reply_pre_title', 'wp_filter_kses'  );
    104 add_filter( 'bbp_edit_topic_pre_title', 'wp_filter_kses'  );
     110add_filter( 'bbp_new_forum_pre_title',  'wp_filter_kses' );
     111add_filter( 'bbp_new_reply_pre_title',  'wp_filter_kses' );
     112add_filter( 'bbp_new_topic_pre_title',  'wp_filter_kses' );
     113add_filter( 'bbp_edit_forum_pre_title', 'wp_filter_kses' );
     114add_filter( 'bbp_edit_reply_pre_title', 'wp_filter_kses' );
     115add_filter( 'bbp_edit_topic_pre_title', 'wp_filter_kses' );
    105116
    106117// Prevent posting malicious or malformed content on new/edit topic/reply
  • trunk/src/includes/core/sub-actions.php

    r6876 r6919  
    524524    return (array) apply_filters( 'bbp_map_meta_caps', $caps, $cap, $user_id, $args );
    525525}
     526
     527/**
     528 * Filter the arguments used by wp_mail for bbPress specific emails
     529 *
     530 * @since 2.6.0 bbPress (r6918)
     531 *
     532 * @param array $args A compacted array of wp_mail() arguments, including the "to" email,
     533 *                    subject, message, headers, and attachments values.
     534 *
     535 * @return array Array of capabilities
     536 */
     537function bbp_mail( $args = array() ) {
     538
     539    // Bail if headers are missing/malformed
     540    if ( empty( $args['headers'] ) || ! is_array( $args['headers'] ) ) {
     541        return $args;
     542    }
     543
     544    // Header to search all headers for
     545    $bbp_header = bbp_get_email_header();
     546
     547    // Bail if no bbPress header found
     548    if ( false === array_search( $bbp_header, $args['headers'], true ) ) {
     549        return $args;
     550    }
     551
     552    // Filter & return
     553    return (array) apply_filters( 'bbp_mail', $args );
     554}
Note: See TracChangeset for help on using the changeset viewer.