diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d98352b Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 4513c3d..edf5b1e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ -# hermes -Golang package that generates clean, responsive HTML e-mails for sending transactional mail +# Hermes + +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 diff --git a/default.go b/default.go new file mode 100644 index 0000000..d112606 --- /dev/null +++ b/default.go @@ -0,0 +1,366 @@ +package hermes + +type DefaultTheme struct { + name string +} + +func (dt *DefaultTheme) Name() string { + return "default" +} + +func (dt *DefaultTheme) HtmlTemplate() string { + return ` + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + +` +} + +func (dt *DefaultTheme) PlainTextTemplate() string { + return ` +{{.Email.Body.Greeting}} {{.Email.Body.Name}}, +
+
+ +{{ 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 }} + +{{.Email.Body.Signature}}, +
+{{.Hermes.Product.Name}} + +
+
+{{.Hermes.Product.Copyright}} +` +} diff --git a/examples/.DS_Store b/examples/.DS_Store new file mode 100644 index 0000000..0f32880 Binary files /dev/null and b/examples/.DS_Store differ diff --git a/examples/default/default.html b/examples/default/default.html new file mode 100644 index 0000000..acd3f07 --- /dev/null +++ b/examples/default/default.html @@ -0,0 +1,49 @@ + +Hi Jon Snow, +
+
+ + + +Welcome to Hermes! We're very excited to have you on board.
+ +
+ + + + +Firstname: Jon
+ +Lastname: Snow
+ +Birthday: 01/01/283
+ +
+ + + + +To get started with Hermes, please click here: +
+https://hermes-example.com/confirm?token=d9729feb74992cc3482b350163a1a010 +
+ +
+ + + + + +Need help, or have questions? Just reply to this email, we'd love to help. +
+ +
+ + +Yours truly, +
+Hermes + +
+
+Copyright © 2017 Hermes. All rights reserved. diff --git a/examples/default/default.plaintext.html b/examples/default/default.plaintext.html new file mode 100644 index 0000000..e9f6540 --- /dev/null +++ b/examples/default/default.plaintext.html @@ -0,0 +1,308 @@ + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + diff --git a/examples/default/main.go b/examples/default/main.go new file mode 100644 index 0000000..f407d5a --- /dev/null +++ b/examples/default/main.go @@ -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) +} diff --git a/hermes.go b/hermes.go index f38e434..2d91381 100644 --- a/hermes.go +++ b/hermes.go @@ -1 +1,128 @@ 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 +} diff --git a/hermes_test.go b/hermes_test.go new file mode 100644 index 0000000..a0b02ae --- /dev/null +++ b/hermes_test.go @@ -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) +}