summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
author6543 <6543@obermui.de>2021-04-02 12:25:13 +0200
committerGitHub <noreply@github.com>2021-04-02 11:25:13 +0100
commit80d6c6d7deb3f6a38ff6d09ec38ffb04de9da726 (patch)
tree8651abe082da4e920753f03ee4c37de1668ab826 /services
parentcc2d540092149166c9a0cef886b2e952ac9c75d7 (diff)
downloadgitea-80d6c6d7deb3f6a38ff6d09ec38ffb04de9da726.tar.gz
gitea-80d6c6d7deb3f6a38ff6d09ec38ffb04de9da726.zip
[refactor] mailer service (#15072)
* Unexport SendUserMail * Instead of "[]*models.User" or "[]string" lists infent "[]*MailRecipient" for mailer * adopt * code format * TODOs for "i18n" * clean * no fallback for lang -> just use english * lint * exec testComposeIssueCommentMessage per lang and use only emails * rm MailRecipient * Dont reload from users from db if you alredy have in ram * nits * minimize diff Signed-off-by: 6543 <6543@obermui.de> * localize subjects * linter ... * Tr extend * start tmpl edit ... * Apply suggestions from code review * use translation.Locale * improve mailIssueCommentBatch Signed-off-by: Andrew Thornton <art27@cantab.net> * add i18n to datas Signed-off-by: Andrew Thornton <art27@cantab.net> * a comment Co-authored-by: Andrew Thornton <art27@cantab.net>
Diffstat (limited to 'services')
-rw-r--r--services/mailer/mail.go79
-rw-r--r--services/mailer/mail_comment.go21
-rw-r--r--services/mailer/mail_issue.go95
-rw-r--r--services/mailer/mail_release.go31
-rw-r--r--services/mailer/mail_repo.go54
-rw-r--r--services/mailer/mail_test.go6
-rw-r--r--services/mailer/mailer.go9
7 files changed, 166 insertions, 129 deletions
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 7d6214c742..c50795968a 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/translation"
"gopkg.in/gomail.v2"
)
@@ -57,17 +58,21 @@ func SendTestMail(email string) error {
return gomail.Send(Sender, NewMessage([]string{email}, "Gitea Test Email!", "Gitea Test Email!").ToMessage())
}
-// SendUserMail sends a mail to the user
-func SendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) {
+// sendUserMail sends a mail to the user
+func sendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) {
+ locale := translation.NewLocale(language)
data := map[string]interface{}{
"DisplayName": u.DisplayName(),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, language),
"ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, language),
"Code": code,
+ "i18n": locale,
+ "Language": locale.Language(),
}
var content bytes.Buffer
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&content, string(tpl), data); err != nil {
log.Error("Template: %v", err)
return
@@ -79,33 +84,32 @@ func SendUserMail(language string, u *models.User, tpl base.TplName, code, subje
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")
+func SendActivateAccountMail(locale translation.Locale, u *models.User) {
+ sendUserMail(locale.Language(), u, mailAuthActivate, u.GenerateEmailActivateCode(u.Email), 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")
+func SendResetPasswordMail(u *models.User) {
+ locale := translation.NewLocale(u.Language)
+ sendUserMail(u.Language, u, mailAuthResetPassword, u.GenerateEmailActivateCode(u.Email), 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) {
+func SendActivateEmailMail(u *models.User, email *models.EmailAddress) {
+ locale := translation.NewLocale(u.Language)
data := map[string]interface{}{
"DisplayName": u.DisplayName(),
"ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale.Language()),
"Code": u.GenerateEmailActivateCode(email.Email),
"Email": email.Email,
+ "i18n": locale,
+ "Language": locale.Language(),
}
var content bytes.Buffer
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
log.Error("Template: %v", err)
return
@@ -118,19 +122,19 @@ func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAdd
}
// 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
- }
+func SendRegisterNotifyMail(u *models.User) {
+ locale := translation.NewLocale(u.Language)
data := map[string]interface{}{
"DisplayName": u.DisplayName(),
"Username": u.Name,
+ "i18n": locale,
+ "Language": locale.Language(),
}
var content bytes.Buffer
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
log.Error("Template: %v", err)
return
@@ -144,17 +148,21 @@ func SendRegisterNotifyMail(locale Locale, u *models.User) {
// SendCollaboratorMail sends mail notification to new collaborator.
func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
+ locale := translation.NewLocale(u.Language)
repoName := repo.FullName()
- subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repoName)
+ subject := locale.Tr("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName)
data := map[string]interface{}{
"Subject": subject,
"RepoName": repoName,
"Link": repo.HTMLURL(),
+ "i18n": locale,
+ "Language": locale.Language(),
}
var content bytes.Buffer
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
log.Error("Template: %v", err)
return
@@ -166,7 +174,7 @@ func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
SendAsync(msg)
}
-func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMention bool, info string) []*Message {
+func composeIssueCommentMessages(ctx *mailCommentContext, lang string, tos []string, fromMention bool, info string) []*Message {
var (
subject string
@@ -192,7 +200,6 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
// This is the body of the new issue or comment, not the mail body
body := string(markup.RenderByType(markdown.MarkupName, []byte(ctx.Content), ctx.Issue.Repo.HTMLURL(), ctx.Issue.Repo.ComposeMetas()))
-
actType, actName, tplName := actionToTemplate(ctx.Issue, ctx.ActionType, commentType, reviewType)
if actName != "new" {
@@ -208,6 +215,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
}
}
}
+ locale := translation.NewLocale(lang)
mailMeta := map[string]interface{}{
"FallbackSubject": fallback,
@@ -224,13 +232,16 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
"ActionType": actType,
"ActionName": actName,
"ReviewComments": reviewComments,
+ "i18n": locale,
+ "Language": locale.Language(),
}
var mailSubject bytes.Buffer
+ // TODO: i18n templates?
if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplName), mailMeta); err == nil {
subject = sanitizeSubject(mailSubject.String())
} else {
- log.Error("ExecuteTemplate [%s]: %v", string(tplName)+"/subject", err)
+ log.Error("ExecuteTemplate [%s]: %v", tplName+"/subject", err)
}
if subject == "" {
@@ -243,6 +254,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, tos []string, fromMent
var mailBody bytes.Buffer
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplName), mailMeta); err != nil {
log.Error("ExecuteTemplate [%s]: %v", string(tplName)+"/body", err)
}
@@ -276,14 +288,21 @@ func sanitizeSubject(subject string) string {
}
// SendIssueAssignedMail composes and sends issue assigned email
-func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tos []string) {
- SendAsyncs(composeIssueCommentMessages(&mailCommentContext{
- Issue: issue,
- Doer: doer,
- ActionType: models.ActionType(0),
- Content: content,
- Comment: comment,
- }, tos, false, "issue assigned"))
+func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, recipients []*models.User) {
+ langMap := make(map[string][]string)
+ for _, user := range recipients {
+ langMap[user.Language] = append(langMap[user.Language], user.Email)
+ }
+
+ for lang, tos := range langMap {
+ SendAsyncs(composeIssueCommentMessages(&mailCommentContext{
+ Issue: issue,
+ Doer: doer,
+ ActionType: models.ActionType(0),
+ Content: content,
+ Comment: comment,
+ }, lang, tos, false, "issue assigned"))
+ }
}
// actionToTemplate returns the type and name of the action facing the user
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index 2f166720db..f73c9fb637 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -9,25 +9,16 @@ import (
"code.gitea.io/gitea/modules/log"
)
-// MailParticipantsComment sends new comment emails to repository watchers
-// and mentioned people.
+// MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*models.User) error {
- return mailParticipantsComment(c, opType, issue, mentions)
-}
-
-func mailParticipantsComment(c *models.Comment, opType models.ActionType, issue *models.Issue, mentions []*models.User) (err error) {
- mentionedIDs := make([]int64, len(mentions))
- for i, u := range mentions {
- mentionedIDs[i] = u.ID
- }
- if err = mailIssueCommentToParticipants(
+ if err := mailIssueCommentToParticipants(
&mailCommentContext{
Issue: issue,
Doer: c.Poster,
ActionType: opType,
Content: c.Content,
Comment: c,
- }, mentionedIDs); err != nil {
+ }, mentions); err != nil {
log.Error("mailIssueCommentToParticipants: %v", err)
}
return nil
@@ -35,10 +26,6 @@ func mailParticipantsComment(c *models.Comment, opType models.ActionType, issue
// MailMentionsComment sends email to users mentioned in a code comment
func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*models.User) (err error) {
- mentionedIDs := make([]int64, len(mentions))
- for i, u := range mentions {
- mentionedIDs[i] = u.ID
- }
visited := make(map[int64]bool, len(mentions)+1)
visited[c.Poster.ID] = true
if err = mailIssueCommentBatch(
@@ -48,7 +35,7 @@ func MailMentionsComment(pr *models.PullRequest, c *models.Comment, mentions []*
ActionType: models.ActionCommentPull,
Content: c.Content,
Comment: c,
- }, mentionedIDs, visited, true); err != nil {
+ }, mentions, visited, true); err != nil {
log.Error("mailIssueCommentBatch: %v", err)
}
return nil
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index b600060a67..9786a06f62 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -23,11 +23,16 @@ type mailCommentContext struct {
Comment *models.Comment
}
+const (
+ // MailBatchSize set the batch size used in mailIssueCommentBatch
+ MailBatchSize = 100
+)
+
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
// This function sends two list of emails:
// 1. Repository watchers and users who are participated in comments.
// 2. Users who are not in 1. but get mentioned in current issue/comment.
-func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []int64) error {
+func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error {
// Required by the mail composer; make sure to load these before calling the async function
if err := ctx.Issue.LoadRepo(); err != nil {
@@ -94,78 +99,72 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []int64) e
visited[i] = true
}
- if err = mailIssueCommentBatch(ctx, unfiltered, visited, false); err != nil {
+ unfilteredUsers, err := models.GetMaileableUsersByIDs(unfiltered, false)
+ if err != nil {
+ return err
+ }
+ if err = mailIssueCommentBatch(ctx, unfilteredUsers, visited, false); err != nil {
return fmt.Errorf("mailIssueCommentBatch(): %v", err)
}
return nil
}
-func mailIssueCommentBatch(ctx *mailCommentContext, ids []int64, visited map[int64]bool, fromMention bool) error {
- const batchSize = 100
- for i := 0; i < len(ids); i += batchSize {
- var last int
- if i+batchSize < len(ids) {
- last = i + batchSize
- } else {
- last = len(ids)
- }
- unique := make([]int64, 0, last-i)
- for j := i; j < last; j++ {
- id := ids[j]
- if _, ok := visited[id]; !ok {
- unique = append(unique, id)
- visited[id] = true
- }
- }
- recipients, err := models.GetMaileableUsersByIDs(unique, fromMention)
- if err != nil {
- return err
+func mailIssueCommentBatch(ctx *mailCommentContext, users []*models.User, visited map[int64]bool, fromMention bool) error {
+ checkUnit := models.UnitTypeIssues
+ if ctx.Issue.IsPull {
+ checkUnit = models.UnitTypePullRequests
+ }
+
+ langMap := make(map[string][]string)
+ for _, user := range users {
+ // At this point we exclude:
+ // user that don't have all mails enabled or users only get mail on mention and this is one ...
+ if !(user.EmailNotificationsPreference == models.EmailNotificationsEnabled ||
+ fromMention && user.EmailNotificationsPreference == models.EmailNotificationsOnMention) {
+ continue
}
- checkUnit := models.UnitTypeIssues
- if ctx.Issue.IsPull {
- checkUnit = models.UnitTypePullRequests
+ // if we have already visited this user we exclude them
+ if _, ok := visited[user.ID]; ok {
+ continue
}
- // Make sure all recipients can still see the issue
- idx := 0
- for _, r := range recipients {
- if ctx.Issue.Repo.CheckUnitUser(r, checkUnit) {
- recipients[idx] = r
- idx++
- }
+
+ // now mark them as visited
+ visited[user.ID] = true
+
+ // test if this user is allowed to see the issue/pull
+ if !ctx.Issue.Repo.CheckUnitUser(user, checkUnit) {
+ continue
}
- recipients = recipients[:idx]
- // TODO: Separate recipients by language for i18n mail templates
- tos := make([]string, len(recipients))
- for i := range recipients {
- tos[i] = recipients[i].Email
+ langMap[user.Language] = append(langMap[user.Language], user.Email)
+ }
+
+ for lang, receivers := range langMap {
+ // because we know that the len(receivers) > 0 and we don't care about the order particularly
+ // working backwards from the last (possibly) incomplete batch. If len(receivers) can be 0 this
+ // starting condition will need to be changed slightly
+ for i := ((len(receivers) - 1) / MailBatchSize) * MailBatchSize; i >= 0; i -= MailBatchSize {
+ SendAsyncs(composeIssueCommentMessages(ctx, lang, receivers[i:], fromMention, "issue comments"))
+ receivers = receivers[:i]
}
- SendAsyncs(composeIssueCommentMessages(ctx, tos, fromMention, "issue comments"))
}
+
return nil
}
// MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people.
func MailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) error {
- return mailParticipants(issue, doer, opType, mentions)
-}
-
-func mailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) (err error) {
- mentionedIDs := make([]int64, len(mentions))
- for i, u := range mentions {
- mentionedIDs[i] = u.ID
- }
- if err = mailIssueCommentToParticipants(
+ if err := mailIssueCommentToParticipants(
&mailCommentContext{
Issue: issue,
Doer: doer,
ActionType: opType,
Content: issue.Content,
Comment: nil,
- }, mentionedIDs); err != nil {
+ }, mentions); err != nil {
log.Error("mailIssueCommentToParticipants: %v", err)
}
return nil
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index f278c853ae..22efe2f046 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -6,13 +6,13 @@ package mailer
import (
"bytes"
- "fmt"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/markdown"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/translation"
)
const (
@@ -33,29 +33,40 @@ func MailNewRelease(rel *models.Release) {
return
}
- tos := make([]string, 0, len(recipients))
- for _, to := range recipients {
- if to.ID != rel.PublisherID {
- tos = append(tos, to.Email)
+ langMap := make(map[string][]string)
+ for _, user := range recipients {
+ if user.ID != rel.PublisherID {
+ langMap[user.Language] = append(langMap[user.Language], user.Email)
}
}
+ for lang, tos := range langMap {
+ mailNewRelease(lang, tos, rel)
+ }
+}
+
+func mailNewRelease(lang string, tos []string, rel *models.Release) {
+ locale := translation.NewLocale(lang)
+
rel.RenderedNote = markdown.RenderString(rel.Note, rel.Repo.Link(), rel.Repo.ComposeMetas())
- subject := fmt.Sprintf("%s in %s released", rel.TagName, rel.Repo.FullName())
+ subject := locale.Tr("mail.release.new.subject", rel.TagName, rel.Repo.FullName())
mailMeta := map[string]interface{}{
- "Release": rel,
- "Subject": subject,
+ "Release": rel,
+ "Subject": subject,
+ "i18n": locale,
+ "Language": locale.Language(),
}
var mailBody bytes.Buffer
- if err = bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewReleaseMail), mailMeta); err != nil {
+ // TODO: i18n templates?
+ if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewReleaseMail), mailMeta); err != nil {
log.Error("ExecuteTemplate [%s]: %v", string(tplNewReleaseMail)+"/body", err)
return
}
- msgs := make([]*Message, 0, len(recipients))
+ msgs := make([]*Message, 0, len(tos))
publisherName := rel.Publisher.DisplayName()
relURL := "<" + rel.HTMLURL() + ">"
for _, to := range tos {
diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go
index b9d24f4334..c742101ee1 100644
--- a/services/mailer/mail_repo.go
+++ b/services/mailer/mail_repo.go
@@ -9,42 +9,60 @@ import (
"fmt"
"code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/modules/translation"
)
// SendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created
func SendRepoTransferNotifyMail(doer, newOwner *models.User, repo *models.Repository) error {
- var (
- emails []string
- destination string
- content bytes.Buffer
- )
-
if newOwner.IsOrganization() {
users, err := models.GetUsersWhoCanCreateOrgRepo(newOwner.ID)
if err != nil {
return err
}
- for i := range users {
- emails = append(emails, users[i].Email)
+ langMap := make(map[string][]string)
+ for _, user := range users {
+ langMap[user.Language] = append(langMap[user.Language], user.Email)
+ }
+
+ for lang, tos := range langMap {
+ if err := sendRepoTransferNotifyMailPerLang(lang, newOwner, doer, tos, repo); err != nil {
+ return err
+ }
}
+
+ return nil
+ }
+
+ return sendRepoTransferNotifyMailPerLang(newOwner.Language, newOwner, doer, []string{newOwner.Email}, repo)
+}
+
+// sendRepoTransferNotifyMail triggers a notification e-mail when a pending repository transfer was created for each language
+func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *models.User, emails []string, repo *models.Repository) error {
+ var (
+ locale = translation.NewLocale(lang)
+ content bytes.Buffer
+ )
+
+ destination := locale.Tr("mail.repo.transfer.to_you")
+ subject := locale.Tr("mail.repo.transfer.subject_to_you", doer.DisplayName(), repo.FullName())
+ if newOwner.IsOrganization() {
destination = newOwner.DisplayName()
- } else {
- emails = []string{newOwner.Email}
- destination = "you"
+ subject = locale.Tr("mail.repo.transfer.subject_to", doer.DisplayName(), repo.FullName(), destination)
}
- subject := fmt.Sprintf("%s would like to transfer \"%s\" to %s", doer.DisplayName(), repo.FullName(), destination)
data := map[string]interface{}{
- "Doer": doer,
- "User": repo.Owner,
- "Repo": repo.FullName(),
- "Link": repo.HTMLURL(),
- "Subject": subject,
-
+ "Doer": doer,
+ "User": repo.Owner,
+ "Repo": repo.FullName(),
+ "Link": repo.HTMLURL(),
+ "Subject": subject,
+ "i18n": locale,
+ "Language": locale.Language(),
"Destination": destination,
}
+ // TODO: i18n templates?
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {
return err
}
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index d7d02d9dee..9eef084408 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -59,7 +59,7 @@ func TestComposeIssueCommentMessage(t *testing.T) {
tos := []string{"test@gitea.com", "test2@gitea.com"}
msgs := composeIssueCommentMessages(&mailCommentContext{Issue: issue, Doer: doer, ActionType: models.ActionCommentIssue,
- Content: "test body", Comment: comment}, tos, false, "issue comment")
+ Content: "test body", Comment: comment}, "en-US", tos, false, "issue comment")
assert.Len(t, msgs, 2)
gomailMsg := msgs[0].ToMessage()
mailto := gomailMsg.GetHeader("To")
@@ -93,7 +93,7 @@ func TestComposeIssueMessage(t *testing.T) {
tos := []string{"test@gitea.com", "test2@gitea.com"}
msgs := composeIssueCommentMessages(&mailCommentContext{Issue: issue, Doer: doer, ActionType: models.ActionCreateIssue,
- Content: "test body"}, tos, false, "issue create")
+ Content: "test body"}, "en-US", tos, false, "issue create")
assert.Len(t, msgs, 2)
gomailMsg := msgs[0].ToMessage()
@@ -218,7 +218,7 @@ func TestTemplateServices(t *testing.T) {
}
func testComposeIssueCommentMessage(t *testing.T, ctx *mailCommentContext, tos []string, fromMention bool, info string) *Message {
- msgs := composeIssueCommentMessages(ctx, tos, fromMention, info)
+ msgs := composeIssueCommentMessages(ctx, "en-US", tos, fromMention, info)
assert.Len(t, msgs, 1)
return msgs[0]
}
diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go
index 2e7beffa15..6b86734bf8 100644
--- a/services/mailer/mailer.go
+++ b/services/mailer/mailer.go
@@ -337,13 +337,16 @@ func NewContext() {
// SendAsync send mail asynchronously
func SendAsync(msg *Message) {
- go func() {
- _ = mailQueue.Push(msg)
- }()
+ SendAsyncs([]*Message{msg})
}
// SendAsyncs send mails asynchronously
func SendAsyncs(msgs []*Message) {
+ if setting.MailService == nil {
+ log.Error("Mailer: SendAsyncs is being invoked but mail service hasn't been initialized")
+ return
+ }
+
go func() {
for _, msg := range msgs {
_ = mailQueue.Push(msg)