mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 12:53:43 +01:00 
			
		
		
		
	Send notifications to partecipants in issue comments (#1217)
* Send notifications to partecipants in issue comments Closes #1216 Includes test (still failing) * Do not include "labelers" to participants Fix test to expect what GetParticipants return
This commit is contained in:
		
							parent
							
								
									ca1c3f1926
								
							
						
					
					
						commit
						447c9b428f
					
				@ -2,6 +2,18 @@
 | 
				
			|||||||
  id: 1
 | 
					  id: 1
 | 
				
			||||||
  type: 7 # label
 | 
					  type: 7 # label
 | 
				
			||||||
  poster_id: 2
 | 
					  poster_id: 2
 | 
				
			||||||
  issue_id: 1
 | 
					  issue_id: 1 # in repo_id 1
 | 
				
			||||||
  label_id: 1
 | 
					  label_id: 1
 | 
				
			||||||
  content: "1"
 | 
					  content: "1"
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 2
 | 
				
			||||||
 | 
					  type: 0 # comment
 | 
				
			||||||
 | 
					  poster_id: 3 # user not watching (see watch.yml)
 | 
				
			||||||
 | 
					  issue_id: 1 # in repo_id 1
 | 
				
			||||||
 | 
					  content: "good work!"
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					  id: 3
 | 
				
			||||||
 | 
					  type: 0 # comment
 | 
				
			||||||
 | 
					  poster_id: 5 # user not watching (see watch.yml)
 | 
				
			||||||
 | 
					  issue_id: 1 # in repo_id 1
 | 
				
			||||||
 | 
					  content: "meh..."
 | 
				
			||||||
 | 
				
			|||||||
@ -8,7 +8,7 @@
 | 
				
			|||||||
  content: content1
 | 
					  content: content1
 | 
				
			||||||
  is_closed: false
 | 
					  is_closed: false
 | 
				
			||||||
  is_pull: false
 | 
					  is_pull: false
 | 
				
			||||||
  num_comments: 0
 | 
					  num_comments: 2
 | 
				
			||||||
  created_unix: 946684800
 | 
					  created_unix: 946684800
 | 
				
			||||||
  updated_unix: 978307200
 | 
					  updated_unix: 978307200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1134,6 +1134,24 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
 | 
				
			|||||||
	return issues, nil
 | 
						return issues, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetParticipantsByIssueID returns all users who are participated in comments of an issue.
 | 
				
			||||||
 | 
					func GetParticipantsByIssueID(issueID int64) ([]*User, error) {
 | 
				
			||||||
 | 
						userIDs := make([]int64, 0, 5)
 | 
				
			||||||
 | 
						if err := x.Table("comment").Cols("poster_id").
 | 
				
			||||||
 | 
							Where("issue_id = ?", issueID).
 | 
				
			||||||
 | 
							And("type = ?", CommentTypeComment).
 | 
				
			||||||
 | 
							Distinct("poster_id").
 | 
				
			||||||
 | 
							Find(&userIDs); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("get poster IDs: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(userIDs) == 0 {
 | 
				
			||||||
 | 
							return nil, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						users := make([]*User, 0, len(userIDs))
 | 
				
			||||||
 | 
						return users, x.In("id", userIDs).Find(&users)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateIssueMentions extracts mentioned people from content and
 | 
					// UpdateIssueMentions extracts mentioned people from content and
 | 
				
			||||||
// updates issue-user relations for them.
 | 
					// updates issue-user relations for them.
 | 
				
			||||||
func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
 | 
					func UpdateIssueMentions(e Engine, issueID int64, mentions []string) error {
 | 
				
			||||||
 | 
				
			|||||||
@ -19,15 +19,27 @@ func (issue *Issue) mailSubject() string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment.
 | 
					// mailIssueCommentToParticipants can be used for both new issue creation and comment.
 | 
				
			||||||
 | 
					// This function sends two list of emails:
 | 
				
			||||||
 | 
					// 1. Repository watchers and users who are participated in comments.
 | 
				
			||||||
 | 
					// 2. Users who are not in 1. but get mentioned in current issue/comment.
 | 
				
			||||||
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
 | 
					func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error {
 | 
				
			||||||
	if !setting.Service.EnableNotifyMail {
 | 
						if !setting.Service.EnableNotifyMail {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Mail watchers.
 | 
					 | 
				
			||||||
	watchers, err := GetWatchers(issue.RepoID)
 | 
						watchers, err := GetWatchers(issue.RepoID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return fmt.Errorf("GetWatchers [%d]: %v", issue.RepoID, err)
 | 
							return fmt.Errorf("GetWatchers [repo_id: %d]: %v", issue.RepoID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						participants, err := GetParticipantsByIssueID(issue.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("GetParticipantsByIssueID [issue_id: %d]: %v", issue.ID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// In case the issue poster is not watching the repository,
 | 
				
			||||||
 | 
						// even if we have duplicated in watchers, can be safely filtered out.
 | 
				
			||||||
 | 
						if issue.PosterID != doer.ID {
 | 
				
			||||||
 | 
							participants = append(participants, issue.Poster)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tos := make([]string, 0, len(watchers)) // List of email addresses.
 | 
						tos := make([]string, 0, len(watchers)) // List of email addresses.
 | 
				
			||||||
@ -48,6 +60,16 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 | 
				
			|||||||
		tos = append(tos, to.Email)
 | 
							tos = append(tos, to.Email)
 | 
				
			||||||
		names = append(names, to.Name)
 | 
							names = append(names, to.Name)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						for i := range participants {
 | 
				
			||||||
 | 
							if participants[i].ID == doer.ID {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							} else if com.IsSliceContainsStr(names, participants[i].Name) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tos = append(tos, participants[i].Email)
 | 
				
			||||||
 | 
							names = append(names, participants[i].Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	SendIssueCommentMail(issue, doer, tos)
 | 
						SendIssueCommentMail(issue, doer, tos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Mail mentioned people and exclude watchers.
 | 
						// Mail mentioned people and exclude watchers.
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@
 | 
				
			|||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
@ -58,3 +59,26 @@ func TestGetIssuesByIDs(t *testing.T) {
 | 
				
			|||||||
	testSuccess([]int64{1, 2, 3}, []int64{})
 | 
						testSuccess([]int64{1, 2, 3}, []int64{})
 | 
				
			||||||
	testSuccess([]int64{1, 2, 3}, []int64{NonexistentID})
 | 
						testSuccess([]int64{1, 2, 3}, []int64{NonexistentID})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetParticipantsByIssueID(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						checkPartecipants := func(issueID int64, userIDs []int) {
 | 
				
			||||||
 | 
							partecipants, err := GetParticipantsByIssueID(issueID)
 | 
				
			||||||
 | 
							if assert.NoError(t, err) {
 | 
				
			||||||
 | 
								partecipantsIDs := make([]int,len(partecipants))
 | 
				
			||||||
 | 
								for i,u := range partecipants { partecipantsIDs[i] = int(u.ID) }
 | 
				
			||||||
 | 
								sort.Ints(partecipantsIDs)
 | 
				
			||||||
 | 
								sort.Ints(userIDs)
 | 
				
			||||||
 | 
								assert.Equal(t, userIDs, partecipantsIDs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// User 1 is issue1 poster (see fixtures/issue.yml)
 | 
				
			||||||
 | 
						// User 2 only labeled issue1 (see fixtures/comment.yml)
 | 
				
			||||||
 | 
						// Users 3 and 5 made actual comments (see fixtures/comment.yml)
 | 
				
			||||||
 | 
						checkPartecipants(1, []int{3,5})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user