feat: free content with markdown + new flat theme + minor fixes (#9)

* feat: possibility to use markdown as content of email + use pretty table when generating fallback plain text e-mails
* feat: new flat theme
* fix: add a break word policy in actions troubleshooting section, in order to have a better responsive display when using really long Link URL on action
* feat: better layout of dt and dl in HTML template
* feat: pretty tables in plain text
This commit is contained in:
Mathieu Cornic 2017-05-31 20:36:16 +02:00 committed by GitHub
parent 6bf6103ca6
commit be1f57b7ab
32 changed files with 3533 additions and 441 deletions

View File

@ -334,3 +334,14 @@ In order to support generating RTL e-mails, inject the `textDirection` variable
<body dir="{{.Hermes.TextDirection}}">
```
## FreeMarkdown Injection
In order to support Markdown free content, inject the following code:
````html
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
[... Here is the templating for dictionary, table and actions]
{{ end }}
```

109
README.md
View File

@ -6,7 +6,7 @@
[![Godoc](https://godoc.org/github.com/matcornic/hermes?status.svg)](https://godoc.org/github.com/matcornic/hermes)
Hermes is the Go port of the great [mailgen](https://github.com/eladnava/mailgen) engine for Node.js. Check their work, it's awesome!
It's a package that generates clean, responsive HTML e-mails for sending transactional e-mails (welcome e-mails, reset password e-mails, receipt e-mails and so on).
It's a package that generates clean, responsive HTML e-mails for sending transactional e-mails (welcome e-mails, reset password e-mails, receipt e-mails and so on), and associated plain text fallback.
# Demo
@ -85,15 +85,35 @@ This code would output the following HTML template:
<img src="screens/demo.png" height="400" />
And the following plain text:
```
------------
Hi Jon Snow,
------------
Welcome to Hermes! We're very excited to have you on board.
To get started with Hermes, please click here: https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010
Need help, or have questions? Just reply to this email, we'd love to help.
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.
```
> Theme templates will be embedded in your application binary. If you want to use external templates (for configuration), use your own theme by implementing `hermes.Theme` interface with code searching for your files.
## More Examples
* [Welcome](examples/default/welcome.go)
* [Receipt](examples/default/receipt.go)
* [Password Reset](examples/default/reset.go)
* [Welcome](examples/welcome.go)
* [Receipt](examples/receipt.go)
* [Password Reset](examples/reset.go)
* [Maintenance](examples/maintenance.go)
To run the examples, go to `examples/<theme>/`, then run `go run *.go`. HTML and Plaintext example should be created in the folder.
To run the examples, go to `examples` folder, then run `go run *.go`. HTML and Plaintext example should be created in given theme folders.
## Plaintext E-mails
@ -115,6 +135,10 @@ The following open-source themes are bundled with this package:
<img src="screens/default/welcome.png" height="200" /> <img src="screens/default/reset.png" height="200" /> <img src="screens/default/receipt.png" height="200" />
* `flat`, slightly modified from [Postmark Transactional Email Templates](https://github.com/wildbit/postmark-templates)
<img src="screens/flat/welcome.png" height="200" /> <img src="screens/flat/reset.png" height="200" /> <img src="screens/flat/receipt.png" height="200" />
## RTL Support
To change the default text direction (left-to-right), simply override it as follows:
@ -247,11 +271,9 @@ email := hermes.Email{
}
```
> Note: Tables are currently not supported in plaintext versions of e-mails.
### Dictionary
To inject key-value pairs of data into the e-mail, supply the `dictionary` object as follows:
To inject key-value pairs of data into the e-mail, supply the `Dictionary` object as follows:
```go
email := hermes.Email{
@ -264,6 +286,77 @@ email := hermes.Email{
}
```
### Free Markdown
If you need more flexibility in the content of your generated e-mail, while keeping the same format than any other e-mail, use Markdown content. Supply the `FreeMarkdown` object as follows:
```go
email := hermes.Email{
Body: hermes.Body{
FreeMarkdown: `
> _Hermes_ service will shutdown the **1st August 2017** for maintenance operations.
Services will be unavailable based on the following schedule:
| Services | Downtime |
| :------:| :-----------: |
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
---
Feel free to contact us for any question regarding this matter at [support@hermes-example.com](mailto:support@hermes-example.com) or in our [Gitter](https://gitter.im/)
`,
},
}
}
```
This code would output the following HTML template:
<img src="screens/free-markdown.png" height="400" />
And the following plaintext:
```
------------
Hi Jon Snow,
------------
>
>
>
> Hermes service will shutdown the *1st August 2017* for maintenance
> operations.
>
>
Services will be unavailable based on the following schedule:
+-----------+------------+
| SERVICES | DOWNTIME |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+
Feel free to contact us for any question regarding this matter at support@hermes-example.com ( support@hermes-example.com ) or in our Gitter ( https://gitter.im/ )
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.
```
Be aware that this content will replace existing tables, dictionnary and actions. Only intros, outros, header and footer will be kept.
This is helpful when your application needs sending e-mails, wrote on-the-fly by adminstrators.
> Markdown is rendered with [Blackfriday](https://github.com/russross/blackfriday), so every thing Blackfriday can do, Hermes can do it as well.
## Troubleshooting
1. After sending multiple e-mails to the same Gmail / Inbox address, they become grouped and truncated since they contain similar text, breaking the responsive e-mail layout.

View File

@ -117,6 +117,10 @@ func (dt *Default) HTMLTemplate() string {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
@ -143,6 +147,28 @@ func (dt *Default) HTMLTemplate() string {
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
@ -155,6 +181,29 @@ func (dt *Default) HTMLTemplate() string {
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
/* Data table ------------------------------ */
.data-wrapper {
width: 100%;
@ -213,14 +262,14 @@ func (dt *Default) HTMLTemplate() string {
<body dir="{{.Hermes.TextDirection}}">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<!-- Logo -->
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="{{.Hermes.Product.Link}}" target="_blank">
{{ if .Hermes.Product.Logo }}
<img src="{{.Hermes.Product.Logo}}" class="email-logo" />
<img src="{{.Hermes.Product.Logo | url }}" class="email-logo" />
{{ else }}
{{ .Hermes.Product.Name }}
{{ end }}
@ -243,6 +292,9 @@ func (dt *Default) HTMLTemplate() string {
{{ end }}
{{ end }}
{{ end }}
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
{{ with .Email.Body.Dictionary }}
{{ if gt (len .) 0 }}
@ -326,6 +378,7 @@ func (dt *Default) HTMLTemplate() string {
{{ end }}
{{ end }}
{{ end }}
{{ with .Email.Body.Outros }}
{{ if gt (len .) 0 }}
{{ range $line := . }}
@ -340,6 +393,7 @@ func (dt *Default) HTMLTemplate() string {
{{.Hermes.Product.Name}}
</p>
{{ if (eq .Email.Body.FreeMarkdown "") }}
{{ with .Email.Body.Actions }}
<table class="body-sub">
<tbody>
@ -354,6 +408,7 @@ func (dt *Default) HTMLTemplate() string {
</tbody>
</table>
{{ end }}
{{ end }}
</td>
</tr>
</table>
@ -383,19 +438,58 @@ func (dt *Default) HTMLTemplate() string {
// PlainTextTemplate returns a Golang template that will generate an plain text email.
func (dt *Default) PlainTextTemplate() string {
return `{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }},{{ end }},
{{ with .Email.Body.Intros }}{{ range $line := . }}{{ $line }}{{ end }}{{ end }}
{{ with .Email.Body.Dictionary }}{{ range $entry := . }}
{{ $entry.Key }}: {{ $entry.Value }}{{ end }}{{ end }}
{{ with .Email.Body.Actions }} {{ range $action := . }}
{{ $action.Instructions }}
{{ $action.Button.Link }}{{ end }}{{ end }}
{{ with .Email.Body.Outros }} {{ range $line := . }}
{{ $line }}{{ end }}{{ end }}
return `<h2>{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }}{{ end }},</h2>
{{ with .Email.Body.Intros }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
{{ with .Email.Body.Dictionary }}
<ul>
{{ range $entry := . }}
<li>{{ $entry.Key }}: {{ $entry.Value }}</li>
{{ end }}
</ul>
{{ end }}
{{ with .Email.Body.Table }}
{{ $data := .Data }}
{{ $columns := .Columns }}
{{ if gt (len $data) 0 }}
<table class="data-table" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ $col := index $data 0 }}
{{ range $entry := $col }}
<th>{{ $entry.Key }} </th>
{{ end }}
</tr>
{{ range $row := $data }}
<tr>
{{ range $cell := $row }}
<td>
{{ $cell.Value }}
</td>
{{ end }}
</tr>
{{ end }}
</table>
{{ end }}
{{ end }}
{{ with .Email.Body.Actions }}
{{ range $action := . }}
<p>{{ $action.Instructions }} {{ $action.Button.Link }}</p>
{{ end }}
{{ end }}
{{ end }}
{{ with .Email.Body.Outros }}
{{ range $line := . }}
<p>{{ $line }}<p>
{{ end }}
{{ end }}
<p>{{.Email.Body.Signature}},<br>{{.Hermes.Product.Name}} - {{.Hermes.Product.Link}}</p>
{{.Email.Body.Signature}},
{{.Hermes.Product.Name}} - {{.Hermes.Product.Link}}
{{.Hermes.Product.Copyright}}
<p>{{.Hermes.Product.Copyright}}</p>
`
}

View File

@ -0,0 +1,345 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #F2F4F6;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #F2F4F6;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #AEAEAE;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 10px;
padding: 0;
}
.body-dictionary dd {
margin: 0 0 10px 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
}
.body-dictionary dd {
margin-left: 0;
margin-bottom: 10px;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.button {
display: inline-block;
width: 200px;
background-color: #3869D4;
border-radius: 3px;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<h1>Hi Jon Snow,</h1>
<blockquote>
<p><em>Hermes</em> service will shutdown the <strong>1st August 2017</strong> for maintenance operations.</p>
</blockquote>
<p>Services will be unavailable based on the following schedule:</p>
<table>
<thead>
<tr>
<th align="center">Services</th>
<th align="center">Downtime</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">Service A</td>
<td align="center">2AM to 3AM</td>
</tr>
<tr>
<td align="center">Service B</td>
<td align="center">4AM to 5AM</td>
</tr>
<tr>
<td align="center">Service C</td>
<td align="center">5AM to 6AM</td>
</tr>
</tbody>
</table>
<hr />
<p>Feel free to contact us for any question regarding this matter at <a href="mailto:support@hermes-example.com">support@hermes-example.com</a> or in our <a href="https://gitter.im/">Gitter</a></p>
<p>
Yours truly,
<br />
Hermes
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
Copyright © 2017 Hermes. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,28 @@
------------
Hi Jon Snow,
------------
>
>
>
> Hermes service will shutdown the *1st August 2017* for maintenance
> operations.
>
>
Services will be unavailable based on the following schedule:
+-----------+------------+
| SERVICES | DOWNTIME |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+
Feel free to contact us for any question regarding this matter at support@hermes-example.com ( support@hermes-example.com ) or in our Gitter ( https://gitter.im/ )
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -105,6 +105,10 @@
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
@ -131,6 +135,28 @@
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
@ -143,6 +169,29 @@
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
@ -201,7 +250,7 @@
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
@ -214,6 +263,7 @@
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
@ -236,6 +286,7 @@
<table class="data-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
@ -366,7 +417,9 @@
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/dashboard" class="button" style="background-color: " target="_blank">Go to Dashboard</a>
<a href="https://hermes-example.com/dashboard" class="button" style="background-color: " target="_blank">
Go to Dashboard
</a>
</div>
</td>
</tr>
@ -377,25 +430,29 @@
<p>
Yours truly,
<br>
<br />
Hermes
</p>
<table class="body-sub">
<tbody><tr>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button 'Go to Dashboard', copy and paste the URL below into your web browser.</p>
<p class="sub">If youre having trouble with the button &#39;Go to Dashboard&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/dashboard">https://hermes-example.com/dashboard</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>

View File

@ -1,12 +1,24 @@
------------
Hi Jon Snow,
------------
Your order has been processed successfully.
+--------+--------------------------------+--------+
| ITEM | DESCRIPTION | PRICE |
+--------+--------------------------------+--------+
| Golang | Open source programming | $10.99 |
| | language that makes it easy | |
| | to build simple, reliable, and | |
| | efficient software | |
| Hermes | Programmatically create | $1.99 |
| | beautiful e-mails using | |
| | Golang. | |
+--------+--------------------------------+--------+
You can check the status of your order and more in your dashboard:
https://hermes-example.com/dashboard
You can check the status of your order and more in your dashboard: https://hermes-example.com/dashboard
Yours truly,
Hermes
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -105,6 +105,10 @@
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
@ -131,6 +135,28 @@
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
@ -143,6 +169,29 @@
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
@ -201,7 +250,7 @@
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
@ -214,6 +263,7 @@
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
@ -242,12 +292,15 @@
<p>Click the button below to reset your password:</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: #DC4D2F" target="_blank">Reset your password</a>
<a href="https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: #DC4D2F" target="_blank">
Reset your password
</a>
</div>
</td>
</tr>
@ -259,6 +312,7 @@
<p>If you did not request a password reset, no further action is required on your part.</p>
@ -266,23 +320,26 @@
<p>
Thanks,
<br>
<br />
Hermes
</p>
<table class="body-sub">
<tbody><tr>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button 'Reset your password', copy and paste the URL below into your web browser.</p>
<p class="sub">If youre having trouble with the button &#39;Reset your password&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010">https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>

View File

@ -1,13 +1,14 @@
------------
Hi Jon Snow,
------------
You have received this email because a password reset request for Hermes account was received.
Click the button below to reset your password:
https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010
Click the button below to reset your password: https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010
If you did not request a password reset, no further action is required on your part.
Thanks,
Hermes
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -105,6 +105,10 @@
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
@ -131,6 +135,28 @@
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
@ -143,6 +169,29 @@
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
@ -201,7 +250,7 @@
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
@ -214,6 +263,7 @@
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
@ -231,6 +281,7 @@
<dl class="body-dictionary">
<dt>Firstname:</dt>
@ -262,7 +313,9 @@
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: #22BC66" target="_blank">Confirm your account</a>
<a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: " target="_blank">
Confirm your account
</a>
</div>
</td>
</tr>
@ -274,6 +327,7 @@
<p>Need help, or have questions? Just reply to this email, we&#39;d love to help.</p>
@ -281,23 +335,26 @@
<p>
Yours truly,
<br>
<br />
Hermes
</p>
<table class="body-sub">
<tbody><tr>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button 'Confirm your account', copy and paste the URL below into your web browser.</p>
<p class="sub">If youre having trouble with the button &#39;Confirm your account&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010">https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>

View File

@ -1,16 +1,18 @@
------------
Hi Jon Snow,
Welcome to Hermes! We&#39;re very excited to have you on board.
------------
Firstname: Jon
Lastname: Snow
Birthday: 01/01/283
Welcome to Hermes! We're very excited to have you on board.
To get started with Hermes, please click here:
https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010
* Firstname: Jon
* Lastname: Snow
* Birthday: 01/01/283
Need help, or have questions? Just reply to this email, we&#39;d love to help.
To get started with Hermes, please click here: https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010
Need help, or have questions? Just reply to this email, we'd love to help.
Yours truly,
Hermes
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -0,0 +1,345 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #2c3e50;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #2c3e50;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #eaeaea;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 20px;
padding: 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
float: left;
width: 50%;
padding: 0;
margin: 0;
margin-bottom: 0.3em;
}
.body-dictionary dd {
float: left;
width: 50%;
padding: 0;
margin: 0;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.button {
display: inline-block;
width: 100%;
background-color: #00948d;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
</style>
</head>
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<h1>Hi Jon Snow,</h1>
<blockquote>
<p><em>Hermes</em> service will shutdown the <strong>1st August 2017</strong> for maintenance operations.</p>
</blockquote>
<p>Services will be unavailable based on the following schedule:</p>
<table>
<thead>
<tr>
<th align="center">Services</th>
<th align="center">Downtime</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">Service A</td>
<td align="center">2AM to 3AM</td>
</tr>
<tr>
<td align="center">Service B</td>
<td align="center">4AM to 5AM</td>
</tr>
<tr>
<td align="center">Service C</td>
<td align="center">5AM to 6AM</td>
</tr>
</tbody>
</table>
<hr />
<p>Feel free to contact us for any question regarding this matter at <a href="mailto:support@hermes-example.com">support@hermes-example.com</a> or in our <a href="https://gitter.im/">Gitter</a></p>
<p>
Yours truly,
<br />
Hermes
</p>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
Copyright © 2017 Hermes. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,28 @@
------------
Hi Jon Snow,
------------
>
>
>
> Hermes service will shutdown the *1st August 2017* for maintenance
> operations.
>
>
Services will be unavailable based on the following schedule:
+-----------+------------+
| SERVICES | DOWNTIME |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+
Feel free to contact us for any question regarding this matter at support@hermes-example.com ( support@hermes-example.com ) or in our Gitter ( https://gitter.im/ )
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -0,0 +1,479 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #2c3e50;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #2c3e50;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #eaeaea;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 20px;
padding: 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
float: left;
width: 50%;
padding: 0;
margin: 0;
margin-bottom: 0.3em;
}
.body-dictionary dd {
float: left;
width: 50%;
padding: 0;
margin: 0;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.button {
display: inline-block;
width: 100%;
background-color: #00948d;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
</style>
</head>
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<h1>Hi Jon Snow,</h1>
<p>Your order has been processed successfully.</p>
<table class="data-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
<table class="data-table" width="100%" cellpadding="0" cellspacing="0">
<tr>
<th
width="20%"
>
<p>Item</p>
</th>
<th
>
<p>Description</p>
</th>
<th
width="15%"
style="text-align:right"
>
<p>Price</p>
</th>
</tr>
<tr>
<td
>
Golang
</td>
<td
>
Open source programming language that makes it easy to build simple, reliable, and efficient software
</td>
<td
style="text-align:right"
>
$10.99
</td>
</tr>
<tr>
<td
>
Hermes
</td>
<td
>
Programmatically create beautiful e-mails using Golang.
</td>
<td
style="text-align:right"
>
$1.99
</td>
</tr>
</table>
</td>
</tr>
</table>
<p>You can check the status of your order and more in your dashboard:</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/dashboard" class="button" style="background-color: " target="_blank">
Go to Dashboard
</a>
</div>
</td>
</tr>
</table>
<p>
Yours truly,
<br />
Hermes
</p>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button &#39;Go to Dashboard&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/dashboard">https://hermes-example.com/dashboard</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
Copyright © 2017 Hermes. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,24 @@
------------
Hi Jon Snow,
------------
Your order has been processed successfully.
+--------+--------------------------------+--------+
| ITEM | DESCRIPTION | PRICE |
+--------+--------------------------------+--------+
| Golang | Open source programming | $10.99 |
| | language that makes it easy | |
| | to build simple, reliable, and | |
| | efficient software | |
| Hermes | Programmatically create | $1.99 |
| | beautiful e-mails using | |
| | Golang. | |
+--------+--------------------------------+--------+
You can check the status of your order and more in your dashboard: https://hermes-example.com/dashboard
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -0,0 +1,366 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #2c3e50;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #2c3e50;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #eaeaea;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 20px;
padding: 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
float: left;
width: 50%;
padding: 0;
margin: 0;
margin-bottom: 0.3em;
}
.body-dictionary dd {
float: left;
width: 50%;
padding: 0;
margin: 0;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.button {
display: inline-block;
width: 100%;
background-color: #00948d;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
</style>
</head>
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<h1>Hi Jon Snow,</h1>
<p>You have received this email because a password reset request for Hermes account was received.</p>
<p>Click the button below to reset your password:</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: #DC4D2F" target="_blank">
Reset your password
</a>
</div>
</td>
</tr>
</table>
<p>If you did not request a password reset, no further action is required on your part.</p>
<p>
Thanks,
<br />
Hermes
</p>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button &#39;Reset your password&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010">https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
Copyright © 2017 Hermes. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,14 @@
------------
Hi Jon Snow,
------------
You have received this email because a password reset request for Hermes account was received.
Click the button below to reset your password: https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010
If you did not request a password reset, no further action is required on your part.
Thanks,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

View File

@ -0,0 +1,381 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #2c3e50;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #2c3e50;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #eaeaea;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 20px;
padding: 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
float: left;
width: 50%;
padding: 0;
margin: 0;
margin-bottom: 0.3em;
}
.body-dictionary dd {
float: left;
width: 50%;
padding: 0;
margin: 0;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.button {
display: inline-block;
width: 100%;
background-color: #00948d;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
</style>
</head>
<body dir="ltr">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<h1>Hi Jon Snow,</h1>
<p>Welcome to Hermes! We&#39;re very excited to have you on board.</p>
<dl class="body-dictionary">
<dt>Firstname:</dt>
<dd>Jon</dd>
<dt>Lastname:</dt>
<dd>Snow</dd>
<dt>Birthday:</dt>
<dd>01/01/283</dd>
</dl>
<p>To get started with Hermes, please click here:</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010" class="button" style="background-color: " target="_blank">
Confirm your account
</a>
</div>
</td>
</tr>
</table>
<p>Need help, or have questions? Just reply to this email, we&#39;d love to help.</p>
<p>
Yours truly,
<br />
Hermes
</p>
<table class="body-sub">
<tbody>
<tr>
<td>
<p class="sub">If youre having trouble with the button &#39;Confirm your account&#39;, copy and paste the URL below into your web browser.</p>
<p class="sub"><a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010">https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010</a></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
Copyright © 2017 Hermes. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,18 @@
------------
Hi Jon Snow,
------------
Welcome to Hermes! We're very excited to have you on board.
* Firstname: Jon
* Lastname: Snow
* Birthday: 01/01/283
To get started with Hermes, please click here: https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010
Need help, or have questions? Just reply to this email, we'd love to help.
Yours truly,
Hermes - https://example-hermes.com/
Copyright © 2017 Hermes. All rights reserved.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/matcornic/hermes"
"io/ioutil"
"os"
)
type example interface {
@ -12,6 +13,7 @@ type example interface {
}
func main() {
h := hermes.Hermes{
Product: hermes.Product{
Name: "Hermes",
@ -24,11 +26,20 @@ func main() {
new(welcome),
new(reset),
new(receipt),
new(maintenance),
}
themes := []hermes.Theme{
new(hermes.Default),
new(hermes.Flat),
}
for _, theme := range themes {
h.Theme = theme
for _, e := range examples {
generateEmails(h, e.Email(), e.Name())
}
}
}
@ -38,7 +49,11 @@ func generateEmails(h hermes.Hermes, email hermes.Email, example string) {
if err != nil {
panic(err)
}
err = ioutil.WriteFile(fmt.Sprintf("%v.%v.html", h.Theme.Name(), example), []byte(res), 0644)
err = os.MkdirAll(h.Theme.Name(), 0744)
if err != nil {
panic(err)
}
err = ioutil.WriteFile(fmt.Sprintf("%v/%v.%v.html", h.Theme.Name(), h.Theme.Name(), example), []byte(res), 0644)
if err != nil {
panic(err)
}
@ -48,7 +63,7 @@ func generateEmails(h hermes.Hermes, email hermes.Email, example string) {
if err != nil {
panic(err)
}
err = ioutil.WriteFile(fmt.Sprintf("%v.%v.txt", h.Theme.Name(), example), []byte(res), 0644)
err = ioutil.WriteFile(fmt.Sprintf("%v/%v.%v.txt", h.Theme.Name(), h.Theme.Name(), example), []byte(res), 0644)
if err != nil {
panic(err)
}

36
examples/maintenance.go Normal file
View File

@ -0,0 +1,36 @@
package main
import (
"github.com/matcornic/hermes"
)
type maintenance struct {
}
func (w *maintenance) Name() string {
return "maintenance"
}
func (w *maintenance) Email() hermes.Email {
return hermes.Email{
Body: hermes.Body{
Name: "Jon Snow",
FreeMarkdown: `
> _Hermes_ service will shutdown the **1st August 2017** for maintenance operations.
Services will be unavailable based on the following schedule:
| Services | Downtime |
| :------:| :-----------: |
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
---
Feel free to contact us for any question regarding this matter at [support@hermes-example.com](mailto:support@hermes-example.com) or in our [Gitter](https://gitter.im/)
`,
},
}
}

View File

@ -27,7 +27,6 @@ func (w *welcome) Email() hermes.Email {
{
Instructions: "To get started with Hermes, please click here:",
Button: hermes.Button{
Color: "#22BC66",
Text: "Confirm your account",
Link: "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
},

495
flat.go Normal file
View File

@ -0,0 +1,495 @@
package hermes
// Flat is a theme
type Flat struct{}
// Name returns the name of the flat theme
func (dt *Flat) Name() string {
return "flat"
}
// HTMLTemplate returns a Golang template that will generate an HTML email.
func (dt *Flat) HTMLTemplate() string {
return `
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<style type="text/css" rel="stylesheet" media="all">
/* Base ------------------------------ */
*:not(br):not(tr):not(html) {
font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
width: 100% !important;
height: 100%;
margin: 0;
line-height: 1.4;
background-color: #2c3e50;
color: #74787E;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
/* Layout ------------------------------ */
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #2c3e50;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
/* Masthead ----------------------- */
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead_logo {
max-width: 400px;
border: 0;
}
.email-masthead_name {
font-size: 16px;
font-weight: bold;
color: #2F3133;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-logo {
max-height: 50px;
}
/* Body ------------------------------ */
.email-body {
width: 100%;
margin: 0;
padding: 0;
border-top: 1px solid #EDEFF2;
border-bottom: 1px solid #EDEFF2;
background-color: #FFF;
}
.email-body_inner {
width: 570px;
margin: 0 auto;
padding: 0;
}
.email-footer {
width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
}
.email-footer p {
color: #eaeaea;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-dictionary {
width: 100%;
overflow: hidden;
margin: 20px auto 20px;
padding: 0;
}
.body-dictionary dt {
clear: both;
color: #000;
font-weight: bold;
float: left;
width: 50%;
padding: 0;
margin: 0;
margin-bottom: 0.3em;
}
.body-dictionary dd {
float: left;
width: 50%;
padding: 0;
margin: 0;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EDEFF2;
table-layout: fixed;
}
.body-sub a {
word-break: break-all;
}
.content-cell {
padding: 35px;
}
.align-right {
text-align: right;
}
/* Type ------------------------------ */
h1 {
margin-top: 0;
color: #2F3133;
font-size: 19px;
font-weight: bold;
}
h2 {
margin-top: 0;
color: #2F3133;
font-size: 16px;
font-weight: bold;
}
h3 {
margin-top: 0;
color: #2F3133;
font-size: 14px;
font-weight: bold;
}
blockquote {
margin: 1.7rem 0;
padding-left: 0.85rem;
border-left: 10px solid #F0F2F4;
}
blockquote p {
font-size: 1.1rem;
color: #999;
}
blockquote cite {
display: block;
text-align: right;
color: #666;
font-size: 1.2rem;
}
cite {
display: block;
font-size: 0.925rem;
}
cite:before {
content: "\2014 \0020";
}
p {
margin-top: 0;
color: #74787E;
font-size: 16px;
line-height: 1.5em;
}
p.sub {
font-size: 12px;
}
p.center {
text-align: center;
}
table {
width: 100%;
}
th {
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
.content {
align: center;
padding: 0;
}
/* Data table ------------------------------ */
.data-wrapper {
width: 100%;
margin: 0;
padding: 35px 0;
}
.data-table {
width: 100%;
margin: 0;
}
.data-table th {
text-align: left;
padding: 0px 5px;
padding-bottom: 8px;
border-bottom: 1px solid #EDEFF2;
}
.data-table th p {
margin: 0;
color: #9BA2AB;
font-size: 12px;
}
.data-table td {
padding: 10px 5px;
color: #74787E;
font-size: 15px;
line-height: 18px;
}
/* Buttons ------------------------------ */
.button {
display: inline-block;
width: 100%;
background-color: #00948d;
color: #ffffff;
font-size: 15px;
line-height: 45px;
text-align: center;
text-decoration: none;
-webkit-text-size-adjust: none;
mso-hide: all;
}
/*Media Queries ------------------------------ */
@media only screen and (max-width: 600px) {
.email-body_inner,
.email-footer {
width: 100% !important;
}
}
</style>
</head>
<body dir="{{.Hermes.TextDirection}}">
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td class="content">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
<!-- Logo -->
<tr>
<td class="email-masthead">
<a class="email-masthead_name" href="{{.Hermes.Product.Link}}" target="_blank">
{{ if .Hermes.Product.Logo }}
<img src="{{.Hermes.Product.Logo | url }}" class="email-logo" />
{{ else }}
{{ .Hermes.Product.Name }}
{{ end }}
</a>
</td>
</tr>
<!-- Email Body -->
<tr>
<td class="email-body" width="100%">
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0">
<!-- Body content -->
<tr>
<td class="content-cell">
<h1>{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }},{{ end }}</h1>
{{ with .Email.Body.Intros }}
{{ if gt (len .) 0 }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ end }}
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
{{ with .Email.Body.Dictionary }}
{{ if gt (len .) 0 }}
<dl class="body-dictionary">
{{ range $entry := . }}
<dt>{{ $entry.Key }}:</dt>
<dd>{{ $entry.Value }}</dd>
{{ end }}
</dl>
{{ end }}
{{ end }}
<!-- Table -->
{{ with .Email.Body.Table }}
{{ $data := .Data }}
{{ $columns := .Columns }}
{{ if gt (len $data) 0 }}
<table class="data-wrapper" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td colspan="2">
<table class="data-table" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ $col := index $data 0 }}
{{ range $entry := $col }}
<th
{{ with $columns }}
{{ $width := index .CustomWidth $entry.Key }}
{{ with $width }}
width="{{ . }}"
{{ end }}
{{ $align := index .CustomAlignement $entry.Key }}
{{ with $align }}
style="text-align:{{ . }}"
{{ end }}
{{ end }}
>
<p>{{ $entry.Key }}</p>
</th>
{{ end }}
</tr>
{{ range $row := $data }}
<tr>
{{ range $cell := $row }}
<td
{{ with $columns }}
{{ $align := index .CustomAlignement $cell.Key }}
{{ with $align }}
style="text-align:{{ . }}"
{{ end }}
{{ end }}
>
{{ $cell.Value }}
</td>
{{ end }}
</tr>
{{ end }}
</table>
</td>
</tr>
</table>
{{ end }}
{{ end }}
<!-- Action -->
{{ with .Email.Body.Actions }}
{{ if gt (len .) 0 }}
{{ range $action := . }}
<p>{{ $action.Instructions }}</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center">
<div>
<a href="{{ $action.Button.Link }}" class="button" style="background-color: {{ $action.Button.Color }}" target="_blank">
{{ $action.Button.Text }}
</a>
</div>
</td>
</tr>
</table>
{{ end }}
{{ end }}
{{ end }}
{{ end }}
{{ with .Email.Body.Outros }}
{{ if gt (len .) 0 }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ end }}
<p>
{{.Email.Body.Signature}},
<br />
{{.Hermes.Product.Name}}
</p>
{{ if (eq .Email.Body.FreeMarkdown "") }}
{{ with .Email.Body.Actions }}
<table class="body-sub">
<tbody>
{{ range $action := . }}
<tr>
<td>
<p class="sub">{{$.Hermes.Product.TroubleText | replace "{ACTION}" $action.Button.Text}}</p>
<p class="sub"><a href="{{ $action.Button.Link }}">{{ $action.Button.Link }}</a></p>
</td>
</tr>
{{ end }}
</tbody>
</table>
{{ end }}
{{ end }}
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0">
<tr>
<td class="content-cell">
<p class="sub center">
{{.Hermes.Product.Copyright}}
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`
}
// PlainTextTemplate returns a Golang template that will generate an plain text email.
func (dt *Flat) PlainTextTemplate() string {
return `<h2>{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }}{{ end }},</h2>
{{ with .Email.Body.Intros }}
{{ range $line := . }}
<p>{{ $line }}</p>
{{ end }}
{{ end }}
{{ if (ne .Email.Body.FreeMarkdown "") }}
{{ .Email.Body.FreeMarkdown.ToHTML }}
{{ else }}
{{ with .Email.Body.Dictionary }}
<ul>
{{ range $entry := . }}
<li>{{ $entry.Key }}: {{ $entry.Value }}</li>
{{ end }}
</ul>
{{ end }}
{{ with .Email.Body.Table }}
{{ $data := .Data }}
{{ $columns := .Columns }}
{{ if gt (len $data) 0 }}
<table class="data-table" width="100%" cellpadding="0" cellspacing="0">
<tr>
{{ $col := index $data 0 }}
{{ range $entry := $col }}
<th>{{ $entry.Key }} </th>
{{ end }}
</tr>
{{ range $row := $data }}
<tr>
{{ range $cell := $row }}
<td>
{{ $cell.Value }}
</td>
{{ end }}
</tr>
{{ end }}
</table>
{{ end }}
{{ end }}
{{ with .Email.Body.Actions }}
{{ range $action := . }}
<p>{{ $action.Instructions }} {{ $action.Button.Link }}</p>
{{ end }}
{{ end }}
{{ end }}
{{ with .Email.Body.Outros }}
{{ range $line := . }}
<p>{{ $line }}<p>
{{ end }}
{{ end }}
<p>{{.Email.Body.Signature}},<br>{{.Hermes.Product.Name}} - {{.Hermes.Product.Link}}</p>
<p>{{.Hermes.Product.Copyright}}</p>
`
}

View File

@ -4,6 +4,8 @@ import (
"bytes"
"github.com/Masterminds/sprig"
"github.com/imdario/mergo"
"github.com/jaytaylor/html2text"
"github.com/russross/blackfriday"
"html/template"
)
@ -21,9 +23,15 @@ type Theme interface {
PlainTextTemplate() string // The golang templte for plain text emails (can be basic HTML)
}
// TextDirection of the text in HTML email@
// TextDirection of the text in HTML email
type TextDirection string
var templateFuncs = template.FuncMap{
"url": func(s string) template.URL {
return template.URL(s)
},
}
// TDLeftToRight is the text direction from left to right (default)
const TDLeftToRight TextDirection = "ltr"
@ -45,6 +53,10 @@ type Email struct {
Body Body
}
// Markdown is a HTML template (a string) representing Markdown content
// https://en.wikipedia.org/wiki/Markdown
type Markdown template.HTML
// Body is the body of the email, containing all interesting data
type Body struct {
Name string // The name of the contacted person
@ -56,6 +68,12 @@ type Body struct {
Greeting string // Greeting for the contacted person (default to 'Hi')
Signature string // Signature for the contacted person (default to 'Yours truly')
Title string // Title replaces the greeting+name when set
FreeMarkdown Markdown // Free markdown content that replaces all content other than header and footer
}
// ToHTML converts Markdown to HTML
func (c Markdown) ToHTML() template.HTML {
return template.HTML(blackfriday.MarkdownCommon([]byte(string(c))))
}
// Entry is a simple entry of a map
@ -156,7 +174,11 @@ func (h *Hermes) GeneratePlainText(email Email) (string, error) {
if err != nil {
return "", err
}
return h.generateTemplate(email, h.Theme.PlainTextTemplate())
template, err := h.generateTemplate(email, h.Theme.PlainTextTemplate())
if err != nil {
return "", err
}
return html2text.FromString(template, html2text.Options{PrettyTables: true})
}
func (h *Hermes) generateTemplate(email Email, tplt string) (string, error) {
@ -168,7 +190,7 @@ func (h *Hermes) generateTemplate(email Email, tplt string) (string, error) {
// Generate the email from Golang template
// Allow usage of simple function from sprig : https://github.com/Masterminds/sprig
t, err := template.New("hermes").Funcs(sprig.FuncMap()).Parse(tplt)
t, err := template.New("hermes").Funcs(sprig.FuncMap()).Funcs(templateFuncs).Parse(tplt)
if err != nil {
return "", err
}

View File

@ -8,6 +8,7 @@ import (
var testedThemes = []Theme{
// Insert your new theme here
new(Default),
new(Flat),
}
/////////////////////////////////////////////////////
@ -130,10 +131,18 @@ func (ed *SimpleExample) assertPlainTextContent(t *testing.T, r string) {
assert.Contains(t, r, "Hi Jon Snow", "Name: Should find the name of the person")
assert.Contains(t, r, "Welcome to Hermes", "Intro: Should have intro")
assert.Contains(t, r, "Birthday", "Dictionary: Should have dictionary")
assert.NotContains(t, r, "Open source programming language", "Table: Not possible to have table in plain text")
assert.NotContains(t, r, "Programmatically create beautiful e-mails using Golang", "Table: Not possible to have table in plain text")
assert.NotContains(t, r, "$10.99", "Table: Not possible to have table in plain text")
assert.NotContains(t, r, "$1.99", "Table: Not possible to have table in plain text")
assert.Contains(t, r, "Open source", "Table: Should have table content")
assert.Contains(t, r, `+--------+--------------------------------+--------+
| ITEM | DESCRIPTION | PRICE |
+--------+--------------------------------+--------+
| Golang | Open source programming | $10.99 |
| | language that makes it easy | |
| | to build simple, reliable, and | |
| | efficient software | |
| Hermes | Programmatically create | $1.99 |
| | beautiful e-mails using | |
| | Golang. | |
+--------+--------------------------------+--------`, "Table: Should have pretty table content")
assert.Contains(t, r, "started with Hermes", "Action: Should have instruction")
assert.NotContains(t, r, "Confirm your account", "Action: Should not have button of action in plain text")
assert.NotContains(t, r, "#22BC66", "Action: Button should not have color in plain text")
@ -237,6 +246,103 @@ func (ed *WithSignatureDifferentThanDefault) assertPlainTextContent(t *testing.T
assert.Contains(t, r, "Best regards", "Should have greeting with Dear")
}
type WithFreeMarkdownContent struct {
theme Theme
}
func (ed *WithFreeMarkdownContent) getExample() (Hermes, Email) {
h := Hermes{
Theme: ed.theme,
Product: Product{
Name: "Hermes",
Link: "http://hermes.com",
},
}
email := Email{
Body{
Name: "Jon Snow",
FreeMarkdown: `
> _Hermes_ service will shutdown the **1st August 2017** for maintenance operations.
Services will be unavailable based on the following schedule:
| Services | Downtime |
| :------:| :-----------: |
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
---
Feel free to contact us for any question regarding this matter at [support@hermes-example.com](mailto:support@hermes-example.com) or in our [Gitter](https://gitter.im/)
`,
Intros: []string{
"An intro that should be kept even with FreeMarkdown",
},
Dictionary: []Entry{
{"Dictionary that should not be displayed", "Because of FreeMarkdown"},
},
Table: Table{
Data: [][]Entry{
{
{Key: "Item", Value: "Golang"},
},
{
{Key: "Item", Value: "Hermes"},
},
},
},
Actions: []Action{
{
Instructions: "Action that should not be displayed, because of FreeMarkdown:",
Button: Button{
Color: "#22BC66",
Text: "Button",
Link: "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
},
},
},
Outros: []string{
"An outro that should be kept even with FreeMarkdown",
},
},
}
return h, email
}
func (ed *WithFreeMarkdownContent) assertHTMLContent(t *testing.T, r string) {
assert.Contains(t, r, "Yours truly", "Should find signature with 'Yours truly' which is default")
assert.Contains(t, r, "Jon Snow", "Should find title with 'Jon Snow'")
assert.Contains(t, r, "<em>Hermes</em> service will shutdown", "Should find quote as HTML formatted content")
assert.Contains(t, r, "<td align=\"center\">2AM to 3AM</td>", "Should find cell content as HTML formatted content")
assert.Contains(t, r, "<a href=\"mailto:support@hermes-example.com\">support@hermes-example.com</a>", "Should find link of mailto as HTML formatted content")
assert.Contains(t, r, "An intro that should be kept even with FreeMarkdown", "Should find intro even with FreeMarkdown")
assert.Contains(t, r, "An outro that should be kept even with FreeMarkdown", "Should find outro even with FreeMarkdown")
assert.NotContains(t, r, "should not be displayed", "Should find any other content that the one from FreeMarkdown object")
}
func (ed *WithFreeMarkdownContent) assertPlainTextContent(t *testing.T, r string) {
assert.Contains(t, r, "Yours truly", "Should find signature with 'Yours truly' which is default")
assert.Contains(t, r, "Jon Snow", "Should find title with 'Jon Snow'")
assert.Contains(t, r, "> Hermes service will shutdown", "Should find quote as plain text with quote emphaze on sentence")
assert.Contains(t, r, "2AM to 3AM", "Should find cell content as plain text")
assert.Contains(t, r, `+-----------+------------+
| SERVICES | DOWNTIME |
+-----------+------------+
| Service A | 2AM to 3AM |
| Service B | 4AM to 5AM |
| Service C | 5AM to 6AM |
+-----------+------------+`, "Should find pretty table as plain text")
assert.Contains(t, r, "support@hermes-example.com", "Should find link of mailto as plain text")
assert.NotContains(t, r, "<table>", "Should not find html table tags")
assert.NotContains(t, r, "<tr>", "Should not find html tr tags")
assert.NotContains(t, r, "<a>", "Should not find html link tags")
assert.NotContains(t, r, "should not be displayed", "Should find any other content that the one from FreeMarkdown object")
}
// Test all the themes for the features
func TestThemeSimple(t *testing.T) {
@ -263,6 +369,12 @@ func TestThemeWithGreetingDiffrentThanDefault(t *testing.T) {
}
}
func TestThemeWithFreeMarkdownContent(t *testing.T) {
for _, theme := range testedThemes {
checkExample(t, &WithFreeMarkdownContent{theme})
}
}
func checkExample(t *testing.T, ex Example) {
// Given an example
h, email := ex.getExample()
@ -346,6 +458,7 @@ func TestHermes_Default(t *testing.T) {
assert.Empty(t, email.Body.Table.Data)
assert.Empty(t, email.Body.Table.Columns.CustomWidth)
assert.Empty(t, email.Body.Table.Columns.CustomAlignement)
assert.Empty(t, string(email.Body.FreeMarkdown))
assert.Equal(t, email.Body.Greeting, "Hi")
assert.Equal(t, email.Body.Signature, "Yours truly")

BIN
screens/flat/receipt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
screens/flat/reset.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

BIN
screens/flat/welcome.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
screens/free-markdown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB