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 21KB

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