Add examples
This commit is contained in:
parent
076d2b46d6
commit
0b783aebf0
269
README.md
269
README.md
|
@ -1,11 +1,15 @@
|
||||||
# Hermes
|
# Hermes
|
||||||
|
|
||||||
Hermes is the Go port the great [mailgen](https://github.com/eladnava/mailgen) engine for Node.js.
|
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 mail (welcome email, email validity checking, reset password and so on).
|
It's a package that generates clean, responsive HTML e-mails for sending transactional e-mails (welcome e-mail, reset password e-mails, receipt e-mails and so on).
|
||||||
|
|
||||||
# Demo
|
# Demo
|
||||||
|
|
||||||
TODO
|
![Welcome](screens/default/welcome.png)
|
||||||
|
|
||||||
|
![Reset](screens/default/reset.png)
|
||||||
|
|
||||||
|
![Receipt](screens/default/receipt.png)
|
||||||
|
|
||||||
# Usage
|
# Usage
|
||||||
|
|
||||||
|
@ -15,7 +19,264 @@ First install the package:
|
||||||
go get -u github.com/matcornic/hermes
|
go get -u github.com/matcornic/hermes
|
||||||
```
|
```
|
||||||
|
|
||||||
TODO
|
Then, start using the package by importing and configuring it:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Configure hermes by setting a theme and your product info
|
||||||
|
h := hermes.Hermes{
|
||||||
|
// Optional Theme
|
||||||
|
// Theme: new(Default)
|
||||||
|
Product: hermes.Product{
|
||||||
|
// Appears in header & footer of e-mails
|
||||||
|
Name: "Hermes",
|
||||||
|
Link: "https://example-hermes.com/",
|
||||||
|
//Option product logo
|
||||||
|
//Logo: "http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, generate an e-mail using the following code:
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"Welcome to Hermes! We're very excited to have you on board.",
|
||||||
|
},
|
||||||
|
Actions: []hermes.Action{
|
||||||
|
{
|
||||||
|
Instructions: "To get started with Hermes, please click here:",
|
||||||
|
Button: hermes.Button{
|
||||||
|
Color: "#22BC66", // Optional action button color
|
||||||
|
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 an HTML email with the provided contents(for modern clients)
|
||||||
|
emailBody, err := h.GenerateHTML(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // Tip: Handle error with something else than a panic ;)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the plaintext version of the e-mail (for clients that do not support xHTML)
|
||||||
|
emailText, err := h.GeneratePlainText(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // Tip: Handle error with something else than a panic ;)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionnaly, preview the generated HTML e-mail by writing it to a local file
|
||||||
|
err = ioutil.WriteFile("preview.html", []byte(emailBody), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // Tip: Handle error with something else than a panic ;)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code would output the following HTML template:
|
||||||
|
|
||||||
|
![Demo](screens/demo.png)
|
||||||
|
|
||||||
|
> Theme templates will be incorporated 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)
|
||||||
|
|
||||||
|
To run the examples, go to `examples/<theme>/`, then run `go run *.go`. HTML and Plaintext example should be created in the folder.
|
||||||
|
|
||||||
|
## Plaintext E-mails
|
||||||
|
|
||||||
|
To generate a [plaintext version of the e-mail](https://litmus.com/blog/best-practices-for-plain-text-emails-a-look-at-why-theyre-important), simply call `GeneratePlainText` function:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Generate plaintext email using hermes
|
||||||
|
emailText, err := h.GeneratePlainText(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err) // Tip: Handle error with something else than a panic ;)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Supported Themes
|
||||||
|
|
||||||
|
The following open-source themes are bundled with this package:
|
||||||
|
|
||||||
|
* `default` by [Postmark Transactional Email Templates](https://github.com/wildbit/postmark-templates)
|
||||||
|
|
||||||
|
<img src="https://raw.github.com/matcornic/hermes/master/screens/default/welcome.png" height="200" /> <img src="https://raw.github.com/matcornic/hermes/master/screens/default/reset.png" height="200" /> <img src="https://raw.github.com/matcornic/hermes/master/screens/default/receipt.png" height="200" />
|
||||||
|
|
||||||
|
## RTL Support
|
||||||
|
|
||||||
|
To change the default text direction (left-to-right), simply override it as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Configure hermes by setting a theme and your product info
|
||||||
|
h := hermes.Hermes {
|
||||||
|
// Custom text direction
|
||||||
|
TextDirection: hermes.TDRightToLeft,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Language Customizations
|
||||||
|
|
||||||
|
To customize the e-mail greeting (Hi) or signature (Yours truly), supply custom strings within the e-mail `Body`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Greeting: "Dear",
|
||||||
|
Signature: "Sincerly",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To use a custom title string rather than a greeting/name introduction, provide it instead of `Name`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var email = {
|
||||||
|
body: {
|
||||||
|
// Title will override `name`
|
||||||
|
title: 'Welcome to Mailgen!'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
// Title will override `Name`
|
||||||
|
Title: "Welcome to Mailgen",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To customize the `Copyright`, override it when initializing `Hermes` within your `Product` as follows:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// Configure mailgen
|
||||||
|
var mailGenerator = new Mailgen({
|
||||||
|
theme: 'salted',
|
||||||
|
product: {
|
||||||
|
name: 'Mailgen',
|
||||||
|
link: 'https://mailgen.js/',
|
||||||
|
// Custom copyright notice
|
||||||
|
copyright: 'Copyright © 2016 Mailgen. All rights reserved.',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Configure hermes by setting a theme and your product info
|
||||||
|
h := hermes.Hermes{
|
||||||
|
// Optional Theme
|
||||||
|
// Theme: new(Default)
|
||||||
|
Product: hermes.Product{
|
||||||
|
// Appears in header & footer of e-mails
|
||||||
|
Name: "Hermes",
|
||||||
|
Link: "https://example-hermes.com/",
|
||||||
|
// Custom copyright notice
|
||||||
|
Copyright: "Copyright © 2017 Dharma Initiative. All rights reserved."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Elements
|
||||||
|
|
||||||
|
Mailgen supports injecting custom elements such as dictionaries, tables and action buttons into e-mails.
|
||||||
|
|
||||||
|
### Action
|
||||||
|
|
||||||
|
To inject an action button in to the e-mail, supply the `Actions` object as follows:
|
||||||
|
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Actions: []hermes.Action{
|
||||||
|
{
|
||||||
|
Instructions: "To get started with Hermes, please click here:",
|
||||||
|
Button: hermes.Button{
|
||||||
|
Color: "#22BC66", // Optional action button color
|
||||||
|
Text: "Confirm your account",
|
||||||
|
Link: "https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To inject multiple action buttons in to the e-mail, supply another struct in Actions slice `Action`.
|
||||||
|
|
||||||
|
### Table
|
||||||
|
|
||||||
|
To inject a table into the e-mail, supply the `Table` object as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Table: hermes.Table{
|
||||||
|
Data: [][]hermes.Entry{
|
||||||
|
// List of rows
|
||||||
|
{
|
||||||
|
// Key is the column name, Value is the cell value
|
||||||
|
// First object defines what columns will be displayed
|
||||||
|
{Key: "Item", Value: "Node.js"},
|
||||||
|
{Key: "Description", Value: "Event-driven I/O server-side JavaScript environment based on V8"},
|
||||||
|
{Key: "Price", Value: "$10.99"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{Key: "Item", Value: "Mailgen"},
|
||||||
|
{Key: "Description", Value: "Programmatically create beautiful e-mails using plain old JavaScript."},
|
||||||
|
{Key: "Price", Value: "$1.99"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Columns: hermes.Columns{
|
||||||
|
// Custom style for each rows
|
||||||
|
CustomWidth: map[string]string{
|
||||||
|
"Item": "20%",
|
||||||
|
"Price": "15%",
|
||||||
|
},
|
||||||
|
CustomAlignement: map[string]string{
|
||||||
|
"Price": "right",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 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:
|
||||||
|
|
||||||
|
```go
|
||||||
|
email := hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Dictionary: []hermes.Entry{
|
||||||
|
{Key: "Date", Value: "20 November 1887"},
|
||||||
|
{Key: "Address", Value: "221B Baker Street, London"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
> Simply sending the `X-Entity-Ref-ID` header with your e-mails will prevent grouping / truncation.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
121
default.go
121
default.go
|
@ -1,14 +1,17 @@
|
||||||
package hermes
|
package hermes
|
||||||
|
|
||||||
type DefaultTheme struct {
|
// Default is the theme by default
|
||||||
|
type Default struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dt *DefaultTheme) Name() string {
|
// Name returns the name of the default theme
|
||||||
|
func (dt *Default) Name() string {
|
||||||
return "default"
|
return "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dt *DefaultTheme) HtmlTemplate() string {
|
// HTMLTemplate returns a Golang template that will generate an HTML email.
|
||||||
|
func (dt *Default) HTMLTemplate() string {
|
||||||
return `
|
return `
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
<!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">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
|
@ -233,14 +236,17 @@ func (dt *DefaultTheme) HtmlTemplate() string {
|
||||||
<!-- Body content -->
|
<!-- Body content -->
|
||||||
<tr>
|
<tr>
|
||||||
<td class="content-cell">
|
<td class="content-cell">
|
||||||
<h1>{{.Email.Body.Greeting}} {{.Email.Body.Name}},</h1>
|
<h1>{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }},{{ end }}</h1>
|
||||||
{{ with .Email.Body.Intros }}
|
{{ with .Email.Body.Intros }}
|
||||||
|
{{ if gt (len .) 0 }}
|
||||||
{{ range $line := . }}
|
{{ range $line := . }}
|
||||||
<p>{{ $line }}</p>
|
<p>{{ $line }}</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ with .Email.Body.Dictionary }}
|
{{ with .Email.Body.Dictionary }}
|
||||||
|
{{ if gt (len .) 0 }}
|
||||||
<dl class="body-dictionary">
|
<dl class="body-dictionary">
|
||||||
{{ range $entry := . }}
|
{{ range $entry := . }}
|
||||||
<dt>{{ $entry.Key }}:</dt>
|
<dt>{{ $entry.Key }}:</dt>
|
||||||
|
@ -248,8 +254,62 @@ func (dt *DefaultTheme) HtmlTemplate() string {
|
||||||
{{ end }}
|
{{ end }}
|
||||||
</dl>
|
</dl>
|
||||||
{{ end }}
|
{{ 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 }}
|
{{ with .Email.Body.Actions }}
|
||||||
|
{{ if gt (len .) 0 }}
|
||||||
{{ range $action := . }}
|
{{ range $action := . }}
|
||||||
<p>{{ $action.Instructions }}</p>
|
<p>{{ $action.Instructions }}</p>
|
||||||
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
|
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
@ -263,12 +323,15 @@ func (dt *DefaultTheme) HtmlTemplate() string {
|
||||||
</table>
|
</table>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
{{ with .Email.Body.Outros }}
|
{{ with .Email.Body.Outros }}
|
||||||
|
{{ if gt (len .) 0 }}
|
||||||
{{ range $line := . }}
|
{{ range $line := . }}
|
||||||
<p>{{ $line }}</p>
|
<p>{{ $line }}</p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{.Email.Body.Signature}},
|
{{.Email.Body.Signature}},
|
||||||
|
@ -316,51 +379,21 @@ func (dt *DefaultTheme) HtmlTemplate() string {
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dt *DefaultTheme) PlainTextTemplate() string {
|
// PlainTextTemplate returns a Golang template that will generate an plain text email.
|
||||||
return `
|
func (dt *Default) PlainTextTemplate() string {
|
||||||
{{.Email.Body.Greeting}} {{.Email.Body.Name}},
|
return `{{.Email.Body.Greeting}} {{.Email.Body.Name}},
|
||||||
<br />
|
{{ with .Email.Body.Intros }}{{ range $line := . }}{{ $line }}{{ end }}{{ end }}
|
||||||
<br />
|
{{ with .Email.Body.Dictionary }}{{ range $entry := . }}
|
||||||
|
{{ $entry.Key }}: {{ $entry.Value }}{{ end }}{{ end }}
|
||||||
{{ with .Email.Body.Intros }}
|
{{ with .Email.Body.Actions }} {{ range $action := . }}
|
||||||
{{ 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 }}
|
{{ $action.Instructions }}
|
||||||
<br />
|
{{ $action.Button.Link }}{{ end }}{{ end }}
|
||||||
<a href="{{ $action.Button.Link }}">{{ $action.Button.Link }}</a>
|
{{ with .Email.Body.Outros }} {{ range $line := . }}
|
||||||
<br />
|
{{ $line }}{{ end }}{{ end }}
|
||||||
|
|
||||||
<br />
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ with .Email.Body.Outros }}
|
|
||||||
{{ range $line := . }}
|
|
||||||
{{ $line }}
|
|
||||||
<br />
|
|
||||||
{{ end }}
|
|
||||||
<br />
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{.Email.Body.Signature}},
|
{{.Email.Body.Signature}},
|
||||||
<br />
|
|
||||||
{{.Hermes.Product.Name}}
|
{{.Hermes.Product.Name}}
|
||||||
|
|
||||||
<br />
|
|
||||||
<br />
|
|
||||||
{{.Hermes.Product.Copyright}}
|
{{.Hermes.Product.Copyright}}
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -1,49 +0,0 @@
|
||||||
|
|
||||||
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,422 @@
|
||||||
|
|
||||||
|
<!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="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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
Node.js
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
Event-driven I/O server-side JavaScript environment based on V8
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
style="text-align:right"
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
$10.99
|
||||||
|
</td>
|
||||||
|
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
|
||||||
|
<td
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
Mailgen
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
>
|
||||||
|
Programmatically create beautiful e-mails using plain old JavaScript.
|
||||||
|
</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 you’re having trouble with the button 'Go to Dashboard', 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>
|
|
@ -0,0 +1,12 @@
|
||||||
|
Hi Jon Snow,
|
||||||
|
Your order has been processed successfully.
|
||||||
|
|
||||||
|
|
||||||
|
You can check the status of your order and more in your dashboard:
|
||||||
|
https://hermes-example.com/dashboard
|
||||||
|
|
||||||
|
|
||||||
|
Yours truly,
|
||||||
|
Hermes
|
||||||
|
|
||||||
|
Copyright © 2017 Hermes. All rights reserved.
|
|
@ -0,0 +1,309 @@
|
||||||
|
|
||||||
|
<!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="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 you’re having trouble with the button 'Reset your password', 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>
|
|
@ -0,0 +1,13 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
Copyright © 2017 Hermes. All rights reserved.
|
|
@ -206,9 +206,9 @@
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td class="email-masthead">
|
<td class="email-masthead">
|
||||||
<a class="email-masthead_name" href="http://hermes.com" target="_blank">
|
<a class="email-masthead_name" href="https://example-hermes.com/" target="_blank">
|
||||||
|
|
||||||
Hermes
|
<img src="http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png" class="email-logo" />
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
|
@ -223,23 +223,21 @@
|
||||||
<h1>Hi Jon Snow,</h1>
|
<h1>Hi Jon Snow,</h1>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p>Welcome to Hermes! We're very excited to have you on board.</p>
|
<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>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,10 +257,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p>Need help, or have questions? Just reply to this email, we'd love to help.</p>
|
<p>Need help, or have questions? Just reply to this email, we'd love to help.</p>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Yours truly,
|
Yours truly,
|
||||||
<br>
|
<br>
|
|
@ -0,0 +1,13 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
Copyright © 2017 Hermes. All rights reserved.
|
|
@ -1,66 +1,55 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"fmt"
|
||||||
"github.com/matcornic/hermes"
|
"github.com/matcornic/hermes"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Example interface {
|
||||||
|
Email() hermes.Email
|
||||||
|
Name() string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
h := hermes.Hermes{
|
h := hermes.Hermes{
|
||||||
Product: hermes.Product{
|
Product: hermes.Product{
|
||||||
Name: "Hermes",
|
Name: "Hermes",
|
||||||
Link: "http://hermes.com",
|
Link: "https://example-hermes.com/",
|
||||||
|
Logo: "http://www.duchess-france.org/wp-content/uploads/2016/01/gopher.png",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
email := hermes.Email{
|
examples := []Example{
|
||||||
Body: hermes.Body{
|
new(Welcome),
|
||||||
Name: "Jon Snow",
|
new(Reset),
|
||||||
Intros: []string{
|
new(Receipt),
|
||||||
"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
|
for _, e := range examples {
|
||||||
stream, err := h.GenerateHTML(email)
|
generateEmails(h, e.Email(), e.Name())
|
||||||
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)
|
func generateEmails(h hermes.Hermes, email hermes.Email, example string) {
|
||||||
}
|
// Generate the HTML template and save it
|
||||||
buf = new(bytes.Buffer)
|
res, err := h.GenerateHTML(email)
|
||||||
_, err = buf.ReadFrom(stream)
|
if err != nil {
|
||||||
if err != nil {
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
err = ioutil.WriteFile(fmt.Sprintf("%v.%v.html", h.Theme.Name(), example), []byte(res), 0644)
|
||||||
err = ioutil.WriteFile("default.html", buf.Bytes(), 0644)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the plaintext template and save it
|
||||||
|
res, err = h.GeneratePlainText(email)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(fmt.Sprintf("%v.%v.txt", h.Theme.Name(), example), []byte(res), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matcornic/hermes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Receipt struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Receipt) Name() string {
|
||||||
|
return "receipt"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Receipt) Email() hermes.Email {
|
||||||
|
return hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"Your order has been processed successfully.",
|
||||||
|
},
|
||||||
|
Table: hermes.Table{
|
||||||
|
Data: [][]hermes.Entry{
|
||||||
|
{
|
||||||
|
{Key: "Item", Value: "Node.js"},
|
||||||
|
{Key: "Description", Value: "Event-driven I/O server-side JavaScript environment based on V8"},
|
||||||
|
{Key: "Price", Value: "$10.99"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{Key: "Item", Value: "Mailgen"},
|
||||||
|
{Key: "Description", Value: "Programmatically create beautiful e-mails using plain old JavaScript."},
|
||||||
|
{Key: "Price", Value: "$1.99"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Columns: hermes.Columns{
|
||||||
|
CustomWidth: map[string]string{
|
||||||
|
"Item": "20%",
|
||||||
|
"Price": "15%",
|
||||||
|
},
|
||||||
|
CustomAlignement: map[string]string{
|
||||||
|
"Price": "right",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Actions: []hermes.Action{
|
||||||
|
{
|
||||||
|
Instructions: "You can check the status of your order and more in your dashboard:",
|
||||||
|
Button: hermes.Button{
|
||||||
|
Text: "Go to Dashboard",
|
||||||
|
Link: "https://hermes-example.com/dashboard",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matcornic/hermes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reset struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reset) Name() string {
|
||||||
|
return "reset"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Reset) Email() hermes.Email {
|
||||||
|
return hermes.Email{
|
||||||
|
Body: hermes.Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"You have received this email because a password reset request for Hermes account was received.",
|
||||||
|
},
|
||||||
|
Actions: []hermes.Action{
|
||||||
|
{
|
||||||
|
Instructions: "Click the button below to reset your password:",
|
||||||
|
Button: hermes.Button{
|
||||||
|
Color: "#DC4D2F",
|
||||||
|
Text: "Reset your password",
|
||||||
|
Link: "https://hermes-example.com/reset-password?token=d9729feb74992cc3482b350163a1a010",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outros: []string{
|
||||||
|
"If you did not request a password reset, no further action is required on your part.",
|
||||||
|
},
|
||||||
|
Signature: "Thanks",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matcornic/hermes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Welcome struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Welcome) Name() string {
|
||||||
|
return "welcome"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Welcome) Email() hermes.Email {
|
||||||
|
return 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.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
134
hermes.go
134
hermes.go
|
@ -5,90 +5,127 @@ import (
|
||||||
"github.com/Masterminds/sprig"
|
"github.com/Masterminds/sprig"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type TextDirection string
|
// Hermes is an instance of the hermes email generator
|
||||||
|
|
||||||
const TDLeftToRight TextDirection = "ltr"
|
|
||||||
const TDRightToLeft TextDirection = "rtl"
|
|
||||||
|
|
||||||
type Hermes struct {
|
type Hermes struct {
|
||||||
Theme Theme
|
Theme Theme
|
||||||
TextDirection TextDirection // rtl (right to left) or ltr (left to right)
|
TextDirection TextDirection
|
||||||
Product Product
|
Product Product
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Theme is an interface to implement when creating a new theme
|
||||||
|
type Theme interface {
|
||||||
|
Name() string // The name of the theme
|
||||||
|
HTMLTemplate() string // The golang template for HTML emails
|
||||||
|
PlainTextTemplate() string // The golang templte for plain text emails (can be basic HTML)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextDirection of the text in HTML email@
|
||||||
|
type TextDirection string
|
||||||
|
|
||||||
|
// TDLeftToRight is the text direction from left to right (default)
|
||||||
|
const TDLeftToRight TextDirection = "ltr"
|
||||||
|
|
||||||
|
// TDRightToLeft is the text direction from right to left
|
||||||
|
const TDRightToLeft TextDirection = "rtl"
|
||||||
|
|
||||||
|
// Product represents your company product (brand)
|
||||||
|
// Appears in header & footer of e-mails
|
||||||
type Product struct {
|
type Product struct {
|
||||||
// Appears in header & footer of e-mails
|
|
||||||
Name string
|
Name string
|
||||||
Link string // e.g. https://matcornic.github.io
|
Link string // e.g. https://matcornic.github.io
|
||||||
Logo string // e.g. https://matcornic.github.io/img/logo.png
|
Logo string // e.g. https://matcornic.github.io/img/logo.png
|
||||||
Copyright string // Copyright © 2017 Hermes. All rights reserved.
|
Copyright string // Copyright © 2017 Hermes. All rights reserved.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Email is the email containing a body
|
||||||
type Email struct {
|
type Email struct {
|
||||||
Body Body
|
Body Body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Body is the body of the email, containing all interesting data
|
||||||
type Body struct {
|
type Body struct {
|
||||||
Name string
|
Name string // The name of the contacted person
|
||||||
Intros []string
|
Intros []string // Intro sentences, first displayed in the email
|
||||||
Dictionary []Entry
|
Dictionary []Entry // A list of key+value (usefull for displaying parameters/settings/personal info)
|
||||||
Actions []Action
|
Table Table // Table is an table where you can put data (pricing grid, a bill, and so on)
|
||||||
Outros []string
|
Actions []Action // Actions are a list of actions that the user will be able to execute via a button click
|
||||||
Greeting string
|
Outros []string // Outro sentences, last displayed in the email
|
||||||
Signature string
|
Greeting string // Greeting for the contacted person (default to 'Hi')
|
||||||
Title string
|
Signature string // Signature for the contacted person (default to 'Yours truly')
|
||||||
|
Title string // Title replaces the greeting+name when set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entry is a simple entry of a map
|
||||||
|
// Allows using a slice of entries instead of a map
|
||||||
|
// Because Golang maps are not ordered
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
Key string
|
Key string
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Table is an table where you can put data (pricing grid, a bill, and so on)
|
||||||
|
type Table struct {
|
||||||
|
Data [][]Entry // Contains data
|
||||||
|
Columns Columns // Contains meta-data for display purpose (width, alignement)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Columns contains meta-data for the different columns
|
||||||
|
type Columns struct {
|
||||||
|
CustomWidth map[string]string
|
||||||
|
CustomAlignement map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action is an action the user can do on the email (click on a button)
|
||||||
type Action struct {
|
type Action struct {
|
||||||
Instructions string
|
Instructions string
|
||||||
Button Button
|
Button Button
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Button defines an action to launch
|
||||||
type Button struct {
|
type Button struct {
|
||||||
Color string
|
Color string
|
||||||
Text string
|
Text string
|
||||||
Link string
|
Link string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Template is the struct given to Golang templating
|
||||||
|
// Root object in a template is this struct
|
||||||
type Template struct {
|
type Template struct {
|
||||||
Hermes Hermes
|
Hermes Hermes
|
||||||
Email Email
|
Email Email
|
||||||
}
|
}
|
||||||
|
|
||||||
type Theme interface {
|
|
||||||
Name() string
|
|
||||||
HtmlTemplate() string
|
|
||||||
PlainTextTemplate() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func setDefaultEmailValues(e *Email) error {
|
func setDefaultEmailValues(e *Email) error {
|
||||||
|
// Default values of an email
|
||||||
defaultEmail := Email{
|
defaultEmail := Email{
|
||||||
Body: Body{
|
Body: Body{
|
||||||
Signature: "Yours truly",
|
Intros: []string{},
|
||||||
Greeting: "Hi",
|
Dictionary: []Entry{},
|
||||||
|
Outros: []string{},
|
||||||
|
Signature: "Yours truly",
|
||||||
|
Greeting: "Hi",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// Merge the given email with default one
|
||||||
|
// Default one overrides all zero values
|
||||||
return mergo.Merge(e, defaultEmail)
|
return mergo.Merge(e, defaultEmail)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// default values of the engine
|
||||||
func setDefaultHermesValues(h *Hermes) error {
|
func setDefaultHermesValues(h *Hermes) error {
|
||||||
defaultTextDirection := TDLeftToRight
|
defaultTextDirection := TDLeftToRight
|
||||||
defaultHermes := Hermes{
|
defaultHermes := Hermes{
|
||||||
Theme: new(DefaultTheme),
|
Theme: new(Default),
|
||||||
TextDirection: defaultTextDirection,
|
TextDirection: defaultTextDirection,
|
||||||
Product: Product{
|
Product: Product{
|
||||||
Name: "Hermes",
|
Name: "Hermes",
|
||||||
Copyright: "Copyright © 2017 Hermes. All rights reserved.",
|
Copyright: "Copyright © 2017 Hermes. All rights reserved.",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
// Merge the given hermes engine coniguration with default one
|
||||||
|
// Default one overrides all zero values
|
||||||
err := mergo.Merge(h, defaultHermes)
|
err := mergo.Merge(h, defaultHermes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -99,30 +136,41 @@ func setDefaultHermesValues(h *Hermes) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hermes) GenerateHTML(email Email) (io.Reader, error) {
|
// GenerateHTML generates the email body from data to an HTML Reader
|
||||||
return h.generateTemplate(email, func() string {
|
// This is for modern email clients
|
||||||
return h.Theme.HtmlTemplate()
|
func (h *Hermes) GenerateHTML(email Email) (string, error) {
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
err := setDefaultHermesValues(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
setDefaultEmailValues(&email)
|
return h.generateTemplate(email, h.Theme.HTMLTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
t, err := template.New("hermes").Funcs(sprig.FuncMap()).Parse(tplt())
|
// GeneratePlainText generates the email body from data
|
||||||
|
// This is for old email clients
|
||||||
|
// Note : this mode is not able to print Tables
|
||||||
|
func (h *Hermes) GeneratePlainText(email Email) (string, error) {
|
||||||
|
err := setDefaultHermesValues(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
|
}
|
||||||
|
return h.generateTemplate(email, h.Theme.PlainTextTemplate())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Hermes) generateTemplate(email Email, tplt string) (string, error) {
|
||||||
|
|
||||||
|
err := setDefaultEmailValues(&email)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
t.Execute(&b, Template{*h, email})
|
t.Execute(&b, Template{*h, email})
|
||||||
return &b, nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHermes(t *testing.T) {
|
func TestHermes_ok(t *testing.T) {
|
||||||
|
|
||||||
h := Hermes{
|
h := Hermes{
|
||||||
Product: Product{
|
Product: Product{
|
||||||
|
@ -44,4 +44,48 @@ func TestHermes(t *testing.T) {
|
||||||
r, err := h.GenerateHTML(email)
|
r, err := h.GenerateHTML(email)
|
||||||
t.Log(r)
|
t.Log(r)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.NotEmpty(t, r)
|
||||||
|
|
||||||
|
r, err = h.GeneratePlainText(email)
|
||||||
|
t.Log(r)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotEmpty(t, r)
|
||||||
|
|
||||||
|
assert.Equal(t, h.Theme.Name(), "default")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHermes_defaultTextDirection(t *testing.T) {
|
||||||
|
h := Hermes{
|
||||||
|
Product: Product{
|
||||||
|
Name: "Hermes",
|
||||||
|
Link: "http://hermes.com",
|
||||||
|
},
|
||||||
|
TextDirection: "not-existing", // Wrong text-direction
|
||||||
|
}
|
||||||
|
|
||||||
|
email := Email{
|
||||||
|
Body{
|
||||||
|
Name: "Jon Snow",
|
||||||
|
Intros: []string{
|
||||||
|
"Welcome to Hermes! We're very excited to have you on board.",
|
||||||
|
},
|
||||||
|
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.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := h.GenerateHTML(email)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, h.TextDirection, TDLeftToRight)
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
Binary file not shown.
After Width: | Height: | Size: 84 KiB |
Binary file not shown.
After Width: | Height: | Size: 166 KiB |
Loading…
Reference in New Issue