Add review comments to mail notifications (#8996)
This commit is contained in:
		
							parent
							
								
									97dc314652
								
							
						
					
					
						commit
						9930d47be2
					
				
					 6 changed files with 73 additions and 35 deletions
				
			
		|  | @ -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…
	
		Reference in a new issue