You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo_list.go 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. // Copyright 2017 The Gitea Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package models
  5. import (
  6. "context"
  7. "fmt"
  8. "strings"
  9. "code.gitea.io/gitea/models/db"
  10. "code.gitea.io/gitea/models/organization"
  11. "code.gitea.io/gitea/models/perm"
  12. repo_model "code.gitea.io/gitea/models/repo"
  13. "code.gitea.io/gitea/models/unit"
  14. user_model "code.gitea.io/gitea/models/user"
  15. "code.gitea.io/gitea/modules/container"
  16. "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/util"
  18. "xorm.io/builder"
  19. )
  20. // RepositoryListDefaultPageSize is the default number of repositories
  21. // to load in memory when running administrative tasks on all (or almost
  22. // all) of them.
  23. // The number should be low enough to avoid filling up all RAM with
  24. // repository data...
  25. const RepositoryListDefaultPageSize = 64
  26. // RepositoryList contains a list of repositories
  27. type RepositoryList []*repo_model.Repository
  28. func (repos RepositoryList) Len() int {
  29. return len(repos)
  30. }
  31. func (repos RepositoryList) Less(i, j int) bool {
  32. return repos[i].FullName() < repos[j].FullName()
  33. }
  34. func (repos RepositoryList) Swap(i, j int) {
  35. repos[i], repos[j] = repos[j], repos[i]
  36. }
  37. // FIXME: Remove in favor of maps.values when MIN_GO_VERSION >= 1.18
  38. func valuesRepository(m map[int64]*repo_model.Repository) []*repo_model.Repository {
  39. values := make([]*repo_model.Repository, 0, len(m))
  40. for _, v := range m {
  41. values = append(values, v)
  42. }
  43. return values
  44. }
  45. // RepositoryListOfMap make list from values of map
  46. func RepositoryListOfMap(repoMap map[int64]*repo_model.Repository) RepositoryList {
  47. return RepositoryList(valuesRepository(repoMap))
  48. }
  49. func (repos RepositoryList) loadAttributes(ctx context.Context) error {
  50. if len(repos) == 0 {
  51. return nil
  52. }
  53. set := make(map[int64]struct{})
  54. repoIDs := make([]int64, len(repos))
  55. for i := range repos {
  56. set[repos[i].OwnerID] = struct{}{}
  57. repoIDs[i] = repos[i].ID
  58. }
  59. // Load owners.
  60. users := make(map[int64]*user_model.User, len(set))
  61. if err := db.GetEngine(ctx).
  62. Where("id > 0").
  63. In("id", container.KeysInt64(set)).
  64. Find(&users); err != nil {
  65. return fmt.Errorf("find users: %v", err)
  66. }
  67. for i := range repos {
  68. repos[i].Owner = users[repos[i].OwnerID]
  69. }
  70. // Load primary language.
  71. stats := make(repo_model.LanguageStatList, 0, len(repos))
  72. if err := db.GetEngine(ctx).
  73. Where("`is_primary` = ? AND `language` != ?", true, "other").
  74. In("`repo_id`", repoIDs).
  75. Find(&stats); err != nil {
  76. return fmt.Errorf("find primary languages: %v", err)
  77. }
  78. stats.LoadAttributes()
  79. for i := range repos {
  80. for _, st := range stats {
  81. if st.RepoID == repos[i].ID {
  82. repos[i].PrimaryLanguage = st
  83. break
  84. }
  85. }
  86. }
  87. return nil
  88. }
  89. // LoadAttributes loads the attributes for the given RepositoryList
  90. func (repos RepositoryList) LoadAttributes() error {
  91. return repos.loadAttributes(db.DefaultContext)
  92. }
  93. // SearchRepoOptions holds the search options
  94. type SearchRepoOptions struct {
  95. db.ListOptions
  96. Actor *user_model.User
  97. Keyword string
  98. OwnerID int64
  99. PriorityOwnerID int64
  100. TeamID int64
  101. OrderBy db.SearchOrderBy
  102. Private bool // Include private repositories in results
  103. StarredByID int64
  104. WatchedByID int64
  105. AllPublic bool // Include also all public repositories of users and public organisations
  106. AllLimited bool // Include also all public repositories of limited organisations
  107. // None -> include public and private
  108. // True -> include just private
  109. // False -> include just public
  110. IsPrivate util.OptionalBool
  111. // None -> include collaborative AND non-collaborative
  112. // True -> include just collaborative
  113. // False -> include just non-collaborative
  114. Collaborate util.OptionalBool
  115. // None -> include forks AND non-forks
  116. // True -> include just forks
  117. // False -> include just non-forks
  118. Fork util.OptionalBool
  119. // None -> include templates AND non-templates
  120. // True -> include just templates
  121. // False -> include just non-templates
  122. Template util.OptionalBool
  123. // None -> include mirrors AND non-mirrors
  124. // True -> include just mirrors
  125. // False -> include just non-mirrors
  126. Mirror util.OptionalBool
  127. // None -> include archived AND non-archived
  128. // True -> include just archived
  129. // False -> include just non-archived
  130. Archived util.OptionalBool
  131. // only search topic name
  132. TopicOnly bool
  133. // only search repositories with specified primary language
  134. Language string
  135. // include description in keyword search
  136. IncludeDescription bool
  137. // None -> include has milestones AND has no milestone
  138. // True -> include just has milestones
  139. // False -> include just has no milestone
  140. HasMilestones util.OptionalBool
  141. // LowerNames represents valid lower names to restrict to
  142. LowerNames []string
  143. }
  144. // SearchOrderBy is used to sort the result
  145. type SearchOrderBy string
  146. func (s SearchOrderBy) String() string {
  147. return string(s)
  148. }
  149. // Strings for sorting result
  150. const (
  151. SearchOrderByAlphabetically SearchOrderBy = "name ASC"
  152. SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
  153. SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
  154. SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
  155. SearchOrderByOldest SearchOrderBy = "created_unix ASC"
  156. SearchOrderByNewest SearchOrderBy = "created_unix DESC"
  157. SearchOrderBySize SearchOrderBy = "size ASC"
  158. SearchOrderBySizeReverse SearchOrderBy = "size DESC"
  159. SearchOrderByID SearchOrderBy = "id ASC"
  160. SearchOrderByIDReverse SearchOrderBy = "id DESC"
  161. SearchOrderByStars SearchOrderBy = "num_stars ASC"
  162. SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
  163. SearchOrderByForks SearchOrderBy = "num_forks ASC"
  164. SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
  165. )
  166. // userOwnedRepoCond returns user ownered repositories
  167. func userOwnedRepoCond(userID int64) builder.Cond {
  168. return builder.Eq{
  169. "repository.owner_id": userID,
  170. }
  171. }
  172. // userAssignedRepoCond return user as assignee repositories list
  173. func userAssignedRepoCond(id string, userID int64) builder.Cond {
  174. return builder.And(
  175. builder.Eq{
  176. "repository.is_private": false,
  177. },
  178. builder.In(id,
  179. builder.Select("issue.repo_id").From("issue_assignees").
  180. InnerJoin("issue", "issue.id = issue_assignees.issue_id").
  181. Where(builder.Eq{
  182. "issue_assignees.assignee_id": userID,
  183. }),
  184. ),
  185. )
  186. }
  187. // userCreateIssueRepoCond return user created issues repositories list
  188. func userCreateIssueRepoCond(id string, userID int64, isPull bool) builder.Cond {
  189. return builder.And(
  190. builder.Eq{
  191. "repository.is_private": false,
  192. },
  193. builder.In(id,
  194. builder.Select("issue.repo_id").From("issue").
  195. Where(builder.Eq{
  196. "issue.poster_id": userID,
  197. "issue.is_pull": isPull,
  198. }),
  199. ),
  200. )
  201. }
  202. // userMentionedRepoCond return user metinoed repositories list
  203. func userMentionedRepoCond(id string, userID int64) builder.Cond {
  204. return builder.And(
  205. builder.Eq{
  206. "repository.is_private": false,
  207. },
  208. builder.In(id,
  209. builder.Select("issue.repo_id").From("issue_user").
  210. InnerJoin("issue", "issue.id = issue_user.issue_id").
  211. Where(builder.Eq{
  212. "issue_user.is_mentioned": true,
  213. "issue_user.uid": userID,
  214. }),
  215. ),
  216. )
  217. }
  218. // teamUnitsRepoCond returns query condition for those repo id in the special org team with special units access
  219. func teamUnitsRepoCond(id string, userID, orgID, teamID int64, units ...unit.Type) builder.Cond {
  220. return builder.In(id,
  221. builder.Select("repo_id").From("team_repo").Where(
  222. builder.Eq{
  223. "team_id": teamID,
  224. }.And(
  225. builder.Or(
  226. // Check if the user is member of the team.
  227. builder.In(
  228. "team_id", builder.Select("team_id").From("team_user").Where(
  229. builder.Eq{
  230. "uid": userID,
  231. },
  232. ),
  233. ),
  234. // Check if the user is in the owner team of the organisation.
  235. builder.Exists(builder.Select("team_id").From("team_user").
  236. Where(builder.Eq{
  237. "org_id": orgID,
  238. "team_id": builder.Select("id").From("team").Where(
  239. builder.Eq{
  240. "org_id": orgID,
  241. "lower_name": strings.ToLower(organization.OwnerTeamName),
  242. }),
  243. "uid": userID,
  244. }),
  245. ),
  246. )).And(
  247. builder.In(
  248. "team_id", builder.Select("team_id").From("team_unit").Where(
  249. builder.Eq{
  250. "`team_unit`.org_id": orgID,
  251. }.And(
  252. builder.In("`team_unit`.type", units),
  253. ),
  254. ),
  255. ),
  256. ),
  257. ))
  258. }
  259. // userCollaborationRepoCond returns user as collabrators repositories list
  260. func userCollaborationRepoCond(idStr string, userID int64) builder.Cond {
  261. return builder.In(idStr, builder.Select("repo_id").
  262. From("`access`").
  263. Where(builder.And(
  264. builder.Eq{"`access`.user_id": userID},
  265. builder.Gt{"`access`.mode": int(perm.AccessModeNone)},
  266. )),
  267. )
  268. }
  269. // userOrgTeamRepoCond selects repos that the given user has access to through team membership
  270. func userOrgTeamRepoCond(idStr string, userID int64) builder.Cond {
  271. return builder.In(idStr, userOrgTeamRepoBuilder(userID))
  272. }
  273. // userOrgTeamRepoBuilder returns repo ids where user's teams can access.
  274. func userOrgTeamRepoBuilder(userID int64) *builder.Builder {
  275. return builder.Select("`team_repo`.repo_id").
  276. From("team_repo").
  277. Join("INNER", "team_user", "`team_user`.team_id = `team_repo`.team_id").
  278. Where(builder.Eq{"`team_user`.uid": userID})
  279. }
  280. // userOrgTeamUnitRepoBuilder returns repo ids where user's teams can access the special unit.
  281. func userOrgTeamUnitRepoBuilder(userID int64, unitType unit.Type) *builder.Builder {
  282. return userOrgTeamRepoBuilder(userID).
  283. Join("INNER", "team_unit", "`team_unit`.team_id = `team_repo`.team_id").
  284. Where(builder.Eq{"`team_unit`.`type`": unitType})
  285. }
  286. // userOrgUnitRepoCond selects repos that the given user has access to through org and the special unit
  287. func userOrgUnitRepoCond(idStr string, userID, orgID int64, unitType unit.Type) builder.Cond {
  288. return builder.In(idStr,
  289. userOrgTeamUnitRepoBuilder(userID, unitType).
  290. And(builder.Eq{"`team_unit`.org_id": orgID}),
  291. )
  292. }
  293. // userOrgPublicRepoCond returns the condition that one user could access all public repositories in organizations
  294. func userOrgPublicRepoCond(userID int64) builder.Cond {
  295. return builder.And(
  296. builder.Eq{"`repository`.is_private": false},
  297. builder.In("`repository`.owner_id",
  298. builder.Select("`org_user`.org_id").
  299. From("org_user").
  300. Where(builder.Eq{"`org_user`.uid": userID}),
  301. ),
  302. )
  303. }
  304. // userOrgPublicRepoCondPrivate returns the condition that one user could access all public repositories in private organizations
  305. func userOrgPublicRepoCondPrivate(userID int64) builder.Cond {
  306. return builder.And(
  307. builder.Eq{"`repository`.is_private": false},
  308. builder.In("`repository`.owner_id",
  309. builder.Select("`org_user`.org_id").
  310. From("org_user").
  311. Join("INNER", "`user`", "`user`.id = `org_user`.org_id").
  312. Where(builder.Eq{
  313. "`org_user`.uid": userID,
  314. "`user`.`type`": user_model.UserTypeOrganization,
  315. "`user`.visibility": structs.VisibleTypePrivate,
  316. }),
  317. ),
  318. )
  319. }
  320. // userOrgPublicUnitRepoCond returns the condition that one user could access all public repositories in the special organization
  321. func userOrgPublicUnitRepoCond(userID, orgID int64) builder.Cond {
  322. return userOrgPublicRepoCond(userID).
  323. And(builder.Eq{"`repository`.owner_id": orgID})
  324. }
  325. // SearchRepositoryCondition creates a query condition according search repository options
  326. func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
  327. cond := builder.NewCond()
  328. if opts.Private {
  329. if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
  330. // OK we're in the context of a User
  331. cond = cond.And(accessibleRepositoryCondition(opts.Actor))
  332. }
  333. } else {
  334. // Not looking at private organisations and users
  335. // We should be able to see all non-private repositories that
  336. // isn't in a private or limited organisation.
  337. cond = cond.And(
  338. builder.Eq{"is_private": false},
  339. builder.NotIn("owner_id", builder.Select("id").From("`user`").Where(
  340. builder.Or(builder.Eq{"visibility": structs.VisibleTypeLimited}, builder.Eq{"visibility": structs.VisibleTypePrivate}),
  341. )))
  342. }
  343. if opts.IsPrivate != util.OptionalBoolNone {
  344. cond = cond.And(builder.Eq{"is_private": opts.IsPrivate.IsTrue()})
  345. }
  346. if opts.Template != util.OptionalBoolNone {
  347. cond = cond.And(builder.Eq{"is_template": opts.Template == util.OptionalBoolTrue})
  348. }
  349. // Restrict to starred repositories
  350. if opts.StarredByID > 0 {
  351. cond = cond.And(builder.In("id", builder.Select("repo_id").From("star").Where(builder.Eq{"uid": opts.StarredByID})))
  352. }
  353. // Restrict to watched repositories
  354. if opts.WatchedByID > 0 {
  355. cond = cond.And(builder.In("id", builder.Select("repo_id").From("watch").Where(builder.Eq{"user_id": opts.WatchedByID})))
  356. }
  357. // Restrict repositories to those the OwnerID owns or contributes to as per opts.Collaborate
  358. if opts.OwnerID > 0 {
  359. accessCond := builder.NewCond()
  360. if opts.Collaborate != util.OptionalBoolTrue {
  361. accessCond = builder.Eq{"owner_id": opts.OwnerID}
  362. }
  363. if opts.Collaborate != util.OptionalBoolFalse {
  364. // A Collaboration is:
  365. collaborateCond := builder.And(
  366. // 1. Repository we don't own
  367. builder.Neq{"owner_id": opts.OwnerID},
  368. // 2. But we can see because of:
  369. builder.Or(
  370. // A. We have access
  371. userCollaborationRepoCond("`repository`.id", opts.OwnerID),
  372. // B. We are in a team for
  373. userOrgTeamRepoCond("`repository`.id", opts.OwnerID),
  374. // C. Public repositories in organizations that we are member of
  375. userOrgPublicRepoCondPrivate(opts.OwnerID),
  376. ),
  377. )
  378. if !opts.Private {
  379. collaborateCond = collaborateCond.And(builder.Expr("owner_id NOT IN (SELECT org_id FROM org_user WHERE org_user.uid = ? AND org_user.is_public = ?)", opts.OwnerID, false))
  380. }
  381. accessCond = accessCond.Or(collaborateCond)
  382. }
  383. if opts.AllPublic {
  384. accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}))))
  385. }
  386. if opts.AllLimited {
  387. accessCond = accessCond.Or(builder.Eq{"is_private": false}.And(builder.In("owner_id", builder.Select("`user`.id").From("`user`").Where(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited}))))
  388. }
  389. cond = cond.And(accessCond)
  390. }
  391. if opts.TeamID > 0 {
  392. cond = cond.And(builder.In("`repository`.id", builder.Select("`team_repo`.repo_id").From("team_repo").Where(builder.Eq{"`team_repo`.team_id": opts.TeamID})))
  393. }
  394. if opts.Keyword != "" {
  395. // separate keyword
  396. subQueryCond := builder.NewCond()
  397. for _, v := range strings.Split(opts.Keyword, ",") {
  398. if opts.TopicOnly {
  399. subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": strings.ToLower(v)})
  400. } else {
  401. subQueryCond = subQueryCond.Or(builder.Like{"topic.name", strings.ToLower(v)})
  402. }
  403. }
  404. subQuery := builder.Select("repo_topic.repo_id").From("repo_topic").
  405. Join("INNER", "topic", "topic.id = repo_topic.topic_id").
  406. Where(subQueryCond).
  407. GroupBy("repo_topic.repo_id")
  408. keywordCond := builder.In("id", subQuery)
  409. if !opts.TopicOnly {
  410. likes := builder.NewCond()
  411. for _, v := range strings.Split(opts.Keyword, ",") {
  412. likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
  413. if opts.IncludeDescription {
  414. likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
  415. }
  416. }
  417. keywordCond = keywordCond.Or(likes)
  418. }
  419. cond = cond.And(keywordCond)
  420. }
  421. if opts.Language != "" {
  422. cond = cond.And(builder.In("id", builder.
  423. Select("repo_id").
  424. From("language_stat").
  425. Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
  426. }
  427. if opts.Fork != util.OptionalBoolNone {
  428. cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
  429. }
  430. if opts.Mirror != util.OptionalBoolNone {
  431. cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
  432. }
  433. if opts.Actor != nil && opts.Actor.IsRestricted {
  434. cond = cond.And(accessibleRepositoryCondition(opts.Actor))
  435. }
  436. if opts.Archived != util.OptionalBoolNone {
  437. cond = cond.And(builder.Eq{"is_archived": opts.Archived == util.OptionalBoolTrue})
  438. }
  439. switch opts.HasMilestones {
  440. case util.OptionalBoolTrue:
  441. cond = cond.And(builder.Gt{"num_milestones": 0})
  442. case util.OptionalBoolFalse:
  443. cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
  444. }
  445. return cond
  446. }
  447. // SearchRepository returns repositories based on search options,
  448. // it returns results in given range and number of total results.
  449. func SearchRepository(opts *SearchRepoOptions) (RepositoryList, int64, error) {
  450. cond := SearchRepositoryCondition(opts)
  451. return SearchRepositoryByCondition(opts, cond, true)
  452. }
  453. // SearchRepositoryByCondition search repositories by condition
  454. func SearchRepositoryByCondition(opts *SearchRepoOptions, cond builder.Cond, loadAttributes bool) (RepositoryList, int64, error) {
  455. ctx := db.DefaultContext
  456. sess, count, err := searchRepositoryByCondition(ctx, opts, cond)
  457. if err != nil {
  458. return nil, 0, err
  459. }
  460. defaultSize := 50
  461. if opts.PageSize > 0 {
  462. defaultSize = opts.PageSize
  463. }
  464. repos := make(RepositoryList, 0, defaultSize)
  465. if err := sess.Find(&repos); err != nil {
  466. return nil, 0, fmt.Errorf("Repo: %v", err)
  467. }
  468. if opts.PageSize <= 0 {
  469. count = int64(len(repos))
  470. }
  471. if loadAttributes {
  472. if err := repos.loadAttributes(ctx); err != nil {
  473. return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
  474. }
  475. }
  476. return repos, count, nil
  477. }
  478. func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, cond builder.Cond) (db.Engine, int64, error) {
  479. if opts.Page <= 0 {
  480. opts.Page = 1
  481. }
  482. if len(opts.OrderBy) == 0 {
  483. opts.OrderBy = db.SearchOrderByAlphabetically
  484. }
  485. if opts.PriorityOwnerID > 0 {
  486. opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = %d THEN 0 ELSE owner_id END, %s", opts.PriorityOwnerID, opts.OrderBy))
  487. }
  488. sess := db.GetEngine(ctx)
  489. var count int64
  490. if opts.PageSize > 0 {
  491. var err error
  492. count, err = sess.
  493. Where(cond).
  494. Count(new(repo_model.Repository))
  495. if err != nil {
  496. return nil, 0, fmt.Errorf("Count: %v", err)
  497. }
  498. }
  499. sess = sess.Where(cond).OrderBy(opts.OrderBy.String())
  500. if opts.PageSize > 0 {
  501. sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
  502. }
  503. return sess, count, nil
  504. }
  505. // accessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
  506. func accessibleRepositoryCondition(user *user_model.User) builder.Cond {
  507. cond := builder.NewCond()
  508. if user == nil || !user.IsRestricted || user.ID <= 0 {
  509. orgVisibilityLimit := []structs.VisibleType{structs.VisibleTypePrivate}
  510. if user == nil || user.ID <= 0 {
  511. orgVisibilityLimit = append(orgVisibilityLimit, structs.VisibleTypeLimited)
  512. }
  513. // 1. Be able to see all non-private repositories that either:
  514. cond = cond.Or(builder.And(
  515. builder.Eq{"`repository`.is_private": false},
  516. // 2. Aren't in an private organisation or limited organisation if we're not logged in
  517. builder.NotIn("`repository`.owner_id", builder.Select("id").From("`user`").Where(
  518. builder.And(
  519. builder.Eq{"type": user_model.UserTypeOrganization},
  520. builder.In("visibility", orgVisibilityLimit)),
  521. ))))
  522. }
  523. if user != nil {
  524. cond = cond.Or(
  525. // 2. Be able to see all repositories that we have access to
  526. userCollaborationRepoCond("`repository`.id", user.ID),
  527. // 3. Repositories that we directly own
  528. builder.Eq{"`repository`.owner_id": user.ID},
  529. // 4. Be able to see all repositories that we are in a team
  530. userOrgTeamRepoCond("`repository`.id", user.ID),
  531. // 5. Be able to see all public repos in private organizations that we are an org_user of
  532. userOrgPublicRepoCond(user.ID),
  533. )
  534. }
  535. return cond
  536. }
  537. // SearchRepositoryByName takes keyword and part of repository name to search,
  538. // it returns results in given range and number of total results.
  539. func SearchRepositoryByName(opts *SearchRepoOptions) (RepositoryList, int64, error) {
  540. opts.IncludeDescription = false
  541. return SearchRepository(opts)
  542. }
  543. // SearchRepositoryIDs takes keyword and part of repository name to search,
  544. // it returns results in given range and number of total results.
  545. func SearchRepositoryIDs(opts *SearchRepoOptions) ([]int64, int64, error) {
  546. opts.IncludeDescription = false
  547. cond := SearchRepositoryCondition(opts)
  548. sess, count, err := searchRepositoryByCondition(db.DefaultContext, opts, cond)
  549. if err != nil {
  550. return nil, 0, err
  551. }
  552. defaultSize := 50
  553. if opts.PageSize > 0 {
  554. defaultSize = opts.PageSize
  555. }
  556. ids := make([]int64, 0, defaultSize)
  557. err = sess.Select("id").Table("repository").Find(&ids)
  558. if opts.PageSize <= 0 {
  559. count = int64(len(ids))
  560. }
  561. return ids, count, err
  562. }
  563. // AccessibleRepoIDsQuery queries accessible repository ids. Usable as a subquery wherever repo ids need to be filtered.
  564. func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
  565. // NB: Please note this code needs to still work if user is nil
  566. return builder.Select("id").From("repository").Where(accessibleRepositoryCondition(user))
  567. }
  568. // FindUserAccessibleRepoIDs find all accessible repositories' ID by user's id
  569. func FindUserAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
  570. repoIDs := make([]int64, 0, 10)
  571. if err := db.GetEngine(db.DefaultContext).
  572. Table("repository").
  573. Cols("id").
  574. Where(accessibleRepositoryCondition(user)).
  575. Find(&repoIDs); err != nil {
  576. return nil, fmt.Errorf("FindUserAccesibleRepoIDs: %v", err)
  577. }
  578. return repoIDs, nil
  579. }
  580. // GetUserRepositories returns a list of repositories of given user.
  581. func GetUserRepositories(opts *SearchRepoOptions) (RepositoryList, int64, error) {
  582. if len(opts.OrderBy) == 0 {
  583. opts.OrderBy = "updated_unix DESC"
  584. }
  585. cond := builder.NewCond()
  586. cond = cond.And(builder.Eq{"owner_id": opts.Actor.ID})
  587. if !opts.Private {
  588. cond = cond.And(builder.Eq{"is_private": false})
  589. }
  590. if opts.LowerNames != nil && len(opts.LowerNames) > 0 {
  591. cond = cond.And(builder.In("lower_name", opts.LowerNames))
  592. }
  593. sess := db.GetEngine(db.DefaultContext)
  594. count, err := sess.Where(cond).Count(new(repo_model.Repository))
  595. if err != nil {
  596. return nil, 0, fmt.Errorf("Count: %v", err)
  597. }
  598. sess = sess.Where(cond).OrderBy(opts.OrderBy.String())
  599. repos := make(RepositoryList, 0, opts.PageSize)
  600. return repos, count, db.SetSessionPagination(sess, opts).Find(&repos)
  601. }