aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Weiske <cweiske@cweiske.de>2016-06-17 21:36:03 +0200
committerChristian Weiske <cweiske@cweiske.de>2016-06-17 21:36:03 +0200
commitd6b4a3d614aeaccf45fb047a3e990f77b935a333 (patch)
tree9024ad23ba303097a33a179215ad1a1474cf0da5
parent5c88f5e19a69485da44e132e0b102b835eee7f05 (diff)
downloadbdrem-d6b4a3d614aeaccf45fb047a3e990f77b935a333.tar.gz
bdrem-d6b4a3d614aeaccf45fb047a3e990f77b935a333.zip
Make CSS inline in HTML mails
-rw-r--r--src/bdrem/Renderer/Html.php27
-rw-r--r--src/bdrem/Renderer/Mail.php139
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