* inform participants on UI too * ajust test * refactor getParticipantIDsByIssuetags/v1.21.12.1
@@ -1275,29 +1275,14 @@ func GetParticipantsIDsByIssueID(issueID int64) ([]int64, error) { | |||||
Find(&userIDs) | Find(&userIDs) | ||||
} | } | ||||
// GetParticipantsByIssueID returns all users who are participated in comments of an issue. | |||||
func GetParticipantsByIssueID(issueID int64) ([]*User, error) { | |||||
return getParticipantsByIssueID(x, issueID) | |||||
} | |||||
func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) { | |||||
userIDs := make([]int64, 0, 5) | |||||
if err := e.Table("comment").Cols("poster_id"). | |||||
Where("`comment`.issue_id = ?", issueID). | |||||
And("`comment`.type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). | |||||
And("`user`.is_active = ?", true). | |||||
And("`user`.prohibit_login = ?", false). | |||||
Join("INNER", "`user`", "`user`.id = `comment`.poster_id"). | |||||
Distinct("poster_id"). | |||||
Find(&userIDs); err != nil { | |||||
return nil, fmt.Errorf("get poster IDs: %v", err) | |||||
} | |||||
if len(userIDs) == 0 { | |||||
return nil, nil | |||||
// IsUserParticipantsOfIssue return true if user is participants of an issue | |||||
func IsUserParticipantsOfIssue(user *User, issue *Issue) bool { | |||||
userIDs, err := issue.getParticipantIDsByIssue(x) | |||||
if err != nil { | |||||
log.Error(err.Error()) | |||||
return false | |||||
} | } | ||||
users := make([]*User, 0, len(userIDs)) | |||||
return users, e.In("id", userIDs).Find(&users) | |||||
return util.IsInt64InSlice(user.ID, userIDs) | |||||
} | } | ||||
// UpdateIssueMentions updates issue-user relations for mentioned users. | // UpdateIssueMentions updates issue-user relations for mentioned users. | ||||
@@ -1691,6 +1676,28 @@ type DependencyInfo struct { | |||||
Repository `xorm:"extends"` | Repository `xorm:"extends"` | ||||
} | } | ||||
// getParticipantIDsByIssue returns all userIDs who are participated in comments of an issue and issue author | |||||
func (issue *Issue) getParticipantIDsByIssue(e Engine) ([]int64, error) { | |||||
if issue == nil { | |||||
return nil, nil | |||||
} | |||||
userIDs := make([]int64, 0, 5) | |||||
if err := e.Table("comment").Cols("poster_id"). | |||||
Where("`comment`.issue_id = ?", issue.ID). | |||||
And("`comment`.type in (?,?,?)", CommentTypeComment, CommentTypeCode, CommentTypeReview). | |||||
And("`user`.is_active = ?", true). | |||||
And("`user`.prohibit_login = ?", false). | |||||
Join("INNER", "`user`", "`user`.id = `comment`.poster_id"). | |||||
Distinct("poster_id"). | |||||
Find(&userIDs); err != nil { | |||||
return nil, fmt.Errorf("get poster IDs: %v", err) | |||||
} | |||||
if !util.IsInt64InSlice(issue.PosterID, userIDs) { | |||||
return append(userIDs, issue.PosterID), nil | |||||
} | |||||
return userIDs, nil | |||||
} | |||||
// Get Blocked By Dependencies, aka all issues this issue is blocked by. | // Get Blocked By Dependencies, aka all issues this issue is blocked by. | ||||
func (issue *Issue) getBlockedByDependencies(e Engine) (issueDeps []*DependencyInfo, err error) { | func (issue *Issue) getBlockedByDependencies(e Engine) (issueDeps []*DependencyInfo, err error) { | ||||
return issueDeps, e. | return issueDeps, e. | ||||
@@ -61,15 +61,17 @@ func TestGetIssuesByIDs(t *testing.T) { | |||||
testSuccess([]int64{1, 2, 3}, []int64{NonexistentID}) | testSuccess([]int64{1, 2, 3}, []int64{NonexistentID}) | ||||
} | } | ||||
func TestGetParticipantsByIssueID(t *testing.T) { | |||||
func TestGetParticipantIDsByIssue(t *testing.T) { | |||||
assert.NoError(t, PrepareTestDatabase()) | assert.NoError(t, PrepareTestDatabase()) | ||||
checkParticipants := func(issueID int64, userIDs []int) { | checkParticipants := func(issueID int64, userIDs []int) { | ||||
participants, err := GetParticipantsByIssueID(issueID) | |||||
issue, err := GetIssueByID(issueID) | |||||
assert.NoError(t, err) | |||||
participants, err := issue.getParticipantIDsByIssue(x) | |||||
if assert.NoError(t, err) { | if assert.NoError(t, err) { | ||||
participantsIDs := make([]int, len(participants)) | participantsIDs := make([]int, len(participants)) | ||||
for i, u := range participants { | |||||
participantsIDs[i] = int(u.ID) | |||||
for i, uid := range participants { | |||||
participantsIDs[i] = int(uid) | |||||
} | } | ||||
sort.Ints(participantsIDs) | sort.Ints(participantsIDs) | ||||
sort.Ints(userIDs) | sort.Ints(userIDs) | ||||
@@ -81,7 +83,7 @@ func TestGetParticipantsByIssueID(t *testing.T) { | |||||
// User 2 only labeled issue1 (see fixtures/comment.yml) | // User 2 only labeled issue1 (see fixtures/comment.yml) | ||||
// Users 3 and 5 made actual comments (see fixtures/comment.yml) | // Users 3 and 5 made actual comments (see fixtures/comment.yml) | ||||
// User 3 is inactive, thus not active participant | // User 3 is inactive, thus not active participant | ||||
checkParticipants(1, []int{5}) | |||||
checkParticipants(1, []int{1, 5}) | |||||
} | } | ||||
func TestIssue_ClearLabels(t *testing.T) { | func TestIssue_ClearLabels(t *testing.T) { | ||||
@@ -159,6 +159,13 @@ func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notifi | |||||
for _, id := range repoWatches { | for _, id := range repoWatches { | ||||
toNotify[id] = struct{}{} | toNotify[id] = struct{}{} | ||||
} | } | ||||
issueParticipants, err := issue.getParticipantIDsByIssue(e) | |||||
if err != nil { | |||||
return err | |||||
} | |||||
for _, id := range issueParticipants { | |||||
toNotify[id] = struct{}{} | |||||
} | |||||
// dont notify user who cause notification | // dont notify user who cause notification | ||||
delete(toNotify, notificationAuthorID) | delete(toNotify, notificationAuthorID) | ||||
@@ -45,6 +45,16 @@ func IsStringInSlice(target string, slice []string) bool { | |||||
return false | return false | ||||
} | } | ||||
// IsInt64InSlice sequential searches if int64 exists in slice. | |||||
func IsInt64InSlice(target int64, slice []int64) bool { | |||||
for i := 0; i < len(slice); i++ { | |||||
if slice[i] == target { | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} | |||||
// IsEqualSlice returns true if slices are equal. | // IsEqualSlice returns true if slices are equal. | ||||
func IsEqualSlice(target []string, source []string) bool { | func IsEqualSlice(target []string, source []string) bool { | ||||
if len(target) != len(source) { | if len(target) != len(source) { | ||||
@@ -704,7 +704,7 @@ func ViewIssue(ctx *context.Context) { | |||||
iw = &models.IssueWatch{ | iw = &models.IssueWatch{ | ||||
UserID: ctx.User.ID, | UserID: ctx.User.ID, | ||||
IssueID: issue.ID, | IssueID: issue.ID, | ||||
IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID), | |||||
IsWatching: models.IsWatching(ctx.User.ID, ctx.Repo.Repository.ID) || models.IsUserParticipantsOfIssue(ctx.User, issue), | |||||
} | } | ||||
} | } | ||||
} | } | ||||