|
- package mail
-
- import (
- "bytes"
- "fmt"
- "html/template"
- "net/mail"
- "net/url"
- "strings"
-
- "git.kirsle.net/apps/gophertype/pkg/console"
- "git.kirsle.net/apps/gophertype/pkg/markdown"
- "git.kirsle.net/apps/gophertype/pkg/models"
- "git.kirsle.net/apps/gophertype/pkg/responses"
- "git.kirsle.net/apps/gophertype/pkg/settings"
- "github.com/microcosm-cc/bluemonday"
- gomail "gopkg.in/gomail.v2"
- )
-
- // Email configuration.
- type Email struct {
- To string
- ReplyTo string
- Admin bool /* admin view of the email */
- Subject string
- UnsubscribeURL string
- Data map[string]interface{}
-
- Template string
- }
-
- // SendEmail sends an email.
- func SendEmail(email Email) {
- s := settings.Current
-
- // Suppress sending any mail when no mail settings are configured, but go
- // through the motions -- great for local dev.
- var doNotMail bool
- if !s.MailEnabled || s.MailHost == "" || s.MailPort == 0 || s.MailSender == "" {
- console.Info("Suppressing email: not completely configured")
- doNotMail = true
- }
-
- // Resolve the template.
- tmpl, err := responses.GetFile(email.Template)
- if err != nil {
- console.Error("SendEmail: %s", err.Error())
- return
- }
-
- // Render the template to HTML.
- var html bytes.Buffer
- t := template.New(email.Template)
- t, err = t.Parse(string(tmpl))
- if err != nil {
- console.Error("SendEmail: template parsing error: %s", err.Error())
- }
-
- // Execute the template.
- err = t.ExecuteTemplate(&html, email.Template, email)
- if err != nil {
- console.Error("SendEmail: template execution error: %s", err.Error())
- }
-
- // Condense the body down to plain text, lazily. Who even has a plain text
- // email client anymore?
- rawLines := strings.Split(
- bluemonday.StrictPolicy().Sanitize(html.String()),
- "\n",
- )
- var lines []string
- for _, line := range rawLines {
- line = strings.TrimSpace(line)
- if len(line) == 0 {
- continue
- }
- lines = append(lines, line)
- }
- plaintext := strings.Join(lines, "\n\n")
-
- // If we're not actually going to send the mail, this is a good place to stop.
- if doNotMail {
- console.Info("Not going to send an email.")
- console.Debug("The message was going to be:\n%s", plaintext)
- return
- }
-
- m := gomail.NewMessage()
- m.SetHeader("From", fmt.Sprintf("%s <%s>", s.Title, s.MailSender))
- m.SetHeader("To", email.To)
- if email.ReplyTo != "" {
- m.SetHeader("Reply-To", email.ReplyTo)
- }
- m.SetHeader("Subject", email.Subject)
- m.SetBody("text/plain", plaintext)
- m.AddAlternative("text/html", html.String())
-
- d := gomail.NewDialer(s.MailHost, s.MailPort, s.MailUsername, s.MailPassword)
-
- console.Info("SendEmail: %s (%s) to %s", email.Subject, email.Template, email.To)
- if err := d.DialAndSend(m); err != nil {
- console.Error("SendEmail: %s", err.Error())
- }
- }
-
- // EmailAdmins sends an e-mail to all admin user email addresses.
- func EmailAdmins(email Email) {
- if adminEmails, err := models.Users.ListAdminEmails(); err == nil {
- email.To = strings.Join(adminEmails, ", ")
- email.Admin = true
- console.Info("Mail site admin '%s' about email '%s'", email.To, email.Subject)
- SendEmail(email)
- }
- }
-
- // NotifyComment sends notification emails about comments.
- func NotifyComment(subject string, originURL string, c models.Comment) {
- s := settings.Current
- if s.BaseURL == "" {
- console.Error("Can't send comment notification because the site URL is not configured")
- return
- }
-
- // Prepare the email payload.
- email := Email{
- Template: "_builtin/email/comment.gohtml",
- Subject: "Comment Added: " + subject,
- Data: map[string]interface{}{
- "Name": c.Name,
- "Subject": subject,
- "Body": template.HTML(markdown.RenderMarkdown(c.Body)),
- "URL": strings.Trim(s.BaseURL, "/") + originURL,
- "QuickDelete": fmt.Sprintf("%s/comments/quick-delete?d=%s&next=%s",
- strings.Trim(s.BaseURL, "/"),
- url.QueryEscape(c.DeleteToken),
- url.QueryEscape(strings.Trim(s.BaseURL, "/")+originURL),
- ),
- },
- }
-
- // Email the site admins.
- if adminEmails, err := models.Users.ListAdminEmails(); err == nil {
- email.To = strings.Join(adminEmails, ", ")
- email.Admin = true
- console.Info("Mail site admin '%s' about comment notification on '%s'", email.To, c.Thread)
- SendEmail(email)
- }
-
- // // Email the subscribers.
- email.Admin = false
- if subscribers, err := models.Comments.GetSubscribers(c.Thread); err == nil {
- email.Subject = "A new comment has been added to: " + subject
-
- for _, to := range subscribers {
- // Don't email to the writer of the comment.
- if to == c.Email {
- continue
- }
- email.To = to
- email.UnsubscribeURL = fmt.Sprintf("%s/comments/subscription?t=%s&e=%s",
- strings.Trim(s.BaseURL, "/"),
- url.QueryEscape(c.Thread),
- url.QueryEscape(to),
- )
- console.Info("Mail subscriber '%s' about comment notification on '%s'", email.To, c.Thread)
- SendEmail(email)
- }
- }
- }
-
- // ParseAddress parses an email address.
- func ParseAddress(addr string) (*mail.Address, error) {
- return mail.ParseAddress(addr)
- }
|