gitea源码

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package feed
  4. import (
  5. "context"
  6. "fmt"
  7. "strings"
  8. activities_model "code.gitea.io/gitea/models/activities"
  9. "code.gitea.io/gitea/models/db"
  10. access_model "code.gitea.io/gitea/models/perm/access"
  11. repo_model "code.gitea.io/gitea/models/repo"
  12. "code.gitea.io/gitea/models/unit"
  13. user_model "code.gitea.io/gitea/models/user"
  14. "code.gitea.io/gitea/modules/setting"
  15. "code.gitea.io/gitea/modules/util"
  16. )
  17. func GetFeedsForDashboard(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int, error) {
  18. opts.DontCount = opts.RequestedTeam == nil && opts.Date == ""
  19. results, cnt, err := activities_model.GetFeeds(ctx, opts)
  20. return results, util.Iif(opts.DontCount, -1, int(cnt)), err
  21. }
  22. // GetFeeds returns actions according to the provided options
  23. func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
  24. return activities_model.GetFeeds(ctx, opts)
  25. }
  26. // notifyWatchers creates batch of actions for every watcher.
  27. // It could insert duplicate actions for a repository action, like this:
  28. // * Original action: UserID=1 (the real actor), ActUserID=1
  29. // * Organization action: UserID=100 (the repo's org), ActUserID=1
  30. // * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
  31. func notifyWatchers(ctx context.Context, act *activities_model.Action, watchers []*repo_model.Watch, permCode, permIssue, permPR []bool) error {
  32. // MySQL has TEXT length limit 65535.
  33. // Sometimes the content is "field1|field2|field3", sometimes the content is JSON (ActionMirrorSyncPush, ActionCommitRepo, ActionPushTag, etc...)
  34. if left, right := util.EllipsisDisplayStringX(act.Content, 65535); right != "" {
  35. if strings.HasPrefix(act.Content, `{"`) && strings.HasSuffix(act.Content, `}`) {
  36. // FIXME: at the moment we can do nothing if the content is JSON and it is too long
  37. act.Content = "{}"
  38. } else {
  39. act.Content = left
  40. }
  41. }
  42. // Add feed for actor.
  43. act.UserID = act.ActUserID
  44. if err := db.Insert(ctx, act); err != nil {
  45. return fmt.Errorf("insert new actioner: %w", err)
  46. }
  47. // Add feed for organization
  48. if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
  49. act.ID = 0
  50. act.UserID = act.Repo.Owner.ID
  51. if err := db.Insert(ctx, act); err != nil {
  52. return fmt.Errorf("insert new actioner: %w", err)
  53. }
  54. }
  55. for i, watcher := range watchers {
  56. if act.ActUserID == watcher.UserID {
  57. continue
  58. }
  59. act.ID = 0
  60. act.UserID = watcher.UserID
  61. act.Repo.Units = nil
  62. switch act.OpType {
  63. case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionPublishRelease, activities_model.ActionDeleteBranch:
  64. if !permCode[i] {
  65. continue
  66. }
  67. case activities_model.ActionCreateIssue, activities_model.ActionCommentIssue, activities_model.ActionCloseIssue, activities_model.ActionReopenIssue:
  68. if !permIssue[i] {
  69. continue
  70. }
  71. case activities_model.ActionCreatePullRequest, activities_model.ActionCommentPull, activities_model.ActionMergePullRequest, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest, activities_model.ActionAutoMergePullRequest:
  72. if !permPR[i] {
  73. continue
  74. }
  75. default:
  76. }
  77. if err := db.Insert(ctx, act); err != nil {
  78. return fmt.Errorf("insert new action: %w", err)
  79. }
  80. }
  81. return nil
  82. }
  83. // NotifyWatchers creates batch of actions for every watcher.
  84. func NotifyWatchers(ctx context.Context, acts ...*activities_model.Action) error {
  85. return db.WithTx(ctx, func(ctx context.Context) error {
  86. if len(acts) == 0 {
  87. return nil
  88. }
  89. repoID := acts[0].RepoID
  90. if repoID == 0 {
  91. setting.PanicInDevOrTesting("action should belong to a repo")
  92. return nil
  93. }
  94. if err := acts[0].LoadRepo(ctx); err != nil {
  95. return err
  96. }
  97. repo := acts[0].Repo
  98. if err := repo.LoadOwner(ctx); err != nil {
  99. return err
  100. }
  101. actUserID := acts[0].ActUserID
  102. // Add feeds for user self and all watchers.
  103. watchers, err := repo_model.GetWatchers(ctx, repoID)
  104. if err != nil {
  105. return fmt.Errorf("get watchers: %w", err)
  106. }
  107. permCode := make([]bool, len(watchers))
  108. permIssue := make([]bool, len(watchers))
  109. permPR := make([]bool, len(watchers))
  110. for i, watcher := range watchers {
  111. user, err := user_model.GetUserByID(ctx, watcher.UserID)
  112. if err != nil {
  113. permCode[i] = false
  114. permIssue[i] = false
  115. permPR[i] = false
  116. continue
  117. }
  118. perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
  119. if err != nil {
  120. permCode[i] = false
  121. permIssue[i] = false
  122. permPR[i] = false
  123. continue
  124. }
  125. permCode[i] = perm.CanRead(unit.TypeCode)
  126. permIssue[i] = perm.CanRead(unit.TypeIssues)
  127. permPR[i] = perm.CanRead(unit.TypePullRequests)
  128. }
  129. for _, act := range acts {
  130. if act.RepoID != repoID {
  131. setting.PanicInDevOrTesting("action should belong to the same repo, expected[%d], got[%d] ", repoID, act.RepoID)
  132. }
  133. if act.ActUserID != actUserID {
  134. setting.PanicInDevOrTesting("action should have the same actor, expected[%d], got[%d] ", actUserID, act.ActUserID)
  135. }
  136. act.Repo = repo
  137. if err := notifyWatchers(ctx, act, watchers, permCode, permIssue, permPR); err != nil {
  138. return err
  139. }
  140. }
  141. return nil
  142. })
  143. }