Make static e-mail template subjects scriptable. Closes #1727.

This commit introduces optional, custom `<title>` 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!</title>
```
This commit is contained in:
Kailash Nadh 2024-03-25 15:49:40 +05:30
parent f04798a329
commit 124af1ee29
2 changed files with 31 additions and 5 deletions

View file

@ -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)<title\s*data-i18n\s*>(.+?)</title>`)
)
// 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(""))
}

View file

@ -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,