From 2714a5d3d9f10eb6108bd67dc8713198fcc67eb5 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 30 Sep 2019 21:34:12 -0300 Subject: [PATCH 01/31] Add template capability for issue mail subject --- models/issue.go | 11 ++++++++ services/mailer/mail.go | 47 +++++++++++++++++++++++-------- services/mailer/mail_comment.go | 6 ++-- services/mailer/mail_issue.go | 16 +++++------ services/mailer/mail_test.go | 8 +++--- templates/mail/issue/subject.tmpl | 19 +++++++++++++ 6 files changed, 80 insertions(+), 27 deletions(-) create mode 100644 templates/mail/issue/subject.tmpl diff --git a/models/issue.go b/models/issue.go index da98fac7df367..a0231ca67418a 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1903,3 +1903,14 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) { } return } + +// ComposeSubjectTplData composes a map of metas for properly rendering a mail subject. +func (issue *Issue) ComposeSubjectTplData(doer *User, actionType ActionType) map[string]interface{} { + return map[string]interface{}{ + "Issue": issue, + "User": issue.Repo.MustOwner().Name, + "Repo": issue.Repo.FullName(), + "Doer": doer.Name, + "Action": actionType, + } +} \ No newline at end of file diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 3f0a789dc4cd4..080e34b6888a9 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -9,7 +9,10 @@ import ( "bytes" "fmt" "html/template" + "mime" "path" + "regexp" + "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" @@ -28,13 +31,19 @@ const ( mailAuthResetPassword base.TplName = "auth/reset_passwd" mailAuthRegisterNotify base.TplName = "auth/register_notify" + mailIssueSubject base.TplName = "issue/subject" mailIssueComment base.TplName = "issue/comment" mailIssueMention base.TplName = "issue/mention" mailNotifyCollaborator base.TplName = "notify/collaborator" + + // There's no actual limit for subject in RFC 5322 + mailMaxSubjectCharacters = 256 ) var templates *template.Template +var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`) + // InitMailRender initializes the mail renderer func InitMailRender(tmpls *template.Template) { @@ -163,20 +172,29 @@ func composeTplData(subject, body, link string) map[string]interface{} { return data } -func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tplName base.TplName, tos []string, info string) *Message { +func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionType models.ActionType, content string, comment *models.Comment, tplBody 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) } + + var mailSubject bytes.Buffer + if err := templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), issue.ComposeSubjectTplData(doer, actionType)); err == nil { + subject = sanitizeSubject(mailSubject.String()) + } else { + log.Error("Template: %v", err) + // Default subject + if comment != nil { + subject = "Re: " + defaultMailSubject(issue) + } else { + subject = defaultMailSubject(issue) + } + } + body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) - var data = make(map[string]interface{}, 10) + var data map[string]interface{} if comment != nil { data = composeTplData(subject, body, issue.HTMLURL()+"#"+comment.HashTag()) } else { @@ -186,7 +204,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content var mailBody bytes.Buffer - if err := templates.ExecuteTemplate(&mailBody, string(tplName), data); err != nil { + if err := templates.ExecuteTemplate(&mailBody, string(tplBody), data); err != nil { log.Error("Template: %v", err) } @@ -205,18 +223,23 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content } // 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, 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) { +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, content, comment, mailIssueMention, tos, "issue mention")) } + +func sanitizeSubject(subject string) string { + sanitized := strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")) + return mime.QEncoding.Encode("utf-8", string([]rune(sanitized)[:mailMaxSubjectCharacters])) +} \ No newline at end of file diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go index cb477f887b5cc..29c89342f463e 100644 --- a/services/mailer/mail_comment.go +++ b/services/mailer/mail_comment.go @@ -25,7 +25,7 @@ func mailParticipantsComment(ctx models.DBContext, c *models.Comment, opType mod } if len(c.Content) > 0 { - if err = mailIssueCommentToParticipants(issue, c.Poster, c.Content, c, mentions); err != nil { + if err = mailIssueCommentToParticipants(issue, c.Poster, opType, c.Content, c, mentions); err != nil { log.Error("mailIssueCommentToParticipants: %v", err) } } @@ -33,12 +33,12 @@ func mailParticipantsComment(ctx models.DBContext, c *models.Comment, opType mod switch opType { case models.ActionCloseIssue: ct := fmt.Sprintf("Closed #%d.", issue.Index) - if err = mailIssueCommentToParticipants(issue, c.Poster, ct, c, mentions); err != nil { + if err = mailIssueCommentToParticipants(issue, c.Poster, opType, 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 { + if err = mailIssueCommentToParticipants(issue, c.Poster, opType, ct, c, mentions); err != nil { log.Error("mailIssueCommentToParticipants: %v", err) } } diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index 92d2c5a8795f8..2fcbd2784073a 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -15,7 +15,7 @@ import ( "github.com/unknwon/com" ) -func mailSubject(issue *models.Issue) string { +func defaultMailSubject(issue *models.Issue) string { return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index) } @@ -23,7 +23,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 { if !setting.Service.EnableNotifyMail { return nil } @@ -93,7 +93,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. @@ -110,7 +110,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 @@ -130,7 +130,7 @@ func mailParticipants(ctx models.DBContext, issue *models.Issue, doer *models.Us } if len(issue.Content) > 0 { - if err = mailIssueCommentToParticipants(issue, doer, issue.Content, nil, mentions); err != nil { + if err = mailIssueCommentToParticipants(issue, doer, opType, issue.Content, nil, mentions); err != nil { log.Error("mailIssueCommentToParticipants: %v", err) } } @@ -139,18 +139,18 @@ func mailParticipants(ctx models.DBContext, issue *models.Issue, doer *models.Us 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 { + if err = mailIssueCommentToParticipants(issue, doer, opType, 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 { + if err = mailIssueCommentToParticipants(issue, doer, opType, 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 { + if err = mailIssueCommentToParticipants(issue, doer, opType, ct, nil, mentions); err != nil { log.Error("mailIssueCommentToParticipants: %v", err) } } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index c7a84d6b33e5f..d0a3078b7c40c 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -51,13 +51,13 @@ func TestComposeIssueCommentMessage(t *testing.T) { InitMailRender(email) 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, "test body", comment, mailIssueComment, 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, subject[0], "Re: "+defaultMailSubject(issue), "Comment reply subject should contain Re:") assert.Equal(t, inreplyTo[0], "", "In-Reply-To header doesn't match") assert.Equal(t, references[0], "", "References header doesn't match") } @@ -79,12 +79,12 @@ func TestComposeIssueMessage(t *testing.T) { InitMailRender(email) 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, "test body", nil, mailIssueComment, 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, subject[0], defaultMailSubject(issue), "Subject not equal to issue.mailSubject()") assert.Nil(t, msg.GetHeader("In-Reply-To")) assert.Nil(t, msg.GetHeader("References")) assert.Equal(t, messageID[0], "", "Message-ID header doesn't match") diff --git a/templates/mail/issue/subject.tmpl b/templates/mail/issue/subject.tmpl new file mode 100644 index 0000000000000..39dd4c14f36b0 --- /dev/null +++ b/templates/mail/issue/subject.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} +{{if eq .Action 6}} + created issue #{{.Issue.Index}} +{{else if eq .Action 7}} + created PR #{{.Issue.Index}} +{{else if eq .Action 10}} + commented on #{{.Issue.Index}} +{{else if eq .Action 12}} + closed issue #{{.Issue.Index}} +{{else if eq .Action 13}} + reopened issue #{{.Issue.Index}} +{{else if eq .Action 14}} + closed PR #{{.Issue.Index}} +{{else if eq .Action 15}} + reopened PR #{{.Issue.Index}} +{{else}} + commented/acted upon #{{.Issue.Index}} +{{end}} + áéíóú - {{.Issue.Title}} \ No newline at end of file From c4f6c1d2490409df47a7dbf79268e4267b75a1fb Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 30 Sep 2019 21:46:20 -0300 Subject: [PATCH 02/31] Remove test string --- templates/mail/issue/subject.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/mail/issue/subject.tmpl b/templates/mail/issue/subject.tmpl index 39dd4c14f36b0..c03f1f287ec9e 100644 --- a/templates/mail/issue/subject.tmpl +++ b/templates/mail/issue/subject.tmpl @@ -16,4 +16,4 @@ {{else}} commented/acted upon #{{.Issue.Index}} {{end}} - áéíóú - {{.Issue.Title}} \ No newline at end of file +- {{.Issue.Title}} \ No newline at end of file From 83624a9d7a7c7db09b9cc3d1b9df36cc06c0120c Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 30 Sep 2019 21:54:39 -0300 Subject: [PATCH 03/31] Fix trim subject length --- services/mailer/mail.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 080e34b6888a9..299eff4777e0f 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -38,7 +38,7 @@ const ( mailNotifyCollaborator base.TplName = "notify/collaborator" // There's no actual limit for subject in RFC 5322 - mailMaxSubjectCharacters = 256 + mailMaxSubjectRunes = 256 ) var templates *template.Template @@ -240,6 +240,9 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType mod } func sanitizeSubject(subject string) string { - sanitized := strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " ")) - return mime.QEncoding.Encode("utf-8", string([]rune(sanitized)[:mailMaxSubjectCharacters])) + runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " "))) + if len(runes) > mailMaxSubjectRunes { + runes = runes[:mailMaxSubjectRunes] + } + return mime.QEncoding.Encode("utf-8", string(runes)) } \ No newline at end of file From c68bbd67d3f2655b9ae5c814e492af5891e64a40 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 30 Sep 2019 22:06:16 -0300 Subject: [PATCH 04/31] Add comment to template and run make fmt --- models/issue.go | 15 ++++++++------- services/mailer/mail.go | 5 ++--- services/mailer/mail_test.go | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/models/issue.go b/models/issue.go index a0231ca67418a..f43d6a6cdbb7e 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1905,12 +1905,13 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) { } // ComposeSubjectTplData composes a map of metas for properly rendering a mail subject. -func (issue *Issue) ComposeSubjectTplData(doer *User, actionType ActionType) map[string]interface{} { +func (issue *Issue) ComposeSubjectTplData(doer *User, comment *Comment, actionType ActionType) map[string]interface{} { return map[string]interface{}{ - "Issue": issue, - "User": issue.Repo.MustOwner().Name, - "Repo": issue.Repo.FullName(), - "Doer": doer.Name, - "Action": actionType, + "Issue": issue, + "Comment": comment, + "User": issue.Repo.MustOwner().Name, + "Repo": issue.Repo.FullName(), + "Doer": doer.Name, + "Action": actionType, } -} \ No newline at end of file +} diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 299eff4777e0f..b96c12268ad88 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -44,7 +44,6 @@ const ( var templates *template.Template var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`) - // InitMailRender initializes the mail renderer func InitMailRender(tmpls *template.Template) { templates = tmpls @@ -180,7 +179,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy } var mailSubject bytes.Buffer - if err := templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), issue.ComposeSubjectTplData(doer, actionType)); err == nil { + if err := templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), issue.ComposeSubjectTplData(doer, comment, actionType)); err == nil { subject = sanitizeSubject(mailSubject.String()) } else { log.Error("Template: %v", err) @@ -245,4 +244,4 @@ func sanitizeSubject(subject string) string { runes = runes[:mailMaxSubjectRunes] } return mime.QEncoding.Encode("utf-8", string(runes)) -} \ No newline at end of file +} diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index d0a3078b7c40c..48e46bd4fad0c 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -57,7 +57,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { inreplyTo := msg.GetHeader("In-Reply-To") references := msg.GetHeader("References") - assert.Equal(t, subject[0], "Re: "+defaultMailSubject(issue), "Comment reply subject should contain Re:") + assert.Equal(t, subject[0], "Re: "+defaultMailSubject(issue), "Comment reply subject should contain Re:") assert.Equal(t, inreplyTo[0], "", "In-Reply-To header doesn't match") assert.Equal(t, references[0], "", "References header doesn't match") } From 3d25d115e48f866f898f603d6bf191eafac5a9ce Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 1 Oct 2019 01:24:49 -0300 Subject: [PATCH 05/31] Add information for the template --- models/issue.go | 21 +++++++++++------- services/mailer/mail.go | 37 ++++++++++++++++++------------- services/mailer/mail_test.go | 4 ++-- templates/mail/issue/subject.tmpl | 2 +- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/models/issue.go b/models/issue.go index f43d6a6cdbb7e..550fd8adaf281 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1905,13 +1905,18 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) { } // ComposeSubjectTplData composes a map of metas for properly rendering a mail subject. -func (issue *Issue) ComposeSubjectTplData(doer *User, comment *Comment, actionType ActionType) map[string]interface{} { - return map[string]interface{}{ - "Issue": issue, - "Comment": comment, - "User": issue.Repo.MustOwner().Name, - "Repo": issue.Repo.FullName(), - "Doer": doer.Name, - "Action": actionType, +func (issue *Issue) ComposeSubjectTplData(doer *User, comment *Comment, actionType ActionType, fromMention bool) (map[string]interface{}, error) { + if err := issue.LoadPullRequest(); err != nil { + return nil, err } + return map[string]interface{}{ + "Issue": issue, + "Comment": comment, + "IsPull": issue.IsPull, + "User": issue.Repo.MustOwner().Name, + "Repo": issue.Repo.FullName(), + "Doer": doer.Name, + "Action": actionType, + "FromMention": fromMention, + }, nil } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index b96c12268ad88..57850326daa24 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -171,7 +171,8 @@ func composeTplData(subject, body, link string) map[string]interface{} { return data } -func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionType models.ActionType, content string, comment *models.Comment, tplBody base.TplName, tos []string, info string) *Message { +func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionType models.ActionType, fromMention bool, + content string, comment *models.Comment, tplBody base.TplName, tos []string, info string) *Message { var subject string err := issue.LoadRepo() if err != nil { @@ -179,10 +180,15 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy } var mailSubject bytes.Buffer - if err := templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), issue.ComposeSubjectTplData(doer, comment, actionType)); err == nil { - subject = sanitizeSubject(mailSubject.String()) - } else { - log.Error("Template: %v", err) + if subjectData, err := issue.ComposeSubjectTplData(doer, comment, actionType, fromMention); err == nil { + if err = templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), subjectData); err == nil { + subject = sanitizeSubject(mailSubject.String()) + } + } + if subject == "" { + if err != nil { + log.Error("Template: %v", err) + } // Default subject if comment != nil { subject = "Re: " + defaultMailSubject(issue) @@ -221,13 +227,22 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy 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, actionType models.ActionType, content string, comment *models.Comment, tos []string) { if len(tos) == 0 { return } - SendAsync(composeIssueCommentMessage(issue, doer, actionType, content, comment, mailIssueComment, tos, "issue comment")) + SendAsync(composeIssueCommentMessage(issue, doer, actionType, false, content, comment, mailIssueComment, tos, "issue comment")) } // SendIssueMentionMail composes and sends issue mention emails to target receivers. @@ -235,13 +250,5 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType mod if len(tos) == 0 { return } - SendAsync(composeIssueCommentMessage(issue, doer, actionType, content, comment, mailIssueMention, tos, "issue mention")) -} - -func sanitizeSubject(subject string) string { - runes := []rune(strings.TrimSpace(subjectRemoveSpaces.ReplaceAllLiteralString(subject, " "))) - if len(runes) > mailMaxSubjectRunes { - runes = runes[:mailMaxSubjectRunes] - } - return mime.QEncoding.Encode("utf-8", string(runes)) + SendAsync(composeIssueCommentMessage(issue, doer, actionType, true, content, comment, mailIssueMention, tos, "issue mention")) } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 48e46bd4fad0c..057d71db6328a 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -51,7 +51,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { InitMailRender(email) tos := []string{"test@gitea.com", "test2@gitea.com"} - msg := composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, "test body", comment, mailIssueComment, tos, "issue comment") + msg := composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, false, "test body", comment, mailIssueComment, tos, "issue comment") subject := msg.GetHeader("Subject") inreplyTo := msg.GetHeader("In-Reply-To") @@ -79,7 +79,7 @@ func TestComposeIssueMessage(t *testing.T) { InitMailRender(email) tos := []string{"test@gitea.com", "test2@gitea.com"} - msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, "test body", nil, mailIssueComment, tos, "issue create") + msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, false, "test body", nil, mailIssueComment, tos, "issue create") subject := msg.GetHeader("Subject") messageID := msg.GetHeader("Message-ID") diff --git a/templates/mail/issue/subject.tmpl b/templates/mail/issue/subject.tmpl index c03f1f287ec9e..d32362f564fc4 100644 --- a/templates/mail/issue/subject.tmpl +++ b/templates/mail/issue/subject.tmpl @@ -16,4 +16,4 @@ {{else}} commented/acted upon #{{.Issue.Index}} {{end}} -- {{.Issue.Title}} \ No newline at end of file +- {{.Issue.Title}} From 6049215955a71076ef73b3db570e43acff8aef95 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 16 Oct 2019 02:19:17 -0300 Subject: [PATCH 06/31] Rename defaultMailSubject() to fallbackMailSubject() --- services/mailer/mail.go | 4 ++-- services/mailer/mail_issue.go | 2 +- services/mailer/mail_test.go | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 57850326daa24..65c481a2093e6 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -191,9 +191,9 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy } // Default subject if comment != nil { - subject = "Re: " + defaultMailSubject(issue) + subject = "Re: " + fallbackMailSubject(issue) } else { - subject = defaultMailSubject(issue) + subject = fallbackMailSubject(issue) } } diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index 530b7b806356d..2e5496f0f162c 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -15,7 +15,7 @@ import ( "github.com/unknwon/com" ) -func defaultMailSubject(issue *models.Issue) string { +func fallbackMailSubject(issue *models.Issue) string { return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index) } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 057d71db6328a..65117fc2c20b1 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -57,7 +57,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { inreplyTo := msg.GetHeader("In-Reply-To") references := msg.GetHeader("References") - assert.Equal(t, subject[0], "Re: "+defaultMailSubject(issue), "Comment reply subject should contain Re:") + assert.Equal(t, subject[0], "Re: "+fallbackMailSubject(issue), "Comment reply subject should contain Re:") assert.Equal(t, inreplyTo[0], "", "In-Reply-To header doesn't match") assert.Equal(t, references[0], "", "References header doesn't match") } @@ -84,7 +84,7 @@ func TestComposeIssueMessage(t *testing.T) { subject := msg.GetHeader("Subject") messageID := msg.GetHeader("Message-ID") - assert.Equal(t, subject[0], defaultMailSubject(issue), "Subject not equal to issue.mailSubject()") + assert.Equal(t, subject[0], fallbackMailSubject(issue), "Subject not equal to issue.mailSubject()") assert.Nil(t, msg.GetHeader("In-Reply-To")) assert.Nil(t, msg.GetHeader("References")) assert.Equal(t, messageID[0], "", "Message-ID header doesn't match") From 70d1d44dd5b6bb1de996fe85a2f4ccb2a1724561 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 01:56:53 -0300 Subject: [PATCH 07/31] General rewrite of the mail template code --- models/issue.go | 20 +-- modules/templates/dynamic.go | 45 +++--- services/mailer/mail.go | 146 +++++++++++++----- services/mailer/mail_test.go | 4 +- templates/mail/issue/close.tmpl | 19 +++ templates/mail/issue/comment.tmpl | 3 + .../mail/issue/{mention.tmpl => default.tmpl} | 4 +- templates/mail/issue/new.tmpl | 19 +++ templates/mail/issue/reopen.tmpl | 19 +++ templates/mail/issue/subject.tmpl | 19 --- templates/mail/pull/close.tmpl | 19 +++ templates/mail/pull/comment.tmpl | 19 +++ templates/mail/pull/default.tmpl | 19 +++ templates/mail/pull/merge.tmpl | 19 +++ templates/mail/pull/new.tmpl | 19 +++ templates/mail/pull/reopen.tmpl | 19 +++ 16 files changed, 320 insertions(+), 92 deletions(-) create mode 100644 templates/mail/issue/close.tmpl rename templates/mail/issue/{mention.tmpl => default.tmpl} (57%) create mode 100644 templates/mail/issue/new.tmpl create mode 100644 templates/mail/issue/reopen.tmpl delete mode 100644 templates/mail/issue/subject.tmpl create mode 100644 templates/mail/pull/close.tmpl create mode 100644 templates/mail/pull/comment.tmpl create mode 100644 templates/mail/pull/default.tmpl create mode 100644 templates/mail/pull/merge.tmpl create mode 100644 templates/mail/pull/new.tmpl create mode 100644 templates/mail/pull/reopen.tmpl diff --git a/models/issue.go b/models/issue.go index c39a44b988e00..1af04c9ee167b 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1730,20 +1730,20 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) { return } -// ComposeSubjectTplData composes a map of metas for properly rendering a mail subject. -func (issue *Issue) ComposeSubjectTplData(doer *User, comment *Comment, actionType ActionType, fromMention bool) (map[string]interface{}, error) { +// ComposeMailMetas composes a map of metas used for rendering a mail subject or body. +func (issue *Issue) ComposeMailMetas(doer *User, comment *Comment, actionType ActionType, fromMention bool) (map[string]interface{}, error) { if err := issue.LoadPullRequest(); err != nil { return nil, err } return map[string]interface{}{ - "Issue": issue, - "Comment": comment, - "IsPull": issue.IsPull, - "User": issue.Repo.MustOwner().Name, - "Repo": issue.Repo.FullName(), - "Doer": doer.Name, - "Action": actionType, - "FromMention": fromMention, + "Issue": issue, + "Comment": comment, + "IsPull": issue.IsPull, + "User": issue.Repo.MustOwner().Name, + "Repo": issue.Repo.FullName(), + "Doer": doer.Name, + "Action": actionType, + "IsMention": fromMention, }, nil } diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index 6217f1c3b038f..02247fbc48efb 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -10,6 +10,7 @@ import ( "html/template" "io/ioutil" "path" + "regexp" "strings" "code.gitea.io/gitea/modules/log" @@ -20,7 +21,8 @@ import ( ) var ( - templates = template.New("") + templates = template.New("") + mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`) ) // HTMLRenderer implements the macaron handler for serving HTML templates. @@ -84,15 +86,7 @@ func Mailer() *template.Template { continue } - _, err = templates.New( - strings.TrimSuffix( - filePath, - ".tmpl", - ), - ).Parse(string(content)) - if err != nil { - log.Warn("Failed to parse template %v", err) - } + buildSubjectBodyTemplate(strings.TrimSuffix(filePath, ".tmpl"), content) } } } @@ -117,18 +111,31 @@ func Mailer() *template.Template { continue } - _, err = templates.New( - strings.TrimSuffix( - filePath, - ".tmpl", - ), - ).Parse(string(content)) - if err != nil { - log.Warn("Failed to parse template %v", err) - } + buildSubjectBodyTemplate(strings.TrimSuffix(filePath, ".tmpl"), content) } } } return templates } + +func buildSubjectBodyTemplate(name string, content []byte) { + // Split template into subject and body + var subjectContent []byte + bodyContent := content + loc := mailSubjectSplit.FindIndex(content) + if loc != nil { + subjectContent = content[0:loc[0]] + bodyContent = content[loc[1]:] + } + body := templates.New(name + "/body") + if _, err := body.Parse(string(bodyContent)); err != nil { + log.Warn("Failed to parse template [%s/body]: %v", name, err) + return + } + subject := templates.New(name + "/subject") + if _, err := subject.Parse(string(subjectContent)); err != nil { + log.Warn("Failed to parse template [%s/subject]: %v", name, err) + return + } +} diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 65c481a2093e6..732dbf6fec6a7 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -31,9 +31,18 @@ const ( mailAuthResetPassword base.TplName = "auth/reset_passwd" mailAuthRegisterNotify base.TplName = "auth/register_notify" - mailIssueSubject base.TplName = "issue/subject" - mailIssueComment base.TplName = "issue/comment" - mailIssueMention base.TplName = "issue/mention" + mailIssueDefault base.TplName = "issue/default" + mailNewIssue base.TplName = "issue/new" + mailCommentIssue base.TplName = "issue/comment" + mailCloseIssue base.TplName = "issue/close" + mailReopenIssue base.TplName = "issue/reopen" + + mailPullRequestDefault base.TplName = "pull/default" + mailNewPullRequest base.TplName = "pull/new" + mailCommentPullRequest base.TplName = "pull/comment" + mailClosePullRequest base.TplName = "pull/close" + mailReopenPullRequest base.TplName = "pull/reopen" + mailMergePullRequest base.TplName = "pull/merge" // FIXME: Where can I use this? mailNotifyCollaborator base.TplName = "notify/collaborator" @@ -41,8 +50,10 @@ const ( mailMaxSubjectRunes = 256 ) -var templates *template.Template -var subjectRemoveSpaces = regexp.MustCompile(`[\s]+`) +var ( + templates *template.Template + subjectRemoveSpaces = regexp.MustCompile(`[\s]+`) +) // InitMailRender initializes the mail renderer func InitMailRender(tmpls *template.Template) { @@ -163,54 +174,74 @@ 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, tplBody base.TplName, tos []string, info string) *Message { - var subject string - err := issue.LoadRepo() - if err != nil { + content string, comment *models.Comment, tos []string, info string) *Message { + + if err := issue.LoadRepo(); err != nil { log.Error("LoadRepo: %v", err) + return nil } - - var mailSubject bytes.Buffer - if subjectData, err := issue.ComposeSubjectTplData(doer, comment, actionType, fromMention); err == nil { - if err = templates.ExecuteTemplate(&mailSubject, string(mailIssueSubject), subjectData); err == nil { - subject = sanitizeSubject(mailSubject.String()) - } + if err := issue.LoadPullRequest(); err != nil { + log.Error("LoadPullRequest: %v", err) + return nil } - if subject == "" { - if err != nil { - log.Error("Template: %v", err) - } - // Default subject - if comment != nil { - subject = "Re: " + fallbackMailSubject(issue) - } else { - subject = fallbackMailSubject(issue) - } + + var ( + subject string + link string + prefix string + // Fall back subject for bad templates, make sure subject is never empty + fallback string + ) + + if comment != nil { + prefix = "Re: " } + 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 map[string]interface{} if comment != nil { - data = composeTplData(subject, body, issue.HTMLURL()+"#"+comment.HashTag()) + link = issue.HTMLURL() + "#" + comment.HashTag() } else { - data = composeTplData(subject, body, issue.HTMLURL()) + link = issue.HTMLURL() + } + + mailMeta := map[string]interface{}{ + "FallbackSubject": fallback, + "Body": body, + "Link": link, + "Issue": issue, + "Comment": comment, + "IsPull": issue.IsPull, + "User": issue.Repo.MustOwner().Name, + "Repo": issue.Repo.FullName(), + "Doer": doer, + "Action": actionType, + "IsMention": fromMention, + "SubjectPrefix": prefix, } - data["Doer"] = doer + + tplBody := actionToTemplate(issue, actionType) + + var mailSubject bytes.Buffer + if err := templates.ExecuteTemplate(&mailSubject, string(tplBody)+"/subject", mailMeta); err == nil { + subject = sanitizeSubject(mailSubject.String()) + } else { + log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/subject", err) + } + + if subject == "" { + subject = fallback + } + mailMeta["Subject"] = subject var mailBody bytes.Buffer - if err := templates.ExecuteTemplate(&mailBody, string(tplBody), data); err != nil { - log.Error("Template: %v", err) + if err := templates.ExecuteTemplate(&mailBody, string(tplBody)+"/body", mailMeta); err != nil { + log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/body", err) } msg := NewMessageFrom(tos, doer.DisplayName(), setting.MailService.FromEmail, subject, mailBody.String()) @@ -242,7 +273,7 @@ func SendIssueCommentMail(issue *models.Issue, doer *models.User, actionType mod return } - SendAsync(composeIssueCommentMessage(issue, doer, actionType, false, 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. @@ -250,5 +281,38 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType mod if len(tos) == 0 { return } - SendAsync(composeIssueCommentMessage(issue, doer, actionType, true, content, comment, mailIssueMention, tos, "issue mention")) + SendAsync(composeIssueCommentMessage(issue, doer, actionType, true, content, comment, tos, "issue mention")) +} + +func actionToTemplate(issue *models.Issue, actionType models.ActionType) base.TplName { + var name base.TplName + switch actionType { + case models.ActionCreateIssue: + name = mailNewIssue + case models.ActionCreatePullRequest: + name = mailNewPullRequest + case models.ActionCommentIssue: + if issue.IsPull { + name = mailCommentPullRequest + } else { + name = mailCommentIssue + } + case models.ActionCloseIssue: + name = mailCloseIssue + case models.ActionReopenIssue: + name = mailReopenIssue + case models.ActionClosePullRequest: + name = mailClosePullRequest + case models.ActionReopenPullRequest: + name = mailReopenPullRequest + case models.ActionMergePullRequest: + name = mailMergePullRequest + } + if name != "" && templates.Lookup(string(name)+"/body") != nil { + return name + } + if issue.IsPull { + return mailPullRequestDefault + } + return mailIssueDefault } diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index 65117fc2c20b1..d8778188aa241 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -51,7 +51,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { InitMailRender(email) tos := []string{"test@gitea.com", "test2@gitea.com"} - msg := composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, false, "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") @@ -79,7 +79,7 @@ func TestComposeIssueMessage(t *testing.T) { InitMailRender(email) tos := []string{"test@gitea.com", "test2@gitea.com"} - msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, false, "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") diff --git a/templates/mail/issue/close.tmpl b/templates/mail/issue/close.tmpl new file mode 100644 index 0000000000000..ce2a7bf56771a --- /dev/null +++ b/templates/mail/issue/close.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/issue/comment.tmpl b/templates/mail/issue/comment.tmpl index cc86addaf05ec..cfbcdfe545b3b 100644 --- a/templates/mail/issue/comment.tmpl +++ b/templates/mail/issue/comment.tmpl @@ -1,3 +1,5 @@ +[{{.Repo}}] {{.Doer}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- @@ -6,6 +8,7 @@ + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}}

