You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

mail_issue.go 5.3 kB

Use templates for issue e-mail subject and body (#8329) * Add template capability for issue mail subject * Remove test string * Fix trim subject length * Add comment to template and run make fmt * Add information for the template * Rename defaultMailSubject() to fallbackMailSubject() * General rewrite of the mail template code * Fix .Doer name * Use text/template for subject instead of html * Fix subject Re: prefix * Fix mail tests * Fix static templates * [skip ci] Updated translations via Crowdin * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) * Expose db.SetMaxOpenConns and allow other dbs to set their connection params * Add note about port exhaustion Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Prevent .code-view from overriding font on icon fonts (#8614) * Correct some outdated statements in the contributing guidelines (#8612) * More information for drone-cli in CONTRIBUTING.md * Increases the version of drone-cli to 1.2.0 * Adds a note for the Docker Toolbox on Windows Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Fix the url for the blog repository (now on gitea.com) Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Remove TrN due to lack of lang context * Redo templates to match previous code * Fix extra character in template * Unify PR & Issue tempaltes, fix format * Remove default subject * Add template tests * Fix template * Remove replaced function * Provide User as models.User for better consistency * Add docs * Fix doc inaccuracies, improve examples * Change mail footer to math AppName * Add test for mail subject/body template separation * Add support for code review comments * Update docs/content/doc/advanced/mail-templates-us.md Co-Authored-By: 6543 <24977596+6543@users.noreply.github.com>
5 years ago
Use templates for issue e-mail subject and body (#8329) * Add template capability for issue mail subject * Remove test string * Fix trim subject length * Add comment to template and run make fmt * Add information for the template * Rename defaultMailSubject() to fallbackMailSubject() * General rewrite of the mail template code * Fix .Doer name * Use text/template for subject instead of html * Fix subject Re: prefix * Fix mail tests * Fix static templates * [skip ci] Updated translations via Crowdin * Expose db.SetMaxOpenConns and allow non MySQL dbs to set conn pool params (#8528) * Expose db.SetMaxOpenConns and allow other dbs to set their connection params * Add note about port exhaustion Co-Authored-By: guillep2k <18600385+guillep2k@users.noreply.github.com> * Prevent .code-view from overriding font on icon fonts (#8614) * Correct some outdated statements in the contributing guidelines (#8612) * More information for drone-cli in CONTRIBUTING.md * Increases the version of drone-cli to 1.2.0 * Adds a note for the Docker Toolbox on Windows Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Fix the url for the blog repository (now on gitea.com) Signed-off-by: LukBukkit <luk.bukkit@gmail.com> * Remove TrN due to lack of lang context * Redo templates to match previous code * Fix extra character in template * Unify PR & Issue tempaltes, fix format * Remove default subject * Add template tests * Fix template * Remove replaced function * Provide User as models.User for better consistency * Add docs * Fix doc inaccuracies, improve examples * Change mail footer to math AppName * Add test for mail subject/body template separation * Add support for code review comments * Update docs/content/doc/advanced/mail-templates-us.md Co-Authored-By: 6543 <24977596+6543@users.noreply.github.com>
5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright 2019 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package mailer
  5. import (
  6. "fmt"
  7. "code.gitea.io/gitea/models"
  8. "code.gitea.io/gitea/modules/log"
  9. "code.gitea.io/gitea/modules/references"
  10. )
  11. func fallbackMailSubject(issue *models.Issue) string {
  12. return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.FullName(), issue.Title, issue.Index)
  13. }
  14. type mailCommentContext struct {
  15. Issue *models.Issue
  16. Doer *models.User
  17. ActionType models.ActionType
  18. Content string
  19. Comment *models.Comment
  20. }
  21. // mailIssueCommentToParticipants can be used for both new issue creation and comment.
  22. // This function sends two list of emails:
  23. // 1. Repository watchers and users who are participated in comments.
  24. // 2. Users who are not in 1. but get mentioned in current issue/comment.
  25. func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []int64) error {
  26. // Required by the mail composer; make sure to load these before calling the async function
  27. if err := ctx.Issue.LoadRepo(); err != nil {
  28. return fmt.Errorf("LoadRepo(): %v", err)
  29. }
  30. if err := ctx.Issue.LoadPoster(); err != nil {
  31. return fmt.Errorf("LoadPoster(): %v", err)
  32. }
  33. if err := ctx.Issue.LoadPullRequest(); err != nil {
  34. return fmt.Errorf("LoadPullRequest(): %v", err)
  35. }
  36. // Enough room to avoid reallocations
  37. unfiltered := make([]int64, 1, 64)
  38. // =========== Original poster ===========
  39. unfiltered[0] = ctx.Issue.PosterID
  40. // =========== Assignees ===========
  41. ids, err := models.GetAssigneeIDsByIssue(ctx.Issue.ID)
  42. if err != nil {
  43. return fmt.Errorf("GetAssigneeIDsByIssue(%d): %v", ctx.Issue.ID, err)
  44. }
  45. unfiltered = append(unfiltered, ids...)
  46. // =========== Participants (i.e. commenters, reviewers) ===========
  47. ids, err = models.GetParticipantsIDsByIssueID(ctx.Issue.ID)
  48. if err != nil {
  49. return fmt.Errorf("GetParticipantsIDsByIssueID(%d): %v", ctx.Issue.ID, err)
  50. }
  51. unfiltered = append(unfiltered, ids...)
  52. // =========== Issue watchers ===========
  53. ids, err = models.GetIssueWatchersIDs(ctx.Issue.ID, true)
  54. if err != nil {
  55. return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err)
  56. }
  57. unfiltered = append(unfiltered, ids...)
  58. // =========== Repo watchers ===========
  59. // Make repo watchers last, since it's likely the list with the most users
  60. ids, err = models.GetRepoWatchersIDs(ctx.Issue.RepoID)
  61. if err != nil {
  62. return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err)
  63. }
  64. unfiltered = append(ids, unfiltered...)
  65. visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1)
  66. // Avoid mailing the doer
  67. visited[ctx.Doer.ID] = true
  68. // Avoid mailing explicit unwatched
  69. ids, err = models.GetIssueWatchersIDs(ctx.Issue.ID, false)
  70. if err != nil {
  71. return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err)
  72. }
  73. for _, i := range ids {
  74. visited[i] = true
  75. }
  76. if err = mailIssueCommentBatch(ctx, unfiltered, visited, false); err != nil {
  77. return fmt.Errorf("mailIssueCommentBatch(): %v", err)
  78. }
  79. // =========== Mentions ===========
  80. if err = mailIssueCommentBatch(ctx, mentions, visited, true); err != nil {
  81. return fmt.Errorf("mailIssueCommentBatch() mentions: %v", err)
  82. }
  83. return nil
  84. }
  85. func mailIssueCommentBatch(ctx *mailCommentContext, ids []int64, visited map[int64]bool, fromMention bool) error {
  86. const batchSize = 100
  87. for i := 0; i < len(ids); i += batchSize {
  88. var last int
  89. if i+batchSize < len(ids) {
  90. last = i + batchSize
  91. } else {
  92. last = len(ids)
  93. }
  94. unique := make([]int64, 0, last-i)
  95. for j := i; j < last; j++ {
  96. id := ids[j]
  97. if _, ok := visited[id]; !ok {
  98. unique = append(unique, id)
  99. visited[id] = true
  100. }
  101. }
  102. recipients, err := models.GetMaileableUsersByIDs(unique)
  103. if err != nil {
  104. return err
  105. }
  106. // TODO: Check issue visibility for each user
  107. // TODO: Separate recipients by language for i18n mail templates
  108. tos := make([]string, len(recipients))
  109. for i := range recipients {
  110. tos[i] = recipients[i].Email
  111. }
  112. SendAsyncs(composeIssueCommentMessages(ctx, tos, fromMention, "issue comments"))
  113. }
  114. return nil
  115. }
  116. // MailParticipants sends new issue thread created emails to repository watchers
  117. // and mentioned people.
  118. func MailParticipants(issue *models.Issue, doer *models.User, opType models.ActionType) error {
  119. return mailParticipants(models.DefaultDBContext(), issue, doer, opType)
  120. }
  121. func mailParticipants(ctx models.DBContext, issue *models.Issue, doer *models.User, opType models.ActionType) (err error) {
  122. rawMentions := references.FindAllMentionsMarkdown(issue.Content)
  123. userMentions, err := issue.ResolveMentionsByVisibility(ctx, doer, rawMentions)
  124. if err != nil {
  125. return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", issue.ID, err)
  126. }
  127. if err = models.UpdateIssueMentions(ctx, issue.ID, userMentions); err != nil {
  128. return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
  129. }
  130. mentions := make([]int64, len(userMentions))
  131. for i, u := range userMentions {
  132. mentions[i] = u.ID
  133. }
  134. if err = mailIssueCommentToParticipants(
  135. &mailCommentContext{
  136. Issue: issue,
  137. Doer: doer,
  138. ActionType: opType,
  139. Content: issue.Content,
  140. Comment: nil,
  141. }, mentions); err != nil {
  142. log.Error("mailIssueCommentToParticipants: %v", err)
  143. }
  144. return nil
  145. }