From 124af1ee293ec2b8507c29680f35a696d6310249 Mon Sep 17 00:00:00 2001 From: Kailash Nadh Date: Mon, 25 Mar 2024 15:49:40 +0530 Subject: [PATCH] Make static e-mail template subjects scriptable. Closes #1727. This commit introduces optional, custom `` tags that can be added to `static/email-templates/*.html` so as to make the subjectlines fully scriptable for system e-mails and notifications. The tag must look like this. ``` <title data-i18n>Stuff {{ .Subscriber.Name }} here! ``` --- cmd/notifications.go | 26 +++++++++++++++++++++++--- cmd/public.go | 10 ++++++++-- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/cmd/notifications.go b/cmd/notifications.go index 0b777e07..d2415fe8 100644 --- a/cmd/notifications.go +++ b/cmd/notifications.go @@ -2,6 +2,8 @@ package main import ( "bytes" + "regexp" + "strings" "github.com/knadh/listmonk/models" ) @@ -13,6 +15,10 @@ const ( notifSubscriberData = "subscriber-data" ) +var ( + reTitle = regexp.MustCompile(`(?s)(.+?)`) +) + // notifData represents params commonly used across different notification // templates. type notifData struct { @@ -26,18 +32,21 @@ func (app *App) sendNotification(toEmails []string, subject, tplName string, dat return nil } - var b bytes.Buffer - if err := app.notifTpls.tpls.ExecuteTemplate(&b, tplName, data); err != nil { + var buf bytes.Buffer + if err := app.notifTpls.tpls.ExecuteTemplate(&buf, tplName, data); err != nil { app.log.Printf("error compiling notification template '%s': %v", tplName, err) return err } + body := buf.Bytes() + + subject, body = getTplSubject(subject, body) m := models.Message{} m.ContentType = app.notifTpls.contentType m.From = app.constants.FromEmail m.To = toEmails m.Subject = subject - m.Body = b.Bytes() + m.Body = body m.Messenger = emailMsgr if err := app.manager.PushMessage(m); err != nil { app.log.Printf("error sending admin notification (%s): %v", subject, err) @@ -45,3 +54,14 @@ func (app *App) sendNotification(toEmails []string, subject, tplName string, dat } return nil } + +// getTplSubject extrcts any custom i18n subject rendered in the given rendered +// template body. If it's not found, the incoming subject and body are returned. +func getTplSubject(subject string, body []byte) (string, []byte) { + m := reTitle.FindSubmatch(body) + if len(m) != 2 { + return subject, body + } + + return strings.TrimSpace(string(m[1])), reTitle.ReplaceAll(body, []byte("")) +} diff --git a/cmd/public.go b/cmd/public.go index 8b90d089..7216cb8b 100644 --- a/cmd/public.go +++ b/cmd/public.go @@ -590,14 +590,20 @@ func handleSelfExportSubscriberData(c echo.Context) error { makeMsgTpl(app.i18n.T("public.errorTitle"), "", app.i18n.Ts("public.errorProcessingRequest"))) } + var ( + subject = app.i18n.Ts("email.data.title") + body = msg.Bytes() + ) + subject, body = getTplSubject(subject, body) + // Send the data as a JSON attachment to the subscriber. const fname = "data.json" if err := app.messengers[emailMsgr].Push(models.Message{ ContentType: app.notifTpls.contentType, From: app.constants.FromEmail, To: []string{data.Email}, - Subject: app.i18n.Ts("email.data.title"), - Body: msg.Bytes(), + Subject: subject, + Body: body, Attachments: []models.Attachment{ { Name: fname,