{{.Body | Str2html}}

--- diff --git a/templates/mail/issue/mention.tmpl b/templates/mail/issue/default.tmpl similarity index 57% rename from templates/mail/issue/mention.tmpl rename to templates/mail/issue/default.tmpl index 032eea053d46a..cc3232a75d283 100644 --- a/templates/mail/issue/mention.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,3 +1,5 @@ +[{{.Repo}}] {{.Doer}} Issue #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- @@ -6,7 +8,7 @@ -

@{{.Doer.Name}} mentioned you:

+ {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}}

{{.Body | Str2html}}

--- diff --git a/templates/mail/issue/new.tmpl b/templates/mail/issue/new.tmpl new file mode 100644 index 0000000000000..699c8eb74949f --- /dev/null +++ b/templates/mail/issue/new.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} created issue #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/issue/reopen.tmpl b/templates/mail/issue/reopen.tmpl new file mode 100644 index 0000000000000..8c7fa6db0bff4 --- /dev/null +++ b/templates/mail/issue/reopen.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/issue/subject.tmpl b/templates/mail/issue/subject.tmpl deleted file mode 100644 index d32362f564fc4..0000000000000 --- a/templates/mail/issue/subject.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -[{{.Repo}}] {{.Doer}} -{{if eq .Action 6}} - created issue #{{.Issue.Index}} -{{else if eq .Action 7}} - created PR #{{.Issue.Index}} -{{else if eq .Action 10}} - commented on #{{.Issue.Index}} -{{else if eq .Action 12}} - closed issue #{{.Issue.Index}} -{{else if eq .Action 13}} - reopened issue #{{.Issue.Index}} -{{else if eq .Action 14}} - closed PR #{{.Issue.Index}} -{{else if eq .Action 15}} - reopened PR #{{.Issue.Index}} -{{else}} - commented/acted upon #{{.Issue.Index}} -{{end}} -- {{.Issue.Title}} diff --git a/templates/mail/pull/close.tmpl b/templates/mail/pull/close.tmpl new file mode 100644 index 0000000000000..94f37e7fd7d06 --- /dev/null +++ b/templates/mail/pull/close.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/pull/comment.tmpl b/templates/mail/pull/comment.tmpl new file mode 100644 index 0000000000000..b1918a822ed6f --- /dev/null +++ b/templates/mail/pull/comment.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl new file mode 100644 index 0000000000000..22693e631a0ce --- /dev/null +++ b/templates/mail/pull/default.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/pull/merge.tmpl b/templates/mail/pull/merge.tmpl new file mode 100644 index 0000000000000..3d68867c37664 --- /dev/null +++ b/templates/mail/pull/merge.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/pull/new.tmpl b/templates/mail/pull/new.tmpl new file mode 100644 index 0000000000000..71f4699f81b0f --- /dev/null +++ b/templates/mail/pull/new.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + diff --git a/templates/mail/pull/reopen.tmpl b/templates/mail/pull/reopen.tmpl new file mode 100644 index 0000000000000..3987cf2d0cced --- /dev/null +++ b/templates/mail/pull/reopen.tmpl @@ -0,0 +1,19 @@ +[{{.Repo}}] {{.Doer}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} +------------------------------------------------------------- + + + + + {{.Subject}} + + + + {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} +

