diff --git a/mail/campaign.go b/mail/campaign.go index 324921a..d76fb36 100644 --- a/mail/campaign.go +++ b/mail/campaign.go @@ -66,7 +66,7 @@ func (c *Campaign) renderMessage(m *gomail.Message, i int) error { } // Render content through Markdown and into a layout - hLayoutFile := AppFs.layoutPath("_default.html") + hLayoutFile := AppFs.layoutPath(ctx.Campaign.LayoutFile) htmlBody, err := renderHTML(content.Bytes(), hLayoutFile, ctx) if err != nil { return err @@ -75,13 +75,25 @@ func (c *Campaign) renderMessage(m *gomail.Message, i int) error { toEmail := cast.ToString(ctx.Recipient.Email) toName := cast.ToString(ctx.Recipient.Name) + // Respect recipient subject is it exists + var sub = cast.ToString(ctx.Campaign.Subject) + if ctx.Recipient.Subject != "" { + sub = cast.ToString(ctx.Recipient.Subject) + } + m.Reset() // Return to NewMessage state m.SetAddressHeader("To", toEmail, toName) - m.SetHeader("Subject", cast.ToString(ctx.Campaign.Subject)) + m.SetHeader("Subject", sub) m.SetHeader("From", cast.ToString(ctx.Campaign.From)) m.SetHeader("X-Mailer", xMailer) m.SetBody("text/plain", plainBody) m.AddAlternative("text/html", htmlBody) + + // Add attachments + for _, att := range ctx.Recipient.Attachments { + m.Attach(att) + } + return nil } @@ -176,8 +188,11 @@ func parseRecipients(path string) ([]*ctxRecipient, error) { out := make([]*ctxRecipient, len(data)) for i, rData := range data { - r := newRecipient(rData) - out[i] = &r + r, err := newRecipient(rData) + if err != nil { + return nil, err + } + out[i] = r } return out, nil diff --git a/mail/config.go b/mail/config.go index 428091e..85f43b4 100644 --- a/mail/config.go +++ b/mail/config.go @@ -37,10 +37,11 @@ type ConfigFile struct { DKIM map[string]interface{} // Directories - ContentDir string - LayoutDir string - ThemeDir string - ListDir string + ContentDir string + LayoutDir string + ThemeDir string + ListDir string + AttachmentDir string // Delivery SendRate float32 @@ -83,6 +84,8 @@ func InitConfig(cfgFile string) { // From --config if cfgFile != "" { v.SetConfigFile(cfgFile) + } else { + v.SetConfigName("config") } // Tie configuration to ENV @@ -101,12 +104,12 @@ func InitConfig(cfgFile string) { v.SetDefault("layoutDir", "layouts") v.SetDefault("themeDir", "themes") v.SetDefault("listDir", "lists") + v.SetDefault("attachmentDir", "attachments") // Delivery workers/rate v.SetDefault("sendRate", 1) v.SetDefault("workers", 3) // Prepare for project's config.* - v.SetConfigName("config") v.AddConfigPath(".") } diff --git a/mail/context.go b/mail/context.go index 27169f7..53fdbad 100644 --- a/mail/context.go +++ b/mail/context.go @@ -2,6 +2,8 @@ package mail import ( "encoding/json" + "fmt" + "path/filepath" "strings" ) @@ -24,25 +26,52 @@ func (c *context) toFlatMap() map[string]interface{} { // Recipient variable type ctxRecipient struct { - Name string - Email string - Params map[string]interface{} + Name string + Email string + Subject string + Attachments []string + Params map[string]interface{} } -func newRecipient(data map[string]interface{}) ctxRecipient { - r := ctxRecipient{Params: keysToLower(data)} +func newRecipient(data map[string]interface{}) (*ctxRecipient, error) { + r := &ctxRecipient{Params: keysToLower(data)} r.Email, _ = r.Params["email"].(string) r.Name, _ = r.Params["name"].(string) + r.Subject, _ = r.Params["subject"].(string) + if att, ok := r.Params["attachments"].([]interface{}); ok { + a := make([]string, len(att)) + for i, v := range att { + attName, ok := v.(string) + if !ok { + continue + } + + // Validate if the file exists or not + p, err := filepath.Abs(AppFs.AttachmentPath(attName)) + if err != nil { + return nil, err + } + if !AppFs.isFile(p) { + return nil, fmt.Errorf("Cannot find attachment in %s", p) + } + + a[i] = p + } + r.Attachments = a + } delete(r.Params, "email") delete(r.Params, "name") - return r + delete(r.Params, "subject") + delete(r.Params, "attachments") + return r, nil } // Campaign variable type ctxCampaign struct { - From string - Subject string - Params map[string]interface{} + From string + Subject string + LayoutFile string + Params map[string]interface{} } func newCampaign(data map[string]interface{}) ctxCampaign { @@ -51,9 +80,13 @@ func newCampaign(data map[string]interface{}) ctxCampaign { if c.From, _ = c.Params["from"].(string); c.From == "" { c.From = Config.From } + if c.LayoutFile, _ = c.Params["layout_file"].(string); c.LayoutFile == "" { + c.LayoutFile = "_default.html" + } delete(c.Params, "subject") delete(c.Params, "from") + delete(c.Params, "layout_file") return c } diff --git a/mail/fs.go b/mail/fs.go index be9ff43..a138a37 100644 --- a/mail/fs.go +++ b/mail/fs.go @@ -1,8 +1,9 @@ package mail import ( - "github.com/spf13/afero" "path/filepath" + + "github.com/spf13/afero" ) var ( @@ -28,6 +29,10 @@ func (f *fs) ListPath(name string) string { return filepath.Join(Config.ListDir, name) } +func (f *fs) AttachmentPath(name string) string { + return filepath.Join(Config.AttachmentDir, name) +} + func (f *fs) layoutPath(name string) string { p := []string{filepath.Join(Config.LayoutDir, name)} if t := Config.Theme; t != "" {