mirror of
https://github.com/go-gitea/gitea.git
synced 2024-12-28 16:36:53 +00:00
fe49cb0243
This PR rewrites `GetReviewer` function and move it to service layer. Reviewers should not be watchers, so that this PR removed all watchers from reviewers. When the repository is under an organization, the pull request unit read permission will be checked to resolve the bug of #32394 Fix #32394
170 lines
4.8 KiB
Go
170 lines
4.8 KiB
Go
// 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"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
type StarredReposOptions struct {
|
|
db.ListOptions
|
|
StarrerID int64
|
|
RepoOwnerID int64
|
|
IncludePrivate bool
|
|
}
|
|
|
|
func (opts *StarredReposOptions) ToConds() builder.Cond {
|
|
var cond builder.Cond = builder.Eq{
|
|
"star.uid": opts.StarrerID,
|
|
}
|
|
if opts.RepoOwnerID != 0 {
|
|
cond = cond.And(builder.Eq{
|
|
"repository.owner_id": opts.RepoOwnerID,
|
|
})
|
|
}
|
|
if !opts.IncludePrivate {
|
|
cond = cond.And(builder.Eq{
|
|
"repository.is_private": false,
|
|
})
|
|
}
|
|
return cond
|
|
}
|
|
|
|
func (opts *StarredReposOptions) ToJoins() []db.JoinFunc {
|
|
return []db.JoinFunc{
|
|
func(e db.Engine) error {
|
|
e.Join("INNER", "star", "`repository`.id=`star`.repo_id")
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetStarredRepos returns the repos starred by a particular user
|
|
func GetStarredRepos(ctx context.Context, opts *StarredReposOptions) ([]*Repository, error) {
|
|
return db.Find[Repository](ctx, opts)
|
|
}
|
|
|
|
type WatchedReposOptions struct {
|
|
db.ListOptions
|
|
WatcherID int64
|
|
RepoOwnerID int64
|
|
IncludePrivate bool
|
|
}
|
|
|
|
func (opts *WatchedReposOptions) ToConds() builder.Cond {
|
|
var cond builder.Cond = builder.Eq{
|
|
"watch.user_id": opts.WatcherID,
|
|
}
|
|
if opts.RepoOwnerID != 0 {
|
|
cond = cond.And(builder.Eq{
|
|
"repository.owner_id": opts.RepoOwnerID,
|
|
})
|
|
}
|
|
if !opts.IncludePrivate {
|
|
cond = cond.And(builder.Eq{
|
|
"repository.is_private": false,
|
|
})
|
|
}
|
|
return cond.And(builder.Neq{
|
|
"watch.mode": WatchModeDont,
|
|
})
|
|
}
|
|
|
|
func (opts *WatchedReposOptions) ToJoins() []db.JoinFunc {
|
|
return []db.JoinFunc{
|
|
func(e db.Engine) error {
|
|
e.Join("INNER", "watch", "`repository`.id=`watch`.repo_id")
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
// GetWatchedRepos returns the repos watched by a particular user
|
|
func GetWatchedRepos(ctx context.Context, opts *WatchedReposOptions) ([]*Repository, int64, error) {
|
|
return db.FindAndCount[Repository](ctx, opts)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
uniqueUserIDs := make(container.Set[int64])
|
|
uniqueUserIDs.AddMultiple(userIDs...)
|
|
|
|
if repo.Owner.IsOrganization() {
|
|
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.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(uniqueUserIDs) > 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
|
|
}
|
|
|
|
// 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)
|
|
}
|