Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/campaigns.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ func (a *App) sendTestMessage(sub models.Subscriber, camp *models.Campaign) erro
func (a *App) validateCampaignFields(c campReq) (campReq, error) {
if c.FromEmail == "" {
c.FromEmail = a.cfg.FromEmail
} else if !reFromAddress.Match([]byte(c.FromEmail)) {
} else if !models.HasTplExpr(c.FromEmail) && !reFromAddress.Match([]byte(c.FromEmail)) {
if _, err := a.importer.SanitizeEmail(c.FromEmail); err != nil {
return c, errors.New(a.i18n.T("campaigns.fieldInvalidFromEmail"))
}
Expand Down
9 changes: 9 additions & 0 deletions internal/manager/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,15 @@ func (m *CampaignMessage) render() error {
out.Reset()
}

// Render the From header if it's a template.
if m.Campaign.FromEmailTpl != nil {
if err := m.Campaign.FromEmailTpl.ExecuteTemplate(&out, models.ContentTpl, m); err != nil {
return err
}
m.from = out.String()
out.Reset()
}

// Compile the main template.
if err := m.Campaign.Tpl.ExecuteTemplate(&out, models.BaseTpl, m); err != nil {
return err
Expand Down
86 changes: 46 additions & 40 deletions models/campaigns.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ type Campaign struct {
ArchiveTemplateBody string `db:"archive_template_body" json:"-"`
Tpl *template.Template `json:"-"`
SubjectTpl *txttpl.Template `json:"-"`
FromEmailTpl *txttpl.Template `json:"-"`
AltBodyTpl *template.Template `json:"-"`

// HeaderTpls is holds optionally {{ templated }} campaign headers.
Expand Down Expand Up @@ -139,19 +140,15 @@ func (camps Campaigns) LoadStats(stmt *sqlx.Stmt) error {
// CompileTemplate compiles a campaign body template into its base
// template and sets the resultant template to Campaign.Tpl.
func (c *Campaign) CompileTemplate(f template.FuncMap) error {
// If the subject line has a template string, compile it.
if hasTplExpr(c.Subject) {
subj := c.Subject
for _, r := range regTplFuncs {
subj = r.regExp.ReplaceAllString(subj, r.replace)
}
var err error
c.SubjectTpl, err = compileTxtTpl("subject", c.Subject, f)
if err != nil {
return err
}

var txtFuncs map[string]any = f
subjTpl, err := txttpl.New(ContentTpl).Funcs(txtFuncs).Parse(subj)
if err != nil {
return fmt.Errorf("error compiling subject: %v", err)
}
c.SubjectTpl = subjTpl
c.FromEmailTpl, err = compileTxtTpl("from", c.FromEmail, f)
if err != nil {
return err
}

// Compile the base template.
Expand Down Expand Up @@ -197,7 +194,7 @@ func (c *Campaign) CompileTemplate(f template.FuncMap) error {
}
c.Tpl = out

if hasTplExpr(c.AltBody.String) {
if HasTplExpr(c.AltBody.String) {
b := c.AltBody.String
for _, r := range regTplFuncs {
b = r.regExp.ReplaceAllString(b, r.replace)
Expand All @@ -209,42 +206,45 @@ func (c *Campaign) CompileTemplate(f template.FuncMap) error {
c.AltBodyTpl = bTpl
}

// Compile any header values that contain template expressions.
for _, set := range c.Headers {
for _, val := range set {
if hasTplExpr(val) {
for i, set := range c.Headers {
for hdr, val := range set {
if !HasTplExpr(val) {
continue
}

tpl, err := compileTxtTpl(fmt.Sprintf("header %q", hdr), val, f)
if err != nil {
return err
}

if c.HeaderTpls == nil {
c.HeaderTpls = make([]map[string]*txttpl.Template, len(c.Headers))
break
}
}
if c.HeaderTpls != nil {
break
}
}
if c.HeaderTpls != nil {
var txtFuncs map[string]any = f
for i, set := range c.Headers {
c.HeaderTpls[i] = make(map[string]*txttpl.Template, len(set))
for hdr, val := range set {
if !hasTplExpr(val) {
continue
}
tpl, err := txttpl.New(ContentTpl).Funcs(txtFuncs).Parse(val)
if err != nil {
return fmt.Errorf("error compiling header %q: %v", hdr, err)
}
c.HeaderTpls[i][hdr] = tpl
if c.HeaderTpls[i] == nil {
c.HeaderTpls[i] = make(map[string]*txttpl.Template)
}
c.HeaderTpls[i][hdr] = tpl
}
}

return nil
}

// hasTplExpr checks whether a given string has a Go template expression with {{ and }}.
func hasTplExpr(s string) bool {
_, after, ok := strings.Cut(s, "{{")
return ok && strings.Contains(after, "}}")
func compileTxtTpl(label, val string, f template.FuncMap) (*txttpl.Template, error) {
if !HasTplExpr(val) {
return nil, nil
}

for _, r := range regTplFuncs {
val = r.regExp.ReplaceAllString(val, r.replace)
}

tpl, err := txttpl.New(ContentTpl).Funcs(f).Parse(val)
if err != nil {
return nil, fmt.Errorf("error compiling %s: %v", label, err)
}

return tpl, nil
}

// ConvertContent converts a campaign's body from one format to another,
Expand All @@ -270,3 +270,9 @@ func (c *Campaign) ConvertContent(from, to string) (string, error) {

return out, nil
}

// HasTplExpr checks whether a given string has a Go template expression with {{ and }}.
func HasTplExpr(s string) bool {
_, after, ok := strings.Cut(s, "{{")
return ok && strings.Contains(after, "}}")
}
4 changes: 2 additions & 2 deletions models/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (m *TxMessage) Render(sub Subscriber, tpl *Template, funcs txttpl.FuncMap)
b.Reset()

// Render alt body if it has any templating strings.
if m.AltBody != "" && hasTplExpr(m.AltBody) {
if m.AltBody != "" && HasTplExpr(m.AltBody) {
t, err := txttpl.New(BaseTpl).Funcs(funcs).Parse(m.AltBody)
if err != nil {
return fmt.Errorf("error compiling alt body: %v", err)
Expand All @@ -104,7 +104,7 @@ func (m *TxMessage) Render(sub Subscriber, tpl *Template, funcs txttpl.FuncMap)
subject = m.Subject
)
if subject != "" {
if hasTplExpr(m.Subject) {
if HasTplExpr(m.Subject) {
// If the subject has a template string, render that.
s, err := txttpl.New(BaseTpl).Funcs(funcs).Parse(m.Subject)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions models/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ func (t *Template) Compile(f template.FuncMap) error {
t.Tpl = tpl

// If the subject line has a template string, compile it.
if hasTplExpr(t.Subject) {
if HasTplExpr(t.Subject) {
subj := t.Subject

subjTpl, err := txttpl.New(BaseTpl).Funcs(txttpl.FuncMap(f)).Parse(subj)
subjTpl, err := txttpl.New(BaseTpl).Funcs(f).Parse(subj)
if err != nil {
return fmt.Errorf("error compiling subject: %v", err)
}
Expand Down