Noptin_Mailer
Source Source
File: includes/class-noptin-mailer.php
class Noptin_Mailer { /** * Whether or not we should inline CSS into the email. */ public $inline_css = true; /** * For backwards compatibility; */ private static $initialized = false; /** * The wp_mail() data. */ public $wp_mail_data = null; /** * Whether or not we should disable template plugins. */ public $disable_template_plugins = true; /** * The current emails mailer data. */ public $mailer_data = array(); /** * The class constructor. */ public function __construct() { // We only want to init the class once. if ( empty( self::$initialized ) ) { $this->init(); self::$initialized = true; } } /** * Initialize the class. */ private function init() { // Send any background emails. add_action( 'send_bg_noptin_email', array( $this, '_handle_background_send' ) ); } /** * Prepares an email for sending. */ public function prepare( $data = array() ) { // Ensure that we have merge tags. if ( empty( $data['merge_tags'] ) ) { $data['merge_tags'] = array(); } // Ensure that a few variables are set. $data['email_subject'] = $this->get_subject( $data ); $data['title'] = $data['email_subject']; $data['logo_url'] = $this->get_logo_url( $data ); $data['tracker'] = $this->get_tracker( $data ); $data['permission_text'] = ! isset( $data['permission_text'] ) ? $this->get_permission_text( $data ) : $data['permission_text']; $data['permission_text'] = wpautop( $data['permission_text'] ); $data['footer_text'] = ! isset( $data['footer_text'] ) ? $this->get_footer_text( $data ) : $data['footer_text']; $data['footer_text'] = wpautop( $data['footer_text'] ); $data['hero_text'] = empty( $data['hero_text'] ) ? '' : $data['hero_text']; $data['cta_url'] = empty( $data['cta_url'] ) ? '' : $data['cta_url']; $data['cta_text'] = empty( $data['cta_text'] ) ? '' : $data['cta_text']; $data['after_cta_text'] = empty( $data['after_cta_text'] ) ? '' : $data['after_cta_text']; $data['after_cta_text2'] = empty( $data['after_cta_text2'] ) ? '' : $data['after_cta_text2']; $data['email_body'] = $this->build_email( $data ); $this->mailer_data = $data; return $data; } /** * Prepares an email then sends it. */ public function prepare_then_send( $data = array() ) { $data = $this->prepare( $data ); return $this->send( $data['email'], $data['email_subject'], $data['email_body'] ); } /** * Prepares an email then sends it in the background. */ public function prepare_then_bg_send( $data = array() ) { $data = $this->prepare( $data ); return $this->background_send( $data['email'], $data['email_subject'], $data['email_body'] ); } /** * Returns the email subject with merge tags replaced. */ public function get_subject( $data = array() ) { if ( empty( $data['email_subject'] ) ) { return ''; } $subject = trim( $data['email_subject'] ); if ( empty( $data['merge_tags'] ) ) { $data['merge_tags'] = array(); } $subject = $this->merge( $subject, $data['merge_tags'] ); return $subject; } /** * Returns the email body with the template compiled and merge tags replaced. * */ public function get_email( $data = array() ) { $data = $this->prepare( $data ); return $data['email_body']; } /** * Retrieves the logo URL. */ public function get_logo_url( $data ) { return apply_filters( 'noptin_email_logo_url', get_noptin_option( 'logo_url', '' ), $data, $this ); } /** * Returns the code used to track email opens. */ public function get_tracker( $data = array() ) { if ( empty( $data['campaign_id'] ) || empty( $data['subscriber_id'] ) ) { return ''; } $url = get_noptin_action_url( 'email_open' ); $url = add_query_arg( array( 'sid' => intval( $data['subscriber_id'] ), 'cid' => intval( $data['campaign_id'] ), ), $url ); $url = esc_url( $url ); return "<img src='$url' style='border:0;width:1px;height:1px;' />"; } /** * Retrieves the default merge tags */ public function get_default_merge_tags() { $default_merge_tags = array( 'blog_name' => get_bloginfo( 'name' ), 'blog_description' => get_bloginfo( 'description' ), 'home_url' => get_home_url(), 'noptin' => sprintf( '<a target="_blank" href="https://noptin.com/?utm_medium=powered-by&utm_campaign=email-campaign&utm_source=%s">Noptin</a>', esc_url( get_home_url() ) ), 'noptin_company' => get_noptin_option( 'company', '' ), ); return apply_filters( 'noptin_mailer_default_merge_tags', $default_merge_tags, $this ); } /** * Retrieves the default footer text. */ public function default_footer_text() { $country = get_noptin_option( 'country', 'United States' ); $company = get_noptin_option( 'company', get_option( 'blogname' ) ); $address = get_noptin_option( 'address', '31 North San Juan Ave.' ); $city = get_noptin_option( 'city', 'Santa Clara' ); $state = get_noptin_option( 'state', 'San Francisco' ); $powered = sprintf( __( 'Newsletter powered by %s', 'newsletter-optin-box' ), '[[noptin]]' ); return trim( "$address \n\n$city, $state, $country \n\n$company" ); } /** * Returns the footer text. */ public function get_footer_text( $data ) { $footer_text = get_noptin_option( 'footer_text', $this->default_footer_text() ); return apply_filters( 'noptin_mailer_email_footer_text', $footer_text, $data, $this ); } /** * Returns the default permission text. */ public function default_permission_text() { $permission_text = __( 'You received this email because you are subscribed to our email newsletter.', 'newsletter-optin-box' ); $permission_text2 = sprintf( /* Translators: %1$s Opening link tag, %2$s Closing link tag. */ __( 'To stop receiving these emails, you can %1$sunsubscribe%2$s at any time.', 'newsletter-optin-box' ), '<a href="[[unsubscribe_url]]" rel="nofollow" target="_blank">', '</a>' ); return $permission_text . ' ' . $permission_text2; } /** * Returns the permission text. */ public function get_permission_text( $data = array() ) { $permission_text = get_noptin_option( 'permission_text', $this->default_permission_text() ); return apply_filters( 'noptin_mailer_email_permission_text', $permission_text, $data, $this ); } /** * Merges a string with the specified merge tags */ public function merge( $content, $tags = array() ) { $tags = wp_parse_args( $this->get_default_merge_tags(), $tags ); // Replace all available tags with their values. foreach ( $tags as $key => $value ) { if ( ! is_array( $value ) ) { $content = str_ireplace( "[[$key]]", $value, $content ); } } // Remove unavailable tags. $content = preg_replace( '/\[\[\w+]\]/', '', $content ); return $content; } /** * Makes campaign links trackable. * * @param string $content The email content. * @param array $data The new campaign data. */ public function make_links_trackable( $content, $data ) { if ( empty( $data['campaign_id'] ) || empty( $data['subscriber_id'] ) ) { return $content; } $url = get_noptin_action_url( 'email_click' ); $url = add_query_arg( array( 'sid' => intval( $data['subscriber_id'] ), 'cid' => intval( $data['campaign_id'] ), 'noptin_hide' => 'true' ), $url ); $_content = preg_replace_callback( '/<a(.*?)href=["\'](.*?)["\'](.*?)>/mi', function ( $matches ) use ( $url ) { $_url = add_query_arg( 'to', urlencode( $matches[2] ), $url ); $pre = $matches[1]; $post = $matches[3]; return "<a $pre href='$_url' $post >"; }, $content ); if ( empty( $_content ) ) { return $content; } return $_content; } /** * Inlines CSS into the email to make it compatible with more clients. * * @param string $content The email content. */ public function inline_css( $content ) { // Check if this is PHP 5.6 if ( version_compare( phpversion(), '5.6', '<' ) ) { return $content; } // Maybe abort early; if ( ! class_exists( 'Pelago\Emogrifier\CssInliner' ) || ! $this->inline_css ) { return $content; } try { $emogrifier = Pelago\Emogrifier\CssInliner::fromHtml( $content ); return $emogrifier->inlineCss()->render(); } catch ( Exception $e ) { log_noptin_message( $e->getMessage() ); return $content; } } /** * Post processes an email message. */ public function post_process( $content, $data ) { // Parse merge tags. $content = $this->merge( $content, $data['merge_tags'] ); // Make links clickable. $content = make_clickable( $content ); // Ensure that shortcodes are not wrapped in paragraphs. $content = shortcode_unautop( $content ); // Execute shortcodes. $content = do_shortcode( $content ); // Balance tags. $content = force_balance_tags( $content ); // Make links trackable. $content = $this->make_links_trackable( $content, $data ); if ( 'empty' === $data['template'] ) { return $content; } // Finally, inline the CSS. $content = $this->inline_css( $content ); // inline css adds a body tag, which doesn't play nice if we are using a template plugin. if ( ! $this->disable_template_plugins ) { $matches = array(); preg_match( "/<body[^>]*>(.*?)<\/body>/is", $content, $matches ); if ( isset( $matches[1] ) ) { $content = trim( $matches[1] ); } } // Filters a post processed email. return apply_filters( 'noptin_post_processed_mailer_email_content', $content, $data, $this ); } /** * Given merge tags, this method builds an email. * * @since 1.2.8 * * @param array $data The email data. * * @return string */ public function build_email( $data ) { // Filters email data before the email is generated. $data = apply_filters( 'noptin_mailer_email_data', $data, $this ); // If no template is provided, use the user set template. if ( empty( $data['template'] ) ) { $data['template'] = $this->get_template( $data ); } $template = $data['template']; // Whether or not we should disable template plugins. $this->disable_template_plugins = 'empty' !== $template && 'default' !== $template; // If we are using an empty template, return the content as is. if ( 'empty' === $template ) { return $this->post_process( $data['email_body'], $data ); } ob_start(); // If this is a full path to the template... if ( file_exists( $template ) ) { include $template; return $this->post_process( ob_get_clean(), $data ); } // We are using a template stored in the templates directory. $sections = array( 'header', 'preview-text', 'logo', 'body', 'footer' ); foreach ( apply_filters( 'noptin_mailer_email_sections', $sections, $data, $template, $this ) as $section ) { $section = sanitize_text_field( $section ); // Fires before the section is printed. do_action( "noptin_mailer_before_{$section}_section", $data, $this ); // Load the section. get_noptin_template( "email-templates/$template/$section.php", $data ); // Fires after the section is printed. do_action( "noptin_mailer_after_{$section}_section", $data, $this ); } $email_content = ob_get_clean(); // Filters email content before it is pre-processed. $email_content = apply_filters( 'noptin_mailer_pre_processed_email_content', $email_content, $data, $this ); if ( empty( $email_content ) ) { $email_content = $data['email_body']; } return $this->post_process( $email_content, $data ); } /** * The email template that we'll use to send our emails. * * @since 1.2.8 * */ public function get_template( $data ) { $template = get_noptin_option( 'email_template', 'plain' ); $template = apply_filters( 'noptin_mailer_email_template', $template, $data, $this ); if ( empty( $template ) ) { $template = 'plain'; } return $template; } /** * Retrieves email headers. */ public function get_headers() { $name = $this->get_from_name(); $reply_to = $this->get_reply_to(); $content = $this->get_content_type(); $headers = array( "Reply-To:$name <$reply_to>" ); $headers[] = "Content-Type:$content"; if ( ! empty( $this->mailer_data['merge_tags']['unsubscribe_url'] ) ) { $url = esc_url( $this->mailer_data['merge_tags']['unsubscribe_url'] ); $headers[] = "List-Unsubscribe:<$url>"; } $headers = implode( "\r\n", $headers ); return apply_filters( 'noptin_mailer_email_headers', $headers, $this ); } /** * The default emails from address. * * Defaults to noptin@$sitename * Some hosts will block outgoing mail from this address if it doesn't exist, * but there's no easy alternative. Defaulting to admin_email might appear to be * another option, but some hosts may refuse to relay mail from an unknown domain. * * @since 1.2.8 */ public function default_from_address() { // Get the site domain and get rid of www. $sitename = strtolower( $_SERVER['SERVER_NAME'] ); if ( substr( $sitename, 0, 4 ) == 'www.' ) { $sitename = substr( $sitename, 4 ); } $from_email = 'noptin@' . $sitename; return apply_filters( 'noptin_mailer_default_from_address', $from_email, $this ); } /** * Get the email reply-to. * * @since 1.2.8 * * @return string The email reply-to address. */ public function get_reply_to() { $reply_to = get_noptin_option( 'reply_to', get_option( 'admin_email' ) ); if ( ! is_email( $reply_to ) ) { $reply_to = get_option( 'admin_email' ); } return apply_filters( 'noptin_mailer_email_reply_to', $reply_to, $this ); } /** * Get the email from address. * * @since 1.2.8 * * @return string The email from address address. */ public function get_from_address() { $from_address = get_noptin_option( 'from_email', $this->default_from_address() ); if ( ! is_email( $from_address ) ) { $from_address = $this->default_from_address(); } return apply_filters( 'noptin_mailer_email_from_address', $from_address, $this ); } /** * Get the email from name. * * @since 1.2.8 * * @return string The email from name */ public function get_from_name() { $from_name = get_noptin_option( 'from_name', get_bloginfo( 'name' ) ); if ( empty( $from_name ) ) { $from_name = get_bloginfo( 'name' ); } return apply_filters( 'noptin_mailer_email_from_name', sanitize_text_field( $from_name ), $this ); } /** * Get the email content type. * * @since 1.2.8 * * @return string The email content type. */ public function get_content_type() { return apply_filters( 'noptin_mailer_email_content_type', 'text/html', $this ); } /** * Ensures that our email messages are not messed up by template plugins. * * @since 1.3.0 * * @return array wp_mail_data. */ public function ensure_email_content( $args ) { if ( $this->disable_template_plugins ) { $args['message'] = $this->wp_mail_data['email']; } return $args; } /** * Add filters/actions before the email is sent. * * @since 1.2.8 */ public function before_sending() { add_filter( 'wp_mail_from', array( $this, 'get_from_address' ), 1000 ); add_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ), 1000 ); add_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ), 1000 ); add_filter( 'wp_mail', array( $this, 'ensure_email_content' ), 1000000 ); } /** * Remove filters/actions after the email is sent. * * @since 1.2.8 */ public function after_sending() { remove_filter( 'wp_mail_from', array( $this, 'get_from_address' ), 1000 ); remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ), 1000 ); remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ), 1000 ); remove_filter( 'wp_mail', array( $this, 'ensure_email_content' ), 1000000 ); } /** * Sends an email immeadiately */ public function send( $to, $subject, $email ) { // Don't send if email address is invalid. if ( ! is_email( $to ) ) { return false; } // Hooks before an email is sent. do_action( 'before_noptin_sends_email', $to, $subject, $email, $this ); /* * Allow to filter data on per-email basis. */ $data = apply_filters( 'noptin_mailer_email_data', array( 'to' => $to, 'subject' => $subject, 'email' => $email, 'headers' => $this->get_headers(), 'attachments' => array(), ), $this, $this->mailer_data ); $data = wp_unslash( $data ); $this->wp_mail_data = $data; // Attach our own hooks. $this->before_sending(); // Prepare the sending function. $sending_function = apply_filters( 'noptin_mailer_email_sending_function', 'wp_mail', $this ); // Send the actual email. $result = call_user_func( $sending_function, $data['to'], html_entity_decode( $data['subject'], ENT_QUOTES, get_bloginfo( 'charset' ) ), $data['email'], $data['headers'], $data['attachments'] ); // If the email was not sent, log the error. if ( empty( $result ) ) { log_noptin_message( sprintf( /* Translators: %1$s Email address, %2$s Email subject. */ __( 'Failed sending an email to %1$s with the subject %2$s', 'newsletter-optin-box' ), sanitize_email( $data['to'] ), wp_specialchars_decode ( $data['subject'] ) ) ); } // Remove our hooks. $this->after_sending(); // Hooks after an email is sent. do_action( 'after_noptin_sends_email', $to, $subject, $email, $this, $result ); $this->wp_mail_data = null; return $result; } /** * Schedules an email to send in the background. */ public function background_send( $to, $subject, $email ) { $data = compact( 'to', 'subject', 'email' ); $key = 'noptin_' . md5( uniqid() . time() . wp_rand( 0, 1000 ) ); set_transient( $key, $data ); do_noptin_background_action( 'send_bg_noptin_email', $key ); } /** * Sends a background email. */ public function _handle_background_send( $key ) { $data = get_transient( $key ); delete_transient( $key ); if ( empty( $data ) || ! is_array( $data ) ) { return; } $this->send( $data['to'], $data['subject'], $data['email'] ); } }
Expand full source code Collapse full source code View on GitHub
Methods Methods
- __construct — The class constructor.
- _handle_background_send — Sends a background email.
- after_sending — Remove filters/actions after the email is sent.
- background_send — Schedules an email to send in the background.
- before_sending — Add filters/actions before the email is sent.
- build_email — Given merge tags, this method builds an email.
- default_footer_text — Retrieves the default footer text.
- default_from_address — The default emails from address.
- default_permission_text — Returns the default permission text.
- ensure_email_content — Ensures that our email messages are not messed up by template plugins.
- get_content_type — Get the email content type.
- get_default_merge_tags — Retrieves the default merge tags
- get_email — Returns the email body with the template compiled and merge tags replaced.
- get_footer_text — Returns the footer text.
- get_from_address — Get the email from address.
- get_from_name — Get the email from name.
- get_headers — Retrieves email headers.
- get_logo_url — Retrieves the logo URL.
- get_permission_text — Returns the permission text.
- get_reply_to — Get the email reply-to.
- get_subject — Returns the email subject with merge tags replaced.
- get_template — The email template that we'll use to send our emails.
- get_tracker — Returns the code used to track email opens.
- init — Initialize the class.
- inline_css — Inlines CSS into the email to make it compatible with more clients.
- make_links_trackable — Makes campaign links trackable.
- merge — Merges a string with the specified merge tags
- post_process — Post processes an email message.
- prepare — Prepares an email for sending.
- prepare_then_bg_send — Prepares an email then sends it in the background.
- prepare_then_send — Prepares an email then sends it.
- send — Sends an email immeadiately