feat: add first default working theme
This commit is contained in:
parent
4aaec1e0de
commit
66530ea80e
38
README.md
38
README.md
|
@ -1,2 +1,36 @@
|
||||||
# hermes
|
# Hermes
|
||||||
Golang package that generates clean, responsive HTML e-mails for sending transactional mail
|
|
||||||
|
Hermes is the Go port the great [mailgen](https://github.com/eladnava/mailgen) engine for Node.js.
|
||||||
|
It's a package that generates clean, responsive HTML e-mails for sending transactional mail (welcome email, email validity checking, reset password and so on).
|
||||||
|
|
||||||
|
# Demo
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
First install the package:
|
||||||
|
|
||||||
|
```
|
||||||
|
go get -u github.com/matcornic/hermes
|
||||||
|
```
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Thanks so much for wanting to help! We really appreciate it.
|
||||||
|
|
||||||
|
* Have an idea for a new feature?
|
||||||
|
* Want to add a new built-in theme?
|
||||||
|
|
||||||
|
Excellent! You've come to the right place.
|
||||||
|
|
||||||
|
1. If you find a bug or wish to suggest a new feature, please create an issue first
|
||||||
|
2. Make sure your code & comment conventions are in-line with the project's style
|
||||||
|
3. Make your commits and PRs as tiny as possible - one feature or bugfix at a time
|
||||||
|
4. Write detailed commit messages, in-line with the project's commit naming conventions
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Apache 2.0
|
||||||
|
|
|
@ -0,0 +1,366 @@
|
||||||
|
package hermes
|
||||||
|
|
||||||
|
type DefaultTheme struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *DefaultTheme) Name() string {
|
||||||
|
return "default"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *DefaultTheme) 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: #F2F4F6;
|
||||||
|
color: #74787E;
|
||||||
|
-webkit-text-size-adjust: none;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #3869D4;
|
||||||
|
}
|
||||||
|
/* Layout ------------------------------ */
|
||||||
|
.email-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #F2F4F6;
|
||||||
|
}
|
||||||
|
.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: #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;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #74787E;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
p.sub {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
p.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
/* 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: 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 Queries ------------------------------ */
|
||||||
|
@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="{{.Hermes.TextDirection}}">
|
||||||
|
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td align="center">
|
||||||
|
<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" />
|
||||||
|
{{ 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>{{.Email.Body.Greeting}} {{.Email.Body.Name}},</h1>
|
||||||
|
{{ with .Email.Body.Intros }}
|
||||||
|
{{ range $line := . }}
|
||||||
|
<p>{{ $line }}</p>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Dictionary }}
|
||||||
|
<dl class="body-dictionary">
|
||||||
|
{{ range $entry := . }}
|
||||||
|
<dt>{{ $entry.Key }}:</dt>
|
||||||
|
<dd>{{ $entry.Value }}</dd>
|
||||||
|
{{ end }}
|
||||||
|
</dl>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Actions }}
|
||||||
|
{{ 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 }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Outros }}
|
||||||
|
{{ range $line := . }}
|
||||||
|
<p>{{ $line }}</p>
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{{.Email.Body.Signature}},
|
||||||
|
<br>
|
||||||
|
{{.Hermes.Product.Name}}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{{ with .Email.Body.Actions }}
|
||||||
|
<table class="body-sub">
|
||||||
|
<tbody><tr>
|
||||||
|
{{ range $action := . }}
|
||||||
|
<td>
|
||||||
|
<p class="sub">If you’re having trouble with the button '{{ $action.Button.Text }}', copy and paste the URL below into your web browser.</p>
|
||||||
|
<p class="sub"><a href="{{ $action.Button.Link }}">{{ $action.Button.Link }}</a></p>
|
||||||
|
</td>
|
||||||
|
{{ end }}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{ 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>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dt *DefaultTheme) PlainTextTemplate() string {
|
||||||
|
return `
|
||||||
|
{{.Email.Body.Greeting}} {{.Email.Body.Name}},
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{{ with .Email.Body.Intros }}
|
||||||
|
{{ range $line := . }}
|
||||||
|
{{ $line }}<br />
|
||||||
|
{{ end }}
|
||||||
|
<br />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Dictionary }}
|
||||||
|
{{ range $entry := . }}
|
||||||
|
{{ $entry.Key }}: {{ $entry.Value }}<br />
|
||||||
|
{{ end }}
|
||||||
|
<br />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Actions }}
|
||||||
|
{{ range $action := . }}
|
||||||
|
{{ $action.Instructions }}
|
||||||
|
<br />
|
||||||
|
<a href="{{ $action.Button.Link }}">{{ $action.Button.Link }}</a>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ with .Email.Body.Outros }}
|
||||||
|
{{ range $line := . }}
|
||||||
|
{{ $line }}
|
||||||
|
<br />
|
||||||
|
{{ end }}
|
||||||
|
<br />
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{.Email.Body.Signature}},
|
||||||
|
<br />
|
||||||
|
{{.Hermes.Product.Name}}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
{{.Hermes.Product.Copyright}}
|
||||||
|
`
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
Hi Jon Snow,
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Welcome to Hermes! We're very excited to have you on board.<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Firstname: Jon<br />
|
||||||
|
|
||||||
|
Lastname: Snow<br />
|
||||||
|
|
||||||
|
Birthday: 01/01/283<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
To get started with Hermes, please click here:
|
||||||
|
<br />
|
||||||
|
<a href="https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010">https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010</a>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Need help, or have questions? Just reply to this email, we'd love to help.
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
Yours truly,
|
||||||
|
<br />
|
||||||
|
Hermes
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
Copyright © 2017 Hermes. All rights reserved.
|
|
@ -0,0 +1,308 @@
|
||||||
|
|
||||||
|
<!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;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #74787E;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
p.sub {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
p.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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 align="center">
|
||||||
|
<table class="email-content" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td class="email-masthead">
|
||||||
|
<a class="email-masthead_name" href="http://hermes.com" target="_blank">
|
||||||
|
|
||||||
|
Hermes
|
||||||
|
|
||||||
|
</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'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: #22BC66" target="_blank">Confirm your account</a>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>Need help, or have questions? Just reply to this email, we'd love to help.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Yours truly,
|
||||||
|
<br>
|
||||||
|
Hermes
|
||||||
|
</p>
|
||||||
|
|
||||||
|
|
||||||
|
<table class="body-sub">
|
||||||
|
<tbody><tr>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<p class="sub">If you’re having trouble with the button 'Confirm your account', 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>
|
|
@ -0,0 +1,66 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/matcornic/hermes"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
h := hermes.Hermes{
|
||||||
|
Product: hermes.Product{
|
||||||
|
Name: "Hermes",
|
||||||
|
Link: "http://hermes.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"Welcome to Hermes! We're very excited to have you on board.",
|
||||||
|
},
|
||||||
|
Dictionary: []hermes.Entry{
|
||||||
|
{Key: "Firstname", Value: "Jon"},
|
||||||
|
{Key: "Lastname", Value: "Snow"},
|
||||||
|
{Key: "Birthday", Value: "01/01/283"},
|
||||||
|
},
|
||||||
|
Actions: []hermes.Action{
|
||||||
|
{
|
||||||
|
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",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outros: []string{
|
||||||
|
"Need help, or have questions? Just reply to this email, we'd love to help.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the HTML template
|
||||||
|
stream, err := h.GenerateHTML(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(stream)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile("default.plaintext.html", buf.Bytes(), 0644)
|
||||||
|
|
||||||
|
stream, err = h.GeneratePlainText(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
buf = new(bytes.Buffer)
|
||||||
|
_, err = buf.ReadFrom(stream)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile("default.html", buf.Bytes(), 0644)
|
||||||
|
}
|
127
hermes.go
127
hermes.go
|
@ -1 +1,128 @@
|
||||||
package hermes
|
package hermes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"github.com/Masterminds/sprig"
|
||||||
|
"github.com/imdario/mergo"
|
||||||
|
"html/template"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TextDirection string
|
||||||
|
|
||||||
|
const TDLeftToRight TextDirection = "ltr"
|
||||||
|
const TDRightToLeft TextDirection = "rtl"
|
||||||
|
|
||||||
|
type Hermes struct {
|
||||||
|
Theme Theme
|
||||||
|
TextDirection TextDirection // rtl (right to left) or ltr (left to right)
|
||||||
|
Product Product
|
||||||
|
}
|
||||||
|
|
||||||
|
type Product struct {
|
||||||
|
// Appears in header & footer of e-mails
|
||||||
|
Name string
|
||||||
|
Link string // e.g. https://matcornic.github.io
|
||||||
|
Logo string // e.g. https://matcornic.github.io/img/logo.png
|
||||||
|
Copyright string // Copyright © 2017 Hermes. All rights reserved.
|
||||||
|
}
|
||||||
|
|
||||||
|
type Email struct {
|
||||||
|
Body Body
|
||||||
|
}
|
||||||
|
|
||||||
|
type Body struct {
|
||||||
|
Name string
|
||||||
|
Intros []string
|
||||||
|
Dictionary []Entry
|
||||||
|
Actions []Action
|
||||||
|
Outros []string
|
||||||
|
Greeting string
|
||||||
|
Signature string
|
||||||
|
Title string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Instructions string
|
||||||
|
Button Button
|
||||||
|
}
|
||||||
|
|
||||||
|
type Button struct {
|
||||||
|
Color string
|
||||||
|
Text string
|
||||||
|
Link string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Template struct {
|
||||||
|
Hermes Hermes
|
||||||
|
Email Email
|
||||||
|
}
|
||||||
|
|
||||||
|
type Theme interface {
|
||||||
|
Name() string
|
||||||
|
HtmlTemplate() string
|
||||||
|
PlainTextTemplate() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDefaultEmailValues(e *Email) error {
|
||||||
|
defaultEmail := Email{
|
||||||
|
Body: Body{
|
||||||
|
Signature: "Yours truly",
|
||||||
|
Greeting: "Hi",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return mergo.Merge(e, defaultEmail)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDefaultHermesValues(h *Hermes) error {
|
||||||
|
defaultTextDirection := TDLeftToRight
|
||||||
|
defaultHermes := Hermes{
|
||||||
|
Theme: new(DefaultTheme),
|
||||||
|
TextDirection: defaultTextDirection,
|
||||||
|
Product: Product{
|
||||||
|
Name: "Hermes",
|
||||||
|
Copyright: "Copyright © 2017 Hermes. All rights reserved.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
err := mergo.Merge(h, defaultHermes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if h.TextDirection != TDLeftToRight && h.TextDirection != TDRightToLeft {
|
||||||
|
h.TextDirection = defaultTextDirection
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hermes) GenerateHTML(email Email) (io.Reader, error) {
|
||||||
|
return h.generateTemplate(email, func() string {
|
||||||
|
return h.Theme.HtmlTemplate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hermes) GeneratePlainText(email Email) (io.Reader, error) {
|
||||||
|
return h.generateTemplate(email, func() string {
|
||||||
|
return h.Theme.PlainTextTemplate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hermes) generateTemplate(email Email, tplt func() string) (io.Reader, error) {
|
||||||
|
err := setDefaultHermesValues(h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
setDefaultEmailValues(&email)
|
||||||
|
|
||||||
|
t, err := template.New("hermes").Funcs(sprig.FuncMap()).Parse(tplt())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var b bytes.Buffer
|
||||||
|
t.Execute(&b, Template{*h, email})
|
||||||
|
return &b, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package hermes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHermes(t *testing.T) {
|
||||||
|
|
||||||
|
h := Hermes{
|
||||||
|
Product: Product{
|
||||||
|
Name: "Hermes",
|
||||||
|
Link: "http://hermes.com",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
email := Email{
|
||||||
|
Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"Welcome to Hermes! We're very excited to have you on board.",
|
||||||
|
},
|
||||||
|
Dictionary: []Entry{
|
||||||
|
{"Firstname", "Jon"},
|
||||||
|
{"Lastname", "Snow"},
|
||||||
|
{"Birthday", "01/01/283"},
|
||||||
|
},
|
||||||
|
Actions: []Action{
|
||||||
|
{
|
||||||
|
Instructions: "To get started with Hermes, please click here:",
|
||||||
|
Button: Button{
|
||||||
|
Color: "#22BC66",
|
||||||
|
Text: "Confirm your account",
|
||||||
|
Link: "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outros: []string{
|
||||||
|
"Need help, or have questions? Just reply to this email, we'd love to help.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := h.GenerateHTML(email)
|
||||||
|
t.Log(r)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}
|
Loading…
Reference in New Issue