summaryrefslogtreecommitdiffstats
path: root/services/mailer/mail.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/mailer/mail.go')
-rw-r--r--services/mailer/mail.go222
1 files changed, 222 insertions, 0 deletions
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
new file mode 100644
index 0000000000..3f0a789dc4
--- /dev/null
+++ b/services/mailer/mail.go
@@ -0,0 +1,222 @@
+// Copyright 2016 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mailer
+
+import (
+ "bytes"
+ "fmt"
+ "html/template"
+ "path"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/markup"
+ "code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "gopkg.in/gomail.v2"
+)
+
+const (
+ mailAuthActivate base.TplName = "auth/activate"
+ mailAuthActivateEmail base.TplName = "auth/activate_email"
+ mailAuthResetPassword base.TplName = "auth/reset_passwd"
+ mailAuthRegisterNotify base.TplName = "auth/register_notify"
+
+ mailIssueComment base.TplName = "issue/comment"
+ mailIssueMention base.TplName = "issue/mention"
+
+ mailNotifyCollaborator base.TplName = "notify/collaborator"
+)
+
+var templates *template.Template
+
+// InitMailRender initializes the mail renderer
+func InitMailRender(tmpls *template.Template) {
+ templates = tmpls
+}
+
+// SendTestMail sends a test mail
+func SendTestMail(email string) error {
+ return gomail.Send(Sender, NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").Message)
+}
+
+// SendUserMail sends a mail to the user
+func SendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) {
+ data := map[string]interface{}{
+ "DisplayName": u.DisplayName(),
+ "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, language),
+ "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, language),
+ "Code": code,
+ }
+
+ var content bytes.Buffer
+
+ if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := NewMessage([]string{u.Email}, subject, content.String())
+ msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info)
+
+ SendAsync(msg)
+}
+
+// Locale represents an interface to translation
+type Locale interface {
+ Language() string
+ Tr(string, ...interface{}) string
+}
+
+// SendActivateAccountMail sends an activation mail to the user (new user registration)
+func SendActivateAccountMail(locale Locale, u *models.User) {
+ SendUserMail(locale.Language(), u, mailAuthActivate, u.GenerateActivateCode(), locale.Tr("mail.activate_account"), "activate account")
+}
+
+// SendResetPasswordMail sends a password reset mail to the user
+func SendResetPasswordMail(locale Locale, u *models.User) {
+ SendUserMail(locale.Language(), u, mailAuthResetPassword, u.GenerateActivateCode(), locale.Tr("mail.reset_password"), "recover account")
+}
+
+// SendActivateEmailMail sends confirmation email to confirm new email address
+func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAddress) {
+ data := map[string]interface{}{
+ "DisplayName": u.DisplayName(),
+ "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale.Language()),
+ "Code": u.GenerateEmailActivateCode(email.Email),
+ "Email": email.Email,
+ }
+
+ var content bytes.Buffer
+
+ if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := NewMessage([]string{email.Email}, locale.Tr("mail.activate_email"), content.String())
+ msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID)
+
+ SendAsync(msg)
+}
+
+// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
+func SendRegisterNotifyMail(locale Locale, u *models.User) {
+ if setting.MailService == nil {
+ log.Warn("SendRegisterNotifyMail is being invoked but mail service hasn't been initialized")
+ return
+ }
+
+ data := map[string]interface{}{
+ "DisplayName": u.DisplayName(),
+ "Username": u.Name,
+ }
+
+ var content bytes.Buffer
+
+ if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := NewMessage([]string{u.Email}, locale.Tr("mail.register_notify"), content.String())
+ msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID)
+
+ SendAsync(msg)
+}
+
+// SendCollaboratorMail sends mail notification to new collaborator.
+func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
+ repoName := path.Join(repo.Owner.Name, repo.Name)
+ subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repoName)
+
+ data := map[string]interface{}{
+ "Subject": subject,
+ "RepoName": repoName,
+ "Link": repo.HTMLURL(),
+ }
+
+ var content bytes.Buffer
+
+ if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
+ log.Error("Template: %v", err)
+ return
+ }
+
+ msg := NewMessage([]string{u.Email}, subject, content.String())
+ msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID)
+
+ SendAsync(msg)
+}
+
+func composeTplData(subject, body, link string) map[string]interface{} {
+ data := make(map[string]interface{}, 10)
+ data["Subject"] = subject
+ data["Body"] = body
+ data["Link"] = link
+ return data
+}
+
+func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tplName base.TplName, tos []string, info string) *Message {
+ var subject string
+ if comment != nil {
+ subject = "Re: " + mailSubject(issue)
+ } else {
+ subject = mailSubject(issue)
+ }
+ err := issue.LoadRepo()
+ if err != nil {
+ log.Error("LoadRepo: %v", err)
+ }
+ body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
+
+ var data = make(map[string]interface{}, 10)
+ if comment != nil {
+ data = composeTplData(subject, body, issue.HTMLURL()+"#"+comment.HashTag())
+ } else {
+ data = composeTplData(subject, body, issue.HTMLURL())
+ }
+ data["Doer"] = doer
+
+ var mailBody bytes.Buffer
+
+ if err := templates.ExecuteTemplate(&mailBody, string(tplName), data); err != nil {
+ log.Error("Template: %v", err)
+ }
+
+ msg := NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String())
+ msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
+
+ // Set Message-ID on first message so replies know what to reference
+ if comment == nil {
+ msg.SetHeader("Message-ID", "<"+issue.ReplyReference()+">")
+ } else {
+ msg.SetHeader("In-Reply-To", "<"+issue.ReplyReference()+">")
+ msg.SetHeader("References", "<"+issue.ReplyReference()+">")
+ }
+
+ return msg
+}
+
+// SendIssueCommentMail composes and sends issue comment emails to target receivers.
+func SendIssueCommentMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tos []string) {
+ if len(tos) == 0 {
+ return
+ }
+
+ SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueComment, tos, "issue comment"))
+}
+
+// SendIssueMentionMail composes and sends issue mention emails to target receivers.
+func SendIssueMentionMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tos []string) {
+ if len(tos) == 0 {
+ return
+ }
+ SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueMention, tos, "issue mention"))
+}