summaryrefslogtreecommitdiffstats
path: root/services/mailer
diff options
context:
space:
mode:
authorguillep2k <18600385+guillep2k@users.noreply.github.com>2019-11-07 10:34:28 -0300
committerLunny Xiao <xiaolunwen@gmail.com>2019-11-07 21:34:28 +0800
commit1f90147f3942065e2a7f564e8a3c97d23d41e6c0 (patch)
treefd5fb0bda2d84deb179bf99f9172b28f736ba9fe /services/mailer
parentd5b1e6bc51f87eb1be07a4682798428bf4bbb9ce (diff)
downloadgitea-1f90147f3942065e2a7f564e8a3c97d23d41e6c0.tar.gz
gitea-1f90147f3942065e2a7f564e8a3c97d23d41e6c0.zip
Use templates for issue e-mail subject and body (#8329)
* Add template capability for issue mail subject * Remove test string * Fix trim subject length * Add comment to template and run make fmt * Add information for the template * Rename defaultMailSubject() to fallbackMailSubject() * General rewrite of the mail template code * Fix .Doer name * Use text/template for subject instead of html * Fix subject Re: prefix * Fix mail tests * Fix static templates * [skip ci] Updated translations via Crowdin * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) * Expose db.SetMaxOpenConns and allow other dbs to set their connection params * Add note about port exhaustion Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Prevent .code-view from overriding font on icon fonts (#8614) * Correct some outdated statements in the contributing guidelines (#8612) * More information for drone-cli in CONTRIBUTING.md * Increases the version of drone-cli to 1.2.0 * Adds a note for the Docker Toolbox on Windows Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Fix the url for the blog repository (now on gitea.com) Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Remove TrN due to lack of lang context * Redo templates to match previous code * Fix extra character in template * Unify PR & Issue tempaltes, fix format * Remove default subject * Add template tests * Fix template * Remove replaced function * Provide User as models.User for better consistency * Add docs * Fix doc inaccuracies, improve examples * Change mail footer to math AppName * Add test for mail subject/body template separation * Add support for code review comments * Update docs/content/doc/advanced/mail-templates-us.md Co-Authored-By: 6543 <24977596+6543@users.noreply.github.com>
Diffstat (limited to 'services/mailer')
-rw-r--r--services/mailer/mail.go173
-rw-r--r--services/mailer/mail_comment.go20
-rw-r--r--services/mailer/mail_issue.go36
-rw-r--r--services/mailer/mail_test.go131
4 files changed, 264 insertions, 96 deletions
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index bc2aff7314..fc892f6076 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -9,7 +9,11 @@ import (
"bytes"
"fmt"
"html/template"
+ "mime"
"path"
+ "regexp"
+ "strings"
+ texttmpl "text/template"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
@@ -28,18 +32,22 @@ const (
mailAuthResetPassword base.TplName = "auth/reset_passwd"
mailAuthRegisterNotify base.TplName = "auth/register_notify"
- mailIssueComment base.TplName = "issue/comment"
- mailIssueMention base.TplName = "issue/mention"
- mailIssueAssigned base.TplName = "issue/assigned"
-
mailNotifyCollaborator base.TplName = "notify/collaborator"
+
+ // There's no actual limit for subject in RFC 5322
+ mailMaxSubjectRunes = 256
)
-var templates *template.Template
+var (
+ bodyTemplates *template.Template
+ subjectTemplates *texttmpl.Template
+ subjectRemoveSpaces = regexp.MustCompile(`[\s]+`)
+)
// InitMailRender initializes the mail renderer
-func InitMailRender(tmpls *template.Template) {
- templates = tmpls
+func InitMailRender(subjectTpl *texttmpl.Template, bodyTpl *template.Template) {
+ subjectTemplates = subjectTpl
+ bodyTemplates = bodyTpl
}
// SendTestMail sends a test mail
@@ -58,7 +66,7 @@ func SendUserMail(language string, u *models.User, tpl base.TplName, code, subje
var content bytes.Buffer
- if err := templates.ExecuteTemplate(&content, string(tpl), data); err != nil {
+ if err := bodyTemplates.ExecuteTemplate(&content, string(tpl), data); err != nil {
log.Error("Template: %v", err)
return
}
@@ -96,7 +104,7 @@ func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAdd
var content bytes.Buffer
- if err := templates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthActivateEmail), data); err != nil {
log.Error("Template: %v", err)
return
}
@@ -121,7 +129,7 @@ func SendRegisterNotifyMail(locale Locale, u *models.User) {
var content bytes.Buffer
- if err := templates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailAuthRegisterNotify), data); err != nil {
log.Error("Template: %v", err)
return
}
@@ -145,7 +153,7 @@ func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
var content bytes.Buffer
- if err := templates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
+ if err := bodyTemplates.ExecuteTemplate(&content, string(mailNotifyCollaborator), data); err != nil {
log.Error("Template: %v", err)
return
}
@@ -156,40 +164,70 @@ func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) {
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, actionType models.ActionType, fromMention bool,
+ content string, comment *models.Comment, tos []string, info string) *Message {
+
+ if err := issue.LoadPullRequest(); err != nil {
+ log.Error("LoadPullRequest: %v", err)
+ return nil
+ }
+
+ var (
+ subject string
+ link string
+ prefix string
+ // Fall back subject for bad templates, make sure subject is never empty
+ fallback string
+ )
-func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tplName base.TplName, tos []string, info string) *Message {
- var subject string
+ commentType := models.CommentTypeComment
if comment != nil {
- subject = "Re: " + mailSubject(issue)
+ prefix = "Re: "
+ commentType = comment.Type
+ link = issue.HTMLURL() + "#" + comment.HashTag()
} else {
- subject = mailSubject(issue)
- }
- err := issue.LoadRepo()
- if err != nil {
- log.Error("LoadRepo: %v", err)
+ link = issue.HTMLURL()
}
+
+ fallback = prefix + fallbackMailSubject(issue)
+
+ // This is the body of the new issue or comment, not the mail body
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())
+ actType, actName, tplName := actionToTemplate(issue, actionType, commentType)
+
+ mailMeta := map[string]interface{}{
+ "FallbackSubject": fallback,
+ "Body": body,
+ "Link": link,
+ "Issue": issue,
+ "Comment": comment,
+ "IsPull": issue.IsPull,
+ "User": issue.Repo.MustOwner(),
+ "Repo": issue.Repo.FullName(),
+ "Doer": doer,
+ "IsMention": fromMention,
+ "SubjectPrefix": prefix,
+ "ActionType": actType,
+ "ActionName": actName,
+ }
+
+ var mailSubject bytes.Buffer
+ if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplName), mailMeta); err == nil {
+ subject = sanitizeSubject(mailSubject.String())
} else {
- data = composeTplData(subject, body, issue.HTMLURL())
+ log.Error("ExecuteTemplate [%s]: %v", string(tplName)+"/subject", err)
+ }
+
+ if subject == "" {
+ subject = fallback
}
- data["Doer"] = doer
- data["Issue"] = issue
+ mailMeta["Subject"] = subject
var mailBody bytes.Buffer
- if err := templates.ExecuteTemplate(&mailBody, string(tplName), data); err != nil {
- log.Error("Template: %v", err)
+ if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplName), mailMeta); err != nil {
+ log.Error("ExecuteTemplate [%s]: %v", string(tplName)+"/body", err)
}
msg := NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String())
@@ -206,24 +244,81 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content
return msg
}
+func sanitizeSubject(subject string) string {
+ runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")))
+ if len(runes) > mailMaxSubjectRunes {
+ runes = runes[:mailMaxSubjectRunes]
+ }
+ // Encode non-ASCII characters
+ return mime.QEncoding.Encode("utf-8", string(runes))
+}
+
// 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) {
+func SendIssueCommentMail(issue *models.Issue, doer *models.User, actionType models.ActionType, content string, comment *models.Comment, tos []string) {
if len(tos) == 0 {
return
}
- SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueComment, tos, "issue comment"))
+ SendAsync(composeIssueCommentMessage(issue, doer, actionType, false, content, comment, 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) {
+func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType models.ActionType, content string, comment *models.Comment, tos []string) {
if len(tos) == 0 {
return
}
- SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueMention, tos, "issue mention"))
+ SendAsync(composeIssueCommentMessage(issue, doer, actionType, true, content, comment, tos, "issue mention"))
+}
+
+// actionToTemplate returns the type and name of the action facing the user
+// (slightly different from models.ActionType) and the name of the template to use (based on availability)
+func actionToTemplate(issue *models.Issue, actionType models.ActionType, commentType models.CommentType) (typeName, name, template string) {
+ if issue.IsPull {
+ typeName = "pull"
+ } else {
+ typeName = "issue"
+ }
+ switch actionType {
+ case models.ActionCreateIssue, models.ActionCreatePullRequest:
+ name = "new"
+ case models.ActionCommentIssue:
+ name = "comment"
+ case models.ActionCloseIssue, models.ActionClosePullRequest:
+ name = "close"
+ case models.ActionReopenIssue, models.ActionReopenPullRequest:
+ name = "reopen"
+ case models.ActionMergePullRequest:
+ name = "merge"
+ default:
+ switch commentType {
+ case models.CommentTypeReview:
+ name = "review"
+ case models.CommentTypeCode:
+ name = "code"
+ case models.CommentTypeAssignees:
+ name = "assigned"
+ default:
+ name = "default"
+ }
+ }
+
+ template = typeName + "/" + name
+ ok := bodyTemplates.Lookup(template) != nil
+ if !ok && typeName != "issue" {
+ template = "issue/" + name
+ ok = bodyTemplates.Lookup(template) != nil
+ }
+ if !ok {
+ template = typeName + "/default"
+ ok = bodyTemplates.Lookup(template) != nil
+ }
+ if !ok {
+ template = "issue/default"
+ }
+ return
}
// SendIssueAssignedMail composes and sends issue assigned email
func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tos []string) {
- SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueAssigned, tos, "issue assigned"))
+ SendAsync(composeIssueCommentMessage(issue, doer, models.ActionType(0), false, content, comment, tos, "issue assigned"))
}
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index d306c14f42..6469eb1fa1 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -31,24 +31,8 @@ func mailParticipantsComment(ctx models.DBContext, c *models.Comment, opType mod
for i, u := range userMentions {
mentions[i] = u.LowerName
}
- if len(c.Content) > 0 {
- if err = mailIssueCommentToParticipants(issue, c.Poster, c.Content, c, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
+ if err = mailIssueCommentToParticipants(issue, c.Poster, opType, c.Content, c, mentions); err != nil {
+ log.Error("mailIssueCommentToParticipants: %v", err)
}
-
- switch opType {
- case models.ActionCloseIssue:
- ct := fmt.Sprintf("Closed #%d.", issue.Index)
- if err = mailIssueCommentToParticipants(issue, c.Poster, ct, c, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
- case models.ActionReopenIssue:
- ct := fmt.Sprintf("Reopened #%d.", issue.Index)
- if err = mailIssueCommentToParticipants(issue, c.Poster, ct, c, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
- }
-
return nil
}
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index a5f3251807..32b21b1324 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -14,7 +14,7 @@ import (
"github.com/unknwon/com"
)
-func mailSubject(issue *models.Issue) string {
+func fallbackMailSubject(issue *models.Issue) string {
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
}
@@ -22,7 +22,7 @@ func mailSubject(issue *models.Issue) string {
// 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(issue *models.Issue, doer *models.User, content string, comment *models.Comment, mentions []string) error {
+func mailIssueCommentToParticipants(issue *models.Issue, doer *models.User, actionType models.ActionType, content string, comment *models.Comment, mentions []string) error {
watchers, err := models.GetWatchers(issue.RepoID)
if err != nil {
@@ -89,7 +89,7 @@ func mailIssueCommentToParticipants(issue *models.Issue, doer *models.User, cont
}
for _, to := range tos {
- SendIssueCommentMail(issue, doer, content, comment, []string{to})
+ SendIssueCommentMail(issue, doer, actionType, content, comment, []string{to})
}
// Mail mentioned people and exclude watchers.
@@ -106,7 +106,7 @@ func mailIssueCommentToParticipants(issue *models.Issue, doer *models.User, cont
emails := models.GetUserEmailsByNames(tos)
for _, to := range emails {
- SendIssueMentionMail(issue, doer, content, comment, []string{to})
+ SendIssueMentionMail(issue, doer, actionType, content, comment, []string{to})
}
return nil
@@ -131,32 +131,8 @@ func mailParticipants(ctx models.DBContext, issue *models.Issue, doer *models.Us
for i, u := range userMentions {
mentions[i] = u.LowerName
}
-
- if len(issue.Content) > 0 {
- if err = mailIssueCommentToParticipants(issue, doer, issue.Content, nil, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
- }
-
- switch opType {
- case models.ActionCreateIssue, models.ActionCreatePullRequest:
- if len(issue.Content) == 0 {
- ct := fmt.Sprintf("Created #%d.", issue.Index)
- if err = mailIssueCommentToParticipants(issue, doer, ct, nil, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
- }
- case models.ActionCloseIssue, models.ActionClosePullRequest:
- ct := fmt.Sprintf("Closed #%d.", issue.Index)
- if err = mailIssueCommentToParticipants(issue, doer, ct, nil, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
- case models.ActionReopenIssue, models.ActionReopenPullRequest:
- ct := fmt.Sprintf("Reopened #%d.", issue.Index)
- if err = mailIssueCommentToParticipants(issue, doer, ct, nil, mentions); err != nil {
- log.Error("mailIssueCommentToParticipants: %v", err)
- }
+ if err = mailIssueCommentToParticipants(issue, doer, opType, issue.Content, nil, mentions); err != nil {
+ log.Error("mailIssueCommentToParticipants: %v", err)
}
-
return nil
}
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index c7a84d6b33..a10507e0e4 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -5,8 +5,10 @@
package mailer
import (
+ "bytes"
"html/template"
"testing"
+ texttmpl "text/template"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/setting"
@@ -14,7 +16,11 @@ import (
"github.com/stretchr/testify/assert"
)
-const tmpl = `
+const subjectTpl = `
+{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} #{{.Issue.Index}} - {{.Issue.Title}}
+`
+
+const bodyTpl = `
<!DOCTYPE html>
<html>
<head>
@@ -47,17 +53,19 @@ func TestComposeIssueCommentMessage(t *testing.T) {
issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1, Repo: repo, Poster: doer}).(*models.Issue)
comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2, Issue: issue}).(*models.Comment)
- email := template.Must(template.New("issue/comment").Parse(tmpl))
- InitMailRender(email)
+ stpl := texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
+ btpl := template.Must(template.New("issue/comment").Parse(bodyTpl))
+ InitMailRender(stpl, btpl)
tos := []string{"test@gitea.com", "test2@gitea.com"}
- msg := composeIssueCommentMessage(issue, doer, "test body", comment, mailIssueComment, tos, "issue comment")
+ msg := composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, false, "test body", comment, tos, "issue comment")
subject := msg.GetHeader("Subject")
inreplyTo := msg.GetHeader("In-Reply-To")
references := msg.GetHeader("References")
- assert.Equal(t, subject[0], "Re: "+mailSubject(issue), "Comment reply subject should contain Re:")
+ assert.Equal(t, "Re: ", subject[0][:4], "Comment reply subject should contain Re:")
+ assert.Equal(t, "Re: [user2/repo1] @user2 #1 - issue1", subject[0])
assert.Equal(t, inreplyTo[0], "<user2/repo1/issues/1@localhost>", "In-Reply-To header doesn't match")
assert.Equal(t, references[0], "<user2/repo1/issues/1@localhost>", "References header doesn't match")
}
@@ -75,17 +83,122 @@ func TestComposeIssueMessage(t *testing.T) {
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1, Owner: doer}).(*models.Repository)
issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1, Repo: repo, Poster: doer}).(*models.Issue)
- email := template.Must(template.New("issue/comment").Parse(tmpl))
- InitMailRender(email)
+ stpl := texttmpl.Must(texttmpl.New("issue/new").Parse(subjectTpl))
+ btpl := template.Must(template.New("issue/new").Parse(bodyTpl))
+ InitMailRender(stpl, btpl)
tos := []string{"test@gitea.com", "test2@gitea.com"}
- msg := composeIssueCommentMessage(issue, doer, "test body", nil, mailIssueComment, tos, "issue create")
+ msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, false, "test body", nil, tos, "issue create")
subject := msg.GetHeader("Subject")
messageID := msg.GetHeader("Message-ID")
- assert.Equal(t, subject[0], mailSubject(issue), "Subject not equal to issue.mailSubject()")
+ assert.Equal(t, "[user2/repo1] @user2 #1 - issue1", subject[0])
assert.Nil(t, msg.GetHeader("In-Reply-To"))
assert.Nil(t, msg.GetHeader("References"))
assert.Equal(t, messageID[0], "<user2/repo1/issues/1@localhost>", "Message-ID header doesn't match")
}
+
+func TestTemplateSelection(t *testing.T) {
+ assert.NoError(t, models.PrepareTestDatabase())
+ var mailService = setting.Mailer{
+ From: "test@gitea.com",
+ }
+
+ setting.MailService = &mailService
+ setting.Domain = "localhost"
+
+ doer := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+ repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1, Owner: doer}).(*models.Repository)
+ issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1, Repo: repo, Poster: doer}).(*models.Issue)
+ tos := []string{"test@gitea.com"}
+
+ stpl := texttmpl.Must(texttmpl.New("issue/default").Parse("issue/default/subject"))
+ texttmpl.Must(stpl.New("issue/new").Parse("issue/new/subject"))
+ texttmpl.Must(stpl.New("pull/comment").Parse("pull/comment/subject"))
+ texttmpl.Must(stpl.New("issue/close").Parse("")) // Must default to fallback subject
+
+ btpl := template.Must(template.New("issue/default").Parse("issue/default/body"))
+ template.Must(btpl.New("issue/new").Parse("issue/new/body"))
+ template.Must(btpl.New("pull/comment").Parse("pull/comment/body"))
+ template.Must(btpl.New("issue/close").Parse("issue/close/body"))
+
+ InitMailRender(stpl, btpl)
+
+ expect := func(t *testing.T, msg *Message, expSubject, expBody string) {
+ subject := msg.GetHeader("Subject")
+ msgbuf := new(bytes.Buffer)
+ _, _ = msg.WriteTo(msgbuf)
+ wholemsg := msgbuf.String()
+ assert.Equal(t, []string{expSubject}, subject)
+ assert.Contains(t, wholemsg, expBody)
+ }
+
+ msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, false, "test body", nil, tos, "TestTemplateSelection")
+ expect(t, msg, "issue/new/subject", "issue/new/body")
+
+ comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2, Issue: issue}).(*models.Comment)
+ msg = composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, false, "test body", comment, tos, "TestTemplateSelection")
+ expect(t, msg, "issue/default/subject", "issue/default/body")
+
+ pull := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 2, Repo: repo, Poster: doer}).(*models.Issue)
+ comment = models.AssertExistsAndLoadBean(t, &models.Comment{ID: 4, Issue: pull}).(*models.Comment)
+ msg = composeIssueCommentMessage(pull, doer, models.ActionCommentIssue, false, "test body", comment, tos, "TestTemplateSelection")
+ expect(t, msg, "pull/comment/subject", "pull/comment/body")
+
+ msg = composeIssueCommentMessage(issue, doer, models.ActionCloseIssue, false, "test body", nil, tos, "TestTemplateSelection")
+ expect(t, msg, "[user2/repo1] issue1 (#1)", "issue/close/body")
+}
+
+func TestTemplateServices(t *testing.T) {
+ assert.NoError(t, models.PrepareTestDatabase())
+ var mailService = setting.Mailer{
+ From: "test@gitea.com",
+ }
+
+ setting.MailService = &mailService
+ setting.Domain = "localhost"
+
+ doer := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
+ repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1, Owner: doer}).(*models.Repository)
+ issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: 1, Repo: repo, Poster: doer}).(*models.Issue)
+ comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2, Issue: issue}).(*models.Comment)
+ assert.NoError(t, issue.LoadRepo())
+
+ expect := func(t *testing.T, issue *models.Issue, comment *models.Comment, doer *models.User,
+ actionType models.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string) {
+
+ stpl := texttmpl.Must(texttmpl.New("issue/default").Parse(tplSubject))
+ btpl := template.Must(template.New("issue/default").Parse(tplBody))
+ InitMailRender(stpl, btpl)
+
+ tos := []string{"test@gitea.com"}
+ msg := composeIssueCommentMessage(issue, doer, actionType, fromMention, "test body", comment, tos, "TestTemplateServices")
+
+ subject := msg.GetHeader("Subject")
+ msgbuf := new(bytes.Buffer)
+ _, _ = msg.WriteTo(msgbuf)
+ wholemsg := msgbuf.String()
+
+ assert.Equal(t, []string{expSubject}, subject)
+ assert.Contains(t, wholemsg, "\r\n"+expBody+"\r\n")
+ }
+
+ expect(t, issue, comment, doer, models.ActionCommentIssue, false,
+ "{{.SubjectPrefix}}[{{.Repo}}]: @{{.Doer.Name}} commented on #{{.Issue.Index}} - {{.Issue.Title}}",
+ "//{{.ActionType}},{{.ActionName}},{{if .IsMention}}norender{{end}}//",
+ "Re: [user2/repo1]: @user2 commented on #1 - issue1",
+ "//issue,comment,//")
+
+ expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+ "{{if .IsMention}}must render{{end}}",
+ "//subject is: {{.Subject}}//",
+ "must render",
+ "//subject is: must render//")
+
+ expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+ "{{.FallbackSubject}}",
+ "//{{.SubjectPrefix}}//",
+ "Re: [user2/repo1] issue1 (#1)",
+ "//Re: //")
+}