Skip to content

Commit

Permalink
Merge pull request pentacent#62 from pentacent/feature/improved-outlo…
Browse files Browse the repository at this point in the history
…ok-compatibility

This PR improves display of templates in MS Outlook and derived clients.
  • Loading branch information
wmnnd authored Jul 3, 2021
2 parents 6419894 + b8a4acd commit efcf91b
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 38 deletions.
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,36 @@
# Changelog

## Version 0.5.2

### Fixed
- Fixed broken styling on non-authenticated routes


## Version 0.5.1

### Added
- Improved onboarding experience with empty states for all views

### Changed
- Improved dark app design
- Stricter code-checks in CI

### Fixed
- Default template is now displayed correctly in campaign editor
- Paddle webhooks now have improved idempotency


## Version 0.5.0

## Added
### Added
- Added click/open tracking for campaign emails
- `Precedence: Bulk` header now included in all campaign emails
- Implemented per-instance `SharedSenders`
- Implemented Shared Senders for AWS SES
- Added account and account credits for organizing users and implementing quotas
- Added subscription plans for app.keila.io

## Changed
### Changed
- Updated to Elixir 1.12

### Fixed
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ repository:

Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.

## Translating Keila

Keila uses GNU Gettext for translations. Translation files are found in
`priv/gettext`.

## Extract
mix gettext.extract

## The Name
Keila is the name of the elephant mascot of this project.
She’s a wise and diligent elephant lady, able to remember countless email
Expand Down
3 changes: 2 additions & 1 deletion lib/keila/mailings/builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ defmodule Keila.Mailings.Builder do
html_body =
html_body
|> Html.parse_document!()
|> Html.apply_inline_styles(styles)
|> Html.apply_email_markup()
|> Html.apply_inline_styles(styles, ignore_inherit: true)
|> Html.to_document()

