2022-03-29 14:29:02 +08:00
// Copyright 2022 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2022-03-29 14:29:02 +08:00
package repo
import (
2022-06-06 16:01:49 +08:00
"context"
2025-02-23 20:33:43 +08:00
"strings"
2022-06-06 16:01:49 +08:00
2022-03-29 14:29:02 +08:00
"code.gitea.io/gitea/models/db"
2026-04-24 03:15:53 +08:00
"code.gitea.io/gitea/models/organization"
2022-06-06 16:01:49 +08:00
"code.gitea.io/gitea/models/perm"
2024-02-24 22:14:48 +08:00
"code.gitea.io/gitea/models/unit"
2022-06-06 16:01:49 +08:00
user_model "code.gitea.io/gitea/models/user"
2022-10-12 07:18:26 +02:00
"code.gitea.io/gitea/modules/container"
2022-06-06 16:01:49 +08:00
"xorm.io/builder"
2022-03-29 14:29:02 +08:00
)
2024-03-04 09:16:03 +01:00
type StarredReposOptions struct {
db . ListOptions
StarrerID int64
RepoOwnerID int64
IncludePrivate bool
}
2022-03-29 14:29:02 +08:00
2026-05-19 08:38:51 -07:00
func ( opts * StarredReposOptions ) ApplyPublicOnly ( publicOnly bool ) {
if publicOnly {
opts . IncludePrivate = false
}
}
2024-03-04 09:16:03 +01:00
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
}
2022-03-29 14:29:02 +08:00
2024-03-04 09:16:03 +01:00
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
} ,
2022-03-29 14:29:02 +08:00
}
2024-03-04 09:16:03 +01:00
}
// GetStarredRepos returns the repos starred by a particular user
func GetStarredRepos ( ctx context . Context , opts * StarredReposOptions ) ( [ ] * Repository , error ) {
return db . Find [ Repository ] ( ctx , opts )
}
2022-03-29 14:29:02 +08:00
2024-03-04 09:16:03 +01:00
type WatchedReposOptions struct {
db . ListOptions
WatcherID int64
RepoOwnerID int64
IncludePrivate bool
2022-03-29 14:29:02 +08:00
}
2026-05-19 08:38:51 -07:00
func ( opts * WatchedReposOptions ) ApplyPublicOnly ( publicOnly bool ) {
if publicOnly {
opts . IncludePrivate = false
}
}
2024-03-04 09:16:03 +01:00
func ( opts * WatchedReposOptions ) ToConds ( ) builder . Cond {
var cond builder . Cond = builder . Eq {
"watch.user_id" : opts . WatcherID ,
2022-03-29 14:29:02 +08:00
}
2024-03-04 09:16:03 +01:00
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 ,
} )
}
2022-03-29 14:29:02 +08:00
2024-03-04 09:16:03 +01:00
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
} ,
2022-03-29 14:29:02 +08:00
}
2024-03-04 09:16:03 +01:00
}
2022-03-29 14:29:02 +08:00
2024-03-04 09:16:03 +01:00
// 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 )
2022-03-29 14:29:02 +08:00
}
2022-06-06 16:01:49 +08:00
2026-04-24 03:15:53 +08:00
// GetRepoAssignees returns all users that have write access and can be assigned to issues or pull-requests of the repository,
2022-06-06 16:01:49 +08:00
func GetRepoAssignees ( ctx context . Context , repo * Repository ) ( _ [ ] * user_model . User , err error ) {
2023-02-18 21:11:03 +09:00
if err = repo . LoadOwner ( ctx ) ; err != nil {
2022-06-06 16:01:49 +08:00
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
}
2022-10-12 07:18:26 +02:00
uniqueUserIDs := make ( container . Set [ int64 ] )
uniqueUserIDs . AddMultiple ( userIDs ... )
2024-11-04 23:13:52 -08:00
if repo . Owner . IsOrganization ( ) {
2026-04-24 03:15:53 +08:00
// issues and pull requests both need "assignee list"
additionalUserIDs , err := organization . GetTeamUserIDsWithAccessToAnyRepoUnit ( ctx , repo . OwnerID , repo . ID , perm . AccessModeRead , unit . TypeIssues , unit . TypePullRequests )
if err != nil {
2024-11-04 23:13:52 -08:00
return nil , err
}
uniqueUserIDs . AddMultiple ( additionalUserIDs ... )
}
2022-06-06 16:01:49 +08:00
// 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.
2022-10-12 07:18:26 +02:00
users := make ( [ ] * user_model . User , 0 , len ( uniqueUserIDs ) + 1 )
2024-11-04 23:13:52 -08:00
if len ( uniqueUserIDs ) > 0 {
2024-04-30 10:43:08 +02:00
if err = e . In ( "id" , uniqueUserIDs . Values ( ) ) .
Where ( builder . Eq { "`user`.is_active" : true } ) .
OrderBy ( user_model . GetOrderByName ( ) ) .
Find ( & users ) ; err != nil {
2022-06-06 16:01:49 +08:00
return nil , err
}
}
2022-10-12 07:18:26 +02:00
if ! repo . Owner . IsOrganization ( ) && ! uniqueUserIDs . Contains ( repo . OwnerID ) {
2022-06-06 16:01:49 +08:00
users = append ( users , repo . Owner )
}
return users , nil
}
2023-04-07 08:11:02 +08:00
// GetIssuePostersWithSearch returns users with limit of 30 whose username started with prefix that have authored an issue/pull request for the given repository
2026-03-04 21:23:17 +08:00
// It searches with the "user.name" and "user.full_name" fields case-insensitively.
func GetIssuePostersWithSearch ( ctx context . Context , repo * Repository , isPull bool , search string ) ( [ ] * user_model . User , error ) {
2023-04-07 08:11:02 +08:00
users := make ( [ ] * user_model . User , 0 , 30 )
2022-08-08 15:03:58 -05:00
cond := builder . In ( "`user`.id" ,
builder . Select ( "poster_id" ) . From ( "issue" ) . Where (
builder . Eq { "repo_id" : repo . ID } .
And ( builder . Eq { "is_pull" : isPull } ) ,
2026-03-04 21:23:17 +08:00
) . GroupBy ( "poster_id" ) )
if search != "" {
var prefixCond builder . Cond = builder . Like { "lower_name" , strings . ToLower ( search ) + "%" }
prefixCond = prefixCond . Or ( db . BuildCaseInsensitiveLike ( "full_name" , "%" + search + "%" ) )
cond = cond . And ( prefixCond )
}
2023-04-07 08:11:02 +08:00
return users , db . GetEngine ( ctx ) .
Where ( cond ) .
Cols ( "id" , "name" , "full_name" , "avatar" , "avatar_email" , "use_custom_avatar" ) .
OrderBy ( "name" ) .
Limit ( 30 ) .
Find ( & users )
2022-08-08 15:03:58 -05:00
}