diff options
| author | Christian Weiske <cweiske@cweiske.de> | 2016-06-17 21:36:03 +0200 |
|---|---|---|
| committer | Christian Weiske <cweiske@cweiske.de> | 2016-06-17 21:36:03 +0200 |
| commit | d6b4a3d614aeaccf45fb047a3e990f77b935a333 (patch) | |
| tree | 9024ad23ba303097a33a179215ad1a1474cf0da5 | |
| parent | 5c88f5e19a69485da44e132e0b102b835eee7f05 (diff) | |
| download | bdrem-d6b4a3d614aeaccf45fb047a3e990f77b935a333.tar.gz bdrem-d6b4a3d614aeaccf45fb047a3e990f77b935a333.zip | |
Make CSS inline in HTML mails
| -rw-r--r-- | src/bdrem/Renderer/Html.php | 27 | ||||
| -rw-r--r-- | src/bdrem/Renderer/Mail.php | 139 |
2 files changed, 154 insertions, 12 deletions
diff --git a/src/bdrem/Renderer/Html.php b/src/bdrem/Renderer/Html.php index e802faf..10059ab 100644 --- a/src/bdrem/Renderer/Html.php +++ b/src/bdrem/Renderer/Html.php @@ -76,6 +76,7 @@ class Renderer_Html extends Renderer $tr = new Renderer_HtmlTable(); $table = $tr->render($arEvents); + $css = static::getCss(); $s = <<<HTM <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" @@ -84,7 +85,22 @@ class Renderer_Html extends Renderer <head> <title>bdrem</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -$links <style type="text/css"> +$links <style type="text/css">$css</style> + </head> + <body> +$table + </body> +</html> +HTM; + return $s; + } + + /** + * Get the CSS for the HTML table + */ + public static function getCss() + { + return <<<CSS table { border: 1px solid black; border-collapse: collapse; @@ -150,14 +166,7 @@ tr.d2 td.icon:before { tr.d3 td.icon:before { content: "\342\227\224" } - </style> - </head> - <body> -$table - </body> -</html> -HTM; - return $s; +CSS; } } ?> diff --git a/src/bdrem/Renderer/Mail.php b/src/bdrem/Renderer/Mail.php index d7aef3e..0efa3af 100644 --- a/src/bdrem/Renderer/Mail.php +++ b/src/bdrem/Renderer/Mail.php @@ -29,6 +29,18 @@ require_once 'Mail/mime.php'; class Renderer_Mail extends Renderer { /** + * Add HTML part to email + * @var bool + */ + public $html = true; + + /** + * CSS "inline" in tags, or "separate" in a style block + * @var string + */ + public $css = 'inline'; + + /** * Render the events - send out mails. * * Uses the config's "mail_to" array as recipients. @@ -52,8 +64,8 @@ class Renderer_Mail extends Renderer $subject .= ': ' . implode(', ', $todays); } - $rc = new Renderer_Console(); - $rh = new Renderer_Html(); + $rc = new Renderer_Console(); + $rht = new Renderer_HtmlTable(); $hdrs = array( 'From' => $this->config->get('mail_from', 'birthday@example.org'), @@ -69,7 +81,20 @@ class Renderer_Mail extends Renderer ); $mime->setTXTBody($rc->render($arEvents)); - $mime->setHTMLBody($rh->render($arEvents)); + if ($this->html) { + if ($this->css == 'inline') { + $html = $this->inlineCss( + $rht->render($arEvents), + Renderer_Html::getCss() + ); + } else { + $html = '<style type="text/css">' + . Renderer_Html::getCss() + . '</style>' + . $rht->render($arEvents); + } + $mime->setHTMLBody($this->minifyHtml($html)); + } $body = $mime->get(); $hdrs = $mime->headers($hdrs); @@ -107,5 +132,113 @@ class Renderer_Mail extends Renderer return mb_substr($str, 0, $len - 1) . '…'; } + + /** + * Takes the HTML and CSS code and inlines CSS into HTML. + * + * This is important for some e-mail clients which do + * not interpret <style> tags but only support inline styles. + * + * Works nicely with bdrem's CSS. If you need more CSS selector + * support, have a look at https://github.com/jjriv/emogrifier + * + * @param string $html HTML code + * @param string $css CSS code + * + * @return string HTML with inlined CSS + */ + protected function inlineCss($html, $css) + { + preg_match_all( + '#([^{]+) {([^}]+)}#m', + $css, + $parts + ); + $rules = array(); + foreach ($parts[1] as $key => $rule) { + $mrules = explode(',', $rule); + foreach ($mrules as $rule) { + $rule = trim($rule); + $style = trim($parts[2][$key]); + $rules[$rule] = preg_replace( + '#([:;]) +#', '\1', + str_replace( + ["\r", "\n", ' '], + ['', '', ' '], + $style + ) + ); + } + } + $sx = simplexml_load_string($html); + foreach ($rules as $rule => $style) { + $mode = null; + $parts = explode(' ', $rule); + $xp = ''; + foreach ($parts as $part) { + $part = trim($part); + if (strpos($part, ':') !== false) { + //.foo:before + list($part, $mode) = explode(':', $part); + if ($mode == 'hover') { + continue 2; + } + } + if (strpos($part, '.') === false) { + //tag only + if ($part == '') { + $xp = '//*'; + } else { + $xp .= '//' . $part; + } + } else { + //tag.class + list($tag, $class) = explode('.', $part); + if ($tag == '') { + $tag = '*'; + } + $xp .= '//' . $tag + . '[contains(' + . 'concat(" ", normalize-space(@class), " "), ' + . '" ' . $class . ' "' + . ')]'; + } + } + $res = $sx->xpath($xp); + //var_dump($res);die(); + //var_dump($xp, $style); + foreach ($res as $xelem) { + if ($mode === null) { + $xelem['style'] .= $style; + } else if ($mode == 'before') { + $xelem[0] = preg_replace( + '#content:\s*"(.+)"#', '\1', $style + ); + } + } + } + + $html = $sx->asXML(); + //strip xml header + $lines = explode("\n", $html); + unset($lines[0]); + $html = implode("\n", $lines); + + //echo $html . "\n";die(); + return $html; + } + + /** + * Remove whitespace between tags + * + * @param string $html HTML code + * + * @return string Smaller HTML code + */ + protected function minifyHtml($html) + { + $html = trim(preg_replace("#[\n\r ]+<#", '<', $html)); + return $html; + } } ?>
\ No newline at end of file |
