diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66b66e3..943f0e3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -334,3 +334,14 @@ In order to support generating RTL e-mails, inject the `textDirection` variable ``` +## 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 }} +``` diff --git a/README.md b/README.md index 79d0acd..c20ddf3 100644 --- a/README.md +++ b/README.md @@ -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: +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//`, 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: +* `flat`, slightly modified from [Postmark Transactional Email Templates](https://github.com/wildbit/postmark-templates) + + + ## RTL Support To change the default text direction (left-to-right), simply override it as follows: @@ -247,13 +271,11 @@ 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 +```go email := hermes.Email{ Body: hermes.Body{ Dictionary: []hermes.Entry{ @@ -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: + + + +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. diff --git a/default.go b/default.go index c632288..527069e 100644 --- a/default.go +++ b/default.go @@ -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 { -
+ @@ -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 `

{{if .Email.Body.Title }}{{ .Email.Body.Title }}{{ else }}{{ .Email.Body.Greeting }} {{ .Email.Body.Name }}{{ end }},

+{{ with .Email.Body.Intros }} + {{ range $line := . }} +

{{ $line }}

+ {{ end }} +{{ end }} +{{ if (ne .Email.Body.FreeMarkdown "") }} + {{ .Email.Body.FreeMarkdown.ToHTML }} +{{ else }} + {{ with .Email.Body.Dictionary }} +
    + {{ range $entry := . }} +
  • {{ $entry.Key }}: {{ $entry.Value }}
  • + {{ end }} +
+ {{ end }} + {{ with .Email.Body.Table }} + {{ $data := .Data }} + {{ $columns := .Columns }} + {{ if gt (len $data) 0 }} + + + {{ $col := index $data 0 }} + {{ range $entry := $col }} + + {{ end }} + + {{ range $row := $data }} + + {{ range $cell := $row }} + + {{ end }} + + {{ end }} +
{{ $entry.Key }}
+ {{ $cell.Value }} +
+ {{ end }} + {{ end }} + {{ with .Email.Body.Actions }} + {{ range $action := . }} +

{{ $action.Instructions }} {{ $action.Button.Link }}

+ {{ end }} + {{ end }} +{{ end }} +{{ with .Email.Body.Outros }} + {{ range $line := . }} +

{{ $line }}

+ {{ end }} +{{ end }} +

{{.Email.Body.Signature}},
{{.Hermes.Product.Name}} - {{.Hermes.Product.Link}}

-{{.Email.Body.Signature}}, -{{.Hermes.Product.Name}} - {{.Hermes.Product.Link}} - -{{.Hermes.Product.Copyright}} +

{{.Hermes.Product.Copyright}}

` } diff --git a/examples/default/default.maintenance.html b/examples/default/default.maintenance.html new file mode 100644 index 0000000..467dd97 --- /dev/null +++ b/examples/default/default.maintenance.html @@ -0,0 +1,345 @@ + + + + + + + + + + + + + + + + diff --git a/examples/default/default.maintenance.txt b/examples/default/default.maintenance.txt new file mode 100644 index 0000000..2b290fe --- /dev/null +++ b/examples/default/default.maintenance.txt @@ -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. \ No newline at end of file diff --git a/examples/default/default.receipt.html b/examples/default/default.receipt.html index 2d821ff..eca17f9 100644 --- a/examples/default/default.receipt.html +++ b/examples/default/default.receipt.html @@ -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 @@ -