{{.Body | Str2html}}

+

+ --- +
+ View it on Gitea. +

+ + From 679338b711fcf2acd1c03da624bad38f6be3777b Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 20:33:55 -0300 Subject: [PATCH 08/31] Fix .Doer name --- templates/mail/issue/close.tmpl | 2 +- templates/mail/issue/comment.tmpl | 2 +- templates/mail/issue/default.tmpl | 2 +- templates/mail/issue/new.tmpl | 2 +- templates/mail/issue/reopen.tmpl | 2 +- templates/mail/pull/close.tmpl | 2 +- templates/mail/pull/comment.tmpl | 2 +- templates/mail/pull/default.tmpl | 2 +- templates/mail/pull/merge.tmpl | 2 +- templates/mail/pull/new.tmpl | 2 +- templates/mail/pull/reopen.tmpl | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/templates/mail/issue/close.tmpl b/templates/mail/issue/close.tmpl index ce2a7bf56771a..b7f4626108b3c 100644 --- a/templates/mail/issue/close.tmpl +++ b/templates/mail/issue/close.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/comment.tmpl b/templates/mail/issue/comment.tmpl index cfbcdfe545b3b..948ea059bd7a1 100644 --- a/templates/mail/issue/comment.tmpl +++ b/templates/mail/issue/comment.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index cc3232a75d283..2a1264901c114 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} Issue #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} Issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/new.tmpl b/templates/mail/issue/new.tmpl index 699c8eb74949f..33aecb259d86b 100644 --- a/templates/mail/issue/new.tmpl +++ b/templates/mail/issue/new.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} created issue #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} created issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/reopen.tmpl b/templates/mail/issue/reopen.tmpl index 8c7fa6db0bff4..1200e8254ec91 100644 --- a/templates/mail/issue/reopen.tmpl +++ b/templates/mail/issue/reopen.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/close.tmpl b/templates/mail/pull/close.tmpl index 94f37e7fd7d06..09b4fc703882b 100644 --- a/templates/mail/pull/close.tmpl +++ b/templates/mail/pull/close.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/comment.tmpl b/templates/mail/pull/comment.tmpl index b1918a822ed6f..677860dd9006b 100644 --- a/templates/mail/pull/comment.tmpl +++ b/templates/mail/pull/comment.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl index 22693e631a0ce..8c0392c6e1c1b 100644 --- a/templates/mail/pull/default.tmpl +++ b/templates/mail/pull/default.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/merge.tmpl b/templates/mail/pull/merge.tmpl index 3d68867c37664..3999def39f9ad 100644 --- a/templates/mail/pull/merge.tmpl +++ b/templates/mail/pull/merge.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/new.tmpl b/templates/mail/pull/new.tmpl index 71f4699f81b0f..ce7b28cf63bc0 100644 --- a/templates/mail/pull/new.tmpl +++ b/templates/mail/pull/new.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/reopen.tmpl b/templates/mail/pull/reopen.tmpl index 3987cf2d0cced..20f28bdc2ff3c 100644 --- a/templates/mail/pull/reopen.tmpl +++ b/templates/mail/pull/reopen.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] {{.Doer}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} +[{{.Repo}}] @{{.Doer.Name}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- From 24967be632705c45e1b14d0bcc9bdb829f601352 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 20:35:23 -0300 Subject: [PATCH 09/31] Use text/template for subject instead of html --- modules/templates/dynamic.go | 27 +++++---- modules/templates/helper.go | 108 +++++++++++++++++++++++++++++++++++ services/mailer/mail.go | 23 ++++---- services/mailer/mail_test.go | 5 +- 4 files changed, 139 insertions(+), 24 deletions(-) diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index 02247fbc48efb..6495a93b75192 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -12,6 +12,7 @@ import ( "path" "regexp" "strings" + texttmpl "text/template" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -21,7 +22,8 @@ import ( ) var ( - templates = template.New("") + bodyTemplates = template.New("") + subjectTemplates = texttmpl.New("") mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`) ) @@ -61,9 +63,12 @@ func JSRenderer() macaron.Handler { } // Mailer provides the templates required for sending notification mails. -func Mailer() *template.Template { +func Mailer() (*texttmpl.Template, *template.Template) { + for _, funcs := range NewTextFuncMap() { + subjectTemplates.Funcs(funcs) + } for _, funcs := range NewFuncMap() { - templates.Funcs(funcs) + bodyTemplates.Funcs(funcs) } staticDir := path.Join(setting.StaticRootPath, "templates", "mail") @@ -116,7 +121,7 @@ func Mailer() *template.Template { } } - return templates + return subjectTemplates, bodyTemplates } func buildSubjectBodyTemplate(name string, content []byte) { @@ -128,14 +133,12 @@ func buildSubjectBodyTemplate(name string, content []byte) { subjectContent = content[0:loc[0]] bodyContent = content[loc[1]:] } - body := templates.New(name + "/body") - if _, err := body.Parse(string(bodyContent)); err != nil { - log.Warn("Failed to parse template [%s/body]: %v", name, err) - return - } - subject := templates.New(name + "/subject") - if _, err := subject.Parse(string(subjectContent)); err != nil { + if _, err := subjectTemplates.New(name). + Parse(string(subjectContent)); err != nil { log.Warn("Failed to parse template [%s/subject]: %v", name, err) - return + } + if _, err := bodyTemplates.New(name). + Parse(string(bodyContent)); err != nil { + log.Warn("Failed to parse template [%s/body]: %v", name, err) } } diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 9bb803c01047f..32baf85facde3 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -18,6 +18,7 @@ import ( "path/filepath" "runtime" "strings" + texttmpl "text/template" "time" "code.gitea.io/gitea/models" @@ -245,6 +246,113 @@ func NewFuncMap() []template.FuncMap { }} } +// NewTextFuncMap returns functions for injecting to text templates +// It's a subset of those used for HTML and other templates +func NewTextFuncMap() []texttmpl.FuncMap { + return []texttmpl.FuncMap{map[string]interface{}{ + "GoVer": func() string { + return strings.Title(runtime.Version()) + }, + "AppName": func() string { + return setting.AppName + }, + "AppSubUrl": func() string { + return setting.AppSubURL + }, + "AppUrl": func() string { + return setting.AppURL + }, + "AppVer": func() string { + return setting.AppVer + }, + "AppBuiltWith": func() string { + return setting.AppBuiltWith + }, + "AppDomain": func() string { + return setting.Domain + }, + "TimeSince": timeutil.TimeSince, + "TimeSinceUnix": timeutil.TimeSinceUnix, + "RawTimeSince": timeutil.RawTimeSince, + "DateFmtLong": func(t time.Time) string { + return t.Format(time.RFC1123Z) + }, + "DateFmtShort": func(t time.Time) string { + return t.Format("Jan 02, 2006") + }, + "List": List, + "SubStr": func(str string, start, length int) string { + if len(str) == 0 { + return "" + } + end := start + length + if length == -1 { + end = len(str) + } + if len(str) < end { + return str + } + return str[start:end] + }, + "EllipsisString": base.EllipsisString, + "URLJoin": util.URLJoin, + "TrN": TrN, + "Dict": func(values ...interface{}) (map[string]interface{}, error) { + if len(values)%2 != 0 { + return nil, errors.New("invalid dict call") + } + dict := make(map[string]interface{}, len(values)/2) + for i := 0; i < len(values); i += 2 { + key, ok := values[i].(string) + if !ok { + return nil, errors.New("dict keys must be strings") + } + dict[key] = values[i+1] + } + return dict, nil + }, + "Printf": fmt.Sprintf, + "Escape": Escape, + "Sec2Time": models.SecToTime, + "ParseDeadline": func(deadline string) []string { + return strings.Split(deadline, "|") + }, + "dict": func(values ...interface{}) (map[string]interface{}, error) { + if len(values) == 0 { + return nil, errors.New("invalid dict call") + } + + dict := make(map[string]interface{}) + + for i := 0; i < len(values); i++ { + switch key := values[i].(type) { + case string: + i++ + if i == len(values) { + return nil, errors.New("specify the key for non array values") + } + dict[key] = values[i] + case map[string]interface{}: + m := values[i].(map[string]interface{}) + for i, v := range m { + dict[i] = v + } + default: + return nil, errors.New("dict values must be maps") + } + } + return dict, nil + }, + "percentage": func(n int, values ...int) float32 { + var sum = 0 + for i := 0; i < len(values); i++ { + sum += values[i] + } + return float32(n) * 100 / float32(sum) + }, + }} +} + // Safe render raw as HTML func Safe(raw string) template.HTML { return template.HTML(raw) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 732dbf6fec6a7..51388d9bebc77 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -13,6 +13,7 @@ import ( "path" "regexp" "strings" + texttmpl "text/template" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" @@ -51,13 +52,15 @@ const ( ) var ( - templates *template.Template + 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 @@ -76,7 +79,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 } @@ -114,7 +117,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 } @@ -139,7 +142,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 } @@ -163,7 +166,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 } @@ -227,7 +230,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy tplBody := actionToTemplate(issue, actionType) var mailSubject bytes.Buffer - if err := templates.ExecuteTemplate(&mailSubject, string(tplBody)+"/subject", mailMeta); err == nil { + if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplBody), mailMeta); err == nil { subject = sanitizeSubject(mailSubject.String()) } else { log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/subject", err) @@ -240,7 +243,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy var mailBody bytes.Buffer - if err := templates.ExecuteTemplate(&mailBody, string(tplBody)+"/body", mailMeta); err != nil { + if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplBody), mailMeta); err != nil { log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/body", err) } @@ -308,7 +311,7 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType) base.Tp case models.ActionMergePullRequest: name = mailMergePullRequest } - if name != "" && templates.Lookup(string(name)+"/body") != nil { + if name != "" && bodyTemplates.Lookup(string(name)) != nil { return name } if issue.IsPull { diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index d8778188aa241..ace737f51eb9d 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -7,6 +7,7 @@ package mailer import ( "html/template" "testing" + texttmpl "text/template" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/setting" @@ -48,7 +49,7 @@ func TestComposeIssueCommentMessage(t *testing.T) { comment := models.AssertExistsAndLoadBean(t, &models.Comment{ID: 2, Issue: issue}).(*models.Comment) email := template.Must(template.New("issue/comment").Parse(tmpl)) - InitMailRender(email) + InitMailRender(texttmpl.New(""), email) tos := []string{"test@gitea.com", "test2@gitea.com"} msg := composeIssueCommentMessage(issue, doer, models.ActionCommentIssue, false, "test body", comment, tos, "issue comment") @@ -76,7 +77,7 @@ func TestComposeIssueMessage(t *testing.T) { 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) + InitMailRender(texttmpl.New(""), email) tos := []string{"test@gitea.com", "test2@gitea.com"} msg := composeIssueCommentMessage(issue, doer, models.ActionCreateIssue, false, "test body", nil, tos, "issue create") From 1ce253058bf57095d9670ce9630f49a5378a116d Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 20:59:55 -0300 Subject: [PATCH 10/31] Fix subject Re: prefix --- templates/mail/issue/close.tmpl | 2 +- templates/mail/issue/comment.tmpl | 2 +- templates/mail/issue/default.tmpl | 2 +- templates/mail/issue/new.tmpl | 2 +- templates/mail/issue/reopen.tmpl | 2 +- templates/mail/pull/close.tmpl | 2 +- templates/mail/pull/comment.tmpl | 2 +- templates/mail/pull/default.tmpl | 2 +- templates/mail/pull/merge.tmpl | 2 +- templates/mail/pull/new.tmpl | 2 +- templates/mail/pull/reopen.tmpl | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/templates/mail/issue/close.tmpl b/templates/mail/issue/close.tmpl index b7f4626108b3c..ffeeaf183a30c 100644 --- a/templates/mail/issue/close.tmpl +++ b/templates/mail/issue/close.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/comment.tmpl b/templates/mail/issue/comment.tmpl index 948ea059bd7a1..a5d0b02417b2d 100644 --- a/templates/mail/issue/comment.tmpl +++ b/templates/mail/issue/comment.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 2a1264901c114..afcc5661f38e4 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} Issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} Issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/new.tmpl b/templates/mail/issue/new.tmpl index 33aecb259d86b..4993f3332b9f9 100644 --- a/templates/mail/issue/new.tmpl +++ b/templates/mail/issue/new.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} created issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} created issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/issue/reopen.tmpl b/templates/mail/issue/reopen.tmpl index 1200e8254ec91..82c1dee85df43 100644 --- a/templates/mail/issue/reopen.tmpl +++ b/templates/mail/issue/reopen.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/close.tmpl b/templates/mail/pull/close.tmpl index 09b4fc703882b..932c9be5f7ac2 100644 --- a/templates/mail/pull/close.tmpl +++ b/templates/mail/pull/close.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/comment.tmpl b/templates/mail/pull/comment.tmpl index 677860dd9006b..46c408771e6bf 100644 --- a/templates/mail/pull/comment.tmpl +++ b/templates/mail/pull/comment.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl index 8c0392c6e1c1b..2787c35fd0bee 100644 --- a/templates/mail/pull/default.tmpl +++ b/templates/mail/pull/default.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/merge.tmpl b/templates/mail/pull/merge.tmpl index 3999def39f9ad..c54b106f7280c 100644 --- a/templates/mail/pull/merge.tmpl +++ b/templates/mail/pull/merge.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/new.tmpl b/templates/mail/pull/new.tmpl index ce7b28cf63bc0..b1f246c051763 100644 --- a/templates/mail/pull/new.tmpl +++ b/templates/mail/pull/new.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- diff --git a/templates/mail/pull/reopen.tmpl b/templates/mail/pull/reopen.tmpl index 20f28bdc2ff3c..015a6d4541d80 100644 --- a/templates/mail/pull/reopen.tmpl +++ b/templates/mail/pull/reopen.tmpl @@ -1,4 +1,4 @@ -[{{.Repo}}] @{{.Doer.Name}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} ------------------------------------------------------------- From 2bba3635ab5a224c27dbf31a38b3eed1d5bf1870 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 21:00:27 -0300 Subject: [PATCH 11/31] Fix mail tests --- services/mailer/mail_test.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index ace737f51eb9d..f32cbc61125b7 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -15,7 +15,11 @@ import ( "github.com/stretchr/testify/assert" ) -const tmpl = ` +const subjectTpl = ` +{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} #{{.Issue.Index}} - {{.Issue.Title}} +` + +const bodyTpl = ` @@ -48,8 +52,9 @@ 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(texttmpl.New(""), 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, models.ActionCommentIssue, false, "test body", comment, tos, "issue comment") @@ -58,7 +63,8 @@ func TestComposeIssueCommentMessage(t *testing.T) { inreplyTo := msg.GetHeader("In-Reply-To") references := msg.GetHeader("References") - assert.Equal(t, subject[0], "Re: "+fallbackMailSubject(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], "", "In-Reply-To header doesn't match") assert.Equal(t, references[0], "", "References header doesn't match") } @@ -76,8 +82,9 @@ 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(texttmpl.New(""), 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, models.ActionCreateIssue, false, "test body", nil, tos, "issue create") @@ -85,8 +92,11 @@ func TestComposeIssueMessage(t *testing.T) { subject := msg.GetHeader("Subject") messageID := msg.GetHeader("Message-ID") - assert.Equal(t, subject[0], fallbackMailSubject(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], "", "Message-ID header doesn't match") + + // GAP: TODO: test fallback subject + default subject + // assert.Equal(t, subject[0], fallbackMailSubject(issue), "Subject not equal to issue.mailSubject()") } From 6aa4433a2da2ed8b4eda9a54e4e5de6f092ccba6 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Mon, 21 Oct 2019 21:54:32 -0300 Subject: [PATCH 12/31] Fix static templates --- modules/templates/dynamic.go | 27 +++------------------------ modules/templates/helper.go | 23 +++++++++++++++++++++++ modules/templates/static.go | 23 +++++++++++++++-------- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go index 6495a93b75192..6153e8d027334 100644 --- a/modules/templates/dynamic.go +++ b/modules/templates/dynamic.go @@ -10,7 +10,6 @@ import ( "html/template" "io/ioutil" "path" - "regexp" "strings" texttmpl "text/template" @@ -22,9 +21,8 @@ import ( ) var ( - bodyTemplates = template.New("") subjectTemplates = texttmpl.New("") - mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`) + bodyTemplates = template.New("") ) // HTMLRenderer implements the macaron handler for serving HTML templates. @@ -91,7 +89,7 @@ func Mailer() (*texttmpl.Template, *template.Template) { continue } - buildSubjectBodyTemplate(strings.TrimSuffix(filePath, ".tmpl"), content) + buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, strings.TrimSuffix(filePath, ".tmpl"), content) } } } @@ -116,29 +114,10 @@ func Mailer() (*texttmpl.Template, *template.Template) { continue } - buildSubjectBodyTemplate(strings.TrimSuffix(filePath, ".tmpl"), content) + buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, strings.TrimSuffix(filePath, ".tmpl"), content) } } } return subjectTemplates, bodyTemplates } - -func buildSubjectBodyTemplate(name string, content []byte) { - // Split template into subject and body - var subjectContent []byte - bodyContent := content - loc := mailSubjectSplit.FindIndex(content) - if loc != nil { - subjectContent = content[0:loc[0]] - bodyContent = content[loc[1]:] - } - if _, err := subjectTemplates.New(name). - Parse(string(subjectContent)); err != nil { - log.Warn("Failed to parse template [%s/subject]: %v", name, err) - } - if _, err := bodyTemplates.New(name). - Parse(string(bodyContent)); err != nil { - log.Warn("Failed to parse template [%s/body]: %v", name, err) - } -} diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 32baf85facde3..e30f1f4e6cb6a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -16,6 +16,7 @@ import ( "mime" "net/url" "path/filepath" + "regexp" "runtime" "strings" texttmpl "text/template" @@ -34,6 +35,9 @@ import ( "github.com/editorconfig/editorconfig-core-go/v2" ) +// Used from static.go && dynamic.go +var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`) + // NewFuncMap returns functions for injecting to templates func NewFuncMap() []template.FuncMap { return []template.FuncMap{map[string]interface{}{ @@ -631,3 +635,22 @@ func MigrationIcon(hostname string) string { return "fa-git-alt" } } + +func buildSubjectBodyTemplate(stpl *texttmpl.Template, btpl *template.Template, name string, content []byte) { + // Split template into subject and body + var subjectContent []byte + bodyContent := content + loc := mailSubjectSplit.FindIndex(content) + if loc != nil { + subjectContent = content[0:loc[0]] + bodyContent = content[loc[1]:] + } + if _, err := stpl.New(name). + Parse(string(subjectContent)); err != nil { + log.Warn("Failed to parse template [%s/subject]: %v", name, err) + } + if _, err := btpl.New(name). + Parse(string(bodyContent)); err != nil { + log.Warn("Failed to parse template [%s/body]: %v", name, err) + } +} diff --git a/modules/templates/static.go b/modules/templates/static.go index f7e53ce887ec1..435ccb1f95650 100644 --- a/modules/templates/static.go +++ b/modules/templates/static.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "path" "strings" + texttmpl "text/template" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -23,7 +24,8 @@ import ( ) var ( - templates = template.New("") + subjectTemplates = texttmpl.New("") + bodyTemplates = template.New("") ) type templateFileSystem struct { @@ -140,9 +142,12 @@ func JSRenderer() macaron.Handler { } // Mailer provides the templates required for sending notification mails. -func Mailer() *template.Template { +func Mailer() (*texttmpl.Template, *template.Template) { + for _, funcs := range NewTextFuncMap() { + subjectTemplates.Funcs(funcs) + } for _, funcs := range NewFuncMap() { - templates.Funcs(funcs) + bodyTemplates.Funcs(funcs) } for _, assetPath := range AssetNames() { @@ -161,7 +166,8 @@ func Mailer() *template.Template { continue } - templates.New( + buildSubjectBodyTemplate(subjectTemplates, + bodyTemplates, strings.TrimPrefix( strings.TrimSuffix( assetPath, @@ -169,7 +175,7 @@ func Mailer() *template.Template { ), "mail/", ), - ).Parse(string(content)) + content) } customDir := path.Join(setting.CustomPath, "templates", "mail") @@ -192,17 +198,18 @@ func Mailer() *template.Template { continue } - templates.New( + buildSubjectBodyTemplate(subjectTemplates, + bodyTemplates, strings.TrimSuffix( filePath, ".tmpl", ), - ).Parse(string(content)) + content) } } } - return templates + return subjectTemplates, bodyTemplates } func Asset(name string) ([]byte, error) { From 8383b95b24f46030b2e17730b4f03c89e2a3cb12 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 21 Oct 2019 20:22:31 +0000 Subject: [PATCH 13/31] [skip ci] Updated translations via Crowdin --- options/locale/locale_es-ES.ini | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index d9e18977b4c4a..b31151ac9c8ac 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -1309,20 +1309,20 @@ settings.add_discord_hook_desc=Integrar Discord en su repositor settings.add_dingtalk_hook_desc=Integrar Dingtalk en su repositorio. settings.add_telegram_hook_desc=Integrar Telegrama en tu repositorio. settings.add_msteams_hook_desc=Integrar Microsoft Teams en tu repositorio. -settings.deploy_keys=Claves de Despliegue -settings.add_deploy_key=Añadir Clave de Despliegue -settings.deploy_key_desc=Las claves de despliegue tienen acceso de sólo lectura al repositorio. +settings.deploy_keys=Claves de Implementación +settings.add_deploy_key=Añadir Clave de Implementación +settings.deploy_key_desc=Las claves de implementación tienen acceso de sólo lectura al repositorio. settings.is_writable=Habilitar acceso de escritura -settings.is_writable_info=Permitir que esta clave de despliegue pueda hacer push a este repositorio. -settings.no_deploy_keys=Aún no existen claves de despliegue. +settings.is_writable_info=Permitir que esta clave de implementación pueda hacer push a este repositorio. +settings.no_deploy_keys=Aún no existen claves de implementación. settings.title=Título settings.deploy_key_content=Contenido -settings.key_been_used=Una clave de despliegue con contenido idéntico ya se encuentra en uso. -settings.key_name_used=Ya existe una clave de despliegue con el mismo nombre. -settings.add_key_success=La clave de despliegue '%s' ha sido añadida. -settings.deploy_key_deletion=Eliminar clave de despliegue -settings.deploy_key_deletion_desc=Eliminar una clave de despliegue revocará el acceso de la misma a este repositorio. ¿Continuar? -settings.deploy_key_deletion_success=La clave de despliegue ha sido eliminada. +settings.key_been_used=Una clave de implementación con contenido idéntico ya se encuentra en uso. +settings.key_name_used=Ya existe una clave de implementación con el mismo nombre. +settings.add_key_success=La clave de implementación '%s' ha sido añadida. +settings.deploy_key_deletion=Eliminar clave de implementación +settings.deploy_key_deletion_desc=Eliminar una clave de implementación revocará el acceso de la misma a este repositorio. ¿Continuar? +settings.deploy_key_deletion_success=La clave de implementación ha sido eliminada. settings.branches=Ramas settings.protected_branch=Protección de rama settings.protected_branch_can_push=¿Permitir hacer push? @@ -1333,7 +1333,7 @@ settings.protect_this_branch=Activar protección de rama settings.protect_this_branch_desc=Prevenir eliminar y desactivar hacer git push en esta rama. settings.protect_whitelist_committers=Activar lista blanca para hacer push settings.protect_whitelist_committers_desc=Permitir hacer push en esta rama a los usuarios o equipos en la lista blanca (pero no hacer push forzado). -settings.protect_whitelist_deploy_keys=Poner en la lista blanca las claves de despliegue permitidas hacer push +settings.protect_whitelist_deploy_keys=Poner en lista blanca las claves de implementación con permisos de hacer push settings.protect_whitelist_users=Usuarios en la lista blanca para hacer push: settings.protect_whitelist_search_users=Buscar usuarios… settings.protect_whitelist_teams=Equipos en la lista blanca para hacer push: From 3db7c21403902c22a506394ba20e793b3c44421b Mon Sep 17 00:00:00 2001 From: zeripath Date: Mon, 21 Oct 2019 22:20:47 +0100 Subject: [PATCH 14/31] 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> --- custom/conf/app.ini.sample | 10 ++++++---- .../doc/advanced/config-cheat-sheet.en-us.md | 8 ++++++-- models/models.go | 8 +++----- modules/setting/database.go | 14 +++++++++----- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index c08dd62e7d28f..337676016476d 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -317,10 +317,12 @@ LOG_SQL = true DB_RETRIES = 10 ; Backoff time per DB retry (time.Duration) DB_RETRY_BACKOFF = 3s -; Max idle database connections on connnection pool, default is 0 -MAX_IDLE_CONNS = 0 -; Database connection max life time, default is 3s +; Max idle database connections on connnection pool, default is 2 +MAX_IDLE_CONNS = 2 +; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning) CONN_MAX_LIFETIME = 3s +; Database maximum number of open connections, default is 0 meaning no maximum +MAX_OPEN_CONNS = 0 [indexer] ; Issue indexer type, currently support: bleve or db, default is bleve @@ -870,6 +872,6 @@ TOKEN = QUEUE_TYPE = channel ; Task queue length, available only when `QUEUE_TYPE` is `channel`. QUEUE_LENGTH = 1000 -; Task queue connction string, available only when `QUEUE_TYPE` is `redis`. +; Task queue connection string, available only when `QUEUE_TYPE` is `redis`. ; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`. QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0" diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 678f8df2382d3..f99e9f661aeb6 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -192,8 +192,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. - `LOG_SQL`: **true**: Log the executed SQL. - `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed. - `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured. -- `MAX_IDLE_CONNS` **0**: Max idle database connections on connnection pool, default is 0 -- `CONN_MAX_LIFETIME` **3s**: Database connection max lifetime +- `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit. +- `MAX_IDLE_CONNS` **2**: Max idle database connections on connnection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`. +- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071). + +Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their +relation to port exhaustion. ## Indexer (`indexer`) diff --git a/models/models.go b/models/models.go index 0454ec6add497..854cb33b147fe 100644 --- a/models/models.go +++ b/models/models.go @@ -157,11 +157,9 @@ func SetEngine() (err error) { // so use log file to instead print to stdout. x.SetLogger(NewXORMLogger(setting.Database.LogSQL)) x.ShowSQL(setting.Database.LogSQL) - if setting.Database.UseMySQL { - x.SetMaxIdleConns(setting.Database.MaxIdleConns) - x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) - } - + x.SetMaxOpenConns(setting.Database.MaxOpenConns) + x.SetMaxIdleConns(setting.Database.MaxIdleConns) + x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) return nil } diff --git a/modules/setting/database.go b/modules/setting/database.go index 2cac4824dfbcd..8c49ba3c5a179 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -42,12 +42,11 @@ var ( DBConnectRetries int DBConnectBackoff time.Duration MaxIdleConns int + MaxOpenConns int ConnMaxLifetime time.Duration IterateBufferSize int }{ - Timeout: 500, - MaxIdleConns: 0, - ConnMaxLifetime: 3 * time.Second, + Timeout: 500, } ) @@ -80,8 +79,13 @@ func InitDBConfig() { Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"}) Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db")) Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500) - Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(0) - Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second) + Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2) + if Database.UseMySQL { + Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second) + } else { + Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0) + } + Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) Database.LogSQL = sec.Key("LOG_SQL").MustBool(true) From ab1ab0f2b02e2549849775109bc90d914622ee1c Mon Sep 17 00:00:00 2001 From: zeripath Date: Mon, 21 Oct 2019 23:23:35 +0100 Subject: [PATCH 15/31] Prevent .code-view from overriding font on icon fonts (#8614) --- public/css/index.css | 2 +- public/less/_base.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/css/index.css b/public/css/index.css index 03a199d0b9af4..9292604422dd1 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -243,7 +243,7 @@ i.icon.centerlock{top:1.5em} .lines-commit .ui.avatar.image{height:18px;width:18px} .lines-code .bottom-line,.lines-commit .bottom-line,.lines-num .bottom-line{border-bottom:1px solid #eaecef} .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden} -.code-view *{font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px} +.code-view :not(.fa):not(.octicon):not(.icon){font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px} .code-view table{width:100%} .code-view .active{background:#fff866} .markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word} diff --git a/public/less/_base.less b/public/less/_base.less index 62b2084a3b3d7..1a386f17b0cec 100644 --- a/public/less/_base.less +++ b/public/less/_base.less @@ -1063,7 +1063,7 @@ i.icon.centerlock { overflow-x: auto; overflow-y: hidden; - * { + *:not(.fa):not(.octicon):not(.icon) { font-size: 12px; font-family: @monospaced-fonts, monospace; line-height: 20px; From 7d5074b8dcd418aab76b221ca34ee582a14cb4da Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 22 Oct 2019 01:45:53 +0200 Subject: [PATCH 16/31] 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 * Fix the url for the blog repository (now on gitea.com) Signed-off-by: LukBukkit --- CONTRIBUTING.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 93083dc491fb5..04ffebe62877c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -72,13 +72,15 @@ Here's how to run the test suite: - Install the correct version of the drone-cli package. As of this writing, the correct drone-cli version is - [1.1.0](https://docs.drone.io/cli/install/). + [1.2.0](https://docs.drone.io/cli/install/). - Ensure you have enough free disk space. You will need at least 15-20 Gb of free disk space to hold all of the containers drone creates (a default AWS or GCE disk size won't work -- see [#6243](https://github.com/go-gitea/gitea/issues/6243)). - Change into the base directory of your copy of the gitea repository, and run `drone exec --event pull_request`. +- At the moment `drone exec` doesn't support the Docker Toolbox on Windows 10 + (see [drone-cli#135](https://github.com/drone/drone-cli/issues/135)) The drone version, command line, and disk requirements do change over time (see [#4053](https://github.com/go-gitea/gitea/issues/4053) and @@ -302,7 +304,7 @@ be reviewed by two maintainers and must pass the automatic tests. * Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`. * And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.) * If needed send PR for changelog on branch `master`. -* Send PR to [blog repository](https://github.com/go-gitea/blog) announcing the release. +* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release. ## Copyright From 9140b3bfcd9e3dde959ed03ba54ed8ad498903c1 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 22 Oct 2019 12:18:53 -0300 Subject: [PATCH 17/31] Remove TrN due to lack of lang context --- modules/templates/helper.go | 1 - services/mailer/mail.go | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/modules/templates/helper.go b/modules/templates/helper.go index e30f1f4e6cb6a..a947b58abee42 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -300,7 +300,6 @@ func NewTextFuncMap() []texttmpl.FuncMap { }, "EllipsisString": base.EllipsisString, "URLJoin": util.URLJoin, - "TrN": TrN, "Dict": func(values ...interface{}) (map[string]interface{}, error) { if len(values)%2 != 0 { return nil, errors.New("invalid dict call") diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 51388d9bebc77..93ec9a22eee97 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -227,13 +227,13 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy "SubjectPrefix": prefix, } - tplBody := actionToTemplate(issue, actionType) + tplName := actionToTemplate(issue, actionType) var mailSubject bytes.Buffer - if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplBody), mailMeta); err == nil { + if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplName), mailMeta); err == nil { subject = sanitizeSubject(mailSubject.String()) } else { - log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/subject", err) + log.Error("ExecuteTemplate [%s]: %v", string(tplName)+"/subject", err) } if subject == "" { @@ -243,8 +243,8 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy var mailBody bytes.Buffer - if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplBody), mailMeta); err != nil { - log.Error("ExecuteTemplate [%s]: %v", string(tplBody)+"/body", 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()) From 554850e889badfeee2cca095711420e80aecb99b Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 22 Oct 2019 20:50:35 -0300 Subject: [PATCH 18/31] Redo templates to match previous code --- services/mailer/mail.go | 80 ++++++++++++++----------------- services/mailer/mail_comment.go | 20 +------- services/mailer/mail_issue.go | 28 +---------- templates/mail/issue/close.tmpl | 19 -------- templates/mail/issue/comment.tmpl | 19 -------- templates/mail/issue/default.tmpl | 18 ++++++- templates/mail/issue/new.tmpl | 19 -------- templates/mail/issue/reopen.tmpl | 19 -------- templates/mail/pull/close.tmpl | 19 -------- templates/mail/pull/comment.tmpl | 19 -------- templates/mail/pull/default.tmpl | 18 ++++++- templates/mail/pull/merge.tmpl | 19 -------- templates/mail/pull/new.tmpl | 19 -------- templates/mail/pull/reopen.tmpl | 19 -------- 14 files changed, 71 insertions(+), 264 deletions(-) delete mode 100644 templates/mail/issue/close.tmpl delete mode 100644 templates/mail/issue/comment.tmpl delete mode 100644 templates/mail/issue/new.tmpl delete mode 100644 templates/mail/issue/reopen.tmpl delete mode 100644 templates/mail/pull/close.tmpl delete mode 100644 templates/mail/pull/comment.tmpl delete mode 100644 templates/mail/pull/merge.tmpl delete mode 100644 templates/mail/pull/new.tmpl delete mode 100644 templates/mail/pull/reopen.tmpl diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 93ec9a22eee97..abca89785c714 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -32,19 +32,6 @@ const ( mailAuthResetPassword base.TplName = "auth/reset_passwd" mailAuthRegisterNotify base.TplName = "auth/register_notify" - mailIssueDefault base.TplName = "issue/default" - mailNewIssue base.TplName = "issue/new" - mailCommentIssue base.TplName = "issue/comment" - mailCloseIssue base.TplName = "issue/close" - mailReopenIssue base.TplName = "issue/reopen" - - mailPullRequestDefault base.TplName = "pull/default" - mailNewPullRequest base.TplName = "pull/new" - mailCommentPullRequest base.TplName = "pull/comment" - mailClosePullRequest base.TplName = "pull/close" - mailReopenPullRequest base.TplName = "pull/reopen" - mailMergePullRequest base.TplName = "pull/merge" // FIXME: Where can I use this? - mailNotifyCollaborator base.TplName = "notify/collaborator" // There's no actual limit for subject in RFC 5322 @@ -180,10 +167,6 @@ func SendCollaboratorMail(u, doer *models.User, repo *models.Repository) { 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.LoadRepo(); err != nil { - log.Error("LoadRepo: %v", err) - return nil - } if err := issue.LoadPullRequest(); err != nil { log.Error("LoadPullRequest: %v", err) return nil @@ -212,6 +195,8 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy link = issue.HTMLURL() } + actType, actName, tplName := actionToTemplate(issue, actionType) + mailMeta := map[string]interface{}{ "FallbackSubject": fallback, "Body": body, @@ -222,13 +207,12 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy "User": issue.Repo.MustOwner().Name, "Repo": issue.Repo.FullName(), "Doer": doer, - "Action": actionType, "IsMention": fromMention, "SubjectPrefix": prefix, + "ActionType": actType, + "ActionName": actName, } - tplName := actionToTemplate(issue, actionType) - var mailSubject bytes.Buffer if err := subjectTemplates.ExecuteTemplate(&mailSubject, string(tplName), mailMeta); err == nil { subject = sanitizeSubject(mailSubject.String()) @@ -287,35 +271,41 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType mod SendAsync(composeIssueCommentMessage(issue, doer, actionType, true, content, comment, tos, "issue mention")) } -func actionToTemplate(issue *models.Issue, actionType models.ActionType) base.TplName { - var name base.TplName +// 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) (typeName, name, template string) { + if issue.IsPull { + typeName = "pull" + } else { + typeName = "issue" + } switch actionType { - case models.ActionCreateIssue: - name = mailNewIssue - case models.ActionCreatePullRequest: - name = mailNewPullRequest + case models.ActionCreateIssue, models.ActionCreatePullRequest: + name = "new" case models.ActionCommentIssue: - if issue.IsPull { - name = mailCommentPullRequest - } else { - name = mailCommentIssue - } - case models.ActionCloseIssue: - name = mailCloseIssue - case models.ActionReopenIssue: - name = mailReopenIssue - case models.ActionClosePullRequest: - name = mailClosePullRequest - case models.ActionReopenPullRequest: - name = mailReopenPullRequest + name = "comment" + case models.ActionCloseIssue, models.ActionClosePullRequest: + name = "close" + case models.ActionReopenIssue, models.ActionReopenPullRequest: + name = "reopen" case models.ActionMergePullRequest: - name = mailMergePullRequest + name = "merge" + default: + name = "default" } - if name != "" && bodyTemplates.Lookup(string(name)) != nil { - return name + + template = typeName + "/" + name + ok := bodyTemplates.Lookup(template) != nil + if !ok && typeName != "issue" { + template = "issue/" + name + ok = bodyTemplates.Lookup(template) != nil } - if issue.IsPull { - return mailPullRequestDefault + if !ok { + template = typeName + "/default" + ok = bodyTemplates.Lookup(template) != nil + } + if !ok { + template = "issue/default" } - return mailIssueDefault + return } diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go index e153857c824bf..6469eb1fa1ea5 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, opType, 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, opType, 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, opType, 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 ac60e77bf7c8c..e5bd4978d1ebf 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -135,32 +135,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, opType, 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, opType, 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, opType, 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, opType, 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/templates/mail/issue/close.tmpl b/templates/mail/issue/close.tmpl deleted file mode 100644 index ffeeaf183a30c..0000000000000 --- a/templates/mail/issue/close.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} closed issue #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/issue/comment.tmpl b/templates/mail/issue/comment.tmpl deleted file mode 100644 index a5d0b02417b2d..0000000000000 --- a/templates/mail/issue/comment.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} commented on issue #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index afcc5661f38e4..e0aef6b05c840 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,4 +1,4 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} Issue #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.FallbackSubject}} ------------------------------------------------------------- @@ -9,7 +9,21 @@ {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

