From 31ffadbc4c207ce2c1fd9860302b627406629c8c Mon Sep 17 00:00:00 2001 From: Samuel Georges Date: Sat, 22 Jul 2017 17:20:48 +1000 Subject: [PATCH] Introduce mail branding settings --- modules/system/ServiceProvider.php | 9 + .../mailbrandsettings/mailbrandsettings.css | 4 + .../js/mailbrandsettings/mailbrandsettings.js | 40 +++ modules/system/classes/MailManager.php | 145 +++++++-- modules/system/classes/MarkupManager.php | 10 +- .../system/controllers/MailBrandSettings.php | 79 +++++ .../mailbrandsettings/_field_mail_preview.htm | 11 + .../mailbrandsettings/config_form.yaml | 15 + .../controllers/mailbrandsettings/index.htm | 68 +++++ modules/system/lang/en/lang.php | 4 + modules/system/models/MailBrandSetting.php | 156 ++++++++++ modules/system/models/MailLayout.php | 68 +++-- modules/system/models/MailPartial.php | 48 +-- modules/system/models/MailTemplate.php | 1 + .../models/mailbrandsetting/custom.less | 285 ++++++++++++++++++ .../models/mailbrandsetting/fields.yaml | 122 ++++++++ .../mailbrandsetting/sample_template.htm | 55 ++++ 17 files changed, 1042 insertions(+), 78 deletions(-) create mode 100644 modules/system/assets/css/mailbrandsettings/mailbrandsettings.css create mode 100644 modules/system/assets/js/mailbrandsettings/mailbrandsettings.js create mode 100644 modules/system/controllers/MailBrandSettings.php create mode 100644 modules/system/controllers/mailbrandsettings/_field_mail_preview.htm create mode 100644 modules/system/controllers/mailbrandsettings/config_form.yaml create mode 100644 modules/system/controllers/mailbrandsettings/index.htm create mode 100644 modules/system/models/MailBrandSetting.php create mode 100644 modules/system/models/mailbrandsetting/custom.less create mode 100644 modules/system/models/mailbrandsetting/fields.yaml create mode 100644 modules/system/models/mailbrandsetting/sample_template.htm diff --git a/modules/system/ServiceProvider.php b/modules/system/ServiceProvider.php index 05d1e9f87..fe05f1de9 100644 --- a/modules/system/ServiceProvider.php +++ b/modules/system/ServiceProvider.php @@ -435,6 +435,15 @@ class ServiceProvider extends ModuleServiceProvider 'permissions' => ['system.manage_mail_settings'], 'order' => 620 ], + 'mail_brand_settings' => [ + 'label' => 'system::lang.mail_brand.menu_label', + 'description' => 'system::lang.mail_brand.menu_description', + 'category' => SettingsManager::CATEGORY_MAIL, + 'icon' => 'icon-paint-brush', + 'url' => Backend::url('system/mailbrandsettings'), + 'permissions' => ['system.manage_mail_settings'], + 'order' => 630 + ], 'event_logs' => [ 'label' => 'system::lang.event_log.menu_label', 'description' => 'system::lang.event_log.menu_description', diff --git a/modules/system/assets/css/mailbrandsettings/mailbrandsettings.css b/modules/system/assets/css/mailbrandsettings/mailbrandsettings.css new file mode 100644 index 000000000..042ea750f --- /dev/null +++ b/modules/system/assets/css/mailbrandsettings/mailbrandsettings.css @@ -0,0 +1,4 @@ +.field-colorpicker { + float: right; + margin-top: -10px; +} diff --git a/modules/system/assets/js/mailbrandsettings/mailbrandsettings.js b/modules/system/assets/js/mailbrandsettings/mailbrandsettings.js new file mode 100644 index 000000000..5c02b5f49 --- /dev/null +++ b/modules/system/assets/js/mailbrandsettings/mailbrandsettings.js @@ -0,0 +1,40 @@ + +$(document).on('change', '.field-colorpicker', function() { + $('#brandSettingsForm').request('onRefresh', { + data: { 'fields': ['_mail_preview'] } + }) +}) + +function createPreviewContainer(el, content) { + var newiframe + + // Shadow DOM ignores media queries + // if (document.body.attachShadow) { + if (false) { + var shadow = el.attachShadow({ mode: 'open' }) + shadow.innerHTML = content + } + else { + newiframe = document.createElement('iframe') + + 'srcdoc' in newiframe + ? newiframe.srcdoc = content + : newiframe.src = 'data:text/html;charset=UTF-8,' + content + + var parent = el.parentNode + parent.replaceChild(newiframe, el) + + newiframe.style.width = '100%' + newiframe.setAttribute('frameborder', 0) + + newiframe.onload = adjustIframeHeight + } + + function adjustIframeHeight() { + newiframe.style.height = '500px' + newiframe.style.height = 1 + (newiframe.contentWindow.document.getElementsByTagName('body')[0].scrollHeight) +'px' + } + + $(document).render(adjustIframeHeight) + $(window).resize(adjustIframeHeight) +} diff --git a/modules/system/classes/MailManager.php b/modules/system/classes/MailManager.php index 1801e04c9..574c90700 100644 --- a/modules/system/classes/MailManager.php +++ b/modules/system/classes/MailManager.php @@ -4,6 +4,7 @@ use Twig; use Markdown; use System\Models\MailPartial; use System\Models\MailTemplate; +use System\Models\MailBrandSetting; use System\Helpers\View as ViewHelper; use System\Classes\PluginManager; use System\Classes\MarkupManager; @@ -48,7 +49,12 @@ class MailManager /** * @var bool Internal marker for rendering mode */ - protected $isHtmlRenderMode; + protected $isHtmlRenderMode = false; + + /** + * @var bool Internal marker for booting custom twig extensions. + */ + protected $isTwigStarted = false; /** * This function hijacks the `addContent` method of the `October\Rain\Mail\Mailer` @@ -66,12 +72,7 @@ class MailManager /* * Start twig transaction */ - $markupManager = MarkupManager::instance(); - $markupManager->beginTransaction(); - $markupManager->registerTokenParsers([ - new MailPartialTokenParser, - new MailComponentTokenParser - ]); + $this->startTwig(); /* * Inject global view variables @@ -84,55 +85,98 @@ class MailManager /* * Subject */ - $customSubject = $message->getSwiftMessage()->getSubject(); - if (empty($customSubject)) { + $swiftMessage = $message->getSwiftMessage(); + + if (empty($swiftMessage->getSubject())) { $message->subject(Twig::parse($template->subject, $data)); } + if (!isset($data['subject'])) { + $data['subject'] = $swiftMessage->getSubject(); + } + /* * HTML contents */ - $html = $this->renderHtmlContents($template, $data); + $html = $this->renderTemplate($template, $data); $message->setBody($html, 'text/html'); /* * Text contents */ - $text = $this->renderTextContents($template, $data); + $text = $this->renderTextTemplate($template, $data); $message->addPart($text, 'text/plain'); /* * End twig transaction */ - $markupManager->endTransaction(); + $this->stopTwig(); } // // Rendering // - public function renderHtmlContents($template, $data) + /** + * Render the Markdown template into HTML. + * + * @param string $content + * @param array $data + * @return string + */ + public function render($content, $data = []) + { + if (!$content) { + return ''; + } + + $html = $this->renderTwig($content, $data); + + $html = Markdown::parse($html); + + return $html; + } + + public function renderTemplate($template, $data = []) { $this->isHtmlRenderMode = true; - $templateHtml = $template->content_html; - - $html = Twig::parse($templateHtml, $data); - $html = Markdown::parse($html); + $html = $this->render($template->content_html, $data); if ($template->layout) { - $html = Twig::parse($template->layout->content_html, [ + $html = $this->renderTwig($template->layout->content_html, [ 'content' => $html, - 'css' => $template->layout->content_css + 'css' => $template->layout->content_css, + 'brandCss' => MailBrandSetting::compileCss() ] + (array) $data); } return $html; } - public function renderTextContents($template, $data) + /** + * Render the Markdown template into text. + * + * @param string $view + * @param array $data + * @return string + */ + public function renderText($content, $data = []) + { + if (!$content) { + return ''; + } + + $text = $this->renderTwig($content, $data); + + $text = html_entity_decode(preg_replace("/[\r\n]{2,}/", "\n\n", $text), ENT_QUOTES, 'UTF-8'); + + return $text; + } + + public function renderTextTemplate($template, $data = []) { $this->isHtmlRenderMode = false; @@ -142,9 +186,10 @@ class MailManager $templateText = $template->content_html; } - $text = Twig::parse($templateText, $data); + $text = $this->renderText($templateText, $data); + if ($template->layout) { - $text = Twig::parse($template->layout->content_text, [ + $text = $this->renderTwig($template->layout->content_text, [ 'content' => $text ] + (array) $data); } @@ -166,7 +211,7 @@ class MailManager } } - public function renderHtmlPartial($partial, $params) + protected function renderHtmlPartial($partial, $params) { $content = $partial->content_html; @@ -174,12 +219,12 @@ class MailManager return ''; } - $params['body'] = Markdown::parse(array_get($params, 'body')); + $params['body'] = array_get($params, 'body'); - return Twig::parse($content, $params); + return $this->renderTwig($content, $params); } - public function renderTextPartial($partial, $params) + protected function renderTextPartial($partial, $params) { $content = $partial->content_text ?: $partial->content_html; @@ -187,7 +232,53 @@ class MailManager return ''; } - return Twig::parse($content, $params); + return $this->renderTwig($content, $params); + } + + /** + * Internal helper for rendering Twig + */ + protected function renderTwig($content, $data = []) + { + if ($this->isTwigStarted) { + return Twig::parse($content, $data); + } + + $this->startTwig(); + + $result = Twig::parse($content, $data); + + $this->stopTwig(); + + return $result; + } + + protected function startTwig() + { + if ($this->isTwigStarted) { + return; + } + + $this->isTwigStarted = true; + + $markupManager = MarkupManager::instance(); + $markupManager->beginTransaction(); + $markupManager->registerTokenParsers([ + new MailPartialTokenParser, + new MailComponentTokenParser + ]); + } + + protected function stopTwig() + { + if (!$this->isTwigStarted) { + return; + } + + $markupManager = MarkupManager::instance(); + $markupManager->endTransaction(); + + $this->isTwigStarted = false; } // diff --git a/modules/system/classes/MarkupManager.php b/modules/system/classes/MarkupManager.php index 79fc8916b..9d169f277 100644 --- a/modules/system/classes/MarkupManager.php +++ b/modules/system/classes/MarkupManager.php @@ -39,7 +39,7 @@ class MarkupManager /** * @var array Transaction based extension items */ - protected $tranItems; + protected $transactionItems; /** * @var bool Manager is in transaction mode @@ -114,7 +114,7 @@ class MarkupManager */ public function registerExtensions($type, array $definitions) { - $items = $this->transactionMode ? 'tranItems' : 'items'; + $items = $this->transactionMode ? 'transactionItems' : 'items'; if (is_null($this->$items)) { $this->$items = []; @@ -182,8 +182,8 @@ class MarkupManager $results = $this->items[$type]; } - if ($this->tranItems !== null && isset($this->tranItems[$type])) { - $results = array_merge($results, $this->tranItems[$type]); + if ($this->transactionItems !== null && isset($this->transactionItems[$type])) { + $results = array_merge($results, $this->transactionItems[$type]); } return $results; @@ -381,6 +381,6 @@ class MarkupManager { $this->transactionMode = false; - $this->tranItems = null; + $this->transactionItems = null; } } diff --git a/modules/system/controllers/MailBrandSettings.php b/modules/system/controllers/MailBrandSettings.php new file mode 100644 index 000000000..b0539a5b6 --- /dev/null +++ b/modules/system/controllers/MailBrandSettings.php @@ -0,0 +1,79 @@ +pageTitle = 'Customize mail appearance'; + + BackendMenu::setContext('October.System', 'system', 'settings'); + SettingsManager::setContext('October.System', 'mail_brand_settings'); + } + + public function index() + { + $this->addJs('/modules/system/assets/js/mailbrandsettings/mailbrandsettings.js', 'core'); + $this->addCss('/modules/system/assets/css/mailbrandsettings/mailbrandsettings.css', 'core'); + + $setting = MailBrandSetting::instance(); + + if ($setting->exists) { + return $this->update($setting->id); + } + else { + return $this->create(); + } + } + + public function renderSampleMessage() + { + $layout = new MailLayout; + $layout->fillFromCode('default'); + + $template = new MailTemplate; + $template->layout = $layout; + $template->content_html = File::get(base_path('modules/system/models/mailbrandsetting/sample_template.htm')); + + return MailManager::instance()->renderTemplate($template); + } + + public function formCreateModelObject() + { + return MailBrandSetting::instance(); + } +} diff --git a/modules/system/controllers/mailbrandsettings/_field_mail_preview.htm b/modules/system/controllers/mailbrandsettings/_field_mail_preview.htm new file mode 100644 index 000000000..4db8ba03c --- /dev/null +++ b/modules/system/controllers/mailbrandsettings/_field_mail_preview.htm @@ -0,0 +1,11 @@ +
+ + + diff --git a/modules/system/controllers/mailbrandsettings/config_form.yaml b/modules/system/controllers/mailbrandsettings/config_form.yaml new file mode 100644 index 000000000..0d8bec6b8 --- /dev/null +++ b/modules/system/controllers/mailbrandsettings/config_form.yaml @@ -0,0 +1,15 @@ +# =================================== +# Form Behavior Config +# =================================== + +# Record name +name: system::lang.mail_brand.menu_label + +# Fields are defined by extension +form: ~/modules/system/models/mailbrandsetting/fields.yaml + +# Model Class name +modelClass: System\Models\MailBrandSetting + +# Default redirect location +defaultRedirect: system/themes diff --git a/modules/system/controllers/mailbrandsettings/index.htm b/modules/system/controllers/mailbrandsettings/index.htm new file mode 100644 index 000000000..99e4331c3 --- /dev/null +++ b/modules/system/controllers/mailbrandsettings/index.htm @@ -0,0 +1,68 @@ + + + + +fatalError): ?> + + +
+ +
+ formRenderOutsideFields() ?> + formRenderPrimaryTabs() ?> +
+ +
+
+ + + + + + +
+
+ +
+ + + +
formRenderSecondaryTabs() ?>
+ + + + 'brandSettingsForm', 'class'=>'layout stretch']) ?> + makeLayout('form-with-sidebar') ?> + + + + + +

