Skip to content
Snippets Groups Projects
Commit 310a2804 authored by Piotr Potrebka's avatar Piotr Potrebka
Browse files

feat: email abstract

parent 867d3d33
No related branches found
No related tags found
1 merge request!2Devel
Pipeline #152602 passed
...@@ -6,7 +6,6 @@ use WPDesk\View\Renderer\Renderer; ...@@ -6,7 +6,6 @@ use WPDesk\View\Renderer\Renderer;
interface ConditionInterface { interface ConditionInterface {
public function is_valid(): bool; public function is_valid(): bool;
} }
<?php <?php
namespace WPDesk\Library\WPEmail\Parser; namespace WPDesk\Library\WPEmail\Helpers;
use Pelago\Emogrifier\CssInliner; use Pelago\Emogrifier\CssInliner;
use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter; use Pelago\Emogrifier\HtmlProcessor\CssToAttributeConverter;
use Pelago\Emogrifier\HtmlProcessor\HtmlPruner; use Pelago\Emogrifier\HtmlProcessor\HtmlPruner;
class HTMLDecorator { class HTML {
public static function style_inline( $content, $styles = '' ) { public static function style_inline( $content, $styles = '' ) {
if ( class_exists( 'DOMDocument' ) ) { if ( class_exists( 'DOMDocument' ) ) {
......
<?php
namespace WPDesk\Library\WPEmail\Helpers;
class Template {
/**
* Determine whether a hex color is light.
*
* @param mixed $color Color.
*
* @return bool True if a light color.
*/
public static function is_hex_light( $color ) {
$hex = str_replace( '#', '', $color );
$c_r = hexdec( substr( $hex, 0, 2 ) );
$c_g = hexdec( substr( $hex, 2, 2 ) );
$c_b = hexdec( substr( $hex, 4, 2 ) );
$brightness = ( ( $c_r * 299 ) + ( $c_g * 587 ) + ( $c_b * 114 ) ) / 1000;
return $brightness > 155;
}
public static function light_or_dark( $color, $dark = '#000000', $light = '#FFFFFF' ) {
return self::is_hex_light( $color ) ? $dark : $light;
}
/**
* Convert RGB to HEX.
*
* @param mixed $color Color.
*
* @return array
*/
public static function rgb_from_hex( $color ) {
$color = str_replace( '#', '', $color );
// Convert shorthand colors to full format, e.g. "FFF" -> "FFFFFF".
$color = preg_replace( '~^(.)(.)(.)$~', '$1$1$2$2$3$3', $color );
$rgb = [];
$rgb['R'] = hexdec( $color[0] . $color[1] );
$rgb['G'] = hexdec( $color[2] . $color[3] );
$rgb['B'] = hexdec( $color[4] . $color[5] );
return $rgb;
}
/**
* Make HEX color darker.
*
* @param mixed $color Color.
* @param int $factor Darker factor.
* Defaults to 30.
*
* @return string
*/
public static function hex_darker( $color, $factor = 30 ) {
$base = self::rgb_from_hex( $color );
$color = '#';
foreach ( $base as $k => $v ) {
$amount = $v / 100;
$amount = self::round( $amount * $factor );
$new_decimal = $v - $amount;
$new_hex_component = dechex( $new_decimal );
if ( strlen( $new_hex_component ) < 2 ) {
$new_hex_component = '0' . $new_hex_component;
}
$color .= $new_hex_component;
}
return $color;
}
/**
* Make HEX color lighter.
*
* @param mixed $color Color.
* @param int $factor Lighter factor.
* Defaults to 30.
*
* @return string
*/
public static function hex_lighter( $color, $factor = 30 ) {
$base = self::rgb_from_hex( $color );
$color = '#';
foreach ( $base as $k => $v ) {
$amount = 255 - $v;
$amount = $amount / 100;
$amount = self::round( $amount * $factor );
$new_decimal = $v + $amount;
$new_hex_component = dechex( $new_decimal );
if ( strlen( $new_hex_component ) < 2 ) {
$new_hex_component = '0' . $new_hex_component;
}
$color .= $new_hex_component;
}
return $color;
}
/**
* @param $val
* @param int $precision
* @param int $mode
*
* @return float
*/
public static function round( $val, int $precision = 0, int $mode = PHP_ROUND_HALF_UP ): float {
if ( ! is_numeric( $val ) ) {
$val = floatval( $val );
}
return round( $val, $precision, $mode );
}
}
...@@ -3,14 +3,14 @@ ...@@ -3,14 +3,14 @@
namespace WPDesk\Library\WPEmail; namespace WPDesk\Library\WPEmail;
use WPDesk\Library\WPEmail\Abstracts\EmailInterface; use WPDesk\Library\WPEmail\Abstracts\EmailInterface;
use WPDesk\Library\WPEmail\Parser\HTMLDecorator; use WPDesk\Library\WPEmail\Helpers\HTML;
use WPDesk\View\Renderer\Renderer; use WPDesk\View\Renderer\Renderer;
use WPDesk\View\Renderer\SimplePhpRenderer; use WPDesk\View\Renderer\SimplePhpRenderer;
use WPDesk\View\Resolver\ChainResolver; use WPDesk\View\Resolver\ChainResolver;
use WPDesk\View\Resolver\DirResolver; use WPDesk\View\Resolver\DirResolver;
use WPDesk\View\Resolver\WPThemeResolver; use WPDesk\View\Resolver\Resolver;
class EmailSender { class Mailer {
/** /**
* @var EmailInterface[] * @var EmailInterface[]
...@@ -20,12 +20,12 @@ class EmailSender { ...@@ -20,12 +20,12 @@ class EmailSender {
/** /**
* @var string * @var string
*/ */
private $from = 'wordpress@wordpress.org'; private $from;
/** /**
* @var string * @var string
*/ */
private $from_name = 'WordPress'; private $from_name;
/** /**
* @var Renderer * @var Renderer
...@@ -33,36 +33,89 @@ class EmailSender { ...@@ -33,36 +33,89 @@ class EmailSender {
private $renderer; private $renderer;
/** /**
* @param $from * @var array
* @param $from_name
*/ */
public function __construct() { protected $placeholders = [];
$this->init_renderer();
/**
* @param string $from
* @param string $from_name
* @param Resolver[] $dir_resolvers
*/
public function __construct(
string $from = 'wordpress@wordpress.org',
string $from_name = 'WordPress',
array $dir_resolvers = []
) {
$this->from = $from;
$this->from_name = $from_name;
$this->set_renderer( $this->init_renderer( $dir_resolvers ) );
} }
public function init_renderer() { /**
* @param array $dir_resolvers
*
* @return Renderer
*/
private function init_renderer( array $dir_resolvers ): Renderer {
$resolver = new ChainResolver(); $resolver = new ChainResolver();
foreach ( $dir_resolvers as $dir_resolver ) {
if ( $dir_resolver instanceof Resolver ) {
$resolver->appendResolver( $dir_resolver );
}
}
$resolver->appendResolver( new DirResolver( __DIR__ ) ); $resolver->appendResolver( new DirResolver( __DIR__ ) );
$this->renderer = new SimplePhpRenderer( $resolver ); return new SimplePhpRenderer( $resolver );
} }
/**
* @param Renderer $renderer
*
* @return void
*/
public function set_renderer( Renderer $renderer ) { public function set_renderer( Renderer $renderer ) {
$this->renderer = $renderer; $this->renderer = $renderer;
} }
/**
* @param EmailInterface $email
*
* @return void
*/
public function add_email( EmailInterface $email ) { public function add_email( EmailInterface $email ) {
$this->emails[ $email->get_id() ] = $email; $this->emails[ $email->get_id() ] = $email;
} }
/**
* @return EmailInterface[]
*/
public function get_emails(): array { public function get_emails(): array {
return $this->emails; return $this->emails;
} }
public function set_from( string $from ) {
$this->from = $from; /**
* Set placeholders.
*
* @param array $placeholders
*
* @return self
*/
public function set_placeholders( array $placeholders = [] ): self {
$this->placeholders = array_merge( $this->placeholders, $placeholders );
return $this;
} }
/**
* Get defined placeholders.
*
* @return string[]
*/
public function get_placeholders(): array {
return $this->placeholders;
}
/** /**
* WordPress callback for setting the from email * WordPress callback for setting the from email
...@@ -71,7 +124,7 @@ class EmailSender { ...@@ -71,7 +124,7 @@ class EmailSender {
* *
* @return string * @return string
*/ */
public function from( $email ): string { public function from_filter( string $email ): string {
if ( ! empty( $this->from ) && is_email( $this->from ) ) { if ( ! empty( $this->from ) && is_email( $this->from ) ) {
$email = $this->from; $email = $this->from;
} }
...@@ -80,11 +133,6 @@ class EmailSender { ...@@ -80,11 +133,6 @@ class EmailSender {
} }
public function set_from_name( string $from_name ) {
$this->from_name = $from_name;
}
/** /**
* WordPress callback for setting the from name * WordPress callback for setting the from name
* *
...@@ -92,7 +140,7 @@ class EmailSender { ...@@ -92,7 +140,7 @@ class EmailSender {
* *
* @return string * @return string
*/ */
public function from_name( $name ) { public function from_name_filter( string $name ): string {
if ( ! empty( $this->from_name ) ) { if ( ! empty( $this->from_name ) ) {
$name = html_entity_decode( sanitize_text_field( $this->from_name ) ); $name = html_entity_decode( sanitize_text_field( $this->from_name ) );
} }
...@@ -105,41 +153,40 @@ class EmailSender { ...@@ -105,41 +153,40 @@ class EmailSender {
* *
* @return void * @return void
*/ */
private function before_wp_mail() { protected function before_wp_mail() {
add_filter( 'wp_mail_from', array( $this, 'from' ) ); add_filter( 'wp_mail_from', array( $this, 'from_filter' ) );
add_filter( 'wp_mail_from_name', array( $this, 'from_name' ) ); add_filter( 'wp_mail_from_name', array( $this, 'from_name_filter' ) );
} }
public function send( array $placeholders = [] ) { public function send() {
$this->before_wp_mail();
foreach ( $this->get_emails() as $email ) { foreach ( $this->get_emails() as $email ) {
$template = $this->get_template( $email, $placeholders ); $subject = $this->replace_placeholders( $email->get_subject() );
if ( $email->get_is_enable() ) { if ( $email->get_is_enable() ) {
$this->before_wp_mail();
wp_mail( wp_mail(
$email->get_recipients(), $email->get_subject(), $this->css_inline( $template ), $email->get_headers(), $email->get_attachments() $email->get_recipients(), $subject, $this->get_body( $email ), $email->get_headers(), $email->get_attachments()
); );
$this->after_wp_mail();
} }
} }
$this->after_wp_mail();
} }
public function get_template( EmailInterface $email, $placeholders = [] ): string { protected function get_body( EmailInterface $email ): string {
$content = $this->replace_placeholders( $email->get_content(), $placeholders ); $content = $this->replace_placeholders( $email->get_content() );
$output = $this->renderer->render( 'html/email-header', [ 'heading' => $email->get_heading(), 'logo' => '' ] ); $output = $this->renderer->render( 'html/email-header', [ 'heading' => $email->get_heading(), 'logo' => '' ] );
$output .= $this->renderer->render( 'html/' . $email->get_id(), [ 'content' => $content ] ); $output .= $this->renderer->render( 'html/' . $email->get_id(), [ 'content' => $content ] );
$output .= $this->renderer->render( 'html/email-footer', [] ); $output .= $this->renderer->render( 'html/email-footer', [ 'footer' => '' ] );
return $output; return $this->css_inline( $output );
} }
/** /**
* @param string $content * @param string $string
* @param array $placeholders
* *
* @return array|string|string[] * @return array|string|string[]
*/ */
private function replace_placeholders( string $content, array $placeholders = [] ): string { protected function replace_placeholders( string $string ): string {
return str_replace( array_keys( $placeholders ), array_values( $placeholders ), $content ); return (string) str_replace( array_keys( $this->placeholders ), array_values( $this->placeholders ), $string );
} }
/** /**
...@@ -147,10 +194,10 @@ class EmailSender { ...@@ -147,10 +194,10 @@ class EmailSender {
* *
* @return mixed|string * @return mixed|string
*/ */
private function css_inline( string $content ): string { protected function css_inline( string $content ): string {
$styles = $this->renderer->render( 'html/email-styles', [] ); $styles = $this->renderer->render( 'html/email-styles', [] );
return HTMLDecorator::style_inline( $content, $styles ); return HTML::style_inline( $content, $styles );
} }
/** /**
...@@ -158,9 +205,9 @@ class EmailSender { ...@@ -158,9 +205,9 @@ class EmailSender {
* *
* @return void * @return void
*/ */
private function after_wp_mail() { protected function after_wp_mail() {
remove_filter( 'wp_mail_from', array( $this, 'from' ) ); remove_filter( 'wp_mail_from', array( $this, 'from_filter' ) );
remove_filter( 'wp_mail_from_name', array( $this, 'from_name' ) ); remove_filter( 'wp_mail_from_name', array( $this, 'from_name_filter' ) );
} }
} }
<?php
declare( strict_types=1 );
namespace WPDesk\Library\WPEmail;
class MailerException extends \RuntimeException {
public static function with_wp_error( \WP_Error $error ): self {
$errors = $error->get_error_messages( 'wp_mail_failed' );
$message = implode( "\n", $errors );
return new self( sprintf( 'wp_mail() failure. Message [%s]', $message ) );
}
}
...@@ -40,7 +40,7 @@ defined( 'ABSPATH' ) || exit; ...@@ -40,7 +40,7 @@ defined( 'ABSPATH' ) || exit;
<table border="0" cellpadding="10" cellspacing="0" width="100%"> <table border="0" cellpadding="10" cellspacing="0" width="100%">
<tr> <tr>
<td colspan="2" valign="middle" id="credit"> <td colspan="2" valign="middle" id="credit">
<?php echo wp_kses_post( wpautop( wptexturize( apply_filters( 'wpdesk/wp_mail/template/footer', '' ) ) ) ); ?> <?php echo $params['footer'] ?? ''; ?>
</td> </td>
</tr> </tr>
</table> </table>
......
...@@ -47,7 +47,7 @@ $params = isset( $params ) ? $params : []; ...@@ -47,7 +47,7 @@ $params = isset( $params ) ? $params : [];
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="template_header"> <table border="0" cellpadding="0" cellspacing="0" width="100%" id="template_header">
<tr> <tr>
<td id="header_wrapper"> <td id="header_wrapper">
<h1><?php echo $params['heading']; ?></h1> <h1><?php echo $params['heading'] ?? 'Header'; ?></h1>
</td> </td>
</tr> </tr>
</table> </table>
......
<?php <?php
/**
* Email Styles use WPDesk\Library\WPEmail\Helpers\Template;
*
* This template can be overridden by copying it to yourtheme/woocommerce/emails/email-styles.php. $params = isset( $params ) ? $params : [];
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce\Templates\Emails
* @version 4.0.0
*/
if ( ! defined( 'ABSPATH' ) ) { if ( ! defined( 'ABSPATH' ) ) {
exit; exit;
} }
// Load colors. // Load colors.
$bg = get_option( 'woocommerce_email_background_color' ); $bg = $params['bg'] ?? '#f9f9f9';
$body = get_option( 'woocommerce_email_body_background_color' ); $body = $params['body'] ?? '#ffffff';
$base = get_option( 'woocommerce_email_base_color' ); $base = $params['base'] ?? '#d15291';
$base_text = wc_light_or_dark( $base, '#202020', '#ffffff' ); $base_text = Template::light_or_dark( $base, '#171717', '#ffffff' );
$text = get_option( 'woocommerce_email_text_color' ); $text = $params['text'] ?? '#303030';
// Pick a contrasting color for links. // Pick a contrasting color for links.
$link_color = wc_hex_is_light( $base ) ? $base : $base_text; $link_color = Template::is_hex_light( $base ) ? $base : $base_text;
if ( wc_hex_is_light( $body ) ) { if ( Template::is_hex_light( $body ) ) {
$link_color = wc_hex_is_light( $base ) ? $base_text : $base; $link_color = Template::is_hex_light( $base ) ? $base_text : $base;
} }
$bg_darker_10 = wc_hex_darker( $bg, 10 ); $bg_darker_10 = Template::hex_darker( $bg, 10 );
$body_darker_10 = wc_hex_darker( $body, 10 ); $body_darker_10 = Template::hex_darker( $body, 10 );
$base_lighter_20 = wc_hex_lighter( $base, 20 ); $base_lighter_20 = Template::hex_lighter( $base, 20 );
$base_lighter_40 = wc_hex_lighter( $base, 40 ); $base_lighter_40 = Template::hex_lighter( $base, 40 );
$text_lighter_20 = wc_hex_lighter( $text, 20 ); $text_lighter_20 = Template::hex_lighter( $text, 20 );
$text_lighter_40 = wc_hex_lighter( $text, 40 ); $text_lighter_40 = Template::hex_lighter( $text, 40 );
// !important; is a gmail hack to prevent styles being stripped if it doesn't like something. // !important; is a gmail hack to prevent styles being stripped if it doesn't like something.
// body{padding: 0;} ensures proper scale/positioning of the email in the iOS native email app. // body{padding: 0;} ensures proper scale/positioning of the email in the iOS native email app.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment