mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08: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:
		
				
					committed by
					
						
						Lunny Xiao
					
				
			
			
				
	
			
			
			
						parent
						
							ca1c3f1926
						
					
				
				
					commit
					447c9b428f
				
			@@ -2,6 +2,18 @@
 | 
			
		||||
  id: 1
 | 
			
		||||
  type: 7 # label
 | 
			
		||||
  poster_id: 2
 | 
			
		||||
  issue_id: 1
 | 
			
		||||
  issue_id: 1 # in repo_id 1
 | 
			
		||||
  label_id: 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
 | 
			
		||||
  is_closed: false
 | 
			
		||||
  is_pull: false
 | 
			
		||||
  num_comments: 0
 | 
			
		||||
  num_comments: 2
 | 
			
		||||
  created_unix: 946684800
 | 
			
		||||
  updated_unix: 978307200
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1134,6 +1134,24 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
 | 
			
		||||
	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
 | 
			
		||||
// updates issue-user relations for them.
 | 
			
		||||
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.
 | 
			
		||||
// 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 {
 | 
			
		||||
	if !setting.Service.EnableNotifyMail {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Mail watchers.
 | 
			
		||||
	watchers, err := GetWatchers(issue.RepoID)
 | 
			
		||||
	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.
 | 
			
		||||
@@ -48,6 +60,16 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
 | 
			
		||||
		tos = append(tos, to.Email)
 | 
			
		||||
		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)
 | 
			
		||||
 | 
			
		||||
	// Mail mentioned people and exclude watchers.
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
package models
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"sort"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"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{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})
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user