Skip to content

Commit

Permalink
Add support for plaintext system e-mail templates.
Browse files Browse the repository at this point in the history
If `<!doctype html>` is not found in static/email-templates/base.html,
all system e-mail templates are assumed to be plaintext and go out
as content-type: plaintext e-mails. With this, all HTML tags can
be stripped out of the system e-mail templates (while maintaining
Go template tags and logic) to have plaintext system e-mail templates.

Closes #546
  • Loading branch information
knadh committed Oct 28, 2021
1 parent 1c8ac0f commit b290d27
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 12 deletions.
2 changes: 1 addition & 1 deletion cmd/campaigns.go
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ func makeOptinCampaignMessage(o campaignReq, app *App) (campaignReq, error) {

// Prepare sample opt-in message for the campaign.
var b bytes.Buffer
if err := app.notifTpls.ExecuteTemplate(&b, "optin-campaign", struct {
if err := app.notifTpls.tpls.ExecuteTemplate(&b, "optin-campaign", struct {
Lists []models.List
OptinURLAttr template.HTMLAttr
}{lists, optinURLAttr}); err != nil {
Expand Down
38 changes: 35 additions & 3 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"encoding/json"
"fmt"
"html/template"
Expand Down Expand Up @@ -32,6 +33,7 @@ import (
"github.com/knadh/listmonk/internal/messenger/email"
"github.com/knadh/listmonk/internal/messenger/postback"
"github.com/knadh/listmonk/internal/subimporter"
"github.com/knadh/listmonk/models"
"github.com/knadh/stuffbin"
"github.com/labstack/echo"
flag "github.com/spf13/pflag"
Expand Down Expand Up @@ -78,6 +80,11 @@ type constants struct {
BounceSendgridEnabled bool
}

type notifTpls struct {
tpls *template.Template
contentType string
}

func initFlags() {
f := flag.NewFlagSet("config", flag.ContinueOnError)
f.Usage = func() {
Expand Down Expand Up @@ -491,7 +498,7 @@ func initMediaStore() media.Store {

// initNotifTemplates compiles and returns e-mail notification templates that are
// used for sending ad-hoc notifications to admins and subscribers.
func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *constants) *template.Template {
func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *constants) *notifTpls {
// Register utility functions that the e-mail templates can use.
funcs := template.FuncMap{
"RootURL": func() string {
Expand All @@ -505,11 +512,36 @@ func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *c
},
}

tpl, err := stuffbin.ParseTemplatesGlob(funcs, fs, "/static/email-templates/*.html")
tpls, err := stuffbin.ParseTemplatesGlob(funcs, fs, "/static/email-templates/*.html")
if err != nil {
lo.Fatalf("error parsing e-mail notif templates: %v", err)
}
return tpl

html, err := fs.Read("/static/email-templates/base.html")
if err != nil {
lo.Fatalf("error reading static/email-templates/base.html: %v", err)
}

out := &notifTpls{
tpls: tpls,
contentType: models.CampaignContentTypeHTML,
}

// Determine whether the notification templates are HTML or plaintext.
// Copy the first few (arbitrary) bytes of the template and check if has the <!doctype html> tag.
ln := 256
if len(html) < ln {
ln = len(html)
}
h := make([]byte, ln)
copy(h, html[0:ln])

if !bytes.Contains(bytes.ToLower(h), []byte("<!doctype html>")) {
out.contentType = models.CampaignContentTypePlain
lo.Println("system e-mail templates are plaintext")
}

return out
}

// initBounceManager initializes the bounce manager that scans mailboxes and listens to webhooks
Expand Down
3 changes: 1 addition & 2 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main
import (
"context"
"fmt"
"html/template"
"io"
"log"
"os"
Expand Down Expand Up @@ -43,7 +42,7 @@ type App struct {
media media.Store
i18n *i18n.I18n
bounce *bounce.Manager
notifTpls *template.Template
notifTpls *notifTpls
log *log.Logger
bufLog *buflog.BufLog

Expand Down
3 changes: 2 additions & 1 deletion cmd/notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ func (app *App) sendNotification(toEmails []string, subject, tplName string, dat
}

var b bytes.Buffer
if err := app.notifTpls.ExecuteTemplate(&b, tplName, data); err != nil {
if err := app.notifTpls.tpls.ExecuteTemplate(&b, tplName, data); err != nil {
app.log.Printf("error compiling notification template '%s': %v", tplName, err)
return err
}

m := manager.Message{}
m.ContentType = app.notifTpls.contentType
m.From = app.constants.FromEmail
m.To = toEmails
m.Subject = subject
Expand Down
11 changes: 6 additions & 5 deletions cmd/public.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ func handleSelfExportSubscriberData(c echo.Context) error {

// Prepare the attachment e-mail.
var msg bytes.Buffer
if err := app.notifTpls.ExecuteTemplate(&msg, notifSubscriberData, data); err != nil {
if err := app.notifTpls.tpls.ExecuteTemplate(&msg, notifSubscriberData, data); err != nil {
app.log.Printf("error compiling notification template '%s': %v", notifSubscriberData, err)
return c.Render(http.StatusInternalServerError, tplMessage,
makeMsgTpl(app.i18n.T("public.errorTitle"), "",
Expand All @@ -447,10 +447,11 @@ func handleSelfExportSubscriberData(c echo.Context) error {
// Send the data as a JSON attachment to the subscriber.
const fname = "data.json"
if err := app.messengers[emailMsgr].Push(messenger.Message{
From: app.constants.FromEmail,
To: []string{data.Email},
Subject: "Your data",
Body: msg.Bytes(),
ContentType: app.notifTpls.contentType,
From: app.constants.FromEmail,
To: []string{data.Email},
Subject: "Your data",
Body: msg.Bytes(),
Attachments: []messenger.Attachment{
{
Name: fname,
Expand Down

0 comments on commit b290d27

Please sign in to comment.