Skip to:
Content

BuddyPress.org

Ticket #6592: 6592-core.01.patch

File 6592-core.01.patch, 44.2 KB (added by DJPaul, 4 years ago)
  • src/bp-core/bp-core-classes.php

    diff --git a/src/bp-core/bp-core-classes.php b/src/bp-core/bp-core-classes.php
    index d2801ea..1566c85 100644
    a b require dirname( __FILE__ ) . '/classes/class-bp-media-extractor.php'; 
    2525require dirname( __FILE__ ) . '/classes/class-bp-attachment.php';
    2626require dirname( __FILE__ ) . '/classes/class-bp-attachment-avatar.php';
    2727require dirname( __FILE__ ) . '/classes/class-bp-attachment-cover-image.php';
     28require dirname( __FILE__ ) . '/classes/class-bp-email-recipient.php';
     29require dirname( __FILE__ ) . '/classes/class-bp-email.php';
     30require dirname( __FILE__ ) . '/classes/class-bp-email-delivery.php';
     31require dirname( __FILE__ ) . '/classes/class-bp-phpmailer.php';
  • src/bp-core/bp-core-filters.php

    diff --git a/src/bp-core/bp-core-filters.php b/src/bp-core/bp-core-filters.php
    index 0d4b342..6c96811 100644
    a b add_filter( 'bp_core_render_message_content', 'wpautop' ); 
    5353add_filter( 'bp_core_render_message_content', 'shortcode_unautop' );
    5454add_filter( 'bp_core_render_message_content', 'wp_kses_data', 5   );
    5555
     56// Emails.
     57add_filter( 'bp_email_set_content_html', 'wp_filter_post_kses', 6 );
     58add_filter( 'bp_email_set_content_html', 'stripslashes', 8 );
     59add_filter( 'bp_email_set_content_plaintext', 'wp_strip_all_tags', 6 );
     60add_filter( 'bp_email_set_subject', 'sanitize_text_field', 6 );
     61
     62
    5663/**
    5764 * Template Compatibility.
    5865 *
    function _bp_core_inject_bp_widget_css_class( $params ) { 
    10381045        return $params;
    10391046}
    10401047add_filter( 'dynamic_sidebar_params', '_bp_core_inject_bp_widget_css_class' );
     1048
     1049/**
     1050 * Add custom headers to outgoing emails.
     1051 *
     1052 * @since 2.5.0
     1053 *
     1054 * @param array $headers
     1055 * @param string $property Name of property. Unused.
     1056 * @param string $transform Return value transformation. Unused.
     1057 * @param BP_Email $email Email object reference.
     1058 * @return array
     1059 */
     1060function bp_email_set_default_headers( $headers, $property, $transform, $email ) {
     1061        $headers['X-BuddyPress']      = bp_get_version();
     1062        $headers['X-BuddyPress-Type'] = $email->get( 'type' );
     1063
     1064        return $headers;
     1065}
     1066add_filter( 'bp_email_get_headers', 'bp_email_set_default_headers', 6, 4 );
     1067
     1068/**
     1069 * Add default email tokens.
     1070 *
     1071 * @since 2.5.0
     1072 *
     1073 * @param array $tokens Email tokens.
     1074 * @param string $property_name Unused.
     1075 * @param string $transform Unused.
     1076 * @param BP_Email $email Email being sent.
     1077 * @return array
     1078 */
     1079function bp_email_set_default_tokens( $tokens, $property_name, $transform, $email ) {
     1080        $tokens['site.admin-email'] = bp_get_option( 'admin_email' );
     1081        $tokens['site.url']         = home_url();
     1082
     1083        // These options are escaped with esc_html on the way into the database in sanitize_option().
     1084        $tokens['site.description'] = wp_specialchars_decode( bp_get_option( 'blogdescription' ), ENT_QUOTES );
     1085        $tokens['site.name']        = wp_specialchars_decode( bp_get_option( 'blogname' ), ENT_QUOTES );
     1086
     1087        // Default values for tokens set conditionally below.
     1088        $tokens['unsubscribe'] = '';
     1089
     1090
     1091        // Who is the email going to?
     1092        $recipient = $email->get( 'to' );
     1093        if ( $recipient ) {
     1094
     1095                $user = array_shift( $recipient )->get_user( 'search-email' );
     1096                if ( $user ) {
     1097
     1098                        // Unsubscribe link.
     1099                        $tokens['unsubscribe'] = esc_url( sprintf(
     1100                                '%s%s/notifications/',
     1101                                bp_core_get_user_domain( $user->ID ),
     1102                                function_exists( 'bp_get_settings_slug' ) ? bp_get_settings_slug() : 'settings'
     1103                        ) );
     1104                }
     1105        }
     1106
     1107        return $tokens;
     1108}
     1109add_filter( 'bp_email_get_tokens', 'bp_email_set_default_tokens', 6, 4 );
  • new file src/bp-core/classes/class-bp-email-delivery.php

    diff --git a/src/bp-core/classes/class-bp-email-delivery.php b/src/bp-core/classes/class-bp-email-delivery.php
    new file mode 100644
    index 0000000..f96cd01
    - +  
     1<?php
     2/**
     3 * Core component classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * Email delivery implementation base class.
     14 *
     15 * When implementing support for an email delivery service into BuddyPress,
     16 * you are required to create a class that implements this interface.
     17 *
     18 * @since 2.5.0
     19 */
     20interface BP_Email_Delivery {
     21
     22        /**
     23         * Send email(s).
     24         *
     25         * @since 2.5.0
     26         *
     27         * @param BP_Email $email Email to send.
     28         * @return bool|WP_Error Returns true if email send, else a descriptive WP_Error.
     29         */
     30        public function bp_email( BP_Email $email );
     31}
  • new file src/bp-core/classes/class-bp-email-recipient.php

    diff --git a/src/bp-core/classes/class-bp-email-recipient.php b/src/bp-core/classes/class-bp-email-recipient.php
    new file mode 100644
    index 0000000..54e2b88
    - +  
     1<?php
     2/**
     3 * Core component classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * Represents a recipient that an email will be sent to.
     14 *
     15 * @since 2.5.0
     16 */
     17class BP_Email_Recipient {
     18
     19        /**
     20         * Recipient's email address.
     21         *
     22         * @since 2.5.0
     23         *
     24         * @var string
     25         */
     26        protected $address = '';
     27
     28        /**
     29         * Recipient's name.
     30         *
     31         * @since 2.5.0
     32         *
     33         * @var string
     34         */
     35        protected $name = '';
     36
     37        /**
     38         * Optional. A `WP_User` object relating to this recipient.
     39         *
     40         * @since 2.5.0
     41         *
     42         * @var WP_User
     43         */
     44        protected $user_object = null;
     45
     46        /**
     47         * Constructor.
     48         *
     49         * @since 2.5.0
     50         *
     51         * @param string|array|int|WP_User $email_or_user Either a email address, user ID, WP_User object,
     52         *                                                or an array containg the address and name.
     53         * @param string $name Optional. If $email_or_user is a string, this is the recipient's name.
     54         */
     55        public function __construct( $email_or_user, $name = '' ) {
     56                $name = sanitize_text_field( $name );
     57
     58                // User ID, WP_User object.
     59                if ( is_int( $email_or_user ) || is_object( $email_or_user ) ) {
     60                        $this->user_object = is_object( $email_or_user ) ? $email_or_user : get_user_by( 'ID', $email_or_user );
     61
     62                        if ( $this->user_object ) {
     63                                // This is escaped with esc_html in bp_core_get_user_displayname()
     64                                $name = wp_specialchars_decode( bp_core_get_user_displayname( $this->user_object->ID ), ENT_QUOTES );
     65
     66                                $this->address = $this->user_object->user_email;
     67                                $this->name    = sanitize_text_field( $name );
     68                        }
     69
     70                // Array, address, and name.
     71                } else {
     72                        if ( ! is_array( $email_or_user ) ) {
     73                                $email_or_user = array( $email_or_user => $name );
     74                        }
     75
     76                        // Handle numeric arrays.
     77                        if ( is_int( key( $email_or_user ) ) ) {
     78                                $address = current( $email_or_user );
     79                        } else {
     80                                $address = key( $email_or_user );
     81                                $name    = current( $email_or_user );
     82                        }
     83
     84                        if ( is_email( $address ) ) {
     85                                $this->address = sanitize_email( $address );
     86                        }
     87
     88                        $this->name = $name;
     89                }
     90
     91                /**
     92                 * Fires inside __construct() method for BP_Email_Recipient class.
     93                 *
     94                 * @since 2.5.0
     95                 *
     96                 * @param string|array|int|WP_User $email_or_user Either a email address, user ID, WP_User object,
     97                 *                                                or an array containg the address and name.
     98                 * @param string $name If $email_or_user is a string, this is the recipient's name.
     99                 * @param BP_Email_Recipient $this Current instance of the email type class.
     100                 */
     101                do_action( 'bp_email_recipient', $email_or_user, $name, $this );
     102        }
     103
     104        /**
     105         * Get recipient's address.
     106         *
     107         * @since 2.5.0
     108         *
     109         * @return string
     110         */
     111        public function get_address() {
     112
     113                /**
     114                 * Filters the recipient's address before it's returned.
     115                 *
     116                 * @since 2.5.0
     117                 *
     118                 * @param string $address Recipient's address.
     119                 * @param BP_Email $recipient $this Current instance of the email recipient class.
     120                 */
     121                return apply_filters( 'bp_email_recipient_get_address', $this->address, $this );
     122        }
     123
     124        /**
     125         * Get recipient's name.
     126         *
     127         * @since 2.5.0
     128         *
     129         * @return string
     130         */
     131        public function get_name() {
     132
     133                /**
     134                 * Filters the recipient's name before it's returned.
     135                 *
     136                 * @since 2.5.0
     137                 *
     138                 * @param string $name Recipient's name.
     139                 * @param BP_Email $recipient $this Current instance of the email recipient class.
     140                 */
     141                return apply_filters( 'bp_email_recipient_get_name', $this->name, $this );
     142        }
     143
     144        /**
     145         * Get WP_User object for this recipient.
     146         *
     147         * @since 2.5.0
     148         *
     149         * @param string $transform Optional. How to transform the return value.
     150         *                          Accepts 'raw' (default) or 'search-email'.
     151         * @return WP_User|null WP_User object, or null if not set.
     152         */
     153        public function get_user( $transform = 'raw' ) {
     154
     155                // If transform "search-email", find the WP_User if not already set.
     156                if ( $transform === 'search-email' && ! $this->user_object && $this->address ) {
     157                        $this->user_object = get_user_by( 'email', $this->address );
     158                }
     159
     160                /**
     161                 * Filters the WP_User object for this recipient before it's returned.
     162                 *
     163                 * @since 2.5.0
     164                 *
     165                 * @param WP_User $name WP_User object for this recipient, or null if not set.
     166                 * @param string $transform Optional. How the return value was transformed.
     167                 *                          Accepts 'raw' (default) or 'search-email'.
     168                 * @param BP_Email $recipient $this Current instance of the email recipient class.
     169                 */
     170                return apply_filters( 'bp_email_recipient_get_name', $this->user_object, $transform, $this );
     171        }
     172}
  • new file src/bp-core/classes/class-bp-email.php

    diff --git a/src/bp-core/classes/class-bp-email.php b/src/bp-core/classes/class-bp-email.php
    new file mode 100644
    index 0000000..be8f2af
    - +  
     1<?php
     2/**
     3 * Core component classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * Represents an email that will be sent to member(s).
     14 *
     15 * @since 2.5.0
     16 */
     17class BP_Email {
     18        /**
     19         * Addressee details (BCC).
     20         *
     21         * @since 2.5.0
     22         *
     23         * @var BP_Email_Recipient[] BCC recipients.
     24         */
     25        protected $bcc = array();
     26
     27        /**
     28         * Addressee details (CC).
     29         *
     30         * @since 2.5.0
     31         *
     32         * @var BP_Email_Recipient[] CC recipients.
     33         */
     34        protected $cc = array();
     35
     36        /**
     37         * Email content (HTML).
     38         *
     39         * @since 2.5.0
     40         *
     41         * @var string
     42         */
     43        protected $content_html = '';
     44
     45        /**
     46         * Email content (plain text).
     47         *
     48         * @since 2.5.0
     49         *
     50         * @var string
     51         */
     52        protected $content_plaintext = '';
     53
     54        /**
     55         * The content type to send the email in ("html" or "plaintext").
     56         *
     57         * @since 2.5.0
     58         *
     59         * @var string
     60         */
     61        protected $content_type = 'html';
     62
     63        /**
     64         * Sender details.
     65         *
     66         * @since 2.5.0
     67         *
     68         * @var BP_Email_Recipient Sender details.
     69         */
     70        protected $from = null;
     71
     72        /**
     73         * Email headers.
     74         *
     75         * @since 2.5.0
     76         *
     77         * @var string[] Associative pairing of email header name/value.
     78         */
     79        protected $headers = array();
     80
     81        /**
     82         * The Post object (the source of the email's content and subject).
     83         *
     84         * @since 2.5.0
     85         *
     86         * @var WP_Post
     87         */
     88        protected $post_object = null;
     89
     90        /**
     91         * Reply To details.
     92         *
     93         * @since 2.5.0
     94         *
     95         * @var BP_Email_Recipient "Reply to" details.
     96         */
     97        protected $reply_to = null;
     98
     99        /**
     100         * Email subject.
     101         *
     102         * @since 2.5.0
     103         *
     104         * @var string
     105         */
     106        protected $subject = '';
     107
     108        /**
     109         * Email template (the HTML wrapper around the email content).
     110         *
     111         * @since 2.5.0
     112         *
     113         * @var string
     114         */
     115        protected $template = '{{{content}}}';
     116
     117        /**
     118         * Addressee details (to).
     119         *
     120         * @since 2.5.0
     121         *
     122         * @var BP_Email_Recipient[] Email recipients.
     123         * }
     124         */
     125        protected $to = array();
     126
     127        /**
     128         * Unique identifier for this particular type of email.
     129         *
     130         * @since 2.5.0
     131         *
     132         * @var string
     133         */
     134        protected $type = '';
     135
     136        /**
     137         * Token names and replacement values for this email.
     138         *
     139         * @since 2.5.0
     140         *
     141         * @var string[] Associative pairing of token name (key) and replacement value (value).
     142         */
     143        protected $tokens = array();
     144
     145        /**
     146         * Constructor.
     147         *
     148         * Set the email type and default "from" and "reply to" name and address.
     149         *
     150         * @since 2.5.0
     151         *
     152         * @param string $email_type Unique identifier for a particular type of email.
     153         */
     154        public function __construct( $email_type ) {
     155                $this->type = $email_type;
     156
     157                // SERVER_NAME isn't always set (e.g CLI).
     158                if ( ! empty( $_SERVER['SERVER_NAME'] ) ) {
     159                        $domain = strtolower( $_SERVER['SERVER_NAME'] );
     160                        if ( substr( $domain, 0, 4 ) === 'www.' ) {
     161                                $domain = substr( $domain, 4 );
     162                        }
     163
     164                } elseif ( function_exists( 'gethostname' ) && gethostname() !== false ) {
     165                        $domain = gethostname();
     166
     167                } elseif ( php_uname( 'n' ) !== false ) {
     168                        $domain = php_uname( 'n' );
     169
     170                } else {
     171                        $domain = 'localhost.localdomain';
     172                }
     173
     174                // This was escaped with esc_html on the way into the database in sanitize_option().
     175                $site_name = wp_specialchars_decode( bp_get_option( 'blogname' ), ENT_QUOTES );
     176
     177                $this->from( "wordpress@$domain", $site_name );
     178                $this->reply_to( bp_get_option( 'admin_email' ), $site_name );
     179
     180                /**
     181                 * Fires inside __construct() method for BP_Email class.
     182                 *
     183                 * @since 2.5.0
     184                 *
     185                 * @param string $email_type Unique identifier for this type of email.
     186                 * @param BP_Email $this Current instance of the email type class.
     187                 */
     188                do_action( 'bp_email', $email_type, $this );
     189        }
     190
     191
     192        /*
     193         * Psuedo setters/getters.
     194         */
     195
     196        /**
     197         * Getter function to expose object properties.
     198         *
     199         * Unlike most other methods in this class, this one is not chainable.
     200         *
     201         * @since 2.5.0
     202         *
     203         * @param string $property_name Property to access.
     204         * @param string $transform Optional. How to transform the return value.
     205         *                          Accepts 'raw' (default) or 'replace-tokens'.
     206         * @return mixed Returns null if property does not exist, otherwise the value.
     207         */
     208        public function get( $property_name, $transform = 'raw' ) {
     209                // "content" is replaced by HTML or plain text depending on $content_type.
     210                if ( $property_name === 'content' ) {
     211                        $property_name = 'content_' . $this->get( 'content_type' );
     212
     213                        if ( ! in_array( $property_name, array( 'content_html', 'content_plaintext', ), true ) ) {
     214                                $property_name = 'content_html';
     215                        }
     216                }
     217
     218                if ( ! property_exists( $this, $property_name ) ) {
     219                        return null;
     220                }
     221
     222
     223                /**
     224                 * Filters the value of the specified email property.
     225                 *
     226                 * This is a dynamic filter dependent on the specified key.
     227                 *
     228                 * @since 2.5.0
     229                 *
     230                 * @param mixed $property_value Property value.
     231                 * @param string $property_name
     232                 * @param string $transform How to transform the return value.
     233                 *                          Accepts 'raw' (default) or 'replace-tokens'.
     234                 * @param BP_Email $this Current instance of the email type class.
     235                 */
     236                $retval = apply_filters( "bp_email_get_{$property_name}", $this->$property_name, $property_name, $transform, $this );
     237
     238                switch ( $transform ) {
     239                        // Special-case to fill the $template with the email $content.
     240                        case 'add-content':
     241                                $retval = str_replace( '{{{content}}}', nl2br( $this->get( 'content', 'replace-tokens' ) ), $retval );
     242                                // Fall through.
     243
     244                        case 'replace-tokens':
     245                                $retval = self::replace_tokens( $retval, $this->get( 'tokens', 'raw' ) );
     246                                // Fall through.
     247
     248                        case 'raw':
     249                        default:
     250                                // Do nothing.
     251                }
     252
     253                /**
     254                 * Filters the value of the specified email $property.
     255                 *
     256                 * @since 2.5.0
     257                 *
     258                 * @param string $retval Property value.
     259                 * @param string $property_name
     260                 * @param string $transform How to transform the return value.
     261                 *                          Accepts 'raw' (default) or 'replace-tokens'.
     262                 * @param BP_Email $this Current instance of the email type class.
     263                 */
     264                return apply_filters( 'bp_email_get_property', $retval, $property_name, $transform, $this );
     265        }
     266
     267        /**
     268         * Set email headers.
     269         *
     270         * Does NOT let you override to/from, etc. Use the methods provided to set those.
     271         *
     272         * @since 2.5.0
     273         *
     274         * @param string[] $headers Key/value pairs of header name/values (strings).
     275         * @return BP_Email
     276         */
     277        public function headers( array $headers ) {
     278                $new_headers = array();
     279
     280                foreach ( $headers as $name => $content ) {
     281                        $content = str_replace( ':', '', $content );
     282                        $name    = str_replace( ':', '', $name );
     283
     284                        $new_headers[ sanitize_key( $name ) ] = sanitize_text_field( $content );
     285                }
     286
     287                /**
     288                 * Filters the new value of the email's "headers" property.
     289                 *
     290                 * @since 2.5.0
     291                 *
     292                 * @param string[] $new_headers Key/value pairs of new header name/values (strings).
     293                 * @param BP_Email $this Current instance of the email type class.
     294                 */
     295                $this->headers = apply_filters( 'bp_email_set_headers', $new_headers, $this );
     296
     297                return $this;
     298        }
     299
     300        /**
     301         * Set the email's "bcc" address.
     302         *
     303         * To set a single address, the first parameter is the address and the second the name.
     304         * You can also pass a user ID or a WP_User object.
     305         *
     306         * To set multiple addresses, for each array item, the key is the email address and
     307         * the value is the name.
     308         *
     309         * @since 2.5.0
     310         *
     311         * @param string|array|int|WP_User $bcc_address Either a email address, user ID, WP_User object,
     312         *                                              or an array containg the address and name.
     313         * @param string $name Optional. If $bcc_address is a string, this is the recipient's name.
     314         * @return BP_Email
     315         */
     316        public function bcc( $bcc_address, $name = '' ) {
     317                $bcc = array( new BP_Email_Recipient( $bcc_address, $name ) );
     318
     319                /**
     320                 * Filters the new value of the email's "BCC" property.
     321                 *
     322                 * @since 2.5.0
     323                 *
     324                 * @param BP_Email_Recipient[] $bcc BCC recipients.
     325                 * @param string|array|int|WP_User $bcc_address Either a email address, user ID, WP_User object,
     326                 *                                              or an array containg the address and name.
     327                 * @param string $name Optional. If $bcc_address is a string, this is the recipient's name.
     328                 * @param BP_Email $this Current instance of the email type class.
     329                 */
     330                $this->bcc = apply_filters( 'bp_email_set_bcc', $bcc, $bcc_address, $name, $this );
     331
     332                return $this;
     333        }
     334
     335        /**
     336         * Set the email's "cc" address.
     337         *
     338         * To set a single address, the first parameter is the address and the second the name.
     339         * You can also pass a user ID or a WP_User object.
     340         *
     341         * To set multiple addresses, for each array item, the key is the email address and
     342         * the value is the name.
     343         *
     344         * @since 2.5.0
     345         *
     346         * @param string|array|int|WP_User $cc_address Either a email address, user ID, WP_User object,
     347         *                                             or an array containg the address and name.
     348         * @param string $name Optional. If $cc_address is a string, this is the recipient's name.
     349         * @return BP_Email
     350         */
     351        public function cc( $cc_address, $name = '' ) {
     352                $cc = array( new BP_Email_Recipient( $cc_address, $name ) );
     353
     354                /**
     355                 * Filters the new value of the email's "CC" property.
     356                 *
     357                 * @since 2.5.0
     358                 *
     359                 * @param BP_Email_Recipient[] $cc CC recipients.
     360                 * @param string|array|int|WP_User $cc_address Either a email address, user ID, WP_User object,
     361                 *                                             or an array containg the address and name.
     362                 * @param string $name Optional. If $cc_address is a string, this is the recipient's name.
     363                 * @param BP_Email $this Current instance of the email type class.
     364                 */
     365                $this->cc = apply_filters( 'bp_email_set_cc', $cc, $cc_address, $name, $this );
     366
     367                return $this;
     368        }
     369
     370        /**
     371         * Set the email content (HTML).
     372         *
     373         * @since 2.5.0
     374         *
     375         * @param string $content HTML email content.
     376         * @return BP_Email
     377         */
     378        public function content_html( $content ) {
     379
     380                /**
     381                 * Filters the new value of the email's "content" property (HTML).
     382                 *
     383                 * @since 2.5.0
     384                 *
     385                 * @param string $content HTML email content.
     386                 * @param BP_Email $this Current instance of the email type class.
     387                 */
     388                $this->content_html = apply_filters( 'bp_email_set_content_html', $content, $this );
     389
     390                return $this;
     391        }
     392
     393        /**
     394         * Set the email content (plain text).
     395         *
     396         * @since 2.5.0
     397         *
     398         * @param string $content Plain text email content.
     399         * @return BP_Email
     400         */
     401        public function content_plaintext( $content ) {
     402
     403                /**
     404                 * Filters the new value of the email's "content" property (plain text).
     405                 *
     406                 * @since 2.5.0
     407                 *
     408                 * @param string $content Plain text email content.
     409                 * @param BP_Email $this Current instance of the email type class.
     410                 */
     411                $this->content_plaintext = apply_filters( 'bp_email_set_content_plaintext', $content, $this );
     412
     413                return $this;
     414        }
     415
     416        /**
     417         * Set the content type (HTML or plain text) to send the email in.
     418         *
     419         * @since 2.5.0
     420         *
     421         * @param string $content_type Email content type ("html" or "plaintext").
     422         * @return BP_Email
     423         */
     424        public function content_type( $content_type ) {
     425                if ( ! in_array( $content_type, array( 'html', 'plaintext', ), true ) ) {
     426                        $class        = get_class_vars( get_class() );
     427                        $content_type = $class['content_type'];
     428                }
     429
     430                /**
     431                 * Filters the new value of the email's "content type" property.
     432                 *
     433                 * The content type (HTML or plain text) to send the email in.
     434                 *
     435                 * @since 2.5.0
     436                 *
     437                 * @param string $content_type Email content type ("html" or "plaintext").
     438                 * @param BP_Email $this Current instance of the email type class.
     439                 */
     440                $this->content_type = apply_filters( 'bp_email_set_content_type', $content_type, $this );
     441
     442                return $this;
     443        }
     444
     445        /**
     446         * Set the email's "from" address and name.
     447         *
     448         * @since 2.5.0
     449         *
     450         * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object,
     451         *                                                or an array containg the address and name.
     452         * @param string $name Optional. If $email_address is a string, this is the recipient's name.
     453         * @return BP_Email
     454         */
     455        public function from( $email_address, $name = '' ) {
     456                $from = new BP_Email_Recipient( $email_address, $name );
     457
     458                /**
     459                 * Filters the new value of the email's "from" property.
     460                 *
     461                 * @since 2.5.0
     462                 *
     463                 * @param BP_Email_Recipient $from Sender details.
     464                 * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object,
     465                 *                                                or an array containg the address and name.
     466                 * @param string $name Optional. If $email_address is a string, this is the recipient's name.
     467                 * @param BP_Email $this Current instance of the email type class.
     468                 */
     469                $this->from = apply_filters( 'bp_email_set_from', $from, $email_address, $name, $this );
     470
     471                return $this;
     472        }
     473
     474        /**
     475         * Set the Post object containing the email content template.
     476         *
     477         * Also sets the email's subject, content, and template from the Post, for convenience.
     478         *
     479         * @since 2.5.0
     480         *
     481         * @param WP_Post $post
     482         * @return BP_Email
     483         */
     484        public function post_object( WP_Post $post ) {
     485
     486                /**
     487                 * Filters the new value of the email's "post object" property.
     488                 *
     489                 * @since 2.5.0
     490                 *
     491                 * @param WP_Post $post A Post.
     492                 * @param BP_Email $this Current instance of the email type class.
     493                 */
     494                $this->post_object = apply_filters( 'bp_email_set_post_object', $post, $this );
     495
     496                if ( is_a( $this->post_object, 'WP_Post' ) ) {
     497                        $this->subject( $this->post_object->post_title )
     498                                ->content_html( $this->post_object->post_content )
     499                                ->content_plaintext( $this->post_object->post_excerpt );
     500
     501                        ob_start();
     502
     503                        // Load the template.
     504                        bp_locate_template( bp_email_get_template( $this->post_object ), true, false );
     505                        $this->template( ob_get_contents() );
     506
     507                ob_end_clean();
     508                }
     509
     510                return $this;
     511        }
     512
     513        /**
     514         * Set the email's "reply to" address and name.
     515         *
     516         * @since 2.5.0
     517         *
     518         * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object,
     519         *                                                or an array containg the address and name.
     520         * @param string $name Optional. If $email_address is a string, this is the recipient's name.
     521         * @return BP_Email
     522         */
     523        public function reply_to( $email_address, $name = '' ) {
     524                $reply_to = new BP_Email_Recipient( $email_address, $name );
     525
     526                /**
     527                 * Filters the new value of the email's "reply to" property.
     528                 *
     529                 * @since 2.5.0
     530                 *
     531                 * @param BP_Email_Recipient $reply_to "Reply to" recipient.
     532                 * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object,
     533                 *                                                or an array containg the address and name.
     534                 * @param string $name Optional. If $email_address is a string, this is the recipient's name.
     535                 * @param BP_Email $this Current instance of the email type class.
     536                 */
     537                $this->reply_to = apply_filters( 'bp_email_set_reply_to', $reply_to, $email_address, $name, $this );
     538
     539                return $this;
     540        }
     541
     542        /**
     543         * Set the email subject.
     544         *
     545         * @since 2.5.0
     546         *
     547         * @param string $subject Email subject.
     548         * @return BP_Email
     549         */
     550        public function subject( $subject ) {
     551
     552                /**
     553                 * Filters the new value of the subject email property.
     554                 *
     555                 * @since 2.5.0
     556                 *
     557                 * @param string $subject Email subject.
     558                 * @param BP_Email $this Current instance of the email type class.
     559                 */
     560                $this->subject = apply_filters( 'bp_email_set_subject', $subject, $this );
     561
     562                return $this;
     563        }
     564
     565        /**
     566         * Set the email template (the HTML wrapper around the email content).
     567         *
     568         * This needs to include the string "{{{content}}}" to have the post content added
     569         * when the email template is rendered.
     570         *
     571         * @since 2.5.0
     572         *
     573         * @param string $template Email template. Assumed to be HTML.
     574         * @return BP_Email
     575         */
     576        public function template( $template ) {
     577
     578                /**
     579                 * Filters the new value of the template email property.
     580                 *
     581                 * @since 2.5.0
     582                 *
     583                 * @param string $template Email template. Assumed to be HTML.
     584                 * @param BP_Email $this Current instance of the email type class.
     585                 */
     586                $this->template = apply_filters( 'bp_email_set_template', $template, $this );
     587
     588                return $this;
     589        }
     590
     591        /**
     592         * Set the email's "to" address.
     593         *
     594         * IMPORTANT NOTE: the assumption with all emails sent by (and belonging to) BuddyPress itself
     595         * is that there will only be a single `$to_address`. This is to simplify token and templating
     596         * logic (for example, if multiple recipients, the "unsubscribe" link in the emails will all
     597         * only link to the first recipient).
     598         *
     599         * To set a single address, the first parameter is the address and the second the name.
     600         * You can also pass a user ID or a WP_User object.
     601         *
     602         * To set multiple addresses, for each array item, the key is the email address and
     603         * the value is the name.
     604         *
     605         * @since 2.5.0
     606         *
     607         * @param string|array|int|WP_User $to_address Either a email address, user ID, WP_User object,
     608         *                                             or an array containg the address and name.
     609         * @param string $name Optional. If $to_address is a string, this is the recipient's name.
     610         * @return BP_Email
     611         */
     612        public function to( $to_address, $name = '' ) {
     613                $to = array( new BP_Email_Recipient( $to_address, $name ) );
     614
     615                /**
     616                 * Filters the new value of the email's "to" property.
     617                 *
     618                 * @since 2.5.0
     619                 *
     620                 * @param BP_Email_Recipient[] "To" recipients.
     621                 * @param string $to_address "To" address.
     622                 * @param string $name "To" name.
     623                 * @param BP_Email $this Current instance of the email type class.
     624                 */
     625                $this->to = apply_filters( 'bp_email_set_to', $to, $to_address, $name, $this );
     626
     627                return $this;
     628        }
     629
     630        /**
     631         * Set token names and replacement values for this email.
     632         *
     633         * In templates, tokens are inserted with a Handlebars-like syntax, e.g. `{{token_name}}`.
     634         * { and } are reserved characters. There's no need to specify these brackets in your token names.
     635         *
     636         * @since 2.5.0
     637         *
     638         * @param string[] $tokens Associative array, contains key/value pairs of token name/value.
     639         *                         Values are a string or a callable function.
     640         * @return BP_Email
     641         */
     642        public function tokens( array $tokens ) {
     643                $formatted_tokens = array();
     644
     645                foreach ( $tokens as $name => $value ) {
     646                        $name                      = str_replace( array( '{', '}' ), '', sanitize_text_field( $name ) );
     647                        $formatted_tokens[ $name ] = $value;
     648                }
     649
     650                /**
     651                 * Filters the new value of the email's "tokens" property.
     652                 *
     653                 * @since 2.5.0
     654                 *
     655                 * @param string[] $formatted_tokens Associative pairing of token names (key) and replacement values (value).
     656                 * @param string[] $tokens Associative pairing of unformatted token names (key) and replacement values (value).
     657                 * @param BP_Email $this Current instance of the email type class.
     658                 */
     659                $this->tokens = apply_filters( 'bp_email_set_tokens', $formatted_tokens, $tokens, $this );
     660
     661                return $this;
     662        }
     663
     664
     665        /*
     666         * Sanitisation and validation logic.
     667         */
     668
     669        /**
     670         * Check that we'd be able to send this email.
     671         *
     672         * Unlike most other methods in this class, this one is not chainable.
     673         *
     674         * @since 2.5.0
     675         *
     676         * @return bool|WP_Error Returns true if validation succesful, else a descriptive WP_Error.
     677         */
     678        public function validate() {
     679                $retval = true;
     680
     681                // BCC, CC, and token properties are optional.
     682                if (
     683                        ! $this->get( 'from' ) ||
     684                        ! $this->get( 'to' ) ||
     685                        ! $this->get( 'subject' ) ||
     686                        ! $this->get( 'content' ) ||
     687                        ! $this->get( 'template' )
     688                ) {
     689                        $retval = new WP_Error( 'missing_parameter', __CLASS__, $this );
     690                }
     691
     692                /**
     693                 * Filters whether the email passes basic validation checks.
     694                 *
     695                 * @since 2.5.0
     696                 *
     697                 * @param bool|WP_Error $retval Returns true if validation succesful, else a descriptive WP_Error.
     698                 * @param BP_Email $this Current instance of the email type class.
     699                 */
     700                return apply_filters( 'bp_email_validate', $retval, $this );
     701        }
     702
     703
     704        /*
     705         * Utility functions.
     706         *
     707         * Unlike other methods in this class, utility functions are not chainable.
     708         */
     709
     710        /**
     711         * Replace all tokens in the input with appropriate values.
     712         *
     713         * Unlike most other methods in this class, this one is not chainable.
     714         *
     715         * @since 2.5.0
     716         *
     717         * @param string $text
     718         * @param array $tokens Token names and replacement values for the $text.
     719         * @return string
     720         */
     721        public static function replace_tokens( $text, $tokens ) {
     722                $unescaped = array();
     723                $escaped   = array();
     724
     725                foreach ( $tokens as $token => $value ) {
     726                        if ( is_callable( $value ) ) {
     727                                $value = call_user_func( $value );
     728                        }
     729
     730                        // Some tokens are objects or arrays for backwards compatibilty. See bp_core_deprecated_email_filters().
     731                        if ( ! is_scalar( $value ) ) {
     732                                continue;
     733                        }
     734
     735                        $unescaped[ '{{{' . $token . '}}}' ] = $value;
     736                        $escaped[ '{{' . $token . '}}' ]     = esc_html( $value );
     737                }
     738
     739                $text = strtr( $text, $unescaped );  // Do first.
     740                $text = strtr( $text, $escaped );
     741
     742                /**
     743                 * Filters text that has had tokens replaced.
     744                 *
     745                 * @since 2.5.0
     746                 *
     747                 * @param string $text
     748                 * @param array $tokens Token names and replacement values for the $text.
     749                 */
     750                return apply_filters( 'bp_email_replace_tokens', $text, $tokens );
     751        }
     752}
  • new file src/bp-core/classes/class-bp-phpmailer.php

    diff --git a/src/bp-core/classes/class-bp-phpmailer.php b/src/bp-core/classes/class-bp-phpmailer.php
    new file mode 100644
    index 0000000..1610895
    - +  
     1<?php
     2/**
     3 * Core component classes.
     4 *
     5 * @package BuddyPress
     6 * @subpackage Core
     7 */
     8
     9// Exit if accessed directly
     10defined( 'ABSPATH' ) || exit;
     11
     12/**
     13 * Email delivery implementation using PHPMailer.
     14 *
     15 * @since 2.5.0
     16 */
     17class BP_PHPMailer implements BP_Email_Delivery {
     18
     19        /**
     20         * Constructor.
     21         *
     22         * @since 2.5.0
     23         */
     24        public function __construct() {
     25                global $phpmailer;
     26
     27                // We'll try to use the PHPMailer object that might have been created by WordPress.
     28                if ( ! ( $phpmailer instanceof PHPMailer ) ) {
     29                        require_once ABSPATH . WPINC . '/class-phpmailer.php';
     30                        require_once ABSPATH . WPINC . '/class-smtp.php';
     31                        $phpmailer = new PHPMailer( true );
     32                }
     33        }
     34
     35        /**
     36         * Send email(s).
     37         *
     38         * @since 2.5.0
     39         *
     40         * @param BP_Email $email Email to send.
     41         * @return bool|WP_Error Returns true if email send, else a descriptive WP_Error.
     42         */
     43        public function bp_email( BP_Email $email ) {
     44                global $phpmailer;
     45
     46                /*
     47                 * Resets.
     48                 */
     49
     50                $phpmailer->clearAllRecipients();
     51                $phpmailer->clearAttachments();
     52                $phpmailer->clearCustomHeaders();
     53                $phpmailer->clearReplyTos();
     54                $phpmailer->Sender = '';
     55
     56
     57                /*
     58                 * Set up.
     59                 */
     60
     61                $phpmailer->IsMail();
     62                $phpmailer->CharSet  = bp_get_option( 'blog_charset' );
     63                $phpmailer->Hostname = self::get_hostname();
     64
     65
     66                /*
     67                 * Content.
     68                 */
     69
     70                $phpmailer->Subject = $email->get( 'subject', 'replace-tokens' );
     71                $content_plaintext  = PHPMailer::normalizeBreaks( $email->get( 'content_plaintext', 'replace-tokens' ) );
     72
     73                if ( $email->get( 'content_type' ) === 'html' ) {
     74                        $phpmailer->msgHTML( $email->get( 'template', 'add-content' ), '', 'wp_strip_all_tags' );
     75                        $phpmailer->AltBody = $content_plaintext;
     76
     77                } else {
     78                        $phpmailer->IsHTML( false );
     79                        $phpmailer->Body = $content_plaintext;
     80                }
     81
     82                $recipient = $email->get( 'from' );
     83                try {
     84                        $phpmailer->SetFrom( $recipient->get_address(), $recipient->get_name() );
     85                } catch ( phpmailerException $e ) {
     86                }
     87
     88                $recipient = $email->get( 'reply_to' );
     89                try {
     90                        $phpmailer->addReplyTo( $recipient->get_address(), $recipient->get_name() );
     91                } catch ( phpmailerException $e ) {
     92                }
     93
     94                $recipients = $email->get( 'to' );
     95                foreach ( $recipients as $recipient ) {
     96                        try {
     97                                $phpmailer->AddAddress( $recipient->get_address(), $recipient->get_name() );
     98                        } catch ( phpmailerException $e ) {
     99                        }
     100                }
     101
     102                $recipients = $email->get( 'cc' );
     103                foreach ( $recipients as $recipient ) {
     104                        try {
     105                                $phpmailer->AddCc( $recipient->get_address(), $recipient->get_name() );
     106                        } catch ( phpmailerException $e ) {
     107                        }
     108                }
     109
     110                $recipients = $email->get( 'bcc' );
     111                foreach ( $recipients as $recipient ) {
     112                        try {
     113                                $phpmailer->AddBcc( $recipient->get_address(), $recipient->get_name() );
     114                        } catch ( phpmailerException $e ) {
     115                        }
     116                }
     117
     118                $headers = $email->get( 'headers' );
     119                foreach ( $headers as $name => $content ) {
     120                        $phpmailer->AddCustomHeader( $name, $content );
     121                }
     122
     123
     124                /**
     125                 * Fires after PHPMailer is initialised.
     126                 *
     127                 * @since 2.5.0
     128                 *
     129                 * @param PHPMailer $phpmailer The PHPMailer instance.
     130                 */
     131                do_action( 'bp_phpmailer_init', $phpmailer );
     132
     133                try {
     134                        return $phpmailer->Send();
     135                } catch ( phpmailerException $e ) {
     136                        return new WP_Error( $e->getCode(), $e->getMessage(), $email );
     137                }
     138        }
     139
     140
     141        /*
     142         * Utility/helper functions.
     143         */
     144
     145        /**
     146         * Get an appropriate hostname for the email. Varies depending on site configuration.
     147         *
     148         * @since 2.5.0
     149         *
     150         * @return string
     151         */
     152        static public function get_hostname() {
     153                if ( is_multisite() ) {
     154                        return get_current_site()->domain;  // From fix_phpmailer_messageid()
     155                }
     156
     157                return preg_replace( '#^https?://#i', '', bp_get_option( 'home' ) );
     158        }
     159}
  • tests/phpunit/includes/install.php

    diff --git a/tests/phpunit/includes/install.php b/tests/phpunit/includes/install.php
    index fd9495d..a003132 100644
    a b $multisite = ! empty( $argv[3] ); 
    1313
    1414require_once $config_file_path;
    1515require_once $tests_dir_path . '/includes/functions.php';
     16require_once $tests_dir_path . '/includes/mock-mailer.php';
    1617
    1718function _load_buddypress() {
    1819        require dirname( dirname( dirname( dirname( __FILE__ ) ) ) ) . '/src/bp-loader.php';
    foreach ( $wpdb->get_col( "SHOW TABLES LIKE '" . $wpdb->prefix . "bp%'" ) as $bp 
    4849        $wpdb->query( "DROP TABLE {$bp_table}" );
    4950}
    5051
     52function _bp_mock_mailer( $class ) {
     53        return 'BP_UnitTest_Mailer';
     54}
     55tests_add_filter( 'bp_send_email_delivery_class', '_bp_mock_mailer' );
     56
    5157// Install BuddyPress
    5258bp_version_updater();
  • tests/phpunit/includes/loader.php

    diff --git a/tests/phpunit/includes/loader.php b/tests/phpunit/includes/loader.php
    index 6f9b6d2..e6a4838 100644
    a b system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/install.ph 
    77
    88// Bootstrap BP
    99require dirname( __FILE__ ) . '/../../../src/bp-loader.php';
     10
     11require_once( dirname( __FILE__ ) . '/mock-mailer.php' );
     12function _bp_mock_mailer( $class ) {
     13        return 'BP_UnitTest_Mailer';
     14}
     15tests_add_filter( 'bp_send_email_delivery_class', '_bp_mock_mailer' );
  • new file tests/phpunit/includes/mock-mailer.php

    diff --git a/tests/phpunit/includes/mock-mailer.php b/tests/phpunit/includes/mock-mailer.php
    new file mode 100644
    index 0000000..0e0d99e
    - +  
     1<?php
     2
     3/**
     4 * Mock email delivery implementation.
     5 *
     6 * @since 2.5.0
     7 */
     8class BP_UnitTest_Mailer implements BP_Email_Delivery {
     9
     10        /**
     11         * Send email(s).
     12         *
     13         * @param BP_Email $email Email to send.
     14         * @return bool False if some error occurred.
     15         * @since 2.5.0
     16         */
     17        public function bp_email( BP_Email $email ) {
     18                return true;
     19        }
     20}
  • new file tests/phpunit/testcases/core/class-bp-email-recipient.php

    diff --git a/tests/phpunit/testcases/core/class-bp-email-recipient.php b/tests/phpunit/testcases/core/class-bp-email-recipient.php
    new file mode 100644
    index 0000000..052e677
    - +  
     1<?php
     2/**
     3 * @group core
     4 * @group BP_Email_Recipient
     5 */
     6class BP_Email_Recipient_Tests extends BP_UnitTestCase {
     7        protected $u1;
     8
     9        public function setUp() {
     10                parent::setUp();
     11
     12                $this->u1 = $this->factory->user->create( array(
     13                        'display_name' => 'Unit Test',
     14                        'user_email'   => 'test@example.com',
     15                ) );
     16        }
     17
     18        public function test_return_with_address_and_name() {
     19                $email     = 'test@example.com';
     20                $name      = 'Unit Test';
     21                $recipient = new BP_Email_Recipient( $email, $name );
     22
     23                $this->assertSame( $email, $recipient->get_address() );
     24                $this->assertSame( $name, $recipient->get_name() );
     25        }
     26
     27        public function test_return_with_array() {
     28                $email     = 'test@example.com';
     29                $name      = 'Unit Test';
     30                $recipient = new BP_Email_Recipient( array( $email => $name ) );
     31
     32                $this->assertSame( $email, $recipient->get_address() );
     33                $this->assertSame( $name, $recipient->get_name() );
     34        }
     35
     36        public function test_return_with_user_id() {
     37                $recipient = new BP_Email_Recipient( $this->u1 );
     38
     39                $this->assertSame( 'test@example.com', $recipient->get_address() );
     40                $this->assertSame( 'Unit Test', $recipient->get_name() );
     41        }
     42
     43        public function test_return_with_wp_user_object() {
     44                $recipient = new BP_Email_Recipient( get_user_by( 'id', $this->u1 ) );
     45
     46                $this->assertSame( 'test@example.com', $recipient->get_address() );
     47                $this->assertSame( 'Unit Test', $recipient->get_name() );
     48        }
     49
     50        public function test_return_with_address_and_optional_name() {
     51                $email     = 'test@example.com';
     52                $recipient = new BP_Email_Recipient( $email );
     53
     54                $this->assertSame( $email, $recipient->get_address() );
     55                $this->assertEmpty( $recipient->get_name() );
     56        }
     57
     58        public function test_return_with_array_and_optional_name() {
     59                $email     = 'test@example.com';
     60                $recipient = new BP_Email_Recipient( array( $email ) );
     61
     62                $this->assertSame( $email, $recipient->get_address() );
     63                $this->assertEmpty( $recipient->get_name() );
     64        }
     65
     66        public function test_should_return_empty_string_if_user_id_id_invalid() {
     67                $recipient = new BP_Email_Recipient( time() );
     68
     69                $this->assertEmpty( $recipient->get_address() );
     70                $this->assertEmpty( $recipient->get_name() );
     71        }
     72
     73        public function test_get_wp_user_object_from_email_address() {
     74                $recipient = new BP_Email_Recipient( 'test@example.com' );
     75                $recipient = $recipient->get_user( 'search-email' );
     76
     77                $this->assertSame( $this->u1, $recipient->ID );
     78                $this->assertSame( 'test@example.com', $recipient->user_email );
     79        }
     80}
  • new file tests/phpunit/testcases/core/class-bp-email.php

    diff --git a/tests/phpunit/testcases/core/class-bp-email.php b/tests/phpunit/testcases/core/class-bp-email.php
    new file mode 100644
    index 0000000..3eed772
    - +  
     1<?php
     2/**
     3 * @group core
     4 * @group BP_Email
     5 */
     6class BP_Tests_Email extends BP_UnitTestCase {
     7        public function setUp() {
     8                parent::setUp();
     9                remove_filter( 'bp_email_get_headers', 'bp_email_set_default_headers', 6, 4 );
     10                remove_filter( 'bp_email_get_tokens', 'bp_email_set_default_tokens', 6, 4 );
     11        }
     12
     13        public function tearDown() {
     14                add_filter( 'bp_email_get_tokens', 'bp_email_set_default_tokens', 6, 4 );
     15                add_filter( 'bp_email_get_headers', 'bp_email_set_default_headers', 6, 4 );
     16                parent::tearDown();
     17        }
     18
     19        public function test_valid_subject() {
     20                $message = 'test';
     21                $email   = new BP_Email( 'fake_type' );
     22
     23                $email->subject( $message );
     24                $this->assertSame( $message, $email->get( 'subject' ) );
     25        }
     26
     27        public function test_valid_html_content() {
     28                $message = '<b>test</b>';
     29                $email   = new BP_Email( 'fake_type' );
     30
     31                $email->content_html( $message );
     32                $email->content_type( 'html' );
     33
     34                $this->assertSame( $message, $email->get( 'content' ) );
     35        }
     36
     37        public function test_valid_plaintext_content() {
     38                $message = 'test';
     39                $email   = new BP_Email( 'fake_type' );
     40
     41                $email->content_plaintext( $message );
     42                $email->content_type( 'plaintext' );
     43
     44                $this->assertSame( $message, $email->get( 'content' ) );
     45        }
     46
     47        public function test_valid_template() {
     48                $message = 'test';
     49                $email   = new BP_Email( 'fake_type' );
     50
     51                $email->template( $message );
     52                $this->assertSame( $message, $email->get( 'template' ) );
     53        }
     54
     55        public function test_tokens() {
     56                $original = array( 'test1' => 'hello', 'test2' => 'world' );
     57
     58                $email = new BP_Email( 'fake_type' );
     59                $email->tokens( $original );
     60
     61                $this->assertSame(
     62                        array( 'test1', 'test2' ),
     63                        array_keys( $email->get( 'tokens' ) )
     64                );
     65
     66                $this->assertSame(
     67                        array( 'hello', 'world' ),
     68                        array_values( $email->get( 'tokens' ) )
     69                );
     70        }
     71
     72        public function test_headers() {
     73                $email = new BP_Email( 'fake_type' );
     74
     75                $headers = array( 'custom_header' => 'custom_value' );
     76                $email->headers( $headers );
     77                $this->assertSame( $headers, $email->get( 'headers' ) );
     78        }
     79
     80        public function test_validation() {
     81                $email = new BP_Email( 'fake_type' );
     82                $email->from( 'test1@example.com' )->to( 'test2@example.com' )->subject( 'testing' );
     83                $email->content_html( 'testing' );
     84
     85                $this->assertTrue( $email->validate() );
     86        }
     87
     88        public function test_invalid_characters_are_stripped_from_tokens() {
     89                $email = new BP_Email( 'fake_type' );
     90                $email->tokens( array( 'te{st}1' => 'hello world' ) );
     91
     92                $this->assertSame(
     93                        array( 'test1' ),
     94                        array_keys( $email->get( 'tokens' ) )
     95                );
     96        }
     97
     98        public function test_token_are_escaped() {
     99                $token = '<blink>';
     100                $email = new BP_Email( 'fake_type' );
     101                $email->content_html( '{{test}}' )->tokens( array( 'test' => $token ) );
     102
     103                $this->assertSame(
     104                        esc_html( $token ),
     105                        $email->get( 'content', 'replace-tokens' )
     106                );
     107        }
     108
     109        public function test_token_are_not_escaped() {
     110                $token = '<blink>';
     111                $email = new BP_Email( 'fake_type' );
     112                $email->content_html( '{{{test}}}' )->tokens( array( 'test' => $token ) );
     113
     114                $this->assertSame(
     115                        $token,
     116                        $email->get( 'content', 'replace-tokens' )
     117                );
     118        }
     119
     120        public function test_invalid_headers() {
     121                $email = new BP_Email( 'fake_type' );
     122
     123                $headers = array( 'custom:header' => 'custom:value' );
     124                $email->headers( $headers );
     125                $this->assertNotSame( $headers, $email->get( 'headers' ) );
     126                $this->assertSame( array( 'customheader' => 'customvalue' ), $email->get( 'headers' ) );
     127        }
     128
     129        public function test_validation_with_missing_required_data() {
     130                $email  = new BP_Email( 'fake_type' );
     131                $email->from( 'test1@example.com' )->to( 'test2@example.com' )->subject( 'testing' );  // Content
     132                $result = $email->validate();
     133
     134                $this->assertTrue( is_wp_error( $result ) );
     135                $this->assertSame( 'missing_parameter', $result->get_error_code() );
     136        }
     137
     138        public function test_validation_with_missing_template() {
     139                $email  = new BP_Email( 'fake_type' );
     140                $email->from( 'test1@example.com' )->to( 'test2@example.com' )->subject( 'testing' );
     141                $email->content_html( 'testing' )->template( '' );
     142                $result = $email->validate();
     143
     144                // Template has a default value, but it can't be blank.
     145                $this->assertTrue( is_wp_error( $result ) );
     146                $this->assertSame( 'missing_parameter', $result->get_error_code() );
     147        }
     148
     149        public function test_invalid_tags_should_be_removed_from_html_content() {
     150                $message = '<b>hello world</b><iframe src="https://example.com"></iframe><b>hello world</b>';
     151                $email   = new BP_Email( 'fake_type' );
     152
     153                $email->content_html( $message );
     154                $email->content_type( 'html' );
     155
     156                $this->assertSame( '<b>hello world</b><b>hello world</b>', $email->get( 'content' ) );
     157        }
     158}