+

+ {{- if eq .Body "" -}} + {{if .ActionName "create"}} + Created #{{.Issue.Index}}. + {{else if .ActionName "close"}} + Closed #{{.Issue.Index}}. + {{else if .ActionName "reopen"}} + Reopened #{{.Issue.Index}}. + {{else}} + Empty comment on #{{.Issue.Index}}. + {{end}} + {{else}} + {{.Body | Str2html}} + {{- end -}} +

---
diff --git a/templates/mail/issue/new.tmpl b/templates/mail/issue/new.tmpl deleted file mode 100644 index 4993f3332b9f9..0000000000000 --- a/templates/mail/issue/new.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} created issue #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/issue/reopen.tmpl b/templates/mail/issue/reopen.tmpl deleted file mode 100644 index 82c1dee85df43..0000000000000 --- a/templates/mail/issue/reopen.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} reopened issue #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/pull/close.tmpl b/templates/mail/pull/close.tmpl deleted file mode 100644 index 932c9be5f7ac2..0000000000000 --- a/templates/mail/pull/close.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} closed pull request #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/pull/comment.tmpl b/templates/mail/pull/comment.tmpl deleted file mode 100644 index 46c408771e6bf..0000000000000 --- a/templates/mail/pull/comment.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} commented on pull request #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl index 2787c35fd0bee..a8168ac96f877 100644 --- a/templates/mail/pull/default.tmpl +++ b/templates/mail/pull/default.tmpl @@ -1,4 +1,4 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} Pull Request #{{.Issue.Index}} - {{.Issue.Title}} +{{.SubjectPrefix}}[{{.FallbackSubject}} ------------------------------------------------------------- @@ -9,7 +9,21 @@ {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

