diff --git a/email-templates/default.tpl b/email-templates/default.tpl
index f76dc9706..e553d0c4a 100644
--- a/email-templates/default.tpl
+++ b/email-templates/default.tpl
@@ -63,7 +63,7 @@
{{ TrackView }}
diff --git a/main.go b/main.go
index b4d9ca854..1258b37c4 100644
--- a/main.go
+++ b/main.go
@@ -313,7 +313,7 @@ func main() {
FromEmail: app.Constants.FromEmail,
// url.com/unsubscribe/{campaign_uuid}/{subscriber_uuid}
- UnsubscribeURL: fmt.Sprintf("%s/subscription/%%s/%%s", app.Constants.RootURL),
+ UnsubURL: fmt.Sprintf("%s/subscription/%%s/%%s", app.Constants.RootURL),
// url.com/link/{campaign_uuid}/{subscriber_uuid}/{link_uuid}
LinkTrackURL: fmt.Sprintf("%s/link/%%s/%%s/%%s", app.Constants.RootURL),
diff --git a/manager/manager.go b/manager/manager.go
index 4e21ca7e2..1a642b73e 100644
--- a/manager/manager.go
+++ b/manager/manager.go
@@ -59,12 +59,13 @@ type Manager struct {
// Message represents an active subscriber that's being processed.
type Message struct {
- Campaign *models.Campaign
- Subscriber *models.Subscriber
- UnsubscribeURL string
- Body []byte
- from string
- to string
+ Campaign *models.Campaign
+ Subscriber *models.Subscriber
+ Body []byte
+
+ unsubURL string
+ from string
+ to string
}
// Config has parameters for configuring the manager.
@@ -74,7 +75,7 @@ type Config struct {
RequeueOnError bool
FromEmail string
LinkTrackURL string
- UnsubscribeURL string
+ UnsubURL string
ViewTrackURL string
}
@@ -104,11 +105,12 @@ func New(cfg Config, src DataSource, notifCB models.AdminNotifCallback, l *log.L
// to message templates while they're compiled.
func (m *Manager) NewMessage(c *models.Campaign, s *models.Subscriber) *Message {
return &Message{
- from: c.FromEmail,
- to: s.Email,
- Campaign: c,
- Subscriber: s,
- UnsubscribeURL: fmt.Sprintf(m.cfg.UnsubscribeURL, c.UUID, s.UUID),
+ Campaign: c,
+ Subscriber: s,
+
+ from: c.FromEmail,
+ to: s.Email,
+ unsubURL: fmt.Sprintf(m.cfg.UnsubURL, c.UUID, s.UUID),
}
}
@@ -413,12 +415,15 @@ func (m *Manager) sendNotif(c *models.Campaign, status, reason string) error {
// compiled campaign templates.
func (m *Manager) TemplateFuncs(c *models.Campaign) template.FuncMap {
return template.FuncMap{
- "TrackLink": func(url, campUUID, subUUID string) string {
- return m.trackLink(url, campUUID, subUUID)
+ "TrackLink": func(url string, msg *Message) string {
+ return m.trackLink(url, msg.Campaign.UUID, msg.Subscriber.UUID)
},
- "TrackView": func(campUUID, subUUID string) template.HTML {
+ "TrackView": func(msg *Message) template.HTML {
return template.HTML(fmt.Sprintf(``,
- fmt.Sprintf(m.cfg.ViewTrackURL, campUUID, subUUID)))
+ fmt.Sprintf(m.cfg.ViewTrackURL, msg.Campaign.UUID, msg.Subscriber.UUID)))
+ },
+ "UnsubscribeURL": func(msg *Message) string {
+ return msg.unsubURL
},
"Date": func(layout string) string {
if layout == "" {
diff --git a/models/models.go b/models/models.go
index 60c77e278..17cab9925 100644
--- a/models/models.go
+++ b/models/models.go
@@ -48,16 +48,27 @@ const (
ContentTpl = "content"
)
+// regTplFunc represents contains a regular expression for wrapping and
+// substituting a Go template function from the user's shorthand to a full
+// function call.
+type regTplFunc struct {
+ regExp *regexp.Regexp
+ replace string
+}
+
// Regular expression for matching {{ Track "http://link.com" }} in the template
// and substituting it with {{ Track "http://link.com" .Campaign.UUID .Subscriber.UUID }}
// before compilation. This string gimmick is to make linking easier for users.
-var (
- regexpLinkTag = regexp.MustCompile("{{(\\s+)?TrackLink\\s+?(\"|`)(.+?)(\"|`)(\\s+)?}}")
- regexpLinkTagReplace = `{{ TrackLink "$3" .Campaign.UUID .Subscriber.UUID }}`
-
- regexpViewTag = regexp.MustCompile(`{{(\s+)?TrackView(\s+)?}}`)
- regexpViewTagReplace = `{{ TrackView .Campaign.UUID .Subscriber.UUID }}`
-)
+var regTplFuncs = []regTplFunc{
+ regTplFunc{
+ regExp: regexp.MustCompile("{{(\\s+)?TrackLink\\s+?(\"|`)(.+?)(\"|`)(\\s+)?}}"),
+ replace: `{{ TrackLink "$3" . }}`,
+ },
+ regTplFunc{
+ regExp: regexp.MustCompile(`{{(\s+)?(TrackView|UnsubscribeURL|OptinURL)(\s+)?}}`),
+ replace: `{{ $2 . }}`,
+ },
+}
// AdminNotifCallback is a callback function that's called
// when a campaign's status changes.
@@ -264,17 +275,21 @@ func (camps Campaigns) LoadStats(stmt *sqlx.Stmt) error {
// template and sets the resultant template to Campaign.Tpl.
func (c *Campaign) CompileTemplate(f template.FuncMap) error {
// Compile the base template.
- t := regexpLinkTag.ReplaceAllString(c.TemplateBody, regexpLinkTagReplace)
- t = regexpViewTag.ReplaceAllString(t, regexpViewTagReplace)
- baseTPL, err := template.New(BaseTpl).Funcs(f).Parse(t)
+ body := c.TemplateBody
+ for _, r := range regTplFuncs {
+ body = r.regExp.ReplaceAllString(body, r.replace)
+ }
+ baseTPL, err := template.New(BaseTpl).Funcs(f).Parse(body)
if err != nil {
return fmt.Errorf("error compiling base template: %v", err)
}
// Compile the campaign message.
- t = regexpLinkTag.ReplaceAllString(c.Body, regexpLinkTagReplace)
- t = regexpViewTag.ReplaceAllString(t, regexpViewTagReplace)
- msgTpl, err := template.New(ContentTpl).Funcs(f).Parse(t)
+ body = c.Body
+ for _, r := range regTplFuncs {
+ body = r.regExp.ReplaceAllString(body, r.replace)
+ }
+ msgTpl, err := template.New(ContentTpl).Funcs(f).Parse(body)
if err != nil {
return fmt.Errorf("error compiling message: %v", err)
}