email
Expand Down
18 changes: 10 additions & 8 deletions lib/keila/templates/default_template.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,16 @@ defmodule Keila.Templates.DefaultTemplate do
"text-decoration"
])},
{gettext("Button"),
get_styles.("h4 > a", [
"color",
"background-color",
"font-family",
"font-style",
"font-weight",
"text-decoration"
])},
get_styles.("h4>a, div.keila-button a", [
"color"
]) ++
get_styles.("h4>a, div.keila-button", [
"background-color",
"font-family",
"font-style",
"font-weight",
"text-decoration"
])},
{gettext("Signature"),
get_styles.("#signature td", [
"color",
Expand Down
24 changes: 21 additions & 3 deletions lib/keila/templates/html.ex
Original file line number Diff line number Diff line change
Expand Up @@ -123,19 +123,20 @@ defmodule Keila.Templates.Html do
~s{<span style="color:blue">foo</span>}
"""
@spec apply_inline_styles(t(), Css.t()) :: t()
def apply_inline_styles(html, css_list) do
def apply_inline_styles(html, css_list, opts \\ []) do
css_list
|> Enum.reduce(html, fn {selector, styles}, html ->
Floki.find_and_update(html, selector, fn {tag, attributes} ->
attributes = put_inline_styles(attributes, styles)
attributes = put_inline_styles(attributes, styles, opts)
{tag, attributes}
end)
end)
end

defp put_inline_styles(attributes, styles) do
defp put_inline_styles(attributes, styles, opts) do
styles =
styles
|> Enum.filter(fn {_key, value} -> value != "inherit" || !opts[:ignore_inherit] end)
|> Enum.map(fn {key, value} -> "#{key}:#{value}" end)
|> Enum.join(";")

Expand All @@ -149,4 +150,21 @@ defmodule Keila.Templates.Html do
attributes ++ [{"style", styles}]
end
end

@doc """
Apply markup transforms for improved email client compatibility.
## Transforms:
- `h4 a` -> `div.keila-button a`
"""
@spec apply_email_markup(t()) :: t()
def apply_email_markup(html) do
Floki.traverse_and_update(html, &do_apply_email_markup/1)
end

defp do_apply_email_markup({"h4", _, [{"a", a_attrs, a_children}]}) do
{"div", [{"class", "keila-button"}], [{"a", a_attrs, a_children}]}
end

defp do_apply_email_markup(other), do: other
end
3 changes: 1 addition & 2 deletions lib/keila_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ defmodule KeilaWeb.Endpoint do
at: "/",
from: :keila,
gzip: false,
only:
~w(css fonts images js favicon.ico robots.txt keila_import_template.csv keila_import_template.ods)
only: ~w(css fonts images js favicon.ico robots.txt downloads)

# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
Expand Down
13 changes: 9 additions & 4 deletions lib/keila_web/live/campaign_edit_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,15 @@ defmodule KeilaWeb.CampaignEditLive do
styles =
Keila.Templates.Css.merge(default_styles, template_styles)
|> Enum.map(fn {selector, styles} ->
if String.starts_with?(selector, "body") do
{String.replace(selector, "body", "#wysiwyg .editor"), styles}
else
{"#wysiwyg .ProseMirror " <> selector, styles}
cond do
String.starts_with?(selector, "body") ->
{String.replace(selector, "body", "#wysiwyg .editor"), styles}

String.starts_with?(selector, "#content") ->
{String.replace(selector, "#content", "#wysiwyg .editor .ProseMirror"), styles}

true ->
{"#wysiwyg .ProseMirror " <> selector, styles}
end
end)
|> Keila.Templates.Css.encode()
Expand Down
9 changes: 5 additions & 4 deletions lib/keila_web/templates/layout/_header.html.leex
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@
</header>
<% else %>
<header class="bg-gray-900">
<div class="container text-center my-4 text-green-500">
<a href="/">
h-10 w-auto -mt-1 -mr-1 inline-block
<%= render_icon(:logo) %>
<div class="container my-4 text-green-500 flex justify-center">
<a href="/" class="inline-flex items-start">
<span class="flex h-11 w-11 -mr-1">
<%= render_icon(:logo) %>
</span>
<span class="text-3xl font-light">Keila</span>
</a>
</div>
Expand Down
40 changes: 32 additions & 8 deletions priv/email_templates/default.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
a {
color: #1d4ed8;
text-decoration: underline;
}

h1 {
margin: 0 0 20px 0;
color: #4b5563;
Expand All @@ -9,10 +14,20 @@ h1 {

h2 {
margin: 0 0 20px 0;
color: #4b5563;
font-family: inherit;
font-style: normal;
font-weight: bold;
text-decoration: none;
}

h3 {
margin: 0 0 20px 0;
color: #4b5563;
font-family: inherit;
font-style: normal;
font-weight: bold;
text-decoration: none;
}

ul {
Expand All @@ -29,18 +44,26 @@ h4 {
margin: 20px 0;
}

a {
color: #1d4ed8;
text-decoration: underline;
}

h4 > a {
color: #ffffff;
h4 > a, div.keila-button {
background-color: #1d4ed8;
padding: 10px;
display: block;
cursor: pointer;
text-align: center;
border-radius: 5px;
line-height: 40px;
}

h4 > a, div.keila-button a {
display: block;
color: #ffffff;
text-decoration: none;
padding: 0 10px;
}

div.keila-button {
mso-line-height-rule: exactly;
mso-text-raise: 7px;
mso-padding-alt: 0 10px;
}

body, #center-wrapper, #table-wrapper {
Expand All @@ -55,6 +78,7 @@ body, #center-wrapper, #table-wrapper {
}

#content img {
display: inline-block;
width: 100%;
max-width: 600px;
}
Expand Down
6 changes: 1 addition & 5 deletions priv/email_templates/default.html.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,11 @@
2. center tag: for Gmail and Inbox mobile apps and web versions of Gmail, GSuite, Inbox, Yahoo, AOL, Libero, Comcast, freenet, Mail.ru, Orange.fr
3. mso conditional: For Windows 10 Mail
-->
<body width="100%" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly;">
<body width="100%" style="margin: 0; padding: 0 !important;">
<center id="center-wrapper" role="article" aria-roledescription="email" lang="en" style="width: 100%;">
<!--[if mso | IE]>
<table id="table-wrapper" role="presentation" border="0" cellpadding="0" cellspacing="0" width="100%">
<tr>
<td>
<![endif]-->

<!-- Visually Hidden Preheader Text : BEGIN
<div style="max-height:0; overflow:hidden; mso-hide:all;" aria-hidden="true">
Expand Down Expand Up @@ -268,11 +266,9 @@
<![endif]-->
</div>

<!--[if mso | IE]>
</td>
</tr>
</table>
<![endif]-->
</center>
</body>
</html>
13 changes: 12 additions & 1 deletion test/keila/templates/templates_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ defmodule Keila.TemplatesTest do
@input_css """
.foo {
background-color: #f0f;
font-family: inherit;
}
div {
padding: 10px;
Expand All @@ -34,7 +35,7 @@ defmodule Keila.TemplatesTest do
"""

@expected_html """
<div style="margin: 10px;background-color:#f0f;padding:10px" class="foo">
<div style="margin: 10px;background-color:#f0f;font-family:inherit;padding:10px" class="foo">
<a href="#" style="color:blue">Link</a>
<a href="#" style="color:blue">Another Link</a>
</div>
Expand All @@ -48,6 +49,16 @@ defmodule Keila.TemplatesTest do
assert @expected_html == Html.apply_inline_styles(html, css) |> Html.to_fragment()
end

@tag :templates
test "inline css with opts" do
html = Html.parse_fragment!(@input_html)
css = Css.parse!(@input_css)
expected_html_without_inherit = @expected_html |> String.replace("font-family:inherit;", "")

assert expected_html_without_inherit ==
Html.apply_inline_styles(html, css, ignore_inherit: true) |> Html.to_fragment()
end

@tag :templates
test "get CSS values by selector and property" do
css = Css.parse!(@input_css)
Expand Down

0 comments on commit efcf91b

Please sign in to comment.