+

+ {{- if eq .Body ""}} + {{if .ActionName "create"}} + Created #{{.Issue.Index}}. + {{else if .ActionName "close"}} + Closed #{{.Issue.Index}}. + {{else if .ActionName "reopen"}} + Reopened #{{.Issue.Index}}. + {{else}} + Empty comment on #{{.Issue.Index}}. + {{end}} + {{else}} + {{.Body | Str2html}} + {{end -}} +

---
diff --git a/templates/mail/pull/merge.tmpl b/templates/mail/pull/merge.tmpl deleted file mode 100644 index c54b106f7280c..0000000000000 --- a/templates/mail/pull/merge.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} merged pull request #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/pull/new.tmpl b/templates/mail/pull/new.tmpl deleted file mode 100644 index b1f246c051763..0000000000000 --- a/templates/mail/pull/new.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} created pull request #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - diff --git a/templates/mail/pull/reopen.tmpl b/templates/mail/pull/reopen.tmpl deleted file mode 100644 index 015a6d4541d80..0000000000000 --- a/templates/mail/pull/reopen.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -{{.SubjectPrefix}}[{{.Repo}}] @{{.Doer.Name}} reopened pull request #{{.Issue.Index}} - {{.Issue.Title}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

{{.Body | Str2html}}

-

