type = $email_type; // SERVER_NAME isn't always set (e.g CLI). if ( ! empty( $_SERVER['SERVER_NAME'] ) ) { $domain = strtolower( $_SERVER['SERVER_NAME'] ); if ( substr( $domain, 0, 4 ) === 'www.' ) { $domain = substr( $domain, 4 ); } } elseif ( function_exists( 'gethostname' ) && gethostname() !== false ) { $domain = gethostname(); } elseif ( php_uname( 'n' ) !== false ) { $domain = php_uname( 'n' ); } else { $domain = 'localhost.localdomain'; } // This was escaped with esc_html on the way into the database in sanitize_option(). $from_name = wp_specialchars_decode( bp_get_option( 'blogname' ), ENT_QUOTES ); $from_address = "wordpress@$domain"; /** This filter is documented in wp-includes/pluggable.php */ $from_address = apply_filters( 'wp_mail_from', $from_address ); /** This filter is documented in wp-includes/pluggable.php */ $from_name = apply_filters( 'wp_mail_from_name', $from_name ); $this->set_from( $from_address, $from_name ); $this->set_reply_to( bp_get_option( 'admin_email' ), $from_name ); /** * Fires inside __construct() method for BP_Email class. * * @since 2.5.0 * * @param string $email_type Unique identifier for this type of email. * @param BP_Email $this Current instance of the email type class. */ do_action( 'bp_email', $email_type, $this ); } /* * Setters/getters. */ /** * Getter function to expose object properties. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $property_name Property to access. * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return mixed Returns null if property does not exist, otherwise the value. */ public function get( $property_name, $transform = 'raw' ) { // "content" is replaced by HTML or plain text depending on $content_type. if ( $property_name === 'content' ) { $property_name = 'content_' . $this->get_content_type(); if ( ! in_array( $property_name, array( 'content_html', 'content_plaintext', ), true ) ) { $property_name = 'content_html'; } } if ( ! property_exists( $this, $property_name ) ) { return null; } /** * Filters the value of the specified email property before transformation. * * This is a dynamic filter dependent on the specified key. * * @since 2.5.0 * * @param mixed $property_value Property value. * @param string $property_name * @param string $transform How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @param BP_Email $this Current instance of the email type class. */ $retval = apply_filters( "bp_email_get_{$property_name}", $this->$property_name, $property_name, $transform, $this ); switch ( $transform ) { // Special-case to fill the $template with the email $content. case 'add-content': $retval = str_replace( '{{{content}}}', wpautop( $this->get_content( 'replace-tokens' ) ), $retval ); // Fall through. case 'replace-tokens': $retval = bp_core_replace_tokens_in_text( $retval, $this->get_tokens( 'raw' ) ); // Fall through. case 'raw': default: // Do nothing. } /** * Filters the value of the specified email $property after transformation. * * @since 2.5.0 * * @param string $retval Property value. * @param string $property_name * @param string $transform How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @param BP_Email $this Current instance of the email type class. */ return apply_filters( 'bp_email_get_property', $retval, $property_name, $transform, $this ); } /** * Get email preheader. * * @since 4.0.0 */ public function get_preheader() { if ( null !== $this->preheader ) { return $this->preheader; } $preheader = ''; $post = $this->get_post_object(); if ( $post ) { $switched = false; // Switch to the root blog, where the email post lives. if ( ! bp_is_root_blog() ) { switch_to_blog( bp_get_root_blog_id() ); $switched = true; } $preheader = sanitize_text_field( get_post_meta( $post->ID, 'bp_email_preheader', true ) ); if ( $switched ) { restore_current_blog(); } } $this->preheader = $preheader; return $this->preheader; } /** * Get email headers. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string[] Associative pairing of email header name/value. */ public function get_headers( $transform = 'raw' ) { return $this->get( 'headers', $transform ); } /** * Get the email's "bcc" address and name. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return BP_Email_Recipient[] BCC recipients. */ public function get_bcc( $transform = 'raw' ) { return $this->get( 'bcc', $transform ); } /** * Get the email's "cc" address and name. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return BP_Email_Recipient[] CC recipients. */ public function get_cc( $transform = 'raw' ) { return $this->get( 'cc', $transform ); } /** * Get the email content. * * HTML or plaintext is returned, depending on the email's $content_type. * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string HTML or plaintext, depending on $content_type. */ public function get_content( $transform = 'raw' ) { return $this->get( 'content', $transform ); } /** * Get the email content (in HTML). * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string HTML email content. */ public function get_content_html( $transform = 'raw' ) { return $this->get( 'content_html', $transform ); } /** * Get the email content (in plaintext). * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string Plain text email content. */ public function get_content_plaintext( $transform = 'raw' ) { return $this->get( 'content_plaintext', $transform ); } /** * Get the email content type (HTML or plain text) that the email will be sent in. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string Email content type ("html" or "plaintext"). */ public function get_content_type( $transform = 'raw' ) { return $this->get( 'content_type', $transform ); } /** * Get the email's "from" address and name. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return BP_Email_Sender "From" recipient. */ public function get_from( $transform = 'raw' ) { return $this->get( 'from', $transform ); } /** * Get the Post associated with the email. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @return WP_Post The post. */ public function get_post_object( $transform = 'raw' ) { return $this->get( 'post_object', $transform ); } /** * Get the email's "reply to" address and name. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return BP_Email_Recipient "Reply to" recipient. */ public function get_reply_to( $transform = 'raw' ) { return $this->get( 'reply_to', $transform ); } /** * Get the email subject. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string Email subject. */ public function get_subject( $transform = 'raw' ) { return $this->get( 'subject', $transform ); } /** * Get the email template (the HTML wrapper around the email content). * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string Email template. Assumed to be HTML. */ public function get_template( $transform = 'raw' ) { return $this->get( 'template', $transform ); } /** * Get the email's "to" address and name. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return BP_Email_Recipient[] "To" recipients. */ public function get_to( $transform = 'raw' ) { return $this->get( 'to', $transform ); } /** * Get token names and replacement values for this email. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @param string $transform Optional. How to transform the return value. * Accepts 'raw' (default) or 'replace-tokens'. * @return string[] Associative pairing of token name (key) and replacement value (value). */ public function get_tokens( $transform = 'raw' ) { return $this->get( 'tokens', $transform ); } /** * Set email headers. * * Does NOT let you override to/from, etc. Use the methods provided to set those. * * @since 2.5.0 * * @param string[] $headers Key/value pairs of header name/values (strings). * @return BP_Email */ public function set_headers( array $headers ) { $new_headers = array(); foreach ( $headers as $name => $content ) { $content = str_replace( ':', '', $content ); $name = str_replace( ':', '', $name ); $new_headers[ sanitize_key( $name ) ] = sanitize_text_field( $content ); } /** * Filters the new value of the email's "headers" property. * * @since 2.5.0 * * @param string[] $new_headers Key/value pairs of new header name/values (strings). * @param BP_Email $this Current instance of the email type class. */ $this->headers = apply_filters( 'bp_email_set_headers', $new_headers, $this ); return $this; } /** * Set the email's "bcc" address and name. * * To set a single address, the first parameter is the address and the second the name. * You can also pass a user ID or a WP_User object. * * To set multiple addresses, for each array item, the key is the email address and * the value is the name. * * @since 2.5.0 * * @param string|array|int|WP_User $bcc_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $bcc_address is a string, this is the recipient's name. * @param string $operation Optional. If "replace", $to_address replaces current setting (default). * If "add", $to_address is added to the current setting. * @return BP_Email */ public function set_bcc( $bcc_address, $name = '', $operation = 'replace' ) { $bcc = ( $operation !== 'replace' ) ? $this->bcc : array(); if ( is_array( $bcc_address ) ) { foreach ( $bcc_address as $address ) { $bcc[] = new BP_Email_Recipient( $address ); } } else { $bcc[] = new BP_Email_Recipient( $bcc_address, $name ); } /** * Filters the new value of the email's "BCC" property. * * @since 2.5.0 * * @param BP_Email_Recipient[] $bcc BCC recipients. * @param string|array|int|WP_User $bcc_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $bcc_address is a string, this is the recipient's name. * @param string $operation If "replace", $to_address replaced previous recipients. If "add", * $to_address was added to the array of recipients. * @param BP_Email $this Current instance of the email type class. */ $this->bcc = apply_filters( 'bp_email_set_bcc', $bcc, $bcc_address, $name, $operation, $this ); return $this; } /** * Set the email's "cc" address and name. * * To set a single address, the first parameter is the address and the second the name. * You can also pass a user ID or a WP_User object. * * To set multiple addresses, for each array item, the key is the email address and * the value is the name. * * @since 2.5.0 * * @param string|array|int|WP_User $cc_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $cc_address is a string, this is the recipient's name. * @param string $operation Optional. If "replace", $to_address replaces current setting (default). * If "add", $to_address is added to the current setting. * @return BP_Email */ public function set_cc( $cc_address, $name = '', $operation = 'replace' ) { $cc = ( $operation !== 'replace' ) ? $this->cc : array(); if ( is_array( $cc_address ) ) { foreach ( $cc_address as $address ) { $cc[] = new BP_Email_Recipient( $address ); } } else { $cc[] = new BP_Email_Recipient( $cc_address, $name ); } /** * Filters the new value of the email's "CC" property. * * @since 2.5.0 * * @param BP_Email_Recipient[] $cc CC recipients. * @param string|array|int|WP_User $cc_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $cc_address is a string, this is the recipient's name. * @param string $operation If "replace", $to_address replaced previous recipients. If "add", * $to_address was added to the array of recipients. * @param BP_Email $this Current instance of the email type class. */ $this->cc = apply_filters( 'bp_email_set_cc', $cc, $cc_address, $name, $operation, $this ); return $this; } /** * Set the email content (HTML). * * @since 2.5.0 * * @param string $content HTML email content. * @return BP_Email */ public function set_content_html( $content ) { /** * Filters the new value of the email's "content" property (HTML). * * @since 2.5.0 * * @param string $content HTML email content. * @param BP_Email $this Current instance of the email type class. */ $this->content_html = apply_filters( 'bp_email_set_content_html', $content, $this ); return $this; } /** * Set the email content (plain text). * * @since 2.5.0 * * @param string $content Plain text email content. * @return BP_Email */ public function set_content_plaintext( $content ) { /** * Filters the new value of the email's "content" property (plain text). * * @since 2.5.0 * * @param string $content Plain text email content. * @param BP_Email $this Current instance of the email type class. */ $this->content_plaintext = apply_filters( 'bp_email_set_content_plaintext', $content, $this ); return $this; } /** * Set the content type (HTML or plain text) to send the email in. * * @since 2.5.0 * * @param string $content_type Email content type ("html" or "plaintext"). * @return BP_Email */ public function set_content_type( $content_type ) { if ( ! in_array( $content_type, array( 'html', 'plaintext', ), true ) ) { $class = get_class_vars( get_class() ); $content_type = $class['content_type']; } /** * Filters the new value of the email's "content type" property. * * The content type (HTML or plain text) to send the email in. * * @since 2.5.0 * * @param string $content_type Email content type ("html" or "plaintext"). * @param BP_Email $this Current instance of the email type class. */ $this->content_type = apply_filters( 'bp_email_set_content_type', $content_type, $this ); return $this; } /** * Set the email's "from" address and name. * * @since 2.5.0 * * @param string|array|int|WP_User $email_address Either a email address, user ID, or WP_User object. * @param string $name Optional. If $email_address is a string, this is the recipient's name. * @return BP_Email */ public function set_from( $email_address, $name = '' ) { $from = new BP_Email_Sender(); $from->set_address( $email_address ); $from->set_name( $name ); /** * Filters the new value of the email's "from" property. * * @since 2.5.0 * * @param BP_Email_Recipient $from Sender details. * @param string|array|int|WP_User $email_address Either a email address, user ID, or WP_User object. * @param string $name Optional. If $email_address is a string, this is the recipient's name. * @param BP_Email $this Current instance of the email type class. */ $this->from = apply_filters( 'bp_email_set_from', $from, $email_address, $name, $this ); return $this; } /** * Set the Post object containing the email content template. * * Also sets the email's subject, content, and template from the Post, for convenience. * * @since 2.5.0 * * @param WP_Post $post * @return BP_Email */ public function set_post_object( WP_Post $post ) { /** * Filters the new value of the email's "post object" property. * * @since 2.5.0 * * @param WP_Post $post A Post. * @param BP_Email $this Current instance of the email type class. */ $this->post_object = apply_filters( 'bp_email_set_post_object', $post, $this ); if ( is_a( $this->post_object, 'WP_Post' ) ) { $this->set_subject( $this->post_object->post_title ) ->set_content_html( $this->post_object->post_content ) ->set_content_plaintext( $this->post_object->post_excerpt ); ob_start(); // Load the template. add_filter( 'bp_locate_template_and_load', '__return_true' ); // Set up the `$post` global. global $post; $reset_post = $post; $post = $this->post_object; bp_locate_template( bp_email_get_template( $this->post_object ), true, false ); // Reset the `$post` global. $post = $reset_post; remove_filter( 'bp_locate_template_and_load', '__return_true' ); $this->set_template( ob_get_contents() ); ob_end_clean(); } return $this; } /** * Set the email's "reply to" address and name. * * @since 2.5.0 * * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $email_address is a string, this is the recipient's name. * @return BP_Email */ public function set_reply_to( $email_address, $name = '' ) { $reply_to = new BP_Email_Sender(); $reply_to->set_address( $email_address ); $reply_to->set_name( $name ); /** * Filters the new value of the email's "reply to" property. * * @since 2.5.0 * * @param BP_Email_Recipient $reply_to "Reply to" recipient. * @param string|array|int|WP_User $email_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $email_address is a string, this is the recipient's name. * @param BP_Email $this Current instance of the email type class. */ $this->reply_to = apply_filters( 'bp_email_set_reply_to', $reply_to, $email_address, $name, $this ); return $this; } /** * Set the email subject. * * @since 2.5.0 * * @param string $subject Email subject. * @return BP_Email */ public function set_subject( $subject ) { /** * Filters the new value of the subject email property. * * @since 2.5.0 * * @param string $subject Email subject. * @param BP_Email $this Current instance of the email type class. */ $this->subject = apply_filters( 'bp_email_set_subject', $subject, $this ); return $this; } /** * Set the email template (the HTML wrapper around the email content). * * This needs to include the string "{{{content}}}" to have the post content added * when the email template is rendered. * * @since 2.5.0 * * @param string $template Email template. Assumed to be HTML. * @return BP_Email */ public function set_template( $template ) { /** * Filters the new value of the template email property. * * @since 2.5.0 * * @param string $template Email template. Assumed to be HTML. * @param BP_Email $this Current instance of the email type class. */ $this->template = apply_filters( 'bp_email_set_template', $template, $this ); return $this; } /** * Set the email's "to" address and name. * * IMPORTANT NOTE: the assumption with all emails sent by (and belonging to) BuddyPress itself * is that there will only be a single `$to_address`. This is to simplify token and templating * logic (for example, if multiple recipients, the "unsubscribe" link in the emails will all * only link to the first recipient). * * To set a single address, the first parameter is the address and the second the name. * You can also pass a user ID or a WP_User object. * * To set multiple addresses, for each array item, the key is the email address and * the value is the name. * * @since 2.5.0 * * @param string|array|int|WP_User $to_address Either a email address, user ID, WP_User object, * or an array containing any combination of the above. * @param string $name Optional. If $to_address is a string, this is the recipient's name. * @param string $operation Optional. If "replace", $to_address replaces current setting (default). * If "add", $to_address is added to the current setting. * @return BP_Email */ public function set_to( $to_address, $name = '', $operation = 'replace' ) { $to = ( $operation !== 'replace' ) ? $this->to : array(); if ( is_array( $to_address ) ) { foreach ( $to_address as $address ) { $to[] = new BP_Email_Recipient( $address ); } } else { $to[] = new BP_Email_Recipient( $to_address, $name ); } /** * Filters the new value of the email's "to" property. * * @since 2.5.0 * * @param BP_Email_Recipient[] "To" recipients. * @param string $to_address "To" address. * @param string $name "To" name. * @param string $operation If "replace", $to_address replaced previous recipients. If "add", * $to_address was added to the array of recipients. * @param BP_Email $this Current instance of the email type class. */ $this->to = apply_filters( 'bp_email_set_to', $to, $to_address, $name, $operation, $this ); return $this; } /** * Set token names and replacement values for this email. * * In templates, tokens are inserted with a Handlebars-like syntax, e.g. `{{token_name}}`. * { and } are reserved characters. There's no need to specify these brackets in your token names. * * @since 2.5.0 * * @param string[] $tokens Associative array, contains key/value pairs of token name/value. * Values are a string or a callable function. * @return BP_Email */ public function set_tokens( array $tokens ) { $formatted_tokens = array(); foreach ( $tokens as $name => $value ) { $name = str_replace( array( '{', '}' ), '', sanitize_text_field( $name ) ); $formatted_tokens[ $name ] = $value; } /** * Filters the new value of the email's "tokens" property. * * @since 2.5.0 * * @param string[] $formatted_tokens Associative pairing of token names (key) * and replacement values (value). * @param string[] $tokens Associative pairing of unformatted token * names (key) and replacement values (value). * @param BP_Email $this Current instance of the email type class. */ $this->tokens = apply_filters( 'bp_email_set_tokens', $formatted_tokens, $tokens, $this ); return $this; } /* * Sanitisation and validation logic. */ /** * Check that we'd be able to send this email. * * Unlike most other methods in this class, this one is not chainable. * * @since 2.5.0 * * @return bool|WP_Error Returns true if validation succesful, else a descriptive WP_Error. */ public function validate() { $retval = true; // BCC, CC, and token properties are optional. if ( ! $this->get_from() || ! $this->get_to() || ! $this->get_subject() || ! $this->get_content() || ! $this->get_template() ) { $retval = new WP_Error( 'missing_parameter', __CLASS__, $this ); } // Has the user opted out of receiving any email from this site? $recipient_to = $this->get_to(); $recipient_to_first = array_shift( $recipient_to ); $recipient_address = $recipient_to_first->get_address(); if ( bp_user_has_opted_out( $recipient_address ) ) { $retval = new WP_Error( 'user_has_opted_out', __( 'The email recipient has opted out from receiving communication from this site.', 'buddypress' ), $recipient_address ); } /** * Filters whether the email passes basic validation checks. * * @since 2.5.0 * * @param bool|WP_Error $retval Returns true if validation succesful, else a descriptive WP_Error. * @param BP_Email $this Current instance of the email type class. */ return apply_filters( 'bp_email_validate', $retval, $this ); } }