fatalError)) ?>

+

+ + diff --git a/modules/system/lang/en/lang.php b/modules/system/lang/en/lang.php index 205845f22..6f69ca17d 100644 --- a/modules/system/lang/en/lang.php +++ b/modules/system/lang/en/lang.php @@ -224,6 +224,10 @@ return [ 'sending' => 'Sending test message...', 'return' => 'Return to template list' ], + 'mail_brand' => [ + 'menu_label' => 'Customize mail templates', + 'menu_description' => 'Modify the colors and appearance of mail templates.', + ], 'install' => [ 'project_label' => 'Attach to Project', 'plugin_label' => 'Install Plugin', diff --git a/modules/system/models/MailBrandSetting.php b/modules/system/models/MailBrandSetting.php new file mode 100644 index 000000000..a35e0311e --- /dev/null +++ b/modules/system/models/MailBrandSetting.php @@ -0,0 +1,156 @@ + $default) { + $this->{$var} = $config->get('brand.mail.'.Str::studly($var), $default); + } + } + + public function afterSave() + { + Cache::forget(self::CACHE_KEY); + } + + public static function renderCss() + { + if (Cache::has(self::CACHE_KEY)) { + return Cache::get(self::CACHE_KEY); + } + + try { + $customCss = self::compileCss(); + Cache::forever(self::CACHE_KEY, $customCss); + } + catch (Exception $ex) { + $customCss = '/* ' . $ex->getMessage() . ' */'; + } + + return $customCss; + } + + protected static function getCssVars() + { + $vars = [ + 'body_bg' => self::BODY_BG, + 'content_bg' => self::WHITE_COLOR, + 'content_inner_bg' => self::WHITE_COLOR, + 'button_text_color' => self::WHITE_COLOR, + 'button_primary_bg' => self::PRIMARY_BG, + 'button_positive_bg' => self::POSITIVE_BG, + 'button_negative_bg' => self::NEGATIVE_BG, + 'header_color' => self::HEADER_COLOR, + 'heading_color' => self::HEADING_COLOR, + 'text_color' => self::TEXT_COLOR, + 'link_color' => self::LINK_COLOR, + 'footer_color' => self::FOOTER_COLOR, + 'body_border_color' => self::BORDER_COLOR, + 'subcopy_border_color' => self::BORDER_COLOR, + 'table_border_color' => self::BORDER_COLOR, + 'panel_bg' => self::BORDER_COLOR, + 'promotion_bg' => self::WHITE_COLOR, + 'promotion_border_color' => self::PROMOTION_BORDER_COLOR, + ]; + + return $vars; + } + + protected static function makeCssVars() + { + $vars = static::getCssVars(); + + $result = []; + + foreach ($vars as $var => $default) { + // panel_bg -> panel-bg + $cssVar = str_replace('_', '-', $var); + + $result[$cssVar] = self::get($var, $default); + } + + return $result; + } + + public static function compileCss() + { + $parser = new Less_Parser(['compress' => true]); + $basePath = base_path('modules/system/models/mailbrandsetting'); + + $parser->ModifyVars(static::makeCssVars()); + + $parser->parse(File::get($basePath . '/custom.less')); + + $css = $parser->getCss(); + + return $css; + } +} diff --git a/modules/system/models/MailLayout.php b/modules/system/models/MailLayout.php index be5fadb75..ef108d8ec 100644 --- a/modules/system/models/MailLayout.php +++ b/modules/system/models/MailLayout.php @@ -79,45 +79,55 @@ class MailLayout extends Model continue; } - self::createLayoutFromFile($code, $path); + $layout = new static; + $layout->code = $code; + $layout->is_locked = true; + $layout->fillFromView($path); + $layout->save(); } } - /** - * Creates a layout using the contents of a specified file. - * @param string $code New Layout code - * @param string $viewPath View path - * @return void - */ - public static function createLayoutFromFile($code, $viewPath) + public function fillFromCode($code = null) { - $sections = self::getTemplateSections($viewPath); + $definitions = MailManager::instance()->listRegisteredLayouts(); - $name = array_get($sections, 'settings.name', '???'); - - $css = 'a, a:hover { - text-decoration: none; - color: #0862A2; - font-weight: bold; + if ($code === null) { + $code = $this->code; } - td, tr, th, table { - padding: 0px; - margin: 0px; + if (!$definition = array_get($definitions, $code)) { + throw new ApplicationException('Unable to find a registered layout with code: '.$code); } - p { - margin: 10px 0; - }'; + $this->fillFromView($definition); + } - self::create([ - 'is_locked' => true, - 'name' => $name, - 'code' => $code, - 'content_css' => $css, - 'content_html' => array_get($sections, 'html'), - 'content_text' => array_get($sections, 'text') - ]); + public function fillFromView($path) + { + $sections = self::getTemplateSections($path); + + $css = ' + @media only screen and (max-width: 600px) { + .inner-body { + width: 100% !important; + } + + .footer { + width: 100% !important; + } + } + + @media only screen and (max-width: 500px) { + .button { + width: 100% !important; + } + } + '; + + $this->name = array_get($sections, 'settings.name', '???'); + $this->content_css = $css; + $this->content_html = array_get($sections, 'html'); + $this->content_text = array_get($sections, 'text'); } protected static function getTemplateSections($code) diff --git a/modules/system/models/MailPartial.php b/modules/system/models/MailPartial.php index 4f0655262..09c38aeb6 100644 --- a/modules/system/models/MailPartial.php +++ b/modules/system/models/MailPartial.php @@ -43,6 +43,13 @@ class MailPartial extends Model 'content_html' => 'required', ]; + public function afterFetch() + { + if (!$this->is_custom) { + $this->fillFromCode(); + } + } + /** * Loops over each mail layout and ensures the system has a layout, * if the layout does not exist, it will create one. @@ -58,29 +65,36 @@ class MailPartial extends Model continue; } - self::createPartialFromFile($code, $path); + $partial = new static; + $partial->code = $code; + $partial->is_custom = 0; + $partial->fillFromView($path); + $partial->save(); } } - /** - * Creates a layout using the contents of a specified file. - * @param string $code New Partial code - * @param string $viewPath View path - * @return void - */ - public static function createPartialFromFile($code, $viewPath) + public function fillFromCode($code = null) { - $sections = self::getTemplateSections($viewPath); + $definitions = MailManager::instance()->listRegisteredPartials(); - $name = array_get($sections, 'settings.name', '???'); + if ($code === null) { + $code = $this->code; + } - self::create([ - 'name' => $name, - 'code' => $code, - 'is_custom' => 0, - 'content_html' => array_get($sections, 'html'), - 'content_text' => array_get($sections, 'text') - ]); + if (!$definition = array_get($definitions, $code)) { + throw new ApplicationException('Unable to find a registered partial with code: '.$code); + } + + $this->fillFromView($definition); + } + + public function fillFromView($path) + { + $sections = self::getTemplateSections($path); + + $this->name = array_get($sections, 'settings.name', '???'); + $this->content_html = array_get($sections, 'html'); + $this->content_text = array_get($sections, 'text'); } protected static function getTemplateSections($code) diff --git a/modules/system/models/MailTemplate.php b/modules/system/models/MailTemplate.php index 6f4c96222..bfb55bd6a 100644 --- a/modules/system/models/MailTemplate.php +++ b/modules/system/models/MailTemplate.php @@ -112,6 +112,7 @@ class MailTemplate extends Model public function fillFromView() { $sections = self::getTemplateSections($this->code); + $this->content_html = $sections['html']; $this->content_text = $sections['text']; $this->subject = array_get($sections, 'settings.subject', 'No subject'); diff --git a/modules/system/models/mailbrandsetting/custom.less b/modules/system/models/mailbrandsetting/custom.less new file mode 100644 index 000000000..f491d1717 --- /dev/null +++ b/modules/system/models/mailbrandsetting/custom.less @@ -0,0 +1,285 @@ +/* Base */ + +body, body *:not(html):not(style):not(br):not(tr):not(code) { + font-family: Avenir, Helvetica, sans-serif; + box-sizing: border-box; +} + +body { + background-color: @body-bg; + color: #74787E; + height: 100%; + hyphens: auto; + line-height: 1.4; + margin: 0; + -moz-hyphens: auto; + -ms-word-break: break-all; + width: 100% !important; + -webkit-hyphens: auto; + -webkit-text-size-adjust: none; + word-break: break-all; + word-break: break-word; +} + +p, +ul, +ol, +blockquote { + line-height: 1.4; + text-align: left; +} + +a { + color: @link-color; +} + +a img { + border: none; +} + +/* Typography */ + +h1 { + color: #2F3133; + font-size: 19px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h2 { + color: #2F3133; + font-size: 16px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +h3 { + color: #2F3133; + font-size: 14px; + font-weight: bold; + margin-top: 0; + text-align: left; +} + +p { + color: #74787E; + font-size: 16px; + line-height: 1.5em; + margin-top: 0; + text-align: left; +} + +p.sub { + font-size: 12px; +} + +img { + max-width: 100%; +} + +/* Layout */ + +.wrapper { + background-color: @body-bg; + margin: 0; + padding: 0; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +.content { + margin: 0; + padding: 0; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +/* Header */ + +.header { + padding: 25px 0; + text-align: center; +} + +.header a { + color: #bbbfc3; + font-size: 19px; + font-weight: bold; + text-decoration: none; + text-shadow: 0 1px 0 white; +} + +/* Body */ + +.body { + background-color: @content-bg; + border-bottom: 1px solid @body-border-color; + border-top: 1px solid @body-border-color; + margin: 0; + padding: 0; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +.inner-body { + background-color: @content-inner-bg; + margin: 0 auto; + padding: 0; + width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; +} + +/* Subcopy */ + +.subcopy { + border-top: 1px solid @subcopy-border-color; + margin-top: 25px; + padding-top: 25px; +} + +.subcopy p { + font-size: 12px; +} + +/* Footer */ + +.footer { + margin: 0 auto; + padding: 0; + text-align: center; + width: 570px; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 570px; +} + +.footer p { + color: #AEAEAE; + font-size: 12px; + text-align: center; +} + +/* Tables */ + +.table table { + margin: 30px auto; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +.table th { + border-bottom: 1px solid #EDEFF2; + padding-bottom: 8px; +} + +.table td { + color: #74787E; + font-size: 15px; + line-height: 18px; + padding: 10px 0; +} + +.content-cell { + padding: 35px; +} + +/* Buttons */ + +.action { + margin: 30px auto; + padding: 0; + text-align: center; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +.button { + border-radius: 3px; + box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); + color: @button-text-color; + display: inline-block; + text-decoration: none; + -webkit-text-size-adjust: none; +} + +.button-primary { + background-color: @button-primary-bg; + border-top: 10px solid @button-primary-bg; + border-right: 18px solid @button-primary-bg; + border-bottom: 10px solid @button-primary-bg; + border-left: 18px solid @button-primary-bg; +} + +.button-positive { + background-color: @button-positive-bg; + border-top: 10px solid @button-positive-bg; + border-right: 18px solid @button-positive-bg; + border-bottom: 10px solid @button-positive-bg; + border-left: 18px solid @button-positive-bg; +} + +.button-negative { + background-color: @button-negative-bg; + border-top: 10px solid @button-negative-bg; + border-right: 18px solid @button-negative-bg; + border-bottom: 10px solid @button-negative-bg; + border-left: 18px solid @button-negative-bg; +} + +/* Panels */ + +.panel { + margin: 0 0 21px; +} + +.panel-content { + background-color: #EDEFF2; + padding: 16px; +} + +.panel-item { + padding: 0; +} + +.panel-item p:last-of-type { + margin-bottom: 0; + padding-bottom: 0; +} + +/* Promotions */ + +.promotion { + background-color: #FFFFFF; + border: 2px dashed #9BA2AB; + margin: 0; + margin-bottom: 25px; + margin-top: 25px; + padding: 24px; + width: 100%; + -premailer-cellpadding: 0; + -premailer-cellspacing: 0; + -premailer-width: 100%; +} + +.promotion h1 { + text-align: center; +} + +.promotion p { + font-size: 15px; + text-align: center; +} diff --git a/modules/system/models/mailbrandsetting/fields.yaml b/modules/system/models/mailbrandsetting/fields.yaml new file mode 100644 index 000000000..a58ca61cb --- /dev/null +++ b/modules/system/models/mailbrandsetting/fields.yaml @@ -0,0 +1,122 @@ +# =================================== +# Field Definitions +# =================================== + +fields: + + _mail_preview: + type: partial + path: field_mail_preview + +secondaryTabs: + fields: + + _section_background: + label: Background + type: section + + body_bg: + label: Body background + type: colorpicker + availableColors: [] + + content_bg: + label: Content background + type: colorpicker + availableColors: [] + + content_inner_bg: + label: Inner content background + type: colorpicker + availableColors: [] + + _section_buttons: + label: Buttons + type: section + + button_text_color: + label: Button text color + type: colorpicker + availableColors: [] + + button_primary_bg: + label: Primary button background + type: colorpicker + availableColors: [] + + button_positive_bg: + label: Positive button background + type: colorpicker + availableColors: [] + + button_negative_bg: + label: Negative button background + type: colorpicker + availableColors: [] + + _section_type: + label: Typography + type: section + + header_color: + label: Site name color + type: colorpicker + availableColors: [] + + heading_color: + label: Heading color + type: colorpicker + availableColors: [] + + text_color: + label: Text color + type: colorpicker + availableColors: [] + + link_color: + label: Link color + type: colorpicker + availableColors: [] + + footer_color: + label: Footer color + type: colorpicker + availableColors: [] + + _section_borders: + label: Borders + type: section + + body_border_color: + label: Body border color + type: colorpicker + availableColors: [] + + subcopy_border_color: + label: Subcopy border color + type: colorpicker + availableColors: [] + + table_border_color: + label: Table border color + type: colorpicker + availableColors: [] + + _section_components: + label: Components + type: section + + panel_bg: + label: Panel background + type: colorpicker + availableColors: [] + + promotion_bg: + label: Promotion background + type: colorpicker + availableColors: [] + + promotion_border_color: + label: Promotion border color + type: colorpicker + availableColors: [] diff --git a/modules/system/models/mailbrandsetting/sample_template.htm b/modules/system/models/mailbrandsetting/sample_template.htm new file mode 100644 index 000000000..66fc96dc2 --- /dev/null +++ b/modules/system/models/mailbrandsetting/sample_template.htm @@ -0,0 +1,55 @@ +{% component 'header' %} +October CMS +{% endcomponent %} + +# Heading 1 + +This is a paragraph filled with Lorem Ipsum and a link. +Cumque dicta doloremque eaque, enim error laboriosam pariatur possimus tenetur veritatis voluptas. + +## Heading 2 + +{% component 'table' %} +| Item | Description | Price | +| ------------- |:-------------:| --------:| +| Item 1 | Centered | $10 | +| Item 2 | Right-Aligned | $20 | +{% endcomponent %} + +### Heading 3 + +This is a paragraph filled with Lorem Ipsum and a link. +Cumque dicta doloremque eaque, enim error laboriosam pariatur possimus tenetur veritatis voluptas. + +{% component 'button' url='javascript:;' %} +Primary button +{% endcomponent %} + +{% component 'button' type='positive' url='javascript:;' %} +Positive button +{% endcomponent %} + +{% component 'button' type='negative' url='javascript:;' %} +Negative button +{% endcomponent %} + +{% component 'panel' %} +How awesome is this panel? +{% endcomponent %} + +Some more text + +{% component 'promotion' %} +Coupon code: OCTOBER +{% endcomponent %} + +Thanks, +October CMS + +{% component 'subcopy' %} +This is the subcopy of the email +{% endcomponent %} + +{% component 'footer' %} +© 2017 October CMS. All rights reserved. +{% endcomponent %}