Add review comments to mail notifications (#8996)

tokarchuk/v1.17
guillep2k 5 years ago committed by zeripath
parent 97dc314652
commit 9930d47be2
  1. 10
      docs/content/doc/advanced/mail-templates-us.md
  2. 4
      models/issue_comment.go
  3. 2
      models/review.go
  4. 4
      modules/notification/mail/mail.go
  5. 28
      services/mailer/mail.go
  6. 14
      templates/mail/issue/default.tmpl

@ -32,6 +32,8 @@ Currently, the following notification events make use of templates:
| `close` | An issue or pull request was closed. | | `close` | An issue or pull request was closed. |
| `reopen` | An issue or pull request was reopened. | | `reopen` | An issue or pull request was reopened. |
| `review` | The head comment of a review in a pull request. | | `review` | The head comment of a review in a pull request. |
| `approve` | The head comment of a approving review for a pull request. |
| `reject` | The head comment of a review requesting changes for a pull request. |
| `code` | A single comment on the code of a pull request. | | `code` | A single comment on the code of a pull request. |
| `assigned` | Used was assigned to an issue or pull request. | | `assigned` | Used was assigned to an issue or pull request. |
| `default` | Any action not included in the above categories, or when the corresponding category template is not present. | | `default` | Any action not included in the above categories, or when the corresponding category template is not present. |
@ -85,10 +87,10 @@ _Subject_ and _mail body_ are parsed by [Golang's template engine](https://golan
are provided with a _metadata context_ assembled for each notification. The context contains the following elements: are provided with a _metadata context_ assembled for each notification. The context contains the following elements:
| Name | Type | Available | Usage | | Name | Type | Available | Usage |
|--------------------|----------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |--------------------|------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.FallbackSubject` | string | Always | A default subject line. See Below. | | `.FallbackSubject` | string | Always | A default subject line. See Below. |
| `.Subject` | string | Only in body | The _subject_, once resolved. | | `.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_ | | `.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. | | `.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. | | `.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. | | `.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. |
@ -100,6 +102,7 @@ are provided with a _metadata context_ assembled for each notification. The cont
| `.SubjectPrefix` | string | Always | `Re: ` if the notification is about other than issue or pull request creation; otherwise an empty string. | | `.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. | | `.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. | | `.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. |
| `.ReviewComments` | []models.Comment | Always | List of code comments in a review. The comment text will be in `.RenderedContent` and the referenced code will be in `.Patch`. |
All names are case sensitive. All names are case sensitive.
@ -255,12 +258,13 @@ The template system contains several functions that can be used to further proce
the messages. Here's a list of some of them: the messages. Here's a list of some of them:
| Name | Parameters | Available | Usage | | Name | Parameters | Available | Usage |
|----------------------|-------------|-----------|---------------------------------------------------------------------| |----------------------|-------------|-----------|------------------------------------------------------------------------------|
| `AppUrl` | - | Any | Gitea's URL | | `AppUrl` | - | Any | Gitea's URL |
| `AppName` | - | Any | Set from `app.ini`, usually "Gitea" | | `AppName` | - | Any | Set from `app.ini`, usually "Gitea" |
| `AppDomain` | - | Any | Gitea's host name | | `AppDomain` | - | Any | Gitea's host name |
| `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed | | `EllipsisString` | string, int | Any | Truncates a string to the specified length; adds ellipsis as needed |
| `Str2html` | string | Body only | Sanitizes text by removing any HTML tags from it. | | `Str2html` | string | Body only | Sanitizes text by removing any HTML tags from it. |
| `Safe` | string | Body only | Takes the input as HTML; can be used for `.ReviewComments.RenderedContent`. |
These are _functions_, not metadata, so they have to be used: These are _functions_, not metadata, so they have to be used:

@ -1016,10 +1016,6 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review
return nil, err return nil, err
} }
if err := CommentList(comments).loadPosters(e); err != nil {
return nil, err
}
if err := issue.loadRepo(e); err != nil { if err := issue.loadRepo(e); err != nil {
return nil, err return nil, err
} }

@ -62,7 +62,9 @@ type Review struct {
} }
func (r *Review) loadCodeComments(e Engine) (err error) { func (r *Review) loadCodeComments(e Engine) (err error) {
if r.CodeComments == nil {
r.CodeComments, err = fetchCodeCommentsByReview(e, r.Issue, nil, r) r.CodeComments, err = fetchCodeCommentsByReview(e, r.Issue, nil, r)
}
return return
} }

