aboutsummaryrefslogtreecommitdiffstats
path: root/services/feed/feed.go
diff options
context:
space:
mode:
Diffstat (limited to 'services/feed/feed.go')
-rw-r--r--services/feed/feed.go127
1 files changed, 127 insertions, 0 deletions
diff --git a/services/feed/feed.go b/services/feed/feed.go
index 93bf875fd0..a1c327fb51 100644
--- a/services/feed/feed.go
+++ b/services/feed/feed.go
@@ -5,11 +5,138 @@ package feed
import (
"context"
+ "fmt"
activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/db"
+ access_model "code.gitea.io/gitea/models/perm/access"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
)
// GetFeeds returns actions according to the provided options
func GetFeeds(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int64, error) {
return activities_model.GetFeeds(ctx, opts)
}
+
+// notifyWatchers creates batch of actions for every watcher.
+// It could insert duplicate actions for a repository action, like this:
+// * Original action: UserID=1 (the real actor), ActUserID=1
+// * Organization action: UserID=100 (the repo's org), ActUserID=1
+// * Watcher action: UserID=20 (a user who is watching a repo), ActUserID=1
+func notifyWatchers(ctx context.Context, act *activities_model.Action, watchers []*repo_model.Watch, permCode, permIssue, permPR []bool) error {
+ // Add feed for actioner.
+ act.UserID = act.ActUserID
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new actioner: %w", err)
+ }
+
+ // Add feed for organization
+ if act.Repo.Owner.IsOrganization() && act.ActUserID != act.Repo.Owner.ID {
+ act.ID = 0
+ act.UserID = act.Repo.Owner.ID
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new actioner: %w", err)
+ }
+ }
+
+ for i, watcher := range watchers {
+ if act.ActUserID == watcher.UserID {
+ continue
+ }
+ act.ID = 0
+ act.UserID = watcher.UserID
+ act.Repo.Units = nil
+
+ switch act.OpType {
+ case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionPublishRelease, activities_model.ActionDeleteBranch:
+ if !permCode[i] {
+ continue
+ }
+ case activities_model.ActionCreateIssue, activities_model.ActionCommentIssue, activities_model.ActionCloseIssue, activities_model.ActionReopenIssue:
+ if !permIssue[i] {
+ continue
+ }
+ case activities_model.ActionCreatePullRequest, activities_model.ActionCommentPull, activities_model.ActionMergePullRequest, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest, activities_model.ActionAutoMergePullRequest:
+ if !permPR[i] {
+ continue
+ }
+ }
+
+ if err := db.Insert(ctx, act); err != nil {
+ return fmt.Errorf("insert new action: %w", err)
+ }
+ }
+
+ return nil
+}
+
+// NotifyWatchersActions creates batch of actions for every watcher.
+func NotifyWatchers(ctx context.Context, acts ...*activities_model.Action) error {
+ return db.WithTx(ctx, func(ctx context.Context) error {
+ if len(acts) == 0 {
+ return nil
+ }
+
+ repoID := acts[0].RepoID
+ if repoID == 0 {
+ setting.PanicInDevOrTesting("action should belong to a repo")
+ return nil
+ }
+ if err := acts[0].LoadRepo(ctx); err != nil {
+ return err
+ }
+ repo := acts[0].Repo
+ if err := repo.LoadOwner(ctx); err != nil {
+ return err
+ }
+
+ actUserID := acts[0].ActUserID
+
+ // Add feeds for user self and all watchers.
+ watchers, err := repo_model.GetWatchers(ctx, repoID)
+ if err != nil {
+ return fmt.Errorf("get watchers: %w", err)
+ }
+
+ permCode := make([]bool, len(watchers))
+ permIssue := make([]bool, len(watchers))
+ permPR := make([]bool, len(watchers))
+ for i, watcher := range watchers {
+ user, err := user_model.GetUserByID(ctx, watcher.UserID)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
+ if err != nil {
+ permCode[i] = false
+ permIssue[i] = false
+ permPR[i] = false
+ continue
+ }
+ permCode[i] = perm.CanRead(unit.TypeCode)
+ permIssue[i] = perm.CanRead(unit.TypeIssues)
+ permPR[i] = perm.CanRead(unit.TypePullRequests)
+ }
+
+ for _, act := range acts {
+ if act.RepoID != repoID {
+ setting.PanicInDevOrTesting("action should belong to the same repo, expected[%d], got[%d] ", repoID, act.RepoID)
+ }
+ if act.ActUserID != actUserID {
+ setting.PanicInDevOrTesting("action should have the same actor, expected[%d], got[%d] ", actUserID, act.ActUserID)
+ }
+
+ act.Repo = repo
+ if err := notifyWatchers(ctx, act, watchers, permCode, permIssue, permPR); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}