gitea源码

user_repo.go 4.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. // Copyright 2022 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package repo
  4. import (
  5. "context"
  6. "strings"
  7. "code.gitea.io/gitea/models/db"
  8. "code.gitea.io/gitea/models/perm"
  9. "code.gitea.io/gitea/models/unit"
  10. user_model "code.gitea.io/gitea/models/user"
  11. "code.gitea.io/gitea/modules/container"
  12. "xorm.io/builder"
  13. )
  14. type StarredReposOptions struct {
  15. db.ListOptions
  16. StarrerID int64
  17. RepoOwnerID int64
  18. IncludePrivate bool
  19. }
  20. func (opts *StarredReposOptions) ToConds() builder.Cond {
  21. var cond builder.Cond = builder.Eq{
  22. "star.uid": opts.StarrerID,
  23. }
  24. if opts.RepoOwnerID != 0 {
  25. cond = cond.And(builder.Eq{
  26. "repository.owner_id": opts.RepoOwnerID,
  27. })
  28. }
  29. if !opts.IncludePrivate {
  30. cond = cond.And(builder.Eq{
  31. "repository.is_private": false,
  32. })
  33. }
  34. return cond
  35. }
  36. func (opts *StarredReposOptions) ToJoins() []db.JoinFunc {
  37. return []db.JoinFunc{
  38. func(e db.Engine) error {
  39. e.Join("INNER", "star", "`repository`.id=`star`.repo_id")
  40. return nil
  41. },
  42. }
  43. }
  44. // GetStarredRepos returns the repos starred by a particular user
  45. func GetStarredRepos(ctx context.Context, opts *StarredReposOptions) ([]*Repository, error) {
  46. return db.Find[Repository](ctx, opts)
  47. }
  48. type WatchedReposOptions struct {
  49. db.ListOptions
  50. WatcherID int64
  51. RepoOwnerID int64
  52. IncludePrivate bool
  53. }
  54. func (opts *WatchedReposOptions) ToConds() builder.Cond {
  55. var cond builder.Cond = builder.Eq{
  56. "watch.user_id": opts.WatcherID,
  57. }
  58. if opts.RepoOwnerID != 0 {
  59. cond = cond.And(builder.Eq{
  60. "repository.owner_id": opts.RepoOwnerID,
  61. })
  62. }
  63. if !opts.IncludePrivate {
  64. cond = cond.And(builder.Eq{
  65. "repository.is_private": false,
  66. })
  67. }
  68. return cond.And(builder.Neq{
  69. "watch.mode": WatchModeDont,
  70. })
  71. }
  72. func (opts *WatchedReposOptions) ToJoins() []db.JoinFunc {
  73. return []db.JoinFunc{
  74. func(e db.Engine) error {
  75. e.Join("INNER", "watch", "`repository`.id=`watch`.repo_id")
  76. return nil
  77. },
  78. }
  79. }
  80. // GetWatchedRepos returns the repos watched by a particular user
  81. func GetWatchedRepos(ctx context.Context, opts *WatchedReposOptions) ([]*Repository, int64, error) {
  82. return db.FindAndCount[Repository](ctx, opts)
  83. }
  84. // GetRepoAssignees returns all users that have write access and can be assigned to issues
  85. // of the repository,
  86. func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.User, err error) {
  87. if err = repo.LoadOwner(ctx); err != nil {
  88. return nil, err
  89. }
  90. e := db.GetEngine(ctx)
  91. userIDs := make([]int64, 0, 10)
  92. if err = e.Table("access").
  93. Where("repo_id = ? AND mode >= ?", repo.ID, perm.AccessModeWrite).
  94. Select("user_id").
  95. Find(&userIDs); err != nil {
  96. return nil, err
  97. }
  98. uniqueUserIDs := make(container.Set[int64])
  99. uniqueUserIDs.AddMultiple(userIDs...)
  100. if repo.Owner.IsOrganization() {
  101. additionalUserIDs := make([]int64, 0, 10)
  102. if err = e.Table("team_user").
  103. Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
  104. Join("INNER", "team_unit", "`team_unit`.team_id = `team_user`.team_id").
  105. Where("`team_repo`.repo_id = ? AND (`team_unit`.access_mode >= ? OR (`team_unit`.access_mode = ? AND `team_unit`.`type` = ?))",
  106. repo.ID, perm.AccessModeWrite, perm.AccessModeRead, unit.TypePullRequests).
  107. Distinct("`team_user`.uid").
  108. Select("`team_user`.uid").
  109. Find(&additionalUserIDs); err != nil {
  110. return nil, err
  111. }
  112. uniqueUserIDs.AddMultiple(additionalUserIDs...)
  113. }
  114. // Leave a seat for owner itself to append later, but if owner is an organization
  115. // and just waste 1 unit is cheaper than re-allocate memory once.
  116. users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
  117. if len(uniqueUserIDs) > 0 {
  118. if err = e.In("id", uniqueUserIDs.Values()).
  119. Where(builder.Eq{"`user`.is_active": true}).
  120. OrderBy(user_model.GetOrderByName()).
  121. Find(&users); err != nil {
  122. return nil, err
  123. }
  124. }
  125. if !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) {
  126. users = append(users, repo.Owner)
  127. }
  128. return users, nil
  129. }
  130. // GetIssuePostersWithSearch returns users with limit of 30 whose username started with prefix that have authored an issue/pull request for the given repository
  131. // If isShowFullName is set to true, also include full name prefix search
  132. func GetIssuePostersWithSearch(ctx context.Context, repo *Repository, isPull bool, search string, isShowFullName bool) ([]*user_model.User, error) {
  133. users := make([]*user_model.User, 0, 30)
  134. var prefixCond builder.Cond = builder.Like{"lower_name", strings.ToLower(search) + "%"}
  135. if isShowFullName {
  136. prefixCond = prefixCond.Or(db.BuildCaseInsensitiveLike("full_name", "%"+search+"%"))
  137. }
  138. cond := builder.In("`user`.id",
  139. builder.Select("poster_id").From("issue").Where(
  140. builder.Eq{"repo_id": repo.ID}.
  141. And(builder.Eq{"is_pull": isPull}),
  142. ).GroupBy("poster_id")).And(prefixCond)
  143. return users, db.GetEngine(ctx).
  144. Where(cond).
  145. Cols("id", "name", "full_name", "avatar", "avatar_email", "use_custom_avatar").
  146. OrderBy("name").
  147. Limit(30).
  148. Find(&users)
  149. }