summaryrefslogtreecommitdiffstats
path: root/models/repo/user_repo.go
diff options
context:
space:
mode:
Diffstat (limited to 'models/repo/user_repo.go')
-rw-r--r--models/repo/user_repo.go197
1 files changed, 197 insertions, 0 deletions
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
new file mode 100644
index 0000000..6790ee1
--- /dev/null
+++ b/models/repo/user_repo.go
@@ -0,0 +1,197 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package repo
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
+ api "code.gitea.io/gitea/modules/structs"
+
+ "xorm.io/builder"
+)
+
+// GetStarredRepos returns the repos starred by a particular user
+func GetStarredRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions) ([]*Repository, error) {
+ sess := db.GetEngine(ctx).
+ Where("star.uid=?", userID).
+ Join("LEFT", "star", "`repository`.id=`star`.repo_id")
+ if !private {
+ sess = sess.And("is_private=?", false)
+ }
+
+ if listOptions.Page != 0 {
+ sess = db.SetSessionPagination(sess, &listOptions)
+
+ repos := make([]*Repository, 0, listOptions.PageSize)
+ return repos, sess.Find(&repos)
+ }
+
+ repos := make([]*Repository, 0, 10)
+ return repos, sess.Find(&repos)
+}
+
+// GetWatchedRepos returns the repos watched by a particular user
+func GetWatchedRepos(ctx context.Context, userID int64, private bool, listOptions db.ListOptions) ([]*Repository, int64, error) {
+ sess := db.GetEngine(ctx).
+ Where("watch.user_id=?", userID).
+ And("`watch`.mode<>?", WatchModeDont).
+ Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
+ if !private {
+ sess = sess.And("is_private=?", false)
+ }
+
+ if listOptions.Page != 0 {
+ sess = db.SetSessionPagination(sess, &listOptions)
+
+ repos := make([]*Repository, 0, listOptions.PageSize)
+ total, err := sess.FindAndCount(&repos)
+ return repos, total, err
+ }
+
+ repos := make([]*Repository, 0, 10)
+ total, err := sess.FindAndCount(&repos)
+ return repos, total, err
+}
+
+// GetRepoAssignees returns all users that have write access and can be assigned to issues
+// of the repository,
+func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.User, err error) {
+ if err = repo.LoadOwner(ctx); err != nil {
+ return nil, err
+ }
+
+ e := db.GetEngine(ctx)
+ userIDs := make([]int64, 0, 10)
+ if err = e.Table("access").
+ Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeWrite).
+ Select("user_id").
+ Find(&userIDs); err != nil {
+ return nil, err
+ }
+
+ additionalUserIDs := make([]int64, 0, 10)
+ if err = e.Table("team_user").
+ Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
+ Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
+ Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))",
+ repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests).
+ Distinct("`team_user`.uid").
+ Select("`team_user`.uid").
+ Find(&additionalUserIDs); err != nil {
+ return nil, err
+ }
+
+ uniqueUserIDs := make(container.Set[int64])
+ uniqueUserIDs.AddMultiple(userIDs...)
+ uniqueUserIDs.AddMultiple(additionalUserIDs...)
+
+ // Leave a seat for owner itself to append later, but if owner is an organization
+ // and just waste 1 unit is cheaper than re-allocate memory once.
+ users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
+ if len(userIDs) > 0 {
+ if err = e.In("id", uniqueUserIDs.Values()).
+ Where(builder.Eq{"`user`.is_active": true}).
+ OrderBy(user_model.GetOrderByName()).
+ Find(&users); err != nil {
+ return nil, err
+ }
+ }
+ if !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) {
+ users = append(users, repo.Owner)
+ }
+
+ return users, nil
+}
+
+// GetReviewers get all users can be requested to review:
+// * for private repositories this returns all users that have read access or higher to the repository.
+// * for public repositories this returns all users that have read access or higher to the repository,
+// all repo watchers and all organization members.
+// TODO: may be we should have a busy choice for users to block review request to them.
+func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) ([]*user_model.User, error) {
+ // Get the owner of the repository - this often already pre-cached and if so saves complexity for the following queries
+ if err := repo.LoadOwner(ctx); err != nil {
+ return nil, err
+ }
+
+ cond := builder.And(builder.Neq{"`user`.id": posterID}).
+ And(builder.Eq{"`user`.is_active": true})
+
+ if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate {
+ // This a private repository:
+ // Anyone who can read the repository is a requestable reviewer
+
+ cond = cond.And(builder.In("`user`.id",
+ builder.Select("user_id").From("access").Where(
+ builder.Eq{"repo_id": repo.ID}.
+ And(builder.Gte{"mode": perm.AccessModeRead}),
+ ),
+ ))
+
+ if repo.Owner.Type == user_model.UserTypeIndividual && repo.Owner.ID != posterID {
+ // as private *user* repos don't generate an entry in the `access` table,
+ // the owner of a private repo needs to be explicitly added.
+ cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
+ }
+ } else {
+ // This is a "public" repository:
+ // Any user that has read access, is a watcher or organization member can be requested to review
+ cond = cond.And(builder.And(builder.In("`user`.id",
+ builder.Select("user_id").From("access").
+ Where(builder.Eq{"repo_id": repo.ID}.
+ And(builder.Gte{"mode": perm.AccessModeRead})),
+ ).Or(builder.In("`user`.id",
+ builder.Select("user_id").From("watch").
+ Where(builder.Eq{"repo_id": repo.ID}.
+ And(builder.In("mode", WatchModeNormal, WatchModeAuto))),
+ ).Or(builder.In("`user`.id",
+ builder.Select("uid").From("org_user").
+ Where(builder.Eq{"org_id": repo.OwnerID}),
+ )))))
+ }
+
+ users := make([]*user_model.User, 0, 8)
+ return users, db.GetEngine(ctx).Where(cond).OrderBy(user_model.GetOrderByName()).Find(&users)
+}
+
+// GetIssuePostersWithSearch returns users with limit of 30 whose username started with prefix that have authored an issue/pull request for the given repository
+// If isShowFullName is set to true, also include full name prefix search
+func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
+ users := make([]*user_model.User, 0, 30)
+ var prefixCond builder.Cond = builder.Like{"name", search + "%"}
+ if isShowFullName {
+ prefixCond = prefixCond.Or(builder.Like{"full_name", "%" + search + "%"})
+ }
+
+ cond := builder.In("`user`.id",
+ builder.Select("poster_id").From("issue").Where(
+ builder.Eq{"repo_id": repo.ID}.
+ And(builder.Eq{"is_pull": isPull}),
+ ).GroupBy("poster_id")).And(prefixCond)
+
+ return users, db.GetEngine(ctx).
+ Where(cond).
+ Cols("id", "name", "full_name", "avatar", "avatar_email", "use_custom_avatar").
+ OrderBy("name").
+ Limit(30).
+ Find(&users)
+}
+
+// GetWatchedRepoIDsOwnedBy returns the repos owned by a particular user watched by a particular user
+func GetWatchedRepoIDsOwnedBy(ctx context.Context, userID, ownedByUserID int64) ([]int64, error) {
+ repoIDs := make([]int64, 0, 10)
+ err := db.GetEngine(ctx).
+ Table("repository").
+ Select("`repository`.id").
+ Join("LEFT", "watch", "`repository`.id=`watch`.repo_id").
+ Where("`watch`.user_id=?", userID).
+ And("`watch`.mode<>?", WatchModeDont).
+ And("`repository`.owner_id=?", ownedByUserID).Find(&repoIDs)
+ return repoIDs, err
+}