@ -40,7 +40,7 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *models.User, repo *models.
} }
if err := mailer.MailParticipantsComment(comment, act, issue); err != nil { if err := mailer.MailParticipantsComment(comment, act, issue); err != nil {
log.Error("MailParticipants: %v", err) log.Error("MailParticipantsComment: %v", err)
} }
} }
@ -87,7 +87,7 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models
act = models.ActionCommentIssue act = models.ActionCommentIssue
} }
if err := mailer.MailParticipantsComment(comment, act, pr.Issue); err != nil { if err := mailer.MailParticipantsComment(comment, act, pr.Issue); err != nil {
log.Error("MailParticipants: %v", err) log.Error("MailParticipantsComment: %v", err)
} }
} }

@ -178,6 +178,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy
prefix string prefix string
// Fall back subject for bad templates, make sure subject is never empty // Fall back subject for bad templates, make sure subject is never empty
fallback string fallback string
reviewComments []*models.Comment
) )
commentType := models.CommentTypeComment commentType := models.CommentTypeComment
@ -189,12 +190,26 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy
link = issue.HTMLURL() link = issue.HTMLURL()
} }
reviewType := models.ReviewTypeComment
if comment != nil && comment.Review != nil {
reviewType = comment.Review.Type
}
fallback = prefix + fallbackMailSubject(issue) fallback = prefix + fallbackMailSubject(issue)
// This is the body of the new issue or comment, not the mail body // 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())) body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
actType, actName, tplName := actionToTemplate(issue, actionType, commentType) actType, actName, tplName := actionToTemplate(issue, actionType, commentType, reviewType)
if comment != nil && comment.Review != nil {
reviewComments = make([]*models.Comment, 0, 10)
for _, lines := range comment.Review.CodeComments {
for _, comments := range lines {
reviewComments = append(reviewComments, comments...)
}
}
}
mailMeta := map[string]interface{}{ mailMeta := map[string]interface{}{
"FallbackSubject": fallback, "FallbackSubject": fallback,
@ -210,6 +225,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, actionTy
"SubjectPrefix": prefix, "SubjectPrefix": prefix,
"ActionType": actType, "ActionType": actType,
"ActionName": actName, "ActionName": actName,
"ReviewComments": reviewComments,
} }
var mailSubject bytes.Buffer var mailSubject bytes.Buffer
@ -272,7 +288,8 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, actionType mod
// actionToTemplate returns the type and name of the action facing the user // 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) // (slightly different from models.ActionType) and the name of the template to use (based on availability)
func actionToTemplate(issue *models.Issue, actionType models.ActionType, commentType models.CommentType) (typeName, name, template string) { func actionToTemplate(issue *models.Issue, actionType models.ActionType,
commentType models.CommentType, reviewType models.ReviewType) (typeName, name, template string) {
if issue.IsPull { if issue.IsPull {
typeName = "pull" typeName = "pull"
} else { } else {
@ -292,7 +309,14 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType, comment
default: default:
switch commentType { switch commentType {
case models.CommentTypeReview: case models.CommentTypeReview:
switch reviewType {
case models.ReviewTypeApprove:
name = "approve"
case models.ReviewTypeReject:
name = "reject"
default:
name = "review" name = "review"
}
case models.CommentTypeCode: case models.CommentTypeCode:
name = "code" name = "code"
case models.CommentTypeAssignees: case models.CommentTypeAssignees:

@ -3,6 +3,12 @@
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{{.Subject}}</title> <title>{{.Subject}}</title>
{{if .ReviewComments}}
<style>
.review { padding-left: 1em; margin: 1em 0; }
.review > pre { padding: 1em; border-left: 1px solid grey; }
</style>
{{end}}
</head> </head>
<body> <body>
@ -15,12 +21,18 @@
Closed #{{.Issue.Index}}. Closed #{{.Issue.Index}}.
{{else if eq .ActionName "reopen"}} {{else if eq .ActionName "reopen"}}
Reopened #{{.Issue.Index}}. Reopened #{{.Issue.Index}}.
{{else}} {{else if ne .ReviewComments}}
Empty comment on #{{.Issue.Index}}. Empty comment on #{{.Issue.Index}}.
{{end}} {{end}}
{{else}} {{else}}
{{.Body | Str2html}} {{.Body | Str2html}}
{{end -}} {{end -}}
{{- range .ReviewComments}}
<div class="review">
<pre>{{.Patch}}</pre>
<div>{{.RenderedContent | Safe}}</div>
</div>
{{end -}}
</p> </p>
<p> <p>
--- ---

Loading…
Cancel
Save