| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- // Copyright 2024 The Gitea Authors. All rights reserved.
- // SPDX-License-Identifier: MIT
-
- package feed
-
- import (
- "context"
- "fmt"
- "strings"
-
- 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"
- "code.gitea.io/gitea/modules/util"
- )
-
- func GetFeedsForDashboard(ctx context.Context, opts activities_model.GetFeedsOptions) (activities_model.ActionList, int, error) {
- opts.DontCount = opts.RequestedTeam == nil && opts.Date == ""
- results, cnt, err := activities_model.GetFeeds(ctx, opts)
- return results, util.Iif(opts.DontCount, -1, int(cnt)), err
- }
-
- // 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 {
- // MySQL has TEXT length limit 65535.
- // Sometimes the content is "field1|field2|field3", sometimes the content is JSON (ActionMirrorSyncPush, ActionCommitRepo, ActionPushTag, etc...)
- if left, right := util.EllipsisDisplayStringX(act.Content, 65535); right != "" {
- if strings.HasPrefix(act.Content, `{"`) && strings.HasSuffix(act.Content, `}`) {
- // FIXME: at the moment we can do nothing if the content is JSON and it is too long
- act.Content = "{}"
- } else {
- act.Content = left
- }
- }
-
- // Add feed for actor.
- 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
- }
- default:
- }
-
- if err := db.Insert(ctx, act); err != nil {
- return fmt.Errorf("insert new action: %w", err)
- }
- }
-
- return nil
- }
-
- // NotifyWatchers 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
- })
- }
|