// 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 )
}