- --- -
- View it on Gitea. -

- - From f65e5d299513f8b2390546d0143f8a22041631c3 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 22 Oct 2019 21:44:49 -0300 Subject: [PATCH 19/31] Fix extra character in template --- templates/mail/pull/default.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl index a8168ac96f877..0122bc3cda02b 100644 --- a/templates/mail/pull/default.tmpl +++ b/templates/mail/pull/default.tmpl @@ -1,4 +1,4 @@ -{{.SubjectPrefix}}[{{.FallbackSubject}} +{{.SubjectPrefix}}{{.FallbackSubject}} ------------------------------------------------------------- From 111a036f7f645a39aac876d8545007ebfe92bd5a Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 22 Oct 2019 21:47:44 -0300 Subject: [PATCH 20/31] Unify PR & Issue tempaltes, fix format --- templates/mail/issue/default.tmpl | 6 +++--- templates/mail/pull/default.tmpl | 33 ------------------------------- 2 files changed, 3 insertions(+), 36 deletions(-) delete mode 100644 templates/mail/pull/default.tmpl diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index e0aef6b05c840..0122bc3cda02b 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,4 +1,4 @@ -{{.SubjectPrefix}}[{{.FallbackSubject}} +{{.SubjectPrefix}}{{.FallbackSubject}} ------------------------------------------------------------- @@ -10,7 +10,7 @@ {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}}

- {{- if eq .Body "" -}} + {{- if eq .Body ""}} {{if .ActionName "create"}} Created #{{.Issue.Index}}. {{else if .ActionName "close"}} @@ -22,7 +22,7 @@ {{end}} {{else}} {{.Body | Str2html}} - {{- end -}} + {{end -}}

--- diff --git a/templates/mail/pull/default.tmpl b/templates/mail/pull/default.tmpl deleted file mode 100644 index 0122bc3cda02b..0000000000000 --- a/templates/mail/pull/default.tmpl +++ /dev/null @@ -1,33 +0,0 @@ -{{.SubjectPrefix}}{{.FallbackSubject}} -------------------------------------------------------------- - - - - - {{.Subject}} - - - - {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}} -

