mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-05-25 11:22:16 +00:00
[refactor] mailer service (#15072)
* Unexport SendUserMail * Instead of "[]*models.User" or "[]string" lists infent "[]*MailRecipient" for mailer * adopt * code format * TODOs for "i18n" * clean * no fallback for lang -> just use english * lint * exec testComposeIssueCommentMessage per lang and use only emails * rm MailRecipient * Dont reload from users from db if you alredy have in ram * nits * minimize diff Signed-off-by: 6543 <6543@obermui.de> * localize subjects * linter ... * Tr extend * start tmpl edit ... * Apply suggestions from code review * use translation.Locale * improve mailIssueCommentBatch Signed-off-by: Andrew Thornton <art27@cantab.net> * add i18n to datas Signed-off-by: Andrew Thornton <art27@cantab.net> * a comment Co-authored-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
parent
cc2d540092
commit
80d6c6d7de
15 changed files with 191 additions and 151 deletions
|
@ -23,11 +23,16 @@ type mailCommentContext struct {
|
|||
Comment *models.Comment
|
||||
}
|
||||
|
||||
const (
|
||||
// MailBatchSize set the batch size used in mailIssueCommentBatch
|
||||
MailBatchSize = 100
|
||||
)
|
||||
|
||||
// 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(ctx *mailCommentContext, mentions []int64) error {
|
||||
func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*models.User) error {
|
||||
|
||||
// Required by the mail composer; make sure to load these before calling the async function
|
||||
if err := ctx.Issue.LoadRepo(); err != nil {
|
||||
|
@ -94,78 +99,72 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []int64) e
|
|||
visited[i] = true
|
||||
}
|
||||
|
||||
if err = mailIssueCommentBatch(ctx, unfiltered, visited, false); err != nil {
|
||||
unfilteredUsers, err := models.GetMaileableUsersByIDs(unfiltered, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = mailIssueCommentBatch(ctx, unfilteredUsers, visited, false); err != nil {
|
||||
return fmt.Errorf("mailIssueCommentBatch(): %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mailIssueCommentBatch(ctx *mailCommentContext, ids []int64, visited map[int64]bool, fromMention bool) error {
|
||||
const batchSize = 100
|
||||
for i := 0; i < len(ids); i += batchSize {
|
||||
var last int
|
||||
if i+batchSize < len(ids) {
|
||||
last = i + batchSize
|
||||
} else {
|
||||
last = len(ids)
|
||||
}
|
||||
unique := make([]int64, 0, last-i)
|
||||
for j := i; j < last; j++ {
|
||||
id := ids[j]
|
||||
if _, ok := visited[id]; !ok {
|
||||
unique = append(unique, id)
|
||||
visited[id] = true
|
||||
}
|
||||
}
|
||||
recipients, err := models.GetMaileableUsersByIDs(unique, fromMention)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checkUnit := models.UnitTypeIssues
|
||||
if ctx.Issue.IsPull {
|
||||
checkUnit = models.UnitTypePullRequests
|
||||
}
|
||||
// Make sure all recipients can still see the issue
|
||||
idx := 0
|
||||
for _, r := range recipients {
|
||||
if ctx.Issue.Repo.CheckUnitUser(r, checkUnit) {
|
||||
recipients[idx] = r
|
||||
idx++
|
||||
}
|
||||
}
|
||||
recipients = recipients[:idx]
|
||||
|
||||
// TODO: Separate recipients by language for i18n mail templates
|
||||
tos := make([]string, len(recipients))
|
||||
for i := range recipients {
|
||||
tos[i] = recipients[i].Email
|
||||
}
|
||||
SendAsyncs(composeIssueCommentMessages(ctx, tos, fromMention, "issue comments"))
|
||||
func mailIssueCommentBatch(ctx *mailCommentContext, users []*models.User, visited map[int64]bool, fromMention bool) error {
|
||||
checkUnit := models.UnitTypeIssues
|
||||
if ctx.Issue.IsPull {
|
||||
checkUnit = models.UnitTypePullRequests
|
||||
}
|
||||
|
||||
langMap := make(map[string][]string)
|
||||
for _, user := range users {
|
||||
// At this point we exclude:
|
||||
// user that don't have all mails enabled or users only get mail on mention and this is one ...
|
||||
if !(user.EmailNotificationsPreference == models.EmailNotificationsEnabled ||
|
||||
fromMention && user.EmailNotificationsPreference == models.EmailNotificationsOnMention) {
|
||||
continue
|
||||
}
|
||||
|
||||
// if we have already visited this user we exclude them
|
||||
if _, ok := visited[user.ID]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// now mark them as visited
|
||||
visited[user.ID] = true
|
||||
|
||||
// test if this user is allowed to see the issue/pull
|
||||
if !ctx.Issue.Repo.CheckUnitUser(user, checkUnit) {
|
||||
continue
|
||||
}
|
||||
|
||||
langMap[user.Language] = append(langMap[user.Language], user.Email)
|
||||
}
|
||||
|
||||
for lang, receivers := range langMap {
|
||||
// because we know that the len(receivers) > 0 and we don't care about the order particularly
|
||||
// working backwards from the last (possibly) incomplete batch. If len(receivers) can be 0 this
|
||||
// starting condition will need to be changed slightly
|
||||
for i := ((len(receivers) - 1) / MailBatchSize) * MailBatchSize; i >= 0; i -= MailBatchSize {
|
||||
SendAsyncs(composeIssueCommentMessages(ctx, lang, receivers[i:], fromMention, "issue comments"))
|
||||
receivers = receivers[:i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MailParticipants sends new issue thread created emails to repository watchers
|
||||
// and mentioned people.
|
||||
func MailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) error {
|
||||
return mailParticipants(issue, doer, opType, mentions)
|
||||
}
|
||||
|
||||
func mailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType, mentions []*models.User) (err error) {
|
||||
mentionedIDs := make([]int64, len(mentions))
|
||||
for i, u := range mentions {
|
||||
mentionedIDs[i] = u.ID
|
||||
}
|
||||
if err = mailIssueCommentToParticipants(
|
||||
if err := mailIssueCommentToParticipants(
|
||||
&mailCommentContext{
|
||||
Issue: issue,
|
||||
Doer: doer,
|
||||
ActionType: opType,
|
||||
Content: issue.Content,
|
||||
Comment: nil,
|
||||
}, mentionedIDs); err != nil {
|
||||
}, mentions); err != nil {
|
||||
log.Error("mailIssueCommentToParticipants: %v", err)
|
||||
}
|
||||
return nil
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue