diff --git a/models/action.go b/models/action.go index f6170005c..3462a5e3f 100644 --- a/models/action.go +++ b/models/action.go @@ -26,31 +26,32 @@ type ActionType int // Possible action types. const ( - ActionCreateRepo ActionType = iota + 1 // 1 - ActionRenameRepo // 2 - ActionStarRepo // 3 - ActionWatchRepo // 4 - ActionCommitRepo // 5 - ActionCreateIssue // 6 - ActionCreatePullRequest // 7 - ActionTransferRepo // 8 - ActionPushTag // 9 - ActionCommentIssue // 10 - ActionMergePullRequest // 11 - ActionCloseIssue // 12 - ActionReopenIssue // 13 - ActionClosePullRequest // 14 - ActionReopenPullRequest // 15 - ActionDeleteTag // 16 - ActionDeleteBranch // 17 - ActionMirrorSyncPush // 18 - ActionMirrorSyncCreate // 19 - ActionMirrorSyncDelete // 20 - ActionApprovePullRequest // 21 - ActionRejectPullRequest // 22 - ActionCommentPull // 23 - ActionPublishRelease // 24 - ActionPullReviewDismissed // 25 + ActionCreateRepo ActionType = iota + 1 // 1 + ActionRenameRepo // 2 + ActionStarRepo // 3 + ActionWatchRepo // 4 + ActionCommitRepo // 5 + ActionCreateIssue // 6 + ActionCreatePullRequest // 7 + ActionTransferRepo // 8 + ActionPushTag // 9 + ActionCommentIssue // 10 + ActionMergePullRequest // 11 + ActionCloseIssue // 12 + ActionReopenIssue // 13 + ActionClosePullRequest // 14 + ActionReopenPullRequest // 15 + ActionDeleteTag // 16 + ActionDeleteBranch // 17 + ActionMirrorSyncPush // 18 + ActionMirrorSyncCreate // 19 + ActionMirrorSyncDelete // 20 + ActionApprovePullRequest // 21 + ActionRejectPullRequest // 22 + ActionCommentPull // 23 + ActionPublishRelease // 24 + ActionPullReviewDismissed // 25 + ActionPullRequestReadyForReview // 26 ) // Action represents user operation type and other information to diff --git a/models/notification.go b/models/notification.go index 56abd0ed8..c4c7728ad 100644 --- a/models/notification.go +++ b/models/notification.go @@ -207,13 +207,14 @@ func createOrUpdateIssueNotifications(e Engine, issueID, commentID, notification for _, id := range issueWatches { toNotify[id] = struct{}{} } - - repoWatches, err := getRepoWatchersIDs(e, issue.RepoID) - if err != nil { - return err - } - for _, id := range repoWatches { - toNotify[id] = struct{}{} + if !(issue.IsPull && HasWorkInProgressPrefix(issue.Title)) { + repoWatches, err := getRepoWatchersIDs(e, issue.RepoID) + if err != nil { + return err + } + for _, id := range repoWatches { + toNotify[id] = struct{}{} + } } issueParticipants, err := issue.getParticipantIDsByIssue(e) if err != nil { diff --git a/models/pull.go b/models/pull.go index 1abe9fcce..3717878f4 100644 --- a/models/pull.go +++ b/models/pull.go @@ -595,9 +595,13 @@ func (pr *PullRequest) IsWorkInProgress() bool { log.Error("LoadIssue: %v", err) return false } + return HasWorkInProgressPrefix(pr.Issue.Title) +} +// HasWorkInProgressPrefix determines if the given PR title has a Work In Progress prefix +func HasWorkInProgressPrefix(title string) bool { for _, prefix := range setting.Repository.PullRequest.WorkInProgressPrefixes { - if strings.HasPrefix(strings.ToUpper(pr.Issue.Title), prefix) { + if strings.HasPrefix(strings.ToUpper(title), prefix) { return true } } diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go index 0927e182c..5bfb0b3ef 100644 --- a/modules/notification/mail/mail.go +++ b/modules/notification/mail/mail.go @@ -73,6 +73,18 @@ func (m *mailNotifier) NotifyIssueChangeStatus(doer *models.User, issue *models. } } +func (m *mailNotifier) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) { + if err := issue.LoadPullRequest(); err != nil { + log.Error("issue.LoadPullRequest: %v", err) + return + } + if issue.IsPull && models.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { + if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil { + log.Error("MailParticipants: %v", err) + } + } +} + func (m *mailNotifier) NotifyNewPullRequest(pr *models.PullRequest, mentions []*models.User) { if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil { log.Error("MailParticipants: %v", err) diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go index b1374f560..f372d6759 100644 --- a/modules/notification/ui/ui.go +++ b/modules/notification/ui/ui.go @@ -94,6 +94,19 @@ func (ns *notificationService) NotifyIssueChangeStatus(doer *models.User, issue }) } +func (ns *notificationService) NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string) { + if err := issue.LoadPullRequest(); err != nil { + log.Error("issue.LoadPullRequest: %v", err) + return + } + if issue.IsPull && models.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { + _ = ns.issueQueue.Push(issueNotificationOpts{ + IssueID: issue.ID, + NotificationAuthorID: doer.ID, + }) + } +} + func (ns *notificationService) NotifyMergePullRequest(pr *models.PullRequest, doer *models.User) { _ = ns.issueQueue.Push(issueNotificationOpts{ IssueID: pr.Issue.ID, @@ -106,15 +119,32 @@ func (ns *notificationService) NotifyNewPullRequest(pr *models.PullRequest, ment log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err) return } - _ = ns.issueQueue.Push(issueNotificationOpts{ - IssueID: pr.Issue.ID, - NotificationAuthorID: pr.Issue.PosterID, - }) + toNotify := make(map[int64]struct{}, 32) + repoWatchers, err := models.GetRepoWatchersIDs(pr.Issue.RepoID) + if err != nil { + log.Error("GetRepoWatchersIDs: %v", err) + return + } + for _, id := range repoWatchers { + toNotify[id] = struct{}{} + } + issueParticipants, err := models.GetParticipantsIDsByIssueID(pr.IssueID) + if err != nil { + log.Error("GetParticipantsIDsByIssueID: %v", err) + return + } + for _, id := range issueParticipants { + toNotify[id] = struct{}{} + } + delete(toNotify, pr.Issue.PosterID) for _, mention := range mentions { + toNotify[mention.ID] = struct{}{} + } + for receiverID := range toNotify { _ = ns.issueQueue.Push(issueNotificationOpts{ IssueID: pr.Issue.ID, NotificationAuthorID: pr.Issue.PosterID, - ReceiverID: mention.ID, + ReceiverID: receiverID, }) } } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index ea3edaa90..fdc070e4b 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -377,6 +377,8 @@ func actionToTemplate(issue *models.Issue, actionType models.ActionType, name = "merge" case models.ActionPullReviewDismissed: name = "review_dismissed" + case models.ActionPullRequestReadyForReview: + name = "ready_for_review" default: switch commentType { case models.CommentTypeReview: diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go index 867aa3251..6ffc08c8c 100644 --- a/services/mailer/mail_issue.go +++ b/services/mailer/mail_issue.go @@ -30,7 +30,7 @@ const ( // 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. +// 1. Repository watchers (except for WIP pull requests) and users who are participated in comments. // 2. Users who are not in 1. but get mentioned in current issue/comment. func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error { @@ -74,11 +74,13 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models. // =========== Repo watchers =========== // Make repo watchers last, since it's likely the list with the most users - ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID) - if err != nil { - return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err) + if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != models.ActionCreatePullRequest) { + ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID) + if err != nil { + return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err) + } + unfiltered = append(ids, unfiltered...) } - unfiltered = append(ids, unfiltered...) visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1) diff --git a/templates/mail/issue/default.tmpl b/templates/mail/issue/default.tmpl index 4b492dad8..02832c7e4 100644 --- a/templates/mail/issue/default.tmpl +++ b/templates/mail/issue/default.tmpl @@ -51,6 +51,8 @@ @{{.Doer.Name}} commented on this pull request. {{else if eq .ActionName "review_dismissed"}} @{{.Doer.Name}} dismissed last review from {{.Comment.Review.Reviewer.Name}} for this pull request. + {{else if eq .ActionName "ready_for_review"}} + @{{.Doer.Name}} marked this pull request ready for review. {{end}} {{- if eq .Body ""}}