- {{- if eq .Body ""}} - {{if .ActionName "create"}} - Created #{{.Issue.Index}}. - {{else if .ActionName "close"}} - Closed #{{.Issue.Index}}. - {{else if .ActionName "reopen"}} - Reopened #{{.Issue.Index}}. - {{else}} - Empty comment on #{{.Issue.Index}}. - {{end}} - {{else}} - {{.Body | Str2html}} - {{end -}} -

-

- --- -
- View it on Gitea. -

- - From fa9e93b5b1cd2bbe47fb5482007f8e7b812e6786 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Tue, 22 Oct 2019 22:06:29 -0300 Subject: [PATCH 21/31] Remove default subject --- templates/mail/issue/default.tmpl | 2 -- 1 file changed, 2 deletions(-) diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 0122bc3cda02b..2fbaf84102f84 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -1,5 +1,3 @@ -{{.SubjectPrefix}}{{.FallbackSubject}} -------------------------------------------------------------- From cbf32f1ceb84c2da1522f2cd9643d7b8ec27dc7d Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 01:36:17 -0300 Subject: [PATCH 22/31] Add template tests --- services/mailer/mail_test.go | 106 ++++++++++++++++++++++++++++++++++- 1 file changed, 104 insertions(+), 2 deletions(-) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index f32cbc61125b7..a10507e0e436e 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -5,6 +5,7 @@ package mailer import ( + "bytes" "html/template" "testing" texttmpl "text/template" @@ -96,7 +97,108 @@ func TestComposeIssueMessage(t *testing.T) { assert.Nil(t, msg.GetHeader("In-Reply-To")) assert.Nil(t, msg.GetHeader("References")) assert.Equal(t, messageID[0], "", "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") + } - // GAP: TODO: test fallback subject + default subject - // assert.Equal(t, subject[0], fallbackMailSubject(issue), "Subject not equal to issue.mailSubject()") + 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: //") } From d823b73c9610ee67f13f885e1695bfaa097cf144 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 03:11:27 -0300 Subject: [PATCH 23/31] Fix template --- templates/mail/issue/default.tmpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 2fbaf84102f84..629688b1f220f 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -9,11 +9,11 @@ {{if .IsMention}}

@{{.Doer.Name}} mentioned you:

{{end}}

{{- if eq .Body ""}} - {{if .ActionName "create"}} + {{if eq .ActionName "new"}} Created #{{.Issue.Index}}. - {{else if .ActionName "close"}} + {{else if eq .ActionName "close"}} Closed #{{.Issue.Index}}. - {{else if .ActionName "reopen"}} + {{else if eq .ActionName "reopen"}} Reopened #{{.Issue.Index}}. {{else}} Empty comment on #{{.Issue.Index}}. From 083f26ef79f640ea5fd3667213cf9829e9f129c9 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 03:18:40 -0300 Subject: [PATCH 24/31] Remove replaced function --- models/issue.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/models/issue.go b/models/issue.go index 1af04c9ee167b..688a412d8c370 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1730,23 +1730,6 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) { return } -// ComposeMailMetas composes a map of metas used for rendering a mail subject or body. -func (issue *Issue) ComposeMailMetas(doer *User, comment *Comment, actionType ActionType, fromMention bool) (map[string]interface{}, error) { - if err := issue.LoadPullRequest(); err != nil { - return nil, err - } - return map[string]interface{}{ - "Issue": issue, - "Comment": comment, - "IsPull": issue.IsPull, - "User": issue.Repo.MustOwner().Name, - "Repo": issue.Repo.FullName(), - "Doer": doer.Name, - "Action": actionType, - "IsMention": fromMention, - }, nil -} - // ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that // don't have access to reading it. Teams are expanded into their users, but organizations are ignored. func (issue *Issue) ResolveMentionsByVisibility(ctx DBContext, doer *User, mentions []string) (users []*User, err error) { From 74d9c3bcc5122cae039c3c2d65c4c8d47e47b86b Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 16:26:30 -0300 Subject: [PATCH 25/31] Provide User as models.User for better consistency --- services/mailer/mail.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/mailer/mail.go b/services/mailer/mail.go index abca89785c714..11e073f9d4e6b 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -204,7 +204,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy "Issue": issue, "Comment": comment, "IsPull": issue.IsPull, - "User": issue.Repo.MustOwner().Name, + "User": issue.Repo.MustOwner(), "Repo": issue.Repo.FullName(), "Doer": doer, "IsMention": fromMention, From e02aa11959edbaacd09e3dc172331edd9ff19289 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 16:54:09 -0300 Subject: [PATCH 26/31] Add docs --- .../content/doc/advanced/mail-templates-us.md | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 docs/content/doc/advanced/mail-templates-us.md diff --git a/docs/content/doc/advanced/mail-templates-us.md b/docs/content/doc/advanced/mail-templates-us.md new file mode 100644 index 0000000000000..0b911337450d0 --- /dev/null +++ b/docs/content/doc/advanced/mail-templates-us.md @@ -0,0 +1,256 @@ +--- +date: "2019-10-23T17:00:00-03:00" +title: "Mail templates" +slug: "mail-templates" +weight: 45 +toc: true +draft: false +menu: + sidebar: + parent: "advanced" + name: "Mail templates" + weight: 45 + identifier: "mail-templates" +--- + +# Mail templates + +To craft the e-mail subject and contents for certain operations, Gitea can be customized by using templates. The templates +for these functions are located under the [`custom` directory](https://docs.gitea.io/en-us/customizing-gitea/). +Gitea has an internal template that serves as default in case there's no custom alternative. + +Custom templates are loaded when Gitea starts. Changes made to them are not recognized until Gitea is restarted again. + +## Mail notifications supporting templates + +Currently, the following notification events make use of templates: + +| Action name | Usage | +|---------------|--------------------------------------------------------------------------------------------------------------| +| `new` | A new issue or pull request was created. | +| `comment` | A new comment was created in an existing issue or pull request. | +| `close` | An issue or pull request was closed. | +| `reopen` | An issue or pull request was reopened. | +| `default` | Any action not included in the above categories, or when the corresponding category template is not present. | + +The path for the template of a particular message type is: + +``` +custom/templates/mail/{action type}/{action name}.tmpl +``` + +Where `{action type}` is one of `issue` or `pull` (for pull requests), and `{action name}` is one of the names listed above. + +For example, the specific template for a mail regarding a comment in a pull request is: +``` +custom/templates/mail/pull/comment.tmpl +``` + +However, creating templates for each and every action type/name combination is not required. +A fallback system is used to choose the appropriate template for an event. The _first existing_ +template on this list is used: + +* The specific template for the desired **action type** and **action name**. +* The template for action type `issue` and the desired **action name**. +* The template for the desired **action type**, action name `default`. +* The template for action type `issue`, action name `default`. + +The only mandatory template is action type `issue`, action name `default`, which is already embedded in Gitea +unless it's overridden by the user in the `custom` directory. + +## Template syntax + +Mail templates are UTF-8 encoded text files that need to follow one of the following formats: + +``` +Text and macros for the subject line +------------ +Text and macros for the mail body +``` + +or + +``` +Text and macros for the mail body +``` + +Specifying a _subject_ section is optional (and therefore also the dash line separator). When used, the separator between +_subject_ and _mail body_ templates requires at least three dashes; no other characters are allowed in the separator line. + + +_Subject_ and _mail body_ are parsed by [Golang's template engine](https://golang.org/pkg/text/template/) and +are provided with a _metadata context_ assembled for each notification. The context contains the following elements: + +| Name | Type | Available | Usage | +|--------------------|----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `.FallbackSubject` | string | Always | A default subject line. See Below. | +| `.Subject` | string | Only in body | The _subject_, once resolved. | +| `.Body` | string | Always | The message of the issue, pull request or comment, parsed from Markdown into HTML and sanitized. Do not confuse with the _mail body_ | +| `.Link` | string | Always | The address of the originating issue, pull request or comment. | +| `.Issue` | models.Issue | Always | The issue (or pull request) originating the notification. To get data specific to a pull request (e.g. `HasMerged`), `.Issue.PullRequest` can be used, but care should be taken as this field will be `nil` if the issue is *not* a pull request. | +| `.Comment` | models.Comment | If applicable | If the notification is from a comment added to an issue or pull request, this will contain the information about the comment. | +| `.IsPull` | bool | Always | `true` if the mail notification is associated with a pull request (i.e. `.Issue.PullRequest` is not `nil`). | +| `.Repo` | string | Always | Name of the repository, including owner name (e.g. `mike/stuff`) | +| `.User` | models.User | Always | Owner of the repository from which the event originated. To get the user name (e.g. `mike`),`.User.Name` can be used. | +| `.Doer` | models.User | Always | User that executed the action triggering the notification event. To get the user name (e.g. `rhonda`), `.Doer.Name` can be used. | +| `.IsMention` | bool | Always | `true` if this notification was only generated because the user was mentioned in the comment, while not being subscribed to the source. It will be `false` if the recipient was subscribed to the issue or repository. | +| `.SubjectPrefix` | string | Always | `Re: ` if the notification is about other than issue or pull request creation; otherwise an empty string. | +| `.ActionType` | string | Always | `"issue"` or `"pull"`. Will correspond to the actual _action type_ independently of which template was selected. | +| `.ActionName` | string | Always | It will be one of the action types described above (`new`, `comment`, etc.), and will correspond to the actual _action name_ independently of which template was selected. | + +### The _subject_ part of the template + +The template engine used for the mail _subject_ is golang's `text/template`. + +The _subject_ is built using the following steps: + +* A template is selected according to the type of notification and to what templates are present. +* The template is parsed and resolved (e.g. `{{.Issue.Index}}` is converted to the number of the issue + or pull request). +* All space-like characters (e.g. `TAB`, `LF`, etc.) are converted to normal spaces. +* All leading, trailing and redundant spaces are removed. +* The string is truncated to its first 256 runes (characters). + +If the end result is an empty string, **or** no subject template was available (i.e. the selected template +did not include a subject part), Gitea's **internal default** will be used. + +The internal default (fallback) subject is the equivalent of: + +``` +{{SubjectPrefix}}[{{.Repo}}] {{.Issue.Title}} (#.Issue.Index) +``` + +For example: `Re: [mike/stuff] New color palette (#38)` + +Gitea's default subject can also be found in the template _metadata_ as `.FallbackSubject` from any of +the two templates, even if a valid subject template is present. + +### The _mail body_ part of the template + +The template engine used for the _mail body_ is golang's `html/template`. + +The _mail body_ is parsed after the mail subject, so there is an additional _metadata_ field which is +the actual rendered subject, after all considerations. + +The expected result is HTML (including structural elements like``, ``, etc.). +Once the template is executed, the resulting HTML is ran through the same sanitizer used for issues +and pull request, which means that only a specific subset of elements and attributes is permitted. +Everything else is removed. + +The mail is sent with `Content-Type: multipart/alternative`, so the body is sent in both HTML +and text formats. The latter is obtained by stripping the HTML markup. + +## Troubleshooting + +If the template fails to render, it will be noticed only at the moment the mail is sent. +A default subject is used if the subject template fails, and whatever was rendered successfully +from the the _mail body_ is used, disregarding the rest. + +Please check [Gitea's logs](https://docs.gitea.io/en-us/logging-configuration/) for error messages in case of trouble. + +## Example + +`custom/templates/mail/issue/default.tmpl`: + +``` +[{{.Repo}}] @{{.Doer.Name}} +{{if eq .ActionName "new"}} + created +{{else if eq .ActionName "comment"}} + commented on +{{else if eq .ActionName "close"}} + closed +{{else if eq .ActionName "reopen"}} + reopened +{{else}} + updated +{{end}} +{{if eq .ActionType "issue"}} + issue +{{else}} + pull request +{{end}} +#{{.Issue.Index}}: {{.Issue.Title}} +------------ + + + + + {{.Subject}} + + + + {{if .IsMention}} +

+ You are receiving this because @{{.Doer.Name}} mentioned you. +

+ {{end}} +

+

+ @{{.Doer.Name}} + {{if not (eq .Doer.FullName "")}} + ({{.Doer.FullName}}) + {{end}} + {{if eq .ActionName "new"}} + created + {{else if eq .ActionName "close"}} + closed + {{else if eq .ActionName "reopen"}} + reopened + {{else}} + updated + {{end}} + {{.Repo}}#{{.Issue.Index}}. +

+ {{if not (eq .Body "")}} +

Message content:

+
+ {{.Body | Str2html}} + {{end}} +

+
+

+ View it on Gitea. +

+ + +``` + +This template produces something along these lines: + +#### Subject + +> [mike/stuff] @rhonda commented on pull request #38: New color palette + +#### Mail body + +> [@rhonda](#) (Rhonda Myers) updated [mike/stuff#38](#). +> +> #### Message content: +> +> \__________________________________________________________________ +> +> Mike, I think we should tone down the blues a little. +> \__________________________________________________________________ +> +> [View it on Gitea](#). + +## Advanced + +The template system contains several functions that can be used to further process and format +the messages. Here's a list of some of them: + +| Name | Parameters | Usage | +|----------------------|-------------|---------------------------------------------------------------------| +| `AppUrl` | - | Gitea's URL | +| `AppName` | - | Set from `app.ini`, usually "Gitea" | +| `AppDomain` | - | Gitea's host name | +| `EllipsisString` | string, int | Truncates a string to the specified length; adds ellipsis as needed | + + +These are _functions_, not metadata, so they need to be used: + +``` +Like this: {{AppUrl}} +Not like this: {{.AppUrl}} +``` From e1ab7c4f9edac4cb0fe9f4d5b7e0c181cb6d8cc7 Mon Sep 17 00:00:00 2001 From: Guillermo Prandi Date: Wed, 23 Oct 2019 20:10:25 -0300 Subject: [PATCH 27/31] Fix doc inaccuracies, improve examples --- .../content/doc/advanced/mail-templates-us.md | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/docs/content/doc/advanced/mail-templates-us.md b/docs/content/doc/advanced/mail-templates-us.md index 0b911337450d0..ec1350a44ada8 100644 --- a/docs/content/doc/advanced/mail-templates-us.md +++ b/docs/content/doc/advanced/mail-templates-us.md @@ -100,7 +100,8 @@ are provided with a _metadata context_ assembled for each notification. The cont ### The _subject_ part of the template -The template engine used for the mail _subject_ is golang's `text/template`. +The template engine used for the mail _subject_ is golang's [`text/template`](https://golang.org/pkg/text/template/). +Please refer to the linked documentation for details about its syntax. The _subject_ is built using the following steps: @@ -127,21 +128,29 @@ the two templates, even if a valid subject template is present. ### The _mail body_ part of the template -The template engine used for the _mail body_ is golang's `html/template`. +The template engine used for the _mail body_ is golang's [`html/template`](https://golang.org/pkg/html/template/). +Please refer to the linked documentation for details about its syntax. The _mail body_ is parsed after the mail subject, so there is an additional _metadata_ field which is the actual rendered subject, after all considerations. -The expected result is HTML (including structural elements like``, ``, etc.). -Once the template is executed, the resulting HTML is ran through the same sanitizer used for issues -and pull request, which means that only a specific subset of elements and attributes is permitted. -Everything else is removed. +The expected result is HTML (including structural elements like``, ``, etc.). Styling +through `