API add/generalize pagination (#9452)

* paginate results

* fixed deadlock

* prevented breaking change

* updated swagger

* go fmt

* fixed find topic

* go mod tidy

* go mod vendor with go1.13.5

* fixed repo find topics

* fixed unit test

* added Limit method to Engine struct; use engine variable when provided; fixed gitignore

* use ItemsPerPage for default pagesize; fix GetWatchers, getOrgUsersByOrgID and GetStargazers; fix GetAllCommits headers; reverted some changed behaviors

* set Page value on Home route

* improved memory allocations

* fixed response headers

* removed logfiles

* fixed import order

* import order

* improved swagger

* added function to get models.ListOptions from context

* removed pagesize diff on unit test

* fixed imports

* removed unnecessary struct field

* fixed go fmt

* scoped PR

* code improvements

* code improvements

* go mod tidy

* fixed import order

* fixed commit statuses session

* fixed files headers

* fixed headers; added pagination for notifications

* go mod tidy

* go fmt

* removed Private from user search options; added setting.UI.IssuePagingNum as default valeu on repo's issues list

* Apply suggestions from code review

Co-Authored-By: 6543 <6543@obermui.de>
Co-Authored-By: zeripath <art27@cantab.net>

* fixed build error

* CI.restart()

* fixed merge conflicts resolve

* fixed conflicts resolve

* improved FindTrackedTimesOptions.ToOptions() method

* added backwards compatibility on ListReleases request; fixed issue tracked time ToSession

* fixed build error; fixed swagger template

* fixed swagger template

* fixed ListReleases backwards compatibility

* added page to user search route

Co-authored-by: techknowlogick <matti@mdranta.net>
Co-authored-by: 6543 <6543@obermui.de>
Co-authored-by: zeripath <art27@cantab.net>
tokarchuk/v1.17
SpaWn2KiLl 5 years ago committed by techknowlogick
parent 333401e0fd
commit 1f01f53c53
  1. 4
      cmd/admin.go
  2. 2
      models/access.go
  3. 10
      models/commit_status.go
  4. 13
      models/gpg_key.go
  5. 3
      models/issue.go
  6. 6
      models/issue_comment.go
  7. 11
      models/issue_label.go
  8. 2
      models/issue_label_test.go
  9. 8
      models/issue_milestone.go
  10. 4
      models/issue_milestone_test.go
  11. 21
      models/issue_reaction.go
  12. 11
      models/issue_stopwatch.go
  13. 6
      models/issue_test.go
  14. 16
      models/issue_tracked_time.go
  15. 16
      models/issue_watch.go
  16. 8
      models/issue_watch_test.go
  17. 44
      models/list_options.go
  18. 2
      models/models.go
  19. 5
      models/notification.go
  20. 49
      models/org.go
  21. 43
      models/org_team.go
  22. 6
      models/org_team_test.go
  23. 20
      models/org_test.go
  24. 6
      models/pull_list.go
  25. 2
      models/pull_sign.go
  26. 4
      models/pull_test.go
  27. 19
      models/release.go
  28. 39
      models/repo.go
  29. 20
      models/repo_collaboration.go
  30. 2
      models/repo_collaboration_test.go
  31. 4
      models/repo_generate.go
  32. 3
      models/repo_list.go
  33. 80
      models/repo_list_test.go
  34. 6
      models/repo_sign.go
  35. 12
      models/repo_watch.go
  36. 20
      models/repo_watch_test.go
  37. 32
      models/ssh_key.go
  38. 12
      models/star.go
  39. 4
      models/star_test.go
  40. 18
      models/token.go
  41. 6
      models/token_test.go
  42. 7
      models/topic.go
  43. 2
      models/topic_test.go
  44. 94
      models/user.go
  45. 20
      models/user_test.go
  46. 2
      models/userlist.go
  47. 23
      models/webhook.go
  48. 4
      models/webhook_test.go
  49. 3
      modules/context/org.go
  50. 4
      modules/git/commit.go
  51. 7
      modules/git/repo_commit.go
  52. 3
      modules/indexer/issues/indexer.go
  53. 14
      modules/migrations/gitea_test.go
  54. 2
      modules/repository/create_test.go
  55. 5
      modules/repository/repo.go
  56. 3
      routers/admin/orgs.go
  57. 3
      routers/admin/users.go
  58. 4
      routers/api/v1/admin/org.go
  59. 12
      routers/api/v1/admin/user.go
  60. 9
      routers/api/v1/notify/repo.go
  61. 9
      routers/api/v1/notify/user.go
  62. 10
      routers/api/v1/org/hook.go
  63. 20
      routers/api/v1/org/member.go
  64. 26
      routers/api/v1/org/org.go
  65. 59
      routers/api/v1/org/team.go
  66. 11
      routers/api/v1/repo/collaborators.go
  67. 29
      routers/api/v1/repo/commits.go
  68. 12
      routers/api/v1/repo/fork.go
  69. 11
      routers/api/v1/repo/hook.go
  70. 37
      routers/api/v1/repo/issue.go
  71. 12
      routers/api/v1/repo/issue_comment.go
  72. 11
      routers/api/v1/repo/issue_reaction.go
  73. 12
      routers/api/v1/repo/issue_stopwatch.go
  74. 11
      routers/api/v1/repo/issue_subscription.go
  75. 32
      routers/api/v1/repo/issue_tracked_time.go
  76. 12
      routers/api/v1/repo/key.go
  77. 11
      routers/api/v1/repo/label.go
  78. 12
      routers/api/v1/repo/milestone.go
  79. 19
      routers/api/v1/repo/pull.go
  80. 35
      routers/api/v1/repo/release.go
  81. 23
      routers/api/v1/repo/repo.go
  82. 11
      routers/api/v1/repo/star.go
  83. 29
      routers/api/v1/repo/status.go
  84. 11
      routers/api/v1/repo/subscriber.go
  85. 30
      routers/api/v1/repo/topic.go
  86. 11
      routers/api/v1/user/app.go
  87. 40
      routers/api/v1/user/follower.go
  88. 26
      routers/api/v1/user/gpg_key.go
  89. 19
      routers/api/v1/user/key.go
  90. 38
      routers/api/v1/user/repo.go
  91. 27
      routers/api/v1/user/star.go
  92. 10
      routers/api/v1/user/user.go
  93. 26
      routers/api/v1/user/watch.go
  94. 10
      routers/api/v1/utils/utils.go
  95. 8
      routers/home.go
  96. 10
      routers/org/home.go
  97. 7
      routers/org/members.go
  98. 2
      routers/org/setting.go
  99. 6
      routers/org/teams.go
  100. 7
      routers/repo/commit.go
  101. Some files were not shown because too many files have changed in this diff Show More

@ -351,8 +351,10 @@ func runRepoSyncReleases(c *cli.Context) error {
log.Trace("Synchronizing repository releases (this may take a while)") log.Trace("Synchronizing repository releases (this may take a while)")
for page := 1; ; page++ { for page := 1; ; page++ {
repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ repos, count, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
Page: page, ListOptions: models.ListOptions{
PageSize: models.RepositoryListDefaultPageSize, PageSize: models.RepositoryListDefaultPageSize,
Page: page,
},
Private: true, Private: true,
}) })
if err != nil { if err != nil {

@ -215,7 +215,7 @@ func (repo *Repository) refreshAccesses(e Engine, accessMap map[int64]*userAcces
// refreshCollaboratorAccesses retrieves repository collaborations with their access modes. // refreshCollaboratorAccesses retrieves repository collaborations with their access modes.
func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]*userAccess) error { func (repo *Repository) refreshCollaboratorAccesses(e Engine, accessMap map[int64]*userAccess) error {
collaborators, err := repo.getCollaborators(e) collaborators, err := repo.getCollaborators(e, ListOptions{})
if err != nil { if err != nil {
return fmt.Errorf("getCollaborations: %v", err) return fmt.Errorf("getCollaborations: %v", err)
} }

@ -104,7 +104,7 @@ func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
// CommitStatusOptions holds the options for query commit statuses // CommitStatusOptions holds the options for query commit statuses
type CommitStatusOptions struct { type CommitStatusOptions struct {
Page int ListOptions
State string State string
SortType string SortType string
} }
@ -114,18 +114,22 @@ func GetCommitStatuses(repo *Repository, sha string, opts *CommitStatusOptions)
if opts.Page <= 0 { if opts.Page <= 0 {
opts.Page = 1 opts.Page = 1
} }
if opts.PageSize <= 0 {
opts.Page = ItemsPerPage
}
countSession := listCommitStatusesStatement(repo, sha, opts) countSession := listCommitStatusesStatement(repo, sha, opts)
countSession = opts.setSessionPagination(countSession)
maxResults, err := countSession.Count(new(CommitStatus)) maxResults, err := countSession.Count(new(CommitStatus))
if err != nil { if err != nil {
log.Error("Count PRs: %v", err) log.Error("Count PRs: %v", err)
return nil, maxResults, err return nil, maxResults, err
} }
statuses := make([]*CommitStatus, 0, ItemsPerPage) statuses := make([]*CommitStatus, 0, opts.PageSize)
findSession := listCommitStatusesStatement(repo, sha, opts) findSession := listCommitStatusesStatement(repo, sha, opts)
findSession = opts.setSessionPagination(findSession)
sortCommitStatusesSession(findSession, opts.SortType) sortCommitStatusesSession(findSession, opts.SortType)
findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage)
return statuses, maxResults, findSession.Find(&statuses) return statuses, maxResults, findSession.Find(&statuses)
} }

@ -64,9 +64,14 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) {
} }
// ListGPGKeys returns a list of public keys belongs to given user. // ListGPGKeys returns a list of public keys belongs to given user.
func ListGPGKeys(uid int64) ([]*GPGKey, error) { func ListGPGKeys(uid int64, listOptions ListOptions) ([]*GPGKey, error) {
keys := make([]*GPGKey, 0, 5) sess := x.Where("owner_id=? AND primary_key_id=''", uid)
return keys, x.Where("owner_id=? AND primary_key_id=''", uid).Find(&keys) if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
keys := make([]*GPGKey, 0, 2)
return keys, sess.Find(&keys)
} }
// GetGPGKeyByID returns public key by given ID. // GetGPGKeyByID returns public key by given ID.
@ -628,7 +633,7 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
// Now try to associate the signature with the committer, if present // Now try to associate the signature with the committer, if present
if committer.ID != 0 { if committer.ID != 0 {
keys, err := ListGPGKeys(committer.ID) keys, err := ListGPGKeys(committer.ID, ListOptions{})
if err != nil { //Skipping failed to get gpg keys of user if err != nil { //Skipping failed to get gpg keys of user
log.Error("ListGPGKeys: %v", err) log.Error("ListGPGKeys: %v", err)
return &CommitVerification{ return &CommitVerification{

@ -1103,13 +1103,12 @@ func GetIssuesByIDs(issueIDs []int64) ([]*Issue, error) {
// IssuesOptions represents options of an issue. // IssuesOptions represents options of an issue.
type IssuesOptions struct { type IssuesOptions struct {
ListOptions
RepoIDs []int64 // include all repos if empty RepoIDs []int64 // include all repos if empty
AssigneeID int64 AssigneeID int64
PosterID int64 PosterID int64
MentionedID int64 MentionedID int64
MilestoneID int64 MilestoneID int64
Page int
PageSize int
IsClosed util.OptionalBool IsClosed util.OptionalBool
IsPull util.OptionalBool IsPull util.OptionalBool
LabelIDs []int64 LabelIDs []int64

@ -777,6 +777,7 @@ func GetCommentByID(id int64) (*Comment, error) {
// FindCommentsOptions describes the conditions to Find comments // FindCommentsOptions describes the conditions to Find comments
type FindCommentsOptions struct { type FindCommentsOptions struct {
ListOptions
RepoID int64 RepoID int64
IssueID int64 IssueID int64
ReviewID int64 ReviewID int64
@ -814,6 +815,11 @@ func findComments(e Engine, opts FindCommentsOptions) ([]*Comment, error) {
if opts.RepoID > 0 { if opts.RepoID > 0 {
sess.Join("INNER", "issue", "issue.id = comment.issue_id") sess.Join("INNER", "issue", "issue.id = comment.issue_id")
} }
if opts.Page != 0 {
sess = opts.setSessionPagination(sess)
}
return comments, sess. return comments, sess.
Asc("comment.created_unix"). Asc("comment.created_unix").
Asc("comment.id"). Asc("comment.id").

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -298,7 +299,7 @@ func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
Find(&labels) Find(&labels)
} }
func getLabelsByRepoID(e Engine, repoID int64, sortType string) ([]*Label, error) { func getLabelsByRepoID(e Engine, repoID int64, sortType string, listOptions ListOptions) ([]*Label, error) {
labels := make([]*Label, 0, 10) labels := make([]*Label, 0, 10)
sess := e.Where("repo_id = ?", repoID) sess := e.Where("repo_id = ?", repoID)
@ -313,12 +314,16 @@ func getLabelsByRepoID(e Engine, repoID int64, sortType string) ([]*Label, error
sess.Asc("name") sess.Asc("name")
} }
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
return labels, sess.Find(&labels) return labels, sess.Find(&labels)
} }
// GetLabelsByRepoID returns all labels that belong to given repository by ID. // GetLabelsByRepoID returns all labels that belong to given repository by ID.
func GetLabelsByRepoID(repoID int64, sortType string) ([]*Label, error) { func GetLabelsByRepoID(repoID int64, sortType string, listOptions ListOptions) ([]*Label, error) {
return getLabelsByRepoID(x, repoID, sortType) return getLabelsByRepoID(x, repoID, sortType, listOptions)
} }
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) { func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {

@ -131,7 +131,7 @@ func TestGetLabelsInRepoByIDs(t *testing.T) {
func TestGetLabelsByRepoID(t *testing.T) { func TestGetLabelsByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) { testSuccess := func(repoID int64, sortType string, expectedIssueIDs []int64) {
labels, err := GetLabelsByRepoID(repoID, sortType) labels, err := GetLabelsByRepoID(repoID, sortType, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, labels, len(expectedIssueIDs)) assert.Len(t, labels, len(expectedIssueIDs))
for i, label := range labels { for i, label := range labels {

@ -221,7 +221,7 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 {
} }
// GetMilestonesByRepoID returns all opened milestones of a repository. // GetMilestonesByRepoID returns all opened milestones of a repository.
func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, error) { func GetMilestonesByRepoID(repoID int64, state api.StateType, listOptions ListOptions) (MilestoneList, error) {
sess := x.Where("repo_id = ?", repoID) sess := x.Where("repo_id = ?", repoID)
switch state { switch state {
@ -238,7 +238,11 @@ func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, er
sess = sess.And("is_closed = ?", false) sess = sess.And("is_closed = ?", false)
} }
miles := make([]*Milestone, 0, 10) if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
miles := make([]*Milestone, 0, listOptions.PageSize)
return miles, sess.Asc("deadline_unix").Asc("id").Find(&miles) return miles, sess.Asc("deadline_unix").Asc("id").Find(&miles)
} }

@ -71,7 +71,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64, state api.StateType) { test := func(repoID int64, state api.StateType) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
milestones, err := GetMilestonesByRepoID(repo.ID, state) milestones, err := GetMilestonesByRepoID(repo.ID, state, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
var n int var n int
@ -105,7 +105,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
test(3, api.StateClosed) test(3, api.StateClosed)
test(3, api.StateAll) test(3, api.StateAll)
milestones, err := GetMilestonesByRepoID(NonexistentID, api.StateOpen) milestones, err := GetMilestonesByRepoID(NonexistentID, api.StateOpen, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, milestones, 0) assert.Len(t, milestones, 0)
} }

@ -30,6 +30,7 @@ type Reaction struct {
// FindReactionsOptions describes the conditions to Find reactions // FindReactionsOptions describes the conditions to Find reactions
type FindReactionsOptions struct { type FindReactionsOptions struct {
ListOptions
IssueID int64 IssueID int64
CommentID int64 CommentID int64
UserID int64 UserID int64
@ -71,20 +72,28 @@ func FindCommentReactions(comment *Comment) (ReactionList, error) {
} }
// FindIssueReactions returns a ReactionList of all reactions from an issue // FindIssueReactions returns a ReactionList of all reactions from an issue
func FindIssueReactions(issue *Issue) (ReactionList, error) { func FindIssueReactions(issue *Issue, listOptions ListOptions) (ReactionList, error) {
return findReactions(x, FindReactionsOptions{ return findReactions(x, FindReactionsOptions{
ListOptions: listOptions,
IssueID: issue.ID, IssueID: issue.ID,
CommentID: -1, CommentID: -1,
}) })
} }
func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) { func findReactions(e Engine, opts FindReactionsOptions) ([]*Reaction, error) {
reactions := make([]*Reaction, 0, 10) e = e.
sess := e.Where(opts.toConds()) Where(opts.toConds()).
return reactions, sess.
In("reaction.`type`", setting.UI.Reactions). In("reaction.`type`", setting.UI.Reactions).
Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id"). Asc("reaction.issue_id", "reaction.comment_id", "reaction.created_unix", "reaction.id")
Find(&reactions) if opts.Page != 0 {
e = opts.setEnginePagination(e)
reactions := make([]*Reaction, 0, opts.PageSize)
return reactions, e.Find(&reactions)
}
reactions := make([]*Reaction, 0, 10)
return reactions, e.Find(&reactions)
} }
func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) { func createReaction(e *xorm.Session, opts *ReactionOptions) (*Reaction, error) {

@ -33,9 +33,14 @@ func getStopwatch(e Engine, userID, issueID int64) (sw *Stopwatch, exists bool,
} }
// GetUserStopwatches return list of all stopwatches of a user // GetUserStopwatches return list of all stopwatches of a user
func GetUserStopwatches(userID int64) (sws *Stopwatches, err error) { func GetUserStopwatches(userID int64, listOptions ListOptions) (*Stopwatches, error) {
sws = new(Stopwatches) sws := new(Stopwatches)
err = x.Where("stopwatch.user_id = ?", userID).Find(sws) sess := x.Where("stopwatch.user_id = ?", userID)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
err := sess.Find(sws)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -139,25 +139,31 @@ func TestIssues(t *testing.T) {
IssuesOptions{ IssuesOptions{
RepoIDs: []int64{1, 3}, RepoIDs: []int64{1, 3},
SortType: "oldest", SortType: "oldest",
ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 4, PageSize: 4,
}, },
},
[]int64{1, 2, 3, 5}, []int64{1, 2, 3, 5},
}, },
{ {
IssuesOptions{ IssuesOptions{
LabelIDs: []int64{1}, LabelIDs: []int64{1},
ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 4, PageSize: 4,
}, },
},
[]int64{2, 1}, []int64{2, 1},
}, },
{ {
IssuesOptions{ IssuesOptions{
LabelIDs: []int64{1, 2}, LabelIDs: []int64{1, 2},
ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 4, PageSize: 4,
}, },
},
[]int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests []int64{}, // issues with **both** label 1 and 2, none of these issues matches, TODO: add more tests
}, },
} { } {

@ -10,7 +10,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm"
) )
// TrackedTime represents a time that was spent for a specific issue. // TrackedTime represents a time that was spent for a specific issue.
@ -71,6 +70,7 @@ func (tl TrackedTimeList) LoadAttributes() (err error) {
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored. // FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
type FindTrackedTimesOptions struct { type FindTrackedTimesOptions struct {
ListOptions
IssueID int64 IssueID int64
UserID int64 UserID int64
RepositoryID int64 RepositoryID int64
@ -104,11 +104,19 @@ func (opts *FindTrackedTimesOptions) ToCond() builder.Cond {
} }
// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required // ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required
func (opts *FindTrackedTimesOptions) ToSession(e Engine) *xorm.Session { func (opts *FindTrackedTimesOptions) ToSession(e Engine) Engine {
sess := e
if opts.RepositoryID > 0 || opts.MilestoneID > 0 { if opts.RepositoryID > 0 || opts.MilestoneID > 0 {
return e.Join("INNER", "issue", "issue.id = tracked_time.issue_id").Where(opts.ToCond()) sess = e.Join("INNER", "issue", "issue.id = tracked_time.issue_id")
} }
return e.Where(opts.ToCond())
sess = sess.Where(opts.ToCond())
if opts.Page != 0 {
sess = opts.setEnginePagination(sess)
}
return sess
} }
func getTrackedTimes(e Engine, options FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) { func getTrackedTimes(e Engine, options FindTrackedTimesOptions) (trackedTimes TrackedTimeList, err error) {

@ -77,18 +77,22 @@ func GetIssueWatchersIDs(issueID int64) ([]int64, error) {
} }
// GetIssueWatchers returns watchers/unwatchers of a given issue // GetIssueWatchers returns watchers/unwatchers of a given issue
func GetIssueWatchers(issueID int64) (IssueWatchList, error) { func GetIssueWatchers(issueID int64, listOptions ListOptions) (IssueWatchList, error) {
return getIssueWatchers(x, issueID) return getIssueWatchers(x, issueID, listOptions)
} }
func getIssueWatchers(e Engine, issueID int64) (watches IssueWatchList, err error) { func getIssueWatchers(e Engine, issueID int64, listOptions ListOptions) (watches IssueWatchList, err error) {
err = e. sess := e.
Where("`issue_watch`.issue_id = ?", issueID). Where("`issue_watch`.issue_id = ?", issueID).
And("`issue_watch`.is_watching = ?", true). And("`issue_watch`.is_watching = ?", true).
And("`user`.is_active = ?", true). And("`user`.is_active = ?", true).
And("`user`.prohibit_login = ?", false). And("`user`.prohibit_login = ?", false).
Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id"). Join("INNER", "`user`", "`user`.id = `issue_watch`.user_id")
Find(&watches)
if listOptions.Page == 0 {
sess = listOptions.setSessionPagination(sess)
}
err = sess.Find(&watches)
return return
} }

@ -42,22 +42,22 @@ func TestGetIssueWatch(t *testing.T) {
func TestGetIssueWatchers(t *testing.T) { func TestGetIssueWatchers(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
iws, err := GetIssueWatchers(1) iws, err := GetIssueWatchers(1, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
// Watcher is inactive, thus 0 // Watcher is inactive, thus 0
assert.Len(t, iws, 0) assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(2) iws, err = GetIssueWatchers(2, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
// Watcher is explicit not watching // Watcher is explicit not watching
assert.Len(t, iws, 0) assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(5) iws, err = GetIssueWatchers(5, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
// Issue has no Watchers // Issue has no Watchers
assert.Len(t, iws, 0) assert.Len(t, iws, 0)
iws, err = GetIssueWatchers(7) iws, err = GetIssueWatchers(7, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
// Issue has one watcher // Issue has one watcher
assert.Len(t, iws, 1) assert.Len(t, iws, 1)

@ -0,0 +1,44 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"code.gitea.io/gitea/modules/setting"
"xorm.io/xorm"
)
// ListOptions options to paginate results
type ListOptions struct {
PageSize int
Page int
}
func (opts ListOptions) getPaginatedSession() *xorm.Session {
opts.setDefaultValues()
return x.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
func (opts ListOptions) setSessionPagination(sess *xorm.Session) *xorm.Session {
opts.setDefaultValues()
return sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
func (opts ListOptions) setEnginePagination(e Engine) Engine {
opts.setDefaultValues()
return e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
func (opts ListOptions) setDefaultValues() {
if opts.PageSize <= 0 || opts.PageSize > setting.API.MaxResponseItems {
opts.PageSize = setting.API.MaxResponseItems
}
if opts.Page <= 0 {
opts.Page = 1
}
}

@ -44,6 +44,8 @@ type Engine interface {
SQL(interface{}, ...interface{}) *xorm.Session SQL(interface{}, ...interface{}) *xorm.Session
Where(interface{}, ...interface{}) *xorm.Session Where(interface{}, ...interface{}) *xorm.Session
Asc(colNames ...string) *xorm.Session Asc(colNames ...string) *xorm.Session
Limit(limit int, start ...int) *xorm.Session
SumInt(bean interface{}, columnName string) (res int64, err error)
} }
var ( var (

@ -68,6 +68,7 @@ type Notification struct {
// FindNotificationOptions represent the filters for notifications. If an ID is 0 it will be ignored. // FindNotificationOptions represent the filters for notifications. If an ID is 0 it will be ignored.
type FindNotificationOptions struct { type FindNotificationOptions struct {
ListOptions
UserID int64 UserID int64
RepoID int64 RepoID int64
IssueID int64 IssueID int64
@ -102,7 +103,7 @@ func (opts *FindNotificationOptions) ToCond() builder.Cond {
// ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required // ToSession will convert the given options to a xorm Session by using the conditions from ToCond and joining with issue table if required
func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session { func (opts *FindNotificationOptions) ToSession(e Engine) *xorm.Session {
return e.Where(opts.ToCond()) return opts.setSessionPagination(e.Where(opts.ToCond()))
} }
func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) { func getNotifications(e Engine, options FindNotificationOptions) (nl NotificationList, err error) {
@ -132,7 +133,7 @@ func CreateOrUpdateIssueNotifications(issueID, commentID int64, notificationAuth
} }
func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error { func createOrUpdateIssueNotifications(e Engine, issueID, commentID int64, notificationAuthorID int64) error {
issueWatches, err := getIssueWatchers(e, issueID) issueWatches, err := getIssueWatchers(e, issueID, ListOptions{})
if err != nil { if err != nil {
return err return err
} }

@ -62,14 +62,18 @@ func (org *User) getTeams(e Engine) error {
Find(&org.Teams) Find(&org.Teams)
} }
// GetTeams returns all teams that belong to organization. // GetTeams returns paginated teams that belong to organization.
func (org *User) GetTeams() error { func (org *User) GetTeams(opts *SearchTeamOptions) error {
if opts.Page != 0 {
return org.getTeams(opts.getPaginatedSession())
}
return org.getTeams(x) return org.getTeams(x)
} }
// GetMembers returns all members of organization. // GetMembers returns all members of organization.
func (org *User) GetMembers() (err error) { func (org *User) GetMembers() (err error) {
org.Members, org.MembersIsPublic, err = FindOrgMembers(FindOrgMembersOpts{ org.Members, org.MembersIsPublic, err = FindOrgMembers(&FindOrgMembersOpts{
OrgID: org.ID, OrgID: org.ID,
}) })
return return
@ -77,10 +81,9 @@ func (org *User) GetMembers() (err error) {
// FindOrgMembersOpts represensts find org members condtions // FindOrgMembersOpts represensts find org members condtions
type FindOrgMembersOpts struct { type FindOrgMembersOpts struct {
ListOptions
OrgID int64 OrgID int64
PublicOnly bool PublicOnly bool
Start int
Limit int
} }
// CountOrgMembers counts the organization's members // CountOrgMembers counts the organization's members
@ -93,8 +96,8 @@ func CountOrgMembers(opts FindOrgMembersOpts) (int64, error) {
} }
// FindOrgMembers loads organization members according conditions // FindOrgMembers loads organization members according conditions
func FindOrgMembers(opts FindOrgMembersOpts) (UserList, map[int64]bool, error) { func FindOrgMembers(opts *FindOrgMembersOpts) (UserList, map[int64]bool, error) {
ous, err := GetOrgUsersByOrgID(opts.OrgID, opts.PublicOnly, opts.Start, opts.Limit) ous, err := GetOrgUsersByOrgID(opts)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -479,15 +482,20 @@ func GetOrgsCanCreateRepoByUserID(userID int64) ([]*User, error) {
} }
// GetOrgUsersByUserID returns all organization-user relations by user ID. // GetOrgUsersByUserID returns all organization-user relations by user ID.
func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) { func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) {
ous := make([]*OrgUser, 0, 10) ous := make([]*OrgUser, 0, 10)
sess := x. sess := x.
Join("LEFT", "`user`", "`org_user`.org_id=`user`.id"). Join("LEFT", "`user`", "`org_user`.org_id=`user`.id").
Where("`org_user`.uid=?", uid) Where("`org_user`.uid=?", uid)
if !all { if !opts.All {
// Only show public organizations // Only show public organizations
sess.And("is_public=?", true) sess.And("is_public=?", true)
} }
if opts.PageSize != 0 {
sess = opts.setSessionPagination(sess)
}
err := sess. err := sess.
Asc("`user`.name"). Asc("`user`.name").
Find(&ous) Find(&ous)
@ -495,21 +503,24 @@ func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
} }
// GetOrgUsersByOrgID returns all organization-user relations by organization ID. // GetOrgUsersByOrgID returns all organization-user relations by organization ID.
func GetOrgUsersByOrgID(orgID int64, publicOnly bool, start, limit int) ([]*OrgUser, error) { func GetOrgUsersByOrgID(opts *FindOrgMembersOpts) ([]*OrgUser, error) {
return getOrgUsersByOrgID(x, orgID, publicOnly, start, limit) return getOrgUsersByOrgID(x, opts)
} }
func getOrgUsersByOrgID(e Engine, orgID int64, publicOnly bool, start, limit int) ([]*OrgUser, error) { func getOrgUsersByOrgID(e Engine, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
ous := make([]*OrgUser, 0, 10) sess := e.Where("org_id=?", opts.OrgID)
sess := e.Where("org_id=?", orgID) if opts.PublicOnly {
if publicOnly {
sess.And("is_public = ?", true) sess.And("is_public = ?", true)
} }
if limit > 0 { if opts.ListOptions.PageSize > 0 {
sess.Limit(limit, start) sess = opts.setSessionPagination(sess)
ous := make([]*OrgUser, 0, opts.PageSize)
return ous, sess.Find(&ous)
} }
err := sess.Find(&ous)
return ous, err var ous []*OrgUser
return ous, sess.Find(&ous)
} }
// ChangeOrgUserStatus changes public or private membership status. // ChangeOrgUserStatus changes public or private membership status.

@ -39,12 +39,16 @@ type Team struct {
// SearchTeamOptions holds the search options // SearchTeamOptions holds the search options
type SearchTeamOptions struct { type SearchTeamOptions struct {
ListOptions
UserID int64 UserID int64
Keyword string Keyword string
OrgID int64 OrgID int64
IncludeDesc bool IncludeDesc bool
PageSize int }
Page int
// SearchMembersOptions holds the search options
type SearchMembersOptions struct {
ListOptions
} }
// SearchTeam search for teams. Caller is responsible to check permissions. // SearchTeam search for teams. Caller is responsible to check permissions.
@ -160,9 +164,13 @@ func (t *Team) getRepositories(e Engine) error {
Find(&t.Repos) Find(&t.Repos)
} }
// GetRepositories returns all repositories in team of organization. // GetRepositories returns paginated repositories in team of organization.
func (t *Team) GetRepositories() error { func (t *Team) GetRepositories(opts *SearchTeamOptions) error {
if opts.Page == 0 {
return t.getRepositories(x) return t.getRepositories(x)
}
return t.getRepositories(opts.getPaginatedSession())
} }
func (t *Team) getMembers(e Engine) (err error) { func (t *Team) getMembers(e Engine) (err error) {
@ -170,9 +178,13 @@ func (t *Team) getMembers(e Engine) (err error) {
return err return err
} }
// GetMembers returns all members in team of organization. // GetMembers returns paginated members in team of organization.
func (t *Team) GetMembers() (err error) { func (t *Team) GetMembers(opts *SearchMembersOptions) (err error) {
if opts.Page == 0 {
return t.getMembers(x) return t.getMembers(x)
}
return t.getMembers(opts.getPaginatedSession())
} }
// AddMember adds new membership of the team to the organization, // AddMember adds new membership of the team to the organization,
@ -642,7 +654,7 @@ func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) {
// DeleteTeam deletes given team. // DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID. // It's caller's responsibility to assign organization ID.
func DeleteTeam(t *Team) error { func DeleteTeam(t *Team) error {
if err := t.GetRepositories(); err != nil { if err := t.GetRepositories(&SearchTeamOptions{}); err != nil {
return err return err
} }
@ -747,11 +759,14 @@ func GetTeamMembers(teamID int64) ([]*User, error) {
return getTeamMembers(x, teamID) return getTeamMembers(x, teamID)
} }
func getUserTeams(e Engine, userID int64) (teams []*Team, err error) { func getUserTeams(e Engine, userID int64, listOptions ListOptions) (teams []*Team, err error) {
return teams, e. sess := e.
Join("INNER", "team_user", "team_user.team_id = team.id"). Join("INNER", "team_user", "team_user.team_id = team.id").
Where("team_user.uid=?", userID). Where("team_user.uid=?", userID)
Find(&teams) if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
}
return teams, sess.Find(&teams)
} }
func getUserOrgTeams(e Engine, orgID, userID int64) (teams []*Team, err error) { func getUserOrgTeams(e Engine, orgID, userID int64) (teams []*Team, err error) {
@ -778,8 +793,8 @@ func GetUserOrgTeams(orgID, userID int64) ([]*Team, error) {
} }
// GetUserTeams returns all teams that user belongs across all organizations. // GetUserTeams returns all teams that user belongs across all organizations.
func GetUserTeams(userID int64) ([]*Team, error) { func GetUserTeams(userID int64, listOptions ListOptions) ([]*Team, error) {
return getUserTeams(x, userID) return getUserTeams(x, userID, listOptions)
} }
// AddTeamMember adds new membership of given team to given organization, // AddTeamMember adds new membership of given team to given organization,
@ -795,7 +810,7 @@ func AddTeamMember(team *Team, userID int64) error {
} }
// Get team and its repositories. // Get team and its repositories.
if err := team.GetRepositories(); err != nil { if err := team.GetRepositories(&SearchTeamOptions{}); err != nil {
return err return err
} }

@ -40,7 +40,7 @@ func TestTeam_GetRepositories(t *testing.T) {
test := func(teamID int64) { test := func(teamID int64) {
team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
assert.NoError(t, team.GetRepositories()) assert.NoError(t, team.GetRepositories(&SearchTeamOptions{}))
assert.Len(t, team.Repos, team.NumRepos) assert.Len(t, team.Repos, team.NumRepos)
for _, repo := range team.Repos { for _, repo := range team.Repos {
AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repo.ID}) AssertExistsAndLoadBean(t, &TeamRepo{TeamID: teamID, RepoID: repo.ID})
@ -55,7 +55,7 @@ func TestTeam_GetMembers(t *testing.T) {
test := func(teamID int64) { test := func(teamID int64) {
team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team) team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
assert.NoError(t, team.GetMembers()) assert.NoError(t, team.GetMembers(&SearchMembersOptions{}))
assert.Len(t, team.Members, team.NumMembers) assert.Len(t, team.Members, team.NumMembers)
for _, member := range team.Members { for _, member := range team.Members {
AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID}) AssertExistsAndLoadBean(t, &TeamUser{UID: member.ID, TeamID: teamID})
@ -286,7 +286,7 @@ func TestGetTeamMembers(t *testing.T) {
func TestGetUserTeams(t *testing.T) { func TestGetUserTeams(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
test := func(userID int64) { test := func(userID int64) {
teams, err := GetUserTeams(userID) teams, err := GetUserTeams(userID, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
for _, team := range teams { for _, team := range teams {
AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID}) AssertExistsAndLoadBean(t, &TeamUser{TeamID: team.ID, UID: userID})

@ -86,7 +86,7 @@ func TestUser_GetOwnerTeam(t *testing.T) {
func TestUser_GetTeams(t *testing.T) { func TestUser_GetTeams(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User) org := AssertExistsAndLoadBean(t, &User{ID: 3}).(*User)
assert.NoError(t, org.GetTeams()) assert.NoError(t, org.GetTeams(&SearchTeamOptions{}))
if assert.Len(t, org.Teams, 4) { if assert.Len(t, org.Teams, 4) {
assert.Equal(t, int64(1), org.Teams[0].ID) assert.Equal(t, int64(1), org.Teams[0].ID)
assert.Equal(t, int64(2), org.Teams[1].ID) assert.Equal(t, int64(2), org.Teams[1].ID)
@ -367,7 +367,7 @@ func TestGetOwnedOrgsByUserIDDesc(t *testing.T) {
func TestGetOrgUsersByUserID(t *testing.T) { func TestGetOrgUsersByUserID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
orgUsers, err := GetOrgUsersByUserID(5, true) orgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: true})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, orgUsers, 2) { if assert.Len(t, orgUsers, 2) {
assert.Equal(t, OrgUser{ assert.Equal(t, OrgUser{
@ -382,12 +382,12 @@ func TestGetOrgUsersByUserID(t *testing.T) {
IsPublic: false}, *orgUsers[1]) IsPublic: false}, *orgUsers[1])
} }
publicOrgUsers, err := GetOrgUsersByUserID(5, false) publicOrgUsers, err := GetOrgUsersByUserID(5, &SearchOrganizationsOptions{All: false})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, publicOrgUsers, 1) assert.Len(t, publicOrgUsers, 1)
assert.Equal(t, *orgUsers[0], *publicOrgUsers[0]) assert.Equal(t, *orgUsers[0], *publicOrgUsers[0])
orgUsers, err = GetOrgUsersByUserID(1, true) orgUsers, err = GetOrgUsersByUserID(1, &SearchOrganizationsOptions{All: true})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, orgUsers, 0) assert.Len(t, orgUsers, 0)
} }
@ -395,7 +395,11 @@ func TestGetOrgUsersByUserID(t *testing.T) {
func TestGetOrgUsersByOrgID(t *testing.T) { func TestGetOrgUsersByOrgID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
orgUsers, err := GetOrgUsersByOrgID(3, false, 0, 0) orgUsers, err := GetOrgUsersByOrgID(&FindOrgMembersOpts{
ListOptions: ListOptions{},
OrgID: 3,
PublicOnly: false,
})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, orgUsers, 3) { if assert.Len(t, orgUsers, 3) {
assert.Equal(t, OrgUser{ assert.Equal(t, OrgUser{
@ -410,7 +414,11 @@ func TestGetOrgUsersByOrgID(t *testing.T) {
IsPublic: false}, *orgUsers[1]) IsPublic: false}, *orgUsers[1])
} }
orgUsers, err = GetOrgUsersByOrgID(NonexistentID, false, 0, 0) orgUsers, err = GetOrgUsersByOrgID(&FindOrgMembersOpts{
ListOptions: ListOptions{},
OrgID: NonexistentID,
PublicOnly: false,
})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, orgUsers, 0) assert.Len(t, orgUsers, 0)
} }

@ -16,7 +16,7 @@ import (
// PullRequestsOptions holds the options for PRs // PullRequestsOptions holds the options for PRs
type PullRequestsOptions struct { type PullRequestsOptions struct {
Page int ListOptions
State string State string
SortType string SortType string
Labels []string Labels []string
@ -94,14 +94,14 @@ func PullRequests(baseRepoID int64, opts *PullRequestsOptions) ([]*PullRequest,
return nil, maxResults, err return nil, maxResults, err
} }
prs := make([]*PullRequest, 0, ItemsPerPage)
findSession, err := listPullRequestStatement(baseRepoID, opts) findSession, err := listPullRequestStatement(baseRepoID, opts)
sortIssuesSession(findSession, opts.SortType, 0) sortIssuesSession(findSession, opts.SortType, 0)
if err != nil { if err != nil {
log.Error("listPullRequestStatement: %v", err) log.Error("listPullRequestStatement: %v", err)
return nil, maxResults, err return nil, maxResults, err
} }
findSession.Limit(ItemsPerPage, (opts.Page-1)*ItemsPerPage) findSession = opts.setSessionPagination(findSession)
prs := make([]*PullRequest, 0, opts.PageSize)
return prs, maxResults, findSession.Find(&prs) return prs, maxResults, findSession.Find(&prs)
} }

@ -34,7 +34,7 @@ func (pr *PullRequest) SignMerge(u *User, tmpBasePath, baseCommit, headCommit st
case always: case always:
break break
case pubkey: case pubkey:
keys, err := ListGPGKeys(u.ID) keys, err := ListGPGKeys(u.ID, ListOptions{})
if err != nil { if err != nil {
return false, "", err return false, "", err
} }

@ -55,7 +55,9 @@ func TestPullRequest_GetHeadRepo(t *testing.T) {
func TestPullRequestsNewest(t *testing.T) { func TestPullRequestsNewest(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{ prs, count, err := PullRequests(1, &PullRequestsOptions{
ListOptions: ListOptions{
Page: 1, Page: 1,
},
State: "open", State: "open",
SortType: "newest", SortType: "newest",
Labels: []string{}, Labels: []string{},
@ -72,7 +74,9 @@ func TestPullRequestsNewest(t *testing.T) {
func TestPullRequestsOldest(t *testing.T) { func TestPullRequestsOldest(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
prs, count, err := PullRequests(1, &PullRequestsOptions{ prs, count, err := PullRequests(1, &PullRequestsOptions{
ListOptions: ListOptions{
Page: 1, Page: 1,
},
State: "open", State: "open",
SortType: "oldest", SortType: "oldest",
Labels: []string{}, Labels: []string{},

@ -175,6 +175,7 @@ func GetReleaseByID(id int64) (*Release, error) {
// FindReleasesOptions describes the conditions to Find releases // FindReleasesOptions describes the conditions to Find releases
type FindReleasesOptions struct { type FindReleasesOptions struct {
ListOptions
IncludeDrafts bool IncludeDrafts bool
IncludeTags bool IncludeTags bool
TagNames []string TagNames []string
@ -197,17 +198,17 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
} }
// GetReleasesByRepoID returns a list of releases of repository. // GetReleasesByRepoID returns a list of releases of repository.
func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions, page, pageSize int) (rels []*Release, err error) { func GetReleasesByRepoID(repoID int64, opts FindReleasesOptions) ([]*Release, error) {
if page <= 0 { sess := x.
page = 1 Desc("created_unix", "id").
Where(opts.toConds(repoID))
if opts.PageSize != 0 {
sess = opts.setSessionPagination(sess)
} }
err = x. rels := make([]*Release, 0, opts.PageSize)
Desc("created_unix", "id"). return rels, sess.Find(&rels)
Limit(pageSize, (page-1)*pageSize).
Where(opts.toConds(repoID)).
Find(&rels)
return rels, err
} }
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames. // GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.

@ -1024,7 +1024,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
// Give access to all members in teams with access to all repositories. // Give access to all members in teams with access to all repositories.
if u.IsOrganization() { if u.IsOrganization() {
if err := u.GetTeams(); err != nil { if err := u.GetTeams(&SearchTeamOptions{}); err != nil {
return fmt.Errorf("GetTeams: %v", err) return fmt.Errorf("GetTeams: %v", err)
} }
for _, t := range u.Teams { for _, t := range u.Teams {
@ -1141,7 +1141,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
} }
// Remove redundant collaborators. // Remove redundant collaborators.
collaborators, err := repo.getCollaborators(sess) collaborators, err := repo.getCollaborators(sess, ListOptions{})
if err != nil { if err != nil {
return fmt.Errorf("getCollaborators: %v", err) return fmt.Errorf("getCollaborators: %v", err)
} }
@ -1171,7 +1171,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
} }
if newOwner.IsOrganization() { if newOwner.IsOrganization() {
if err := newOwner.GetTeams(); err != nil { if err := newOwner.GetTeams(&SearchTeamOptions{}); err != nil {
return fmt.Errorf("GetTeams: %v", err) return fmt.Errorf("GetTeams: %v", err)
} }
for _, t := range newOwner.Teams { for _, t := range newOwner.Teams {
@ -1422,7 +1422,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err return err
} }
if org.IsOrganization() { if org.IsOrganization() {
if err = org.GetTeams(); err != nil { if err = org.GetTeams(&SearchTeamOptions{}); err != nil {
return err return err
} }
} }
@ -1442,7 +1442,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
} }
// Delete Deploy Keys // Delete Deploy Keys
deployKeys, err := listDeployKeys(sess, repo.ID) deployKeys, err := listDeployKeys(sess, repo.ID, ListOptions{})
if err != nil { if err != nil {
return fmt.Errorf("listDeployKeys: %v", err) return fmt.Errorf("listDeployKeys: %v", err)
} }
@ -1701,25 +1701,22 @@ func GetRepositoriesMapByIDs(ids []int64) (map[int64]*Repository, error) {
} }
// GetUserRepositories returns a list of repositories of given user. // GetUserRepositories returns a list of repositories of given user.
func GetUserRepositories(userID int64, private bool, page, pageSize int, orderBy string) ([]*Repository, error) { func GetUserRepositories(opts *SearchRepoOptions) ([]*Repository, error) {
if len(orderBy) == 0 { if len(opts.OrderBy) == 0 {
orderBy = "updated_unix DESC" opts.OrderBy = "updated_unix DESC"
} }
sess := x. sess := x.
Where("owner_id = ?", userID). Where("owner_id = ?", opts.Actor.ID).
OrderBy(orderBy) OrderBy(opts.OrderBy.String())
if !private { if !opts.Private {
sess.And("is_private=?", false) sess.And("is_private=?", false)
} }
if page <= 0 { sess = opts.setSessionPagination(sess)
page = 1
}
sess.Limit(pageSize, (page-1)*pageSize)
repos := make([]*Repository, 0, pageSize) repos := make([]*Repository, 0, opts.PageSize)
return repos, sess.Find(&repos) return repos, opts.setSessionPagination(sess).Find(&repos)
} }
// GetUserMirrorRepositories returns a list of mirror repositories of given user. // GetUserMirrorRepositories returns a list of mirror repositories of given user.
@ -2029,9 +2026,15 @@ func CopyLFS(ctx DBContext, newRepo, oldRepo *Repository) error {
} }
// GetForks returns all the forks of the repository // GetForks returns all the forks of the repository
func (repo *Repository) GetForks() ([]*Repository, error) { func (repo *Repository) GetForks(listOptions ListOptions) ([]*Repository, error) {
if listOptions.Page == 0 {
forks := make([]*Repository, 0, repo.NumForks) forks := make([]*Repository, 0, repo.NumForks)
return forks, x.Find(&forks, &Repository{ForkID: repo.ID}) return forks, x.Find(&forks, &Repository{ForkID: repo.ID})
}
sess := listOptions.getPaginatedSession()
forks := make([]*Repository, 0, listOptions.PageSize)
return forks, sess.Find(&forks, &Repository{ForkID: repo.ID})
} }
// GetUserFork return user forked repository from this repository, if not forked return nil // GetUserFork return user forked repository from this repository, if not forked return nil

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -52,8 +53,15 @@ func (repo *Repository) AddCollaborator(u *User) error {
return sess.Commit() return sess.Commit()
} }
func (repo *Repository) getCollaborations(e Engine) ([]*Collaboration, error) { func (repo *Repository) getCollaborations(e Engine, listOptions ListOptions) ([]*Collaboration, error) {
var collaborations []*Collaboration if listOptions.Page == 0 {
collaborations := make([]*Collaboration, 0, 8)
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
}
e = listOptions.setEnginePagination(e)
collaborations := make([]*Collaboration, 0, listOptions.PageSize)
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID}) return collaborations, e.Find(&collaborations, &Collaboration{RepoID: repo.ID})
} }
@ -63,8 +71,8 @@ type Collaborator struct {
Collaboration *Collaboration Collaboration *Collaboration
} }
func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) { func (repo *Repository) getCollaborators(e Engine, listOptions ListOptions) ([]*Collaborator, error) {
collaborations, err := repo.getCollaborations(e) collaborations, err := repo.getCollaborations(e, listOptions)
if err != nil { if err != nil {
return nil, fmt.Errorf("getCollaborations: %v", err) return nil, fmt.Errorf("getCollaborations: %v", err)
} }
@ -84,8 +92,8 @@ func (repo *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
} }
// GetCollaborators returns the collaborators for a repository // GetCollaborators returns the collaborators for a repository
func (repo *Repository) GetCollaborators() ([]*Collaborator, error) { func (repo *Repository) GetCollaborators(listOptions ListOptions) ([]*Collaborator, error) {
return repo.getCollaborators(x) return repo.getCollaborators(x, listOptions)
} }
func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) { func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) {

@ -29,7 +29,7 @@ func TestRepository_GetCollaborators(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
test := func(repoID int64) { test := func(repoID int64) {
repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: repoID}).(*Repository)
collaborators, err := repo.GetCollaborators() collaborators, err := repo.GetCollaborators(ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
expectedLen, err := x.Count(&Collaboration{RepoID: repoID}) expectedLen, err := x.Count(&Collaboration{RepoID: repoID})
assert.NoError(t, err) assert.NoError(t, err)

@ -110,7 +110,7 @@ func GenerateGitHooks(ctx DBContext, templateRepo, generateRepo *Repository) err
// GenerateWebhooks generates webhooks from a template repository // GenerateWebhooks generates webhooks from a template repository
func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error { func GenerateWebhooks(ctx DBContext, templateRepo, generateRepo *Repository) error {
templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID) templateWebhooks, err := GetWebhooksByRepoID(templateRepo.ID, ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -148,7 +148,7 @@ func GenerateAvatar(ctx DBContext, templateRepo, generateRepo *Repository) error
// GenerateIssueLabels generates issue labels from a template repository // GenerateIssueLabels generates issue labels from a template repository
func GenerateIssueLabels(ctx DBContext, templateRepo, generateRepo *Repository) error { func GenerateIssueLabels(ctx DBContext, templateRepo, generateRepo *Repository) error {
templateLabels, err := getLabelsByRepoID(ctx.e, templateRepo.ID, "") templateLabels, err := getLabelsByRepoID(ctx.e, templateRepo.ID, "", ListOptions{})
if err != nil { if err != nil {
return err return err
} }

@ -111,6 +111,7 @@ func (repos MirrorRepositoryList) LoadAttributes() error {
// SearchRepoOptions holds the search options // SearchRepoOptions holds the search options
type SearchRepoOptions struct { type SearchRepoOptions struct {
ListOptions
Actor *User Actor *User
Keyword string Keyword string
OwnerID int64 OwnerID int64
@ -118,11 +119,9 @@ type SearchRepoOptions struct {
OrderBy SearchOrderBy OrderBy SearchOrderBy
Private bool // Include private repositories in results Private bool // Include private repositories in results
StarredByID int64 StarredByID int64
Page int
IsProfile bool IsProfile bool
AllPublic bool // Include also all public repositories of users and public organisations AllPublic bool // Include also all public repositories of users and public organisations
AllLimited bool // Include also all public repositories of limited organisations AllLimited bool // Include also all public repositories of limited organisations
PageSize int // Can be smaller than or equal to setting.ExplorePagingNum
// None -> include collaborative AND non-collaborative // None -> include collaborative AND non-collaborative
// True -> include just collaborative // True -> include just collaborative
// False -> incude just non-collaborative // False -> incude just non-collaborative

@ -17,9 +17,11 @@ func TestSearchRepository(t *testing.T) {
// test search public repository on explore page // test search public repository on explore page
repos, count, err := SearchRepositoryByName(&SearchRepoOptions{ repos, count, err := SearchRepositoryByName(&SearchRepoOptions{
Keyword: "repo_12", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "repo_12",
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
}) })
@ -30,9 +32,11 @@ func TestSearchRepository(t *testing.T) {
assert.Equal(t, int64(1), count) assert.Equal(t, int64(1), count)
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
Keyword: "test_repo", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "test_repo",
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
}) })
@ -42,9 +46,11 @@ func TestSearchRepository(t *testing.T) {
// test search private repository on explore page // test search private repository on explore page
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
Keyword: "repo_13", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "repo_13",
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
}) })
@ -56,9 +62,11 @@ func TestSearchRepository(t *testing.T) {
assert.Equal(t, int64(1), count) assert.Equal(t, int64(1), count)
repos, count, err = SearchRepositoryByName(&SearchRepoOptions{ repos, count, err = SearchRepositoryByName(&SearchRepoOptions{
Keyword: "test_repo", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "test_repo",
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
}) })
@ -76,9 +84,11 @@ func TestSearchRepository(t *testing.T) {
// Test search within description // Test search within description
repos, count, err = SearchRepository(&SearchRepoOptions{ repos, count, err = SearchRepository(&SearchRepoOptions{
Keyword: "description_14", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
IncludeDescription: true, IncludeDescription: true,
}) })
@ -91,9 +101,11 @@ func TestSearchRepository(t *testing.T) {
// Test NOT search within description // Test NOT search within description
repos, count, err = SearchRepository(&SearchRepoOptions{ repos, count, err = SearchRepository(&SearchRepoOptions{
Keyword: "description_14", ListOptions: ListOptions{
Page: 1, Page: 1,
PageSize: 10, PageSize: 10,
},
Keyword: "description_14",
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,
IncludeDescription: false, IncludeDescription: false,
}) })
@ -108,88 +120,88 @@ func TestSearchRepository(t *testing.T) {
count int count int
}{ }{
{name: "PublicRepositoriesByName", {name: "PublicRepositoriesByName",
opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{PageSize: 10}, Collaborate: util.OptionalBoolFalse},
count: 7}, count: 7},
{name: "PublicAndPrivateRepositoriesByName", {name: "PublicAndPrivateRepositoriesByName",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 10}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage", {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFirstPage",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage", {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitSecondPage",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 2, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 2, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage", {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitThirdPage",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage", {name: "PublicAndPrivateRepositoriesByNameWithPagesizeLimitFourthPage",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 3, PageSize: 5, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 3, PageSize: 5}, Private: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "PublicRepositoriesOfUser", {name: "PublicRepositoriesOfUser",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Collaborate: util.OptionalBoolFalse},
count: 2}, count: 2},
{name: "PublicRepositoriesOfUser2", {name: "PublicRepositoriesOfUser2",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Collaborate: util.OptionalBoolFalse},
count: 0}, count: 0},
{name: "PublicRepositoriesOfUser3", {name: "PublicRepositoriesOfUser3",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Collaborate: util.OptionalBoolFalse},
count: 2}, count: 2},
{name: "PublicAndPrivateRepositoriesOfUser", {name: "PublicAndPrivateRepositoriesOfUser",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, Collaborate: util.OptionalBoolFalse},
count: 4}, count: 4},
{name: "PublicAndPrivateRepositoriesOfUser2", {name: "PublicAndPrivateRepositoriesOfUser2",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, Collaborate: util.OptionalBoolFalse},
count: 0}, count: 0},
{name: "PublicAndPrivateRepositoriesOfUser3", {name: "PublicAndPrivateRepositoriesOfUser3",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true, Collaborate: util.OptionalBoolFalse},
count: 4}, count: 4},
{name: "PublicRepositoriesOfUserIncludingCollaborative", {name: "PublicRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15},
count: 5}, count: 5},
{name: "PublicRepositoriesOfUser2IncludingCollaborative", {name: "PublicRepositoriesOfUser2IncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18},
count: 1}, count: 1},
{name: "PublicRepositoriesOfUser3IncludingCollaborative", {name: "PublicRepositoriesOfUser3IncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20},
count: 3}, count: 3},
{name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative", {name: "PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true},
count: 9}, count: 9},
{name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative", {name: "PublicAndPrivateRepositoriesOfUser2IncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 18, Private: true}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true},
count: 4}, count: 4},
{name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative", {name: "PublicAndPrivateRepositoriesOfUser3IncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 20, Private: true}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 20, Private: true},
count: 7}, count: 7},
{name: "PublicRepositoriesOfOrganization", {name: "PublicRepositoriesOfOrganization",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Collaborate: util.OptionalBoolFalse},
count: 1}, count: 1},
{name: "PublicAndPrivateRepositoriesOfOrganization", {name: "PublicAndPrivateRepositoriesOfOrganization",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, Private: true, Collaborate: util.OptionalBoolFalse},
count: 2}, count: 2},
{name: "AllPublic/PublicRepositoriesByName", {name: "AllPublic/PublicRepositoriesByName",
opts: &SearchRepoOptions{Keyword: "big_test_", PageSize: 10, AllPublic: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{PageSize: 10}, AllPublic: true, Collaborate: util.OptionalBoolFalse},
count: 7}, count: 7},
{name: "AllPublic/PublicAndPrivateRepositoriesByName", {name: "AllPublic/PublicAndPrivateRepositoriesByName",
opts: &SearchRepoOptions{Keyword: "big_test_", Page: 1, PageSize: 10, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse}, opts: &SearchRepoOptions{Keyword: "big_test_", ListOptions: ListOptions{Page: 1, PageSize: 10}, Private: true, AllPublic: true, Collaborate: util.OptionalBoolFalse},
count: 14}, count: 14},
{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", {name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
count: 25}, count: 25},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
count: 30}, count: 30},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", {name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Keyword: "test", ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true},
count: 15}, count: 15},
{name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName", {name: "AllPublic/PublicAndPrivateRepositoriesOfUser2IncludingCollaborativeByName",
opts: &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 18, Private: true, AllPublic: true}, opts: &SearchRepoOptions{Keyword: "test", ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 18, Private: true, AllPublic: true},
count: 13}, count: 13},
{name: "AllPublic/PublicRepositoriesOfOrganization", {name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
count: 25}, count: 25},
{name: "AllTemplates", {name: "AllTemplates",
opts: &SearchRepoOptions{Page: 1, PageSize: 10, Template: util.OptionalBoolTrue}, opts: &SearchRepoOptions{ListOptions: ListOptions{Page: 1, PageSize: 10}, Template: util.OptionalBoolTrue},
count: 2}, count: 2},
} }

@ -110,7 +110,7 @@ func SignInitialCommit(repoPath string, u *User) (bool, string, error) {
case always: case always:
break break
case pubkey: case pubkey:
keys, err := ListGPGKeys(u.ID) keys, err := ListGPGKeys(u.ID, ListOptions{})
if err != nil { if err != nil {
return false, "", err return false, "", err
} }
@ -145,7 +145,7 @@ func (repo *Repository) SignWikiCommit(u *User) (bool, string, error) {
case always: case always:
break break
case pubkey: case pubkey:
keys, err := ListGPGKeys(u.ID) keys, err := ListGPGKeys(u.ID, ListOptions{})
if err != nil { if err != nil {
return false, "", err return false, "", err
} }
@ -197,7 +197,7 @@ func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string
case always: case always:
break break
case pubkey: case pubkey:
keys, err := ListGPGKeys(u.ID) keys, err := ListGPGKeys(u.ID, ListOptions{})
if err != nil { if err != nil {
return false, "", err return false, "", err
} }

@ -153,14 +153,18 @@ func GetRepoWatchersIDs(repoID int64) ([]int64, error) {
} }
// GetWatchers returns range of users watching given repository. // GetWatchers returns range of users watching given repository.
func (repo *Repository) GetWatchers(page int) ([]*User, error) { func (repo *Repository) GetWatchers(opts ListOptions) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage)
sess := x.Where("watch.repo_id=?", repo.ID). sess := x.Where("watch.repo_id=?", repo.ID).
Join("LEFT", "watch", "`user`.id=`watch`.user_id"). Join("LEFT", "watch", "`user`.id=`watch`.user_id").
And("`watch`.mode<>?", RepoWatchModeDont) And("`watch`.mode<>?", RepoWatchModeDont)
if page > 0 { if opts.Page > 0 {
sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) sess = opts.setSessionPagination(sess)
users := make([]*User, 0, opts.PageSize)
return users, sess.Find(&users)
} }
users := make([]*User, 0, 8)
return users, sess.Find(&users) return users, sess.Find(&users)
} }

@ -59,7 +59,7 @@ func TestRepository_GetWatchers(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
watchers, err := repo.GetWatchers(1) watchers, err := repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches) assert.Len(t, watchers, repo.NumWatches)
for _, watcher := range watchers { for _, watcher := range watchers {
@ -67,7 +67,7 @@ func TestRepository_GetWatchers(t *testing.T) {
} }
repo = AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository) repo = AssertExistsAndLoadBean(t, &Repository{ID: 9}).(*Repository)
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, 0) assert.Len(t, watchers, 0)
} }
@ -113,7 +113,7 @@ func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
watchers, err := repo.GetWatchers(1) watchers, err := repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches) assert.Len(t, watchers, repo.NumWatches)
@ -123,13 +123,13 @@ func TestWatchIfAuto(t *testing.T) {
// Must not add watch // Must not add watch
assert.NoError(t, WatchIfAuto(8, 1, true)) assert.NoError(t, WatchIfAuto(8, 1, true))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
// Should not add watch // Should not add watch
assert.NoError(t, WatchIfAuto(10, 1, true)) assert.NoError(t, WatchIfAuto(10, 1, true))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
@ -137,31 +137,31 @@ func TestWatchIfAuto(t *testing.T) {
// Must not add watch // Must not add watch
assert.NoError(t, WatchIfAuto(8, 1, true)) assert.NoError(t, WatchIfAuto(8, 1, true))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
// Should not add watch // Should not add watch
assert.NoError(t, WatchIfAuto(12, 1, false)) assert.NoError(t, WatchIfAuto(12, 1, false))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
// Should add watch // Should add watch
assert.NoError(t, WatchIfAuto(12, 1, true)) assert.NoError(t, WatchIfAuto(12, 1, true))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount+1) assert.Len(t, watchers, prevCount+1)
// Should remove watch, inhibit from adding auto // Should remove watch, inhibit from adding auto
assert.NoError(t, WatchRepo(12, 1, false)) assert.NoError(t, WatchRepo(12, 1, false))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
// Must not add watch // Must not add watch
assert.NoError(t, WatchIfAuto(12, 1, true)) assert.NoError(t, WatchIfAuto(12, 1, true))
watchers, err = repo.GetWatchers(1) watchers, err = repo.GetWatchers(ListOptions{Page: 1})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, watchers, prevCount) assert.Len(t, watchers, prevCount)
} }

@ -567,11 +567,17 @@ func SearchPublicKey(uid int64, fingerprint string) ([]*PublicKey, error) {
} }
// ListPublicKeys returns a list of public keys belongs to given user. // ListPublicKeys returns a list of public keys belongs to given user.
func ListPublicKeys(uid int64) ([]*PublicKey, error) { func ListPublicKeys(uid int64, listOptions ListOptions) ([]*PublicKey, error) {
sess := x.Where("owner_id = ?", uid)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
keys := make([]*PublicKey, 0, listOptions.PageSize)
return keys, sess.Find(&keys)
}
keys := make([]*PublicKey, 0, 5) keys := make([]*PublicKey, 0, 5)
return keys, x. return keys, sess.Find(&keys)
Where("owner_id = ?", uid).
Find(&keys)
} }
// ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source. // ListPublicLdapSSHKeys returns a list of synchronized public ldap ssh keys belongs to given user and login source.
@ -970,15 +976,21 @@ func deleteDeployKey(sess Engine, doer *User, id int64) error {
} }
// ListDeployKeys returns all deploy keys by given repository ID. // ListDeployKeys returns all deploy keys by given repository ID.
func ListDeployKeys(repoID int64) ([]*DeployKey, error) { func ListDeployKeys(repoID int64, listOptions ListOptions) ([]*DeployKey, error) {
return listDeployKeys(x, repoID) return listDeployKeys(x, repoID, listOptions)
} }
func listDeployKeys(e Engine, repoID int64) ([]*DeployKey, error) { func listDeployKeys(e Engine, repoID int64, listOptions ListOptions) ([]*DeployKey, error) {
sess := e.Where("repo_id = ?", repoID)
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
keys := make([]*DeployKey, 0, listOptions.PageSize)
return keys, sess.Find(&keys)
}
keys := make([]*DeployKey, 0, 5) keys := make([]*DeployKey, 0, 5)
return keys, e. return keys, sess.Find(&keys)
Where("repo_id = ?", repoID).
Find(&keys)
} }
// SearchDeployKeys returns a list of deploy keys matching the provided arguments. // SearchDeployKeys returns a list of deploy keys matching the provided arguments.

@ -64,13 +64,17 @@ func isStaring(e Engine, userID, repoID int64) bool {
} }
// GetStargazers returns the users that starred the repo. // GetStargazers returns the users that starred the repo.
func (repo *Repository) GetStargazers(page int) ([]*User, error) { func (repo *Repository) GetStargazers(opts ListOptions) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage)
sess := x.Where("star.repo_id = ?", repo.ID). sess := x.Where("star.repo_id = ?", repo.ID).
Join("LEFT", "star", "`user`.id = star.uid") Join("LEFT", "star", "`user`.id = star.uid")
if page > 0 { if opts.Page > 0 {
sess = sess.Limit(ItemsPerPage, (page-1)*ItemsPerPage) sess = opts.setSessionPagination(sess)
users := make([]*User, 0, opts.PageSize)
return users, sess.Find(&users)
} }
users := make([]*User, 0, 8)
return users, sess.Find(&users) return users, sess.Find(&users)
} }

@ -33,7 +33,7 @@ func TestRepository_GetStargazers(t *testing.T) {
// repo with stargazers // repo with stargazers
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: 4}).(*Repository)
gazers, err := repo.GetStargazers(0) gazers, err := repo.GetStargazers(ListOptions{Page: 0})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, gazers, 1) { if assert.Len(t, gazers, 1) {
assert.Equal(t, int64(2), gazers[0].ID) assert.Equal(t, int64(2), gazers[0].ID)
@ -44,7 +44,7 @@ func TestRepository_GetStargazers2(t *testing.T) {
// repo with stargazers // repo with stargazers
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) repo := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository)
gazers, err := repo.GetStargazers(0) gazers, err := repo.GetStargazers(ListOptions{Page: 0})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, gazers, 0) assert.Len(t, gazers, 0)
} }

@ -78,12 +78,20 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
} }
// ListAccessTokens returns a list of access tokens belongs to given user. // ListAccessTokens returns a list of access tokens belongs to given user.
func ListAccessTokens(uid int64) ([]*AccessToken, error) { func ListAccessTokens(uid int64, listOptions ListOptions) ([]*AccessToken, error) {
tokens := make([]*AccessToken, 0, 5) sess := x.
return tokens, x.
Where("uid=?", uid). Where("uid=?", uid).
Desc("id"). Desc("id")
Find(&tokens)
if listOptions.Page == 0 {
sess = listOptions.setSessionPagination(sess)
tokens := make([]*AccessToken, 0, listOptions.PageSize)
return tokens, sess.Find(&tokens)
}
tokens := make([]*AccessToken, 0, 5)
return tokens, sess.Find(&tokens)
} }
// UpdateAccessToken updates information of access token. // UpdateAccessToken updates information of access token.

@ -47,7 +47,7 @@ func TestGetAccessTokenBySHA(t *testing.T) {
func TestListAccessTokens(t *testing.T) { func TestListAccessTokens(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
tokens, err := ListAccessTokens(1) tokens, err := ListAccessTokens(1, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, tokens, 2) { if assert.Len(t, tokens, 2) {
assert.Equal(t, int64(1), tokens[0].UID) assert.Equal(t, int64(1), tokens[0].UID)
@ -56,14 +56,14 @@ func TestListAccessTokens(t *testing.T) {
assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B") assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
} }
tokens, err = ListAccessTokens(2) tokens, err = ListAccessTokens(2, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, tokens, 1) { if assert.Len(t, tokens, 1) {
assert.Equal(t, int64(2), tokens[0].UID) assert.Equal(t, int64(2), tokens[0].UID)
assert.Equal(t, "Token A", tokens[0].Name) assert.Equal(t, "Token A", tokens[0].Name)
} }
tokens, err = ListAccessTokens(100) tokens, err = ListAccessTokens(100, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.Empty(t, tokens) assert.Empty(t, tokens)
} }

@ -147,10 +147,9 @@ func removeTopicFromRepo(repoID int64, topic *Topic, e Engine) error {
// FindTopicOptions represents the options when fdin topics // FindTopicOptions represents the options when fdin topics
type FindTopicOptions struct { type FindTopicOptions struct {
ListOptions
RepoID int64 RepoID int64
Keyword string Keyword string
Limit int
Page int
} }
func (opts *FindTopicOptions) toConds() builder.Cond { func (opts *FindTopicOptions) toConds() builder.Cond {
@ -172,8 +171,8 @@ func FindTopics(opts *FindTopicOptions) (topics []*Topic, err error) {
if opts.RepoID > 0 { if opts.RepoID > 0 {
sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id") sess.Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id")
} }
if opts.Limit > 0 { if opts.PageSize != 0 && opts.Page != 0 {
sess.Limit(opts.Limit, opts.Page*opts.Limit) sess = opts.setSessionPagination(sess)
} }
return topics, sess.Desc("topic.repo_count").Find(&topics) return topics, sess.Desc("topic.repo_count").Find(&topics)
} }

@ -22,7 +22,7 @@ func TestAddTopic(t *testing.T) {
assert.EqualValues(t, totalNrOfTopics, len(topics)) assert.EqualValues(t, totalNrOfTopics, len(topics))
topics, err = FindTopics(&FindTopicOptions{ topics, err = FindTopics(&FindTopicOptions{
Limit: 2, ListOptions: ListOptions{Page: 1, PageSize: 2},
}) })
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 2, len(topics)) assert.EqualValues(t, 2, len(topics))

@ -163,6 +163,12 @@ type User struct {
Theme string `xorm:"NOT NULL DEFAULT ''"` Theme string `xorm:"NOT NULL DEFAULT ''"`
} }
// SearchOrganizationsOptions options to filter organizations
type SearchOrganizationsOptions struct {
ListOptions
All bool
}
// ColorFormat writes a colored string to identify this struct // ColorFormat writes a colored string to identify this struct
func (u *User) ColorFormat(s fmt.State) { func (u *User) ColorFormat(s fmt.State) {
log.ColorFprintf(s, "%d:%s", log.ColorFprintf(s, "%d:%s",
@ -430,12 +436,19 @@ func (u *User) AvatarLink() string {
} }
// GetFollowers returns range of user's followers. // GetFollowers returns range of user's followers.
func (u *User) GetFollowers(page int) ([]*User, error) { func (u *User) GetFollowers(listOptions ListOptions) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage)
sess := x. sess := x.
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
Where("follow.follow_id=?", u.ID). Where("follow.follow_id=?", u.ID).
Join("LEFT", "follow", "`user`.id=follow.user_id") Join("LEFT", "follow", "`user`.id=follow.user_id")
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
users := make([]*User, 0, listOptions.PageSize)
return users, sess.Find(&users)
}
users := make([]*User, 0, 8)
return users, sess.Find(&users) return users, sess.Find(&users)
} }
@ -445,12 +458,19 @@ func (u *User) IsFollowing(followID int64) bool {
} }
// GetFollowing returns range of user's following. // GetFollowing returns range of user's following.
func (u *User) GetFollowing(page int) ([]*User, error) { func (u *User) GetFollowing(listOptions ListOptions) ([]*User, error) {
users := make([]*User, 0, ItemsPerPage)
sess := x. sess := x.
Limit(ItemsPerPage, (page-1)*ItemsPerPage).
Where("follow.user_id=?", u.ID). Where("follow.user_id=?", u.ID).
Join("LEFT", "follow", "`user`.id=follow.follow_id") Join("LEFT", "follow", "`user`.id=follow.follow_id")
if listOptions.Page != 0 {
sess = listOptions.setSessionPagination(sess)
users := make([]*User, 0, listOptions.PageSize)
return users, sess.Find(&users)
}
users := make([]*User, 0, 8)
return users, sess.Find(&users) return users, sess.Find(&users)
} }
@ -616,8 +636,8 @@ func (u *User) GetOrganizationCount() (int64, error) {
} }
// GetRepositories returns repositories that user owns, including private repositories. // GetRepositories returns repositories that user owns, including private repositories.
func (u *User) GetRepositories(page, pageSize int) (err error) { func (u *User) GetRepositories(listOpts ListOptions) (err error) {
u.Repos, err = GetUserRepositories(u.ID, true, page, pageSize, "") u.Repos, err = GetUserRepositories(&SearchRepoOptions{Actor: u, Private: true, ListOptions: listOpts})
return err return err
} }
@ -682,9 +702,9 @@ func (u *User) GetOwnedOrganizations() (err error) {
return err return err
} }
// GetOrganizations returns all organizations that user belongs to. // GetOrganizations returns paginated organizations that user belongs to.
func (u *User) GetOrganizations(all bool) error { func (u *User) GetOrganizations(opts *SearchOrganizationsOptions) error {
ous, err := GetOrgUsersByUserID(u.ID, all) ous, err := GetOrgUsersByUserID(u.ID, opts)
if err != nil { if err != nil {
return err return err
} }
@ -1477,14 +1497,13 @@ func GetUser(user *User) (bool, error) {
// SearchUserOptions contains the options for searching // SearchUserOptions contains the options for searching
type SearchUserOptions struct { type SearchUserOptions struct {
ListOptions
Keyword string Keyword string
Type UserType Type UserType
UID int64 UID int64
OrderBy SearchOrderBy OrderBy SearchOrderBy
Page int
Visible []structs.VisibleType Visible []structs.VisibleType
Actor *User // The user doing the search Actor *User // The user doing the search
PageSize int // Can be smaller than or equal to setting.UI.ExplorePagingNum
IsActive util.OptionalBool IsActive util.OptionalBool
SearchByEmail bool // Search by email as well as username/full name SearchByEmail bool // Search by email as well as username/full name
} }
@ -1552,57 +1571,56 @@ func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
return nil, 0, fmt.Errorf("Count: %v", err) return nil, 0, fmt.Errorf("Count: %v", err)
} }
if opts.PageSize == 0 || opts.PageSize > setting.UI.ExplorePagingNum {
opts.PageSize = setting.UI.ExplorePagingNum
}
if opts.Page <= 0 {
opts.Page = 1
}
if len(opts.OrderBy) == 0 { if len(opts.OrderBy) == 0 {
opts.OrderBy = SearchOrderByAlphabetically opts.OrderBy = SearchOrderByAlphabetically
} }
sess := x.Where(cond) sess := x.Where(cond).OrderBy(opts.OrderBy.String())
if opts.PageSize > 0 { if opts.Page != 0 {
sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) sess = opts.setSessionPagination(sess)
}
if opts.PageSize == -1 {
opts.PageSize = int(count)
} }
users = make([]*User, 0, opts.PageSize) users = make([]*User, 0, opts.PageSize)
return users, count, sess.OrderBy(opts.OrderBy.String()).Find(&users) return users, count, sess.Find(&users)
} }
// GetStarredRepos returns the repos starred by a particular user // GetStarredRepos returns the repos starred by a particular user
func GetStarredRepos(userID int64, private bool) ([]*Repository, error) { func GetStarredRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, error) {
sess := x.Where("star.uid=?", userID). sess := x.Where("star.uid=?", userID).
Join("LEFT", "star", "`repository`.id=`star`.repo_id") Join("LEFT", "star", "`repository`.id=`star`.repo_id")
if !private { if !private {
sess = sess.And("is_private=?", false) sess = sess.And("is_private=?", false)
} }
repos := make([]*Repository, 0, 10)
err := sess.Find(&repos) if listOptions.Page != 0 {
if err != nil { sess = listOptions.setSessionPagination(sess)
return nil, err
repos := make([]*Repository, 0, listOptions.PageSize)
return repos, sess.Find(&repos)
} }
return repos, nil
repos := make([]*Repository, 0, 10)
return repos, sess.Find(&repos)
} }
// GetWatchedRepos returns the repos watched by a particular user // GetWatchedRepos returns the repos watched by a particular user
func GetWatchedRepos(userID int64, private bool) ([]*Repository, error) { func GetWatchedRepos(userID int64, private bool, listOptions ListOptions) ([]*Repository, error) {
sess := x.Where("watch.user_id=?", userID). sess := x.Where("watch.user_id=?", userID).
And("`watch`.mode<>?", RepoWatchModeDont). And("`watch`.mode<>?", RepoWatchModeDont).
Join("LEFT", "watch", "`repository`.id=`watch`.repo_id") Join("LEFT", "watch", "`repository`.id=`watch`.repo_id")
if !private { if !private {
sess = sess.And("is_private=?", false) sess = sess.And("is_private=?", false)
} }
repos := make([]*Repository, 0, 10)
err := sess.Find(&repos) if listOptions.Page != 0 {
if err != nil { sess = listOptions.setSessionPagination(sess)
return nil, err
repos := make([]*Repository, 0, listOptions.PageSize)
return repos, sess.Find(&repos)
} }
return repos, nil
repos := make([]*Repository, 0, 10)
return repos, sess.Find(&repos)
} }
// deleteKeysMarkedForDeletion returns true if ssh keys needs update // deleteKeysMarkedForDeletion returns true if ssh keys needs update

@ -131,19 +131,19 @@ func TestSearchUsers(t *testing.T) {
testSuccess(opts, expectedOrgIDs) testSuccess(opts, expectedOrgIDs)
} }
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, PageSize: 2}, testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1, PageSize: 2}},
[]int64{3, 6}) []int64{3, 6})
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 2, PageSize: 2}, testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 2, PageSize: 2}},
[]int64{7, 17}) []int64{7, 17})
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 3, PageSize: 2}, testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 3, PageSize: 2}},
[]int64{19, 25}) []int64{19, 25})
testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 4, PageSize: 2}, testOrgSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 4, PageSize: 2}},
[]int64{26}) []int64{26})
testOrgSuccess(&SearchUserOptions{Page: 5, PageSize: 2}, testOrgSuccess(&SearchUserOptions{ListOptions: ListOptions{Page: 5, PageSize: 2}},
[]int64{}) []int64{})
// test users // test users
@ -152,20 +152,20 @@ func TestSearchUsers(t *testing.T) {
testSuccess(opts, expectedUserIDs) testSuccess(opts, expectedUserIDs)
} }
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1}, testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}},
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29}) []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29})
testUserSuccess(&SearchUserOptions{Page: 1, IsActive: util.OptionalBoolFalse}, testUserSuccess(&SearchUserOptions{ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolFalse},
[]int64{9}) []int64{9})
testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, testUserSuccess(&SearchUserOptions{OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29}) []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29})
testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", Page: 1, IsActive: util.OptionalBoolTrue}, testUserSuccess(&SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
// order by name asc default // order by name asc default
testUserSuccess(&SearchUserOptions{Keyword: "user1", Page: 1, IsActive: util.OptionalBoolTrue}, testUserSuccess(&SearchUserOptions{Keyword: "user1", ListOptions: ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) []int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
} }

@ -97,7 +97,7 @@ func (users UserList) loadTwoFactorStatus(e Engine) (map[int64]*TwoFactor, error
//APIFormat return list of users in api format //APIFormat return list of users in api format
func (users UserList) APIFormat() []*api.User { func (users UserList) APIFormat() []*api.User {
var result []*api.User result := make([]*api.User, 0, len(users))
for _, u := range users { for _, u := range users {
result = append(result, u.APIFormat()) result = append(result, u.APIFormat())
} }

@ -274,9 +274,16 @@ func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
} }
// GetWebhooksByRepoID returns all webhooks of a repository. // GetWebhooksByRepoID returns all webhooks of a repository.
func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) { func GetWebhooksByRepoID(repoID int64, listOptions ListOptions) ([]*Webhook, error) {
if listOptions.Page == 0 {
webhooks := make([]*Webhook, 0, 5) webhooks := make([]*Webhook, 0, 5)
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID}) return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
}
sess := listOptions.getPaginatedSession()
webhooks := make([]*Webhook, 0, listOptions.PageSize)
return webhooks, sess.Find(&webhooks, &Webhook{RepoID: repoID})
} }
// GetActiveWebhooksByOrgID returns all active webhooks for an organization. // GetActiveWebhooksByOrgID returns all active webhooks for an organization.
@ -292,10 +299,16 @@ func getActiveWebhooksByOrgID(e Engine, orgID int64) (ws []*Webhook, err error)
return ws, err return ws, err
} }
// GetWebhooksByOrgID returns all webhooks for an organization. // GetWebhooksByOrgID returns paginated webhooks for an organization.
func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) { func GetWebhooksByOrgID(orgID int64, listOptions ListOptions) ([]*Webhook, error) {
err = x.Find(&ws, &Webhook{OrgID: orgID}) if listOptions.Page == 0 {
return ws, err ws := make([]*Webhook, 0, 5)
return ws, x.Find(&ws, &Webhook{OrgID: orgID})
}
sess := listOptions.getPaginatedSession()
ws := make([]*Webhook, 0, listOptions.PageSize)
return ws, sess.Find(&ws, &Webhook{OrgID: orgID})
} }
// GetDefaultWebhook returns admin-default webhook by given ID. // GetDefaultWebhook returns admin-default webhook by given ID.

@ -120,7 +120,7 @@ func TestGetActiveWebhooksByRepoID(t *testing.T) {
func TestGetWebhooksByRepoID(t *testing.T) { func TestGetWebhooksByRepoID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetWebhooksByRepoID(1) hooks, err := GetWebhooksByRepoID(1, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 2) { if assert.Len(t, hooks, 2) {
assert.Equal(t, int64(1), hooks[0].ID) assert.Equal(t, int64(1), hooks[0].ID)
@ -140,7 +140,7 @@ func TestGetActiveWebhooksByOrgID(t *testing.T) {
func TestGetWebhooksByOrgID(t *testing.T) { func TestGetWebhooksByOrgID(t *testing.T) {
assert.NoError(t, PrepareTestDatabase()) assert.NoError(t, PrepareTestDatabase())
hooks, err := GetWebhooksByOrgID(3) hooks, err := GetWebhooksByOrgID(3, ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, hooks, 1) { if assert.Len(t, hooks, 1) {
assert.Equal(t, int64(3), hooks[0].ID) assert.Equal(t, int64(3), hooks[0].ID)

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -118,7 +119,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
// Team. // Team.
if ctx.Org.IsMember { if ctx.Org.IsMember {
if ctx.Org.IsOwner { if ctx.Org.IsOwner {
if err := org.GetTeams(); err != nil { if err := org.GetTeams(&models.SearchTeamOptions{}); err != nil {
ctx.ServerError("GetTeams", err) ctx.ServerError("GetTeams", err)
return return
} }

@ -297,8 +297,8 @@ func (c *Commit) CommitsCount() (int64, error) {
} }
// CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize // CommitsByRange returns the specific page commits before current revision, every page's number default by CommitsRangeSize
func (c *Commit) CommitsByRange(page int) (*list.List, error) { func (c *Commit) CommitsByRange(page, pageSize int) (*list.List, error) {
return c.repo.commitsByRange(c.ID, page) return c.repo.commitsByRange(c.ID, page, pageSize)
} }
// CommitsBefore returns all the commits before current revision // CommitsBefore returns all the commits before current revision

@ -198,9 +198,10 @@ func (repo *Repository) GetCommitByPath(relpath string) (*Commit, error) {
// CommitsRangeSize the default commits range size // CommitsRangeSize the default commits range size
var CommitsRangeSize = 50 var CommitsRangeSize = 50
func (repo *Repository) commitsByRange(id SHA1, page int) (*list.List, error) { func (repo *Repository) commitsByRange(id SHA1, page, pageSize int) (*list.List, error) {
stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*CommitsRangeSize), stdout, err := NewCommand("log", id.String(), "--skip="+strconv.Itoa((page-1)*pageSize),
"--max-count="+strconv.Itoa(CommitsRangeSize), prettyLogFormat).RunInDirBytes(repo.Path) "--max-count="+strconv.Itoa(pageSize), prettyLogFormat).RunInDirBytes(repo.Path)
if err != nil { if err != nil {
return nil, err return nil, err
} }

@ -221,8 +221,7 @@ func populateIssueIndexer(ctx context.Context) {
default: default:
} }
repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{ repos, _, err := models.SearchRepositoryByName(&models.SearchRepoOptions{
Page: page, ListOptions: models.ListOptions{Page: page, PageSize: models.RepositoryListDefaultPageSize},
PageSize: models.RepositoryListDefaultPageSize,
OrderBy: models.SearchOrderByID, OrderBy: models.SearchOrderByID,
Private: true, Private: true,
Collaborate: util.OptionalBoolFalse, Collaborate: util.OptionalBoolFalse,

@ -59,19 +59,27 @@ func TestGiteaUploadRepo(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 0, len(milestones)) assert.EqualValues(t, 0, len(milestones))
labels, err := models.GetLabelsByRepoID(repo.ID, "") labels, err := models.GetLabelsByRepoID(repo.ID, "", models.ListOptions{})
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 11, len(labels)) assert.EqualValues(t, 11, len(labels))
releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
ListOptions: models.ListOptions{
PageSize: 10,
Page: 0,
},
IncludeTags: true, IncludeTags: true,
}, 0, 10) })
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 8, len(releases)) assert.EqualValues(t, 8, len(releases))
releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{ releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
ListOptions: models.ListOptions{
PageSize: 10,
Page: 0,
},
IncludeTags: false, IncludeTags: false,
}, 0, 10) })
assert.NoError(t, err) assert.NoError(t, err)
assert.EqualValues(t, 1, len(releases)) assert.EqualValues(t, 1, len(releases))

@ -19,7 +19,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
testTeamRepositories := func(teamID int64, repoIds []int64) { testTeamRepositories := func(teamID int64, repoIds []int64) {
team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team) team := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name) assert.NoError(t, team.GetRepositories(&models.SearchTeamOptions{}), "%s: GetRepositories", team.Name)
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name) assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name) assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name)
for i, rid := range repoIds { for i, rid := range repoIds {

@ -183,9 +183,10 @@ func CleanUpMigrateInfo(repo *models.Repository) (*models.Repository, error) {
// SyncReleasesWithTags synchronizes release table with repository tags // SyncReleasesWithTags synchronizes release table with repository tags
func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) error { func SyncReleasesWithTags(repo *models.Repository, gitRepo *git.Repository) error {
existingRelTags := make(map[string]struct{}) existingRelTags := make(map[string]struct{})
opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true} opts := models.FindReleasesOptions{IncludeDrafts: true, IncludeTags: true, ListOptions: models.ListOptions{PageSize: 50}}
for page := 1; ; page++ { for page := 1; ; page++ {
rels, err := models.GetReleasesByRepoID(repo.ID, opts, page, 100) opts.Page = page
rels, err := models.GetReleasesByRepoID(repo.ID, opts)
if err != nil { if err != nil {
return fmt.Errorf("GetReleasesByRepoID: %v", err) return fmt.Errorf("GetReleasesByRepoID: %v", err)
} }

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -25,7 +26,9 @@ func Organizations(ctx *context.Context) {
routers.RenderUserSearch(ctx, &models.SearchUserOptions{ routers.RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeOrganization, Type: models.UserTypeOrganization,
ListOptions: models.ListOptions{
PageSize: setting.UI.Admin.OrgPagingNum, PageSize: setting.UI.Admin.OrgPagingNum,
},
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
}, tplOrgs) }, tplOrgs)
} }

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -34,7 +35,9 @@ func Users(ctx *context.Context) {
routers.RenderUserSearch(ctx, &models.SearchUserOptions{ routers.RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeIndividual, Type: models.UserTypeIndividual,
ListOptions: models.ListOptions{
PageSize: setting.UI.Admin.UserPagingNum, PageSize: setting.UI.Admin.UserPagingNum,
},
SearchByEmail: true, SearchByEmail: true,
}, tplUsers) }, tplUsers)
} }

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// CreateOrg api for create organization // CreateOrg api for create organization
@ -102,8 +103,7 @@ func GetAllOrgs(ctx *context.APIContext) {
users, _, err := models.SearchUsers(&models.SearchUserOptions{ users, _, err := models.SearchUsers(&models.SearchUserOptions{
Type: models.UserTypeOrganization, Type: models.UserTypeOrganization,
OrderBy: models.SearchOrderByAlphabetically, OrderBy: models.SearchOrderByAlphabetically,
Page: ctx.QueryInt("page"), ListOptions: utils.GetListOptions(ctx),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate}, Visible: []api.VisibleType{api.VisibleTypePublic, api.VisibleTypeLimited, api.VisibleTypePrivate},
}) })
if err != nil { if err != nil {

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/password"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/mailer" "code.gitea.io/gitea/services/mailer"
) )
@ -328,6 +329,15 @@ func GetAllUsers(ctx *context.APIContext) {
// summary: List all users // summary: List all users
// produces: // produces:
// - application/json // - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
@ -337,7 +347,7 @@ func GetAllUsers(ctx *context.APIContext) {
users, _, err := models.SearchUsers(&models.SearchUserOptions{ users, _, err := models.SearchUsers(&models.SearchUserOptions{
Type: models.UserTypeIndividual, Type: models.UserTypeIndividual,
OrderBy: models.SearchOrderByAlphabetically, OrderBy: models.SearchOrderByAlphabetically,
PageSize: -1, ListOptions: utils.GetListOptions(ctx),
}) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetAllUsers", err) ctx.Error(http.StatusInternalServerError, "GetAllUsers", err)

@ -51,6 +51,14 @@ func ListRepoNotifications(ctx *context.APIContext) {
// type: string // type: string
// format: date-time // format: date-time
// required: false // required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/NotificationThreadList" // "$ref": "#/responses/NotificationThreadList"
@ -61,6 +69,7 @@ func ListRepoNotifications(ctx *context.APIContext) {
return return
} }
opts := models.FindNotificationOptions{ opts := models.FindNotificationOptions{
ListOptions: utils.GetListOptions(ctx),
UserID: ctx.User.ID, UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
UpdatedBeforeUnix: before, UpdatedBeforeUnix: before,

@ -41,6 +41,14 @@ func ListNotifications(ctx *context.APIContext) {
// type: string // type: string
// format: date-time // format: date-time
// required: false // required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/NotificationThreadList" // "$ref": "#/responses/NotificationThreadList"
@ -51,6 +59,7 @@ func ListNotifications(ctx *context.APIContext) {
return return
} }
opts := models.FindNotificationOptions{ opts := models.FindNotificationOptions{
ListOptions: utils.GetListOptions(ctx),
UserID: ctx.User.ID, UserID: ctx.User.ID,
UpdatedBeforeUnix: before, UpdatedBeforeUnix: before,
UpdatedAfterUnix: since, UpdatedAfterUnix: since,

@ -27,12 +27,20 @@ func ListHooks(ctx *context.APIContext) {
// description: name of the organization // description: name of the organization
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/HookList" // "$ref": "#/responses/HookList"
org := ctx.Org.Organization org := ctx.Org.Organization
orgHooks, err := models.GetWebhooksByOrgID(org.ID) orgHooks, err := models.GetWebhooksByOrgID(org.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err) ctx.Error(http.StatusInternalServerError, "GetWebhooksByOrgID", err)
return return

@ -14,14 +14,16 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// listMembers list an organization's members // listMembers list an organization's members
func listMembers(ctx *context.APIContext, publicOnly bool) { func listMembers(ctx *context.APIContext, publicOnly bool) {
var members []*models.User var members []*models.User
members, _, err := models.FindOrgMembers(models.FindOrgMembersOpts{ members, _, err := models.FindOrgMembers(&models.FindOrgMembersOpts{
OrgID: ctx.Org.Organization.ID, OrgID: ctx.Org.Organization.ID,
PublicOnly: publicOnly, PublicOnly: publicOnly,
ListOptions: utils.GetListOptions(ctx),
}) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err) ctx.Error(http.StatusInternalServerError, "GetUsersByIDs", err)
@ -48,6 +50,14 @@ func ListMembers(ctx *context.APIContext) {
// description: name of the organization // description: name of the organization
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
@ -75,6 +85,14 @@ func ListPublicMembers(ctx *context.APIContext) {
// description: name of the organization // description: name of the organization
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:

@ -13,10 +13,14 @@ import (
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) { func listUserOrgs(ctx *context.APIContext, u *models.User, all bool) {
if err := u.GetOrganizations(all); err != nil { if err := u.GetOrganizations(&models.SearchOrganizationsOptions{
ListOptions: utils.GetListOptions(ctx),
All: all,
}); err != nil {
ctx.Error(http.StatusInternalServerError, "GetOrganizations", err) ctx.Error(http.StatusInternalServerError, "GetOrganizations", err)
return return
} }
@ -35,6 +39,15 @@ func ListMyOrgs(ctx *context.APIContext) {
// summary: List the current user's organizations // summary: List the current user's organizations
// produces: // produces:
// - application/json // - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/OrganizationList" // "$ref": "#/responses/OrganizationList"
@ -55,6 +68,14 @@ func ListUserOrgs(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/OrganizationList" // "$ref": "#/responses/OrganizationList"
@ -95,10 +116,9 @@ func GetAll(ctx *context.APIContext) {
} }
publicOrgs, _, err := models.SearchUsers(&models.SearchUserOptions{ publicOrgs, _, err := models.SearchUsers(&models.SearchUserOptions{
ListOptions: utils.GetListOptions(ctx),
Type: models.UserTypeOrganization, Type: models.UserTypeOrganization,
OrderBy: models.SearchOrderByAlphabetically, OrderBy: models.SearchOrderByAlphabetically,
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
Visible: vMode, Visible: vMode,
}) })
if err != nil { if err != nil {

@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListTeams list all the teams of an organization // ListTeams list all the teams of an organization
@ -30,12 +31,22 @@ func ListTeams(ctx *context.APIContext) {
// description: name of the organization // description: name of the organization
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TeamList" // "$ref": "#/responses/TeamList"
org := ctx.Org.Organization org := ctx.Org.Organization
if err := org.GetTeams(); err != nil { if err := org.GetTeams(&models.SearchTeamOptions{
ListOptions: utils.GetListOptions(ctx),
}); err != nil {
ctx.Error(http.StatusInternalServerError, "GetTeams", err) ctx.Error(http.StatusInternalServerError, "GetTeams", err)
return return
} }
@ -59,11 +70,20 @@ func ListUserTeams(ctx *context.APIContext) {
// summary: List all the teams a user belongs to // summary: List all the teams a user belongs to
// produces: // produces:
// - application/json // - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TeamList" // "$ref": "#/responses/TeamList"
teams, err := models.GetUserTeams(ctx.User.ID) teams, err := models.GetUserTeams(ctx.User.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserTeams", err) ctx.Error(http.StatusInternalServerError, "GetUserTeams", err)
return return
@ -284,6 +304,14 @@ func GetTeamMembers(ctx *context.APIContext) {
// type: integer // type: integer
// format: int64 // format: int64
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
@ -297,7 +325,9 @@ func GetTeamMembers(ctx *context.APIContext) {
return return
} }
team := ctx.Org.Team team := ctx.Org.Team
if err := team.GetMembers(); err != nil { if err := team.GetMembers(&models.SearchMembersOptions{
ListOptions: utils.GetListOptions(ctx),
}); err != nil {
ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err) ctx.Error(http.StatusInternalServerError, "GetTeamMembers", err)
return return
} }
@ -436,12 +466,22 @@ func GetTeamRepos(ctx *context.APIContext) {
// type: integer // type: integer
// format: int64 // format: int64
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
team := ctx.Org.Team team := ctx.Org.Team
if err := team.GetRepositories(); err != nil { if err := team.GetRepositories(&models.SearchTeamOptions{
ListOptions: utils.GetListOptions(ctx),
}); err != nil {
ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err) ctx.Error(http.StatusInternalServerError, "GetTeamRepos", err)
} }
repos := make([]*api.Repository, len(team.Repos)) repos := make([]*api.Repository, len(team.Repos))
@ -589,14 +629,14 @@ func SearchTeam(ctx *context.APIContext) {
// in: query // in: query
// description: include search within team description (defaults to true) // description: include search within team description (defaults to true)
// type: boolean // type: boolean
// - name: limit
// in: query
// description: limit size of results
// type: integer
// - name: page // - name: page
// in: query // in: query
// description: page number of results to return (1-based) // description: page number of results to return (1-based)
// type: integer // type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// description: "SearchResults of a successful search" // description: "SearchResults of a successful search"
@ -615,8 +655,7 @@ func SearchTeam(ctx *context.APIContext) {
Keyword: strings.TrimSpace(ctx.Query("q")), Keyword: strings.TrimSpace(ctx.Query("q")),
OrgID: ctx.Org.Organization.ID, OrgID: ctx.Org.Organization.ID,
IncludeDesc: (ctx.Query("include_desc") == "" || ctx.QueryBool("include_desc")), IncludeDesc: (ctx.Query("include_desc") == "" || ctx.QueryBool("include_desc")),
PageSize: ctx.QueryInt("limit"), ListOptions: utils.GetListOptions(ctx),
Page: ctx.QueryInt("page"),
} }
teams, _, err := models.SearchTeam(opts) teams, _, err := models.SearchTeam(opts)

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListCollaborators list a repository's collaborators // ListCollaborators list a repository's collaborators
@ -33,11 +34,19 @@ func ListCollaborators(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
collaborators, err := ctx.Repo.Repository.GetCollaborators() collaborators, err := ctx.Repo.Repository.GetCollaborators(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListCollaborators", err) ctx.Error(http.StatusInternalServerError, "ListCollaborators", err)
return return

@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// GetSingleCommit get a commit via // GetSingleCommit get a commit via
@ -92,7 +93,11 @@ func GetAllCommits(ctx *context.APIContext) {
// type: string // type: string
// - name: page // - name: page
// in: query // in: query
// description: page number of requested commits // description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer // type: integer
// responses: // responses:
// "200": // "200":
@ -117,9 +122,13 @@ func GetAllCommits(ctx *context.APIContext) {
} }
defer gitRepo.Close() defer gitRepo.Close()
page := ctx.QueryInt("page") listOptions := utils.GetListOptions(ctx)
if page <= 0 { if listOptions.Page <= 0 {
page = 1 listOptions.Page = 1
}
if listOptions.PageSize > git.CommitsRangeSize {
listOptions.PageSize = git.CommitsRangeSize
} }
sha := ctx.Query("sha") sha := ctx.Query("sha")
@ -154,10 +163,10 @@ func GetAllCommits(ctx *context.APIContext) {
return return
} }
pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(git.CommitsRangeSize))) pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
// Query commits // Query commits
commits, err := baseCommit.CommitsByRange(page) commits, err := baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize)
if err != nil { if err != nil {
ctx.ServerError("CommitsByRange", err) ctx.ServerError("CommitsByRange", err)
return return
@ -181,13 +190,13 @@ func GetAllCommits(ctx *context.APIContext) {
i++ i++
} }
ctx.SetLinkHeader(int(commitsCountTotal), git.CommitsRangeSize) ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
ctx.Header().Set("X-Page", strconv.Itoa(page)) ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
ctx.Header().Set("X-PerPage", strconv.Itoa(git.CommitsRangeSize)) ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10)) ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount)) ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
ctx.Header().Set("X-HasMore", strconv.FormatBool(page < pageCount)) ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
ctx.JSON(http.StatusOK, &apiCommits) ctx.JSON(http.StatusOK, &apiCommits)
} }

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -11,6 +12,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
) )
@ -32,11 +34,19 @@ func ListForks(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
forks, err := ctx.Repo.Repository.GetForks() forks, err := ctx.Repo.Repository.GetForks(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetForks", err) ctx.Error(http.StatusInternalServerError, "GetForks", err)
return return

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -34,11 +35,19 @@ func ListHooks(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/HookList" // "$ref": "#/responses/HookList"
hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID) hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err) ctx.Error(http.StatusInternalServerError, "GetWebhooksByRepoID", err)
return return

@ -19,6 +19,7 @@ import (
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
issue_service "code.gitea.io/gitea/services/issue" issue_service "code.gitea.io/gitea/services/issue"
) )
@ -38,10 +39,6 @@ func SearchIssues(ctx *context.APIContext) {
// in: query // in: query
// description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
// type: string // type: string
// - name: page
// in: query
// description: page number of requested issues
// type: integer
// - name: q // - name: q
// in: query // in: query
// description: search string // description: search string
@ -55,6 +52,10 @@ func SearchIssues(ctx *context.APIContext) {
// in: query // in: query
// description: filter by type (issues / pulls) if set // description: filter by type (issues / pulls) if set
// type: string // type: string
// - name: page
// in: query
// description: page number of requested issues
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"
@ -72,7 +73,9 @@ func SearchIssues(ctx *context.APIContext) {
// find repos user can access (for issue search) // find repos user can access (for issue search)
repoIDs := make([]int64, 0) repoIDs := make([]int64, 0)
opts := &models.SearchRepoOptions{ opts := &models.SearchRepoOptions{
ListOptions: models.ListOptions{
PageSize: 15, PageSize: 15,
},
Private: false, Private: false,
AllPublic: true, AllPublic: true,
TopicOnly: false, TopicOnly: false,
@ -146,9 +149,11 @@ func SearchIssues(ctx *context.APIContext) {
// This would otherwise return all issues if no issues were found by the search. // This would otherwise return all issues if no issues were found by the search.
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
issues, err = models.Issues(&models.IssuesOptions{ issues, err = models.Issues(&models.IssuesOptions{
RepoIDs: repoIDs, ListOptions: models.ListOptions{
Page: ctx.QueryInt("page"), Page: ctx.QueryInt("page"),
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
},
RepoIDs: repoIDs,
IsClosed: isClosed, IsClosed: isClosed,
IssueIDs: issueIDs, IssueIDs: issueIDs,
LabelIDs: labelIDs, LabelIDs: labelIDs,
@ -198,10 +203,6 @@ func ListIssues(ctx *context.APIContext) {
// in: query // in: query
// description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded // description: comma separated list of labels. Fetch only issues that have any of this labels. Non existent labels are discarded
// type: string // type: string
// - name: page
// in: query
// description: page number of requested issues
// type: integer
// - name: q // - name: q
// in: query // in: query
// description: search string // description: search string
@ -210,6 +211,14 @@ func ListIssues(ctx *context.APIContext) {
// in: query // in: query
// description: filter by type (issues / pulls) if set // description: filter by type (issues / pulls) if set
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"
@ -245,6 +254,11 @@ func ListIssues(ctx *context.APIContext) {
} }
} }
listOptions := utils.GetListOptions(ctx)
if ctx.QueryInt("limit") == 0 {
listOptions.PageSize = setting.UI.IssuePagingNum
}
var isPull util.OptionalBool var isPull util.OptionalBool
switch ctx.Query("type") { switch ctx.Query("type") {
case "pulls": case "pulls":
@ -259,9 +273,8 @@ func ListIssues(ctx *context.APIContext) {
// This would otherwise return all issues if no issues were found by the search. // This would otherwise return all issues if no issues were found by the search.
if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 { if len(keyword) == 0 || len(issueIDs) > 0 || len(labelIDs) > 0 {
issues, err = models.Issues(&models.IssuesOptions{ issues, err = models.Issues(&models.IssuesOptions{
ListOptions: listOptions,
RepoIDs: []int64{ctx.Repo.Repository.ID}, RepoIDs: []int64{ctx.Repo.Repository.ID},
Page: ctx.QueryInt("page"),
PageSize: setting.UI.IssuePagingNum,
IsClosed: isClosed, IsClosed: isClosed,
IssueIDs: issueIDs, IssueIDs: issueIDs,
LabelIDs: labelIDs, LabelIDs: labelIDs,
@ -279,7 +292,7 @@ func ListIssues(ctx *context.APIContext) {
apiIssues[i] = issues[i].APIFormat() apiIssues[i] = issues[i].APIFormat()
} }
ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, setting.UI.IssuePagingNum) ctx.SetLinkHeader(ctx.Repo.Repository.NumIssues, listOptions.PageSize)
ctx.JSON(http.StatusOK, &apiIssues) ctx.JSON(http.StatusOK, &apiIssues)
} }

@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -117,6 +118,14 @@ func ListRepoIssueComments(ctx *context.APIContext) {
// description: if provided, only comments updated before the provided time are returned. // description: if provided, only comments updated before the provided time are returned.
// type: string // type: string
// format: date-time // format: date-time
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/CommentList" // "$ref": "#/responses/CommentList"
@ -128,10 +137,11 @@ func ListRepoIssueComments(ctx *context.APIContext) {
} }
comments, err := models.FindComments(models.FindCommentsOptions{ comments, err := models.FindComments(models.FindCommentsOptions{
ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
Type: models.CommentTypeComment,
Since: since, Since: since,
Before: before, Before: before,
Type: models.CommentTypeComment,
}) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindComments", err) ctx.Error(http.StatusInternalServerError, "FindComments", err)

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// GetIssueCommentReactions list reactions of a comment from an issue // GetIssueCommentReactions list reactions of a comment from an issue
@ -245,6 +246,14 @@ func GetIssueReactions(ctx *context.APIContext) {
// type: integer // type: integer
// format: int64 // format: int64
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/ReactionList" // "$ref": "#/responses/ReactionList"
@ -266,7 +275,7 @@ func GetIssueReactions(ctx *context.APIContext) {
return return
} }
reactions, err := models.FindIssueReactions(issue) reactions, err := models.FindIssueReactions(issue, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err) ctx.Error(http.StatusInternalServerError, "FindIssueReactions", err)
return return

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// StartIssueStopwatch creates a stopwatch for the given issue. // StartIssueStopwatch creates a stopwatch for the given issue.
@ -197,6 +198,15 @@ func GetStopwatches(ctx *context.APIContext) {
// swagger:operation GET /user/stopwatches user userGetStopWatches // swagger:operation GET /user/stopwatches user userGetStopWatches
// --- // ---
// summary: Get list of all existing stopwatches // summary: Get list of all existing stopwatches
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// consumes: // consumes:
// - application/json // - application/json
// produces: // produces:
@ -205,7 +215,7 @@ func GetStopwatches(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/StopWatchList" // "$ref": "#/responses/StopWatchList"
sws, err := models.GetUserStopwatches(ctx.User.ID) sws, err := models.GetUserStopwatches(ctx.User.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err) ctx.Error(http.StatusInternalServerError, "GetUserStopwatches", err)
return return

@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// AddIssueSubscription Subscribe user to issue // AddIssueSubscription Subscribe user to issue
@ -158,6 +159,14 @@ func GetIssueSubscribers(ctx *context.APIContext) {
// type: integer // type: integer
// format: int64 // format: int64
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
@ -175,7 +184,7 @@ func GetIssueSubscribers(ctx *context.APIContext) {
return return
} }
iwl, err := models.GetIssueWatchers(issue.ID) iwl, err := models.GetIssueWatchers(issue.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetIssueWatchers", err) ctx.Error(http.StatusInternalServerError, "GetIssueWatchers", err)
return return

@ -51,6 +51,14 @@ func ListTrackedTimes(ctx *context.APIContext) {
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
// type: string // type: string
// format: date-time // format: date-time
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TrackedTimeList" // "$ref": "#/responses/TrackedTimeList"
@ -72,6 +80,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
} }
opts := models.FindTrackedTimesOptions{ opts := models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx),
RepositoryID: ctx.Repo.Repository.ID, RepositoryID: ctx.Repo.Repository.ID,
IssueID: issue.ID, IssueID: issue.ID,
} }
@ -435,6 +444,14 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
// description: Only show times updated before the given time. This is a timestamp in RFC 3339 format // description: Only show times updated before the given time. This is a timestamp in RFC 3339 format
// type: string // type: string
// format: date-time // format: date-time
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TrackedTimeList" // "$ref": "#/responses/TrackedTimeList"
@ -449,6 +466,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
} }
opts := models.FindTrackedTimesOptions{ opts := models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx),
RepositoryID: ctx.Repo.Repository.ID, RepositoryID: ctx.Repo.Repository.ID,
} }
@ -495,6 +513,15 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// swagger:operation GET /user/times user userCurrentTrackedTimes // swagger:operation GET /user/times user userCurrentTrackedTimes
// --- // ---
// summary: List the current user's tracked times // summary: List the current user's tracked times
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// parameters: // parameters:
@ -512,7 +539,10 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/TrackedTimeList" // "$ref": "#/responses/TrackedTimeList"
opts := models.FindTrackedTimesOptions{UserID: ctx.User.ID} opts := models.FindTrackedTimesOptions{
ListOptions: utils.GetListOptions(ctx),
UserID: ctx.User.ID,
}
var err error var err error
if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil { if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = utils.GetQueryBeforeSince(ctx); err != nil {

@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -13,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// appendPrivateInformation appends the owner and key type information to api.PublicKey // appendPrivateInformation appends the owner and key type information to api.PublicKey
@ -60,6 +62,14 @@ func ListDeployKeys(ctx *context.APIContext) {
// in: query // in: query
// description: fingerprint of the key // description: fingerprint of the key
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/DeployKeyList" // "$ref": "#/responses/DeployKeyList"
@ -72,7 +82,7 @@ func ListDeployKeys(ctx *context.APIContext) {
if fingerprint != "" || keyID != 0 { if fingerprint != "" || keyID != 0 {
keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint) keys, err = models.SearchDeployKeys(ctx.Repo.Repository.ID, keyID, fingerprint)
} else { } else {
keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID) keys, err = models.ListDeployKeys(ctx.Repo.Repository.ID, utils.GetListOptions(ctx))
} }
if err != nil { if err != nil {

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListLabels list all the labels of a repository // ListLabels list all the labels of a repository
@ -32,11 +33,19 @@ func ListLabels(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/LabelList" // "$ref": "#/responses/LabelList"
labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort")) labels, err := models.GetLabelsByRepoID(ctx.Repo.Repository.ID, ctx.Query("sort"), utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetLabelsByRepoID", err) ctx.Error(http.StatusInternalServerError, "GetLabelsByRepoID", err)
return return

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -12,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListMilestones list milestones for a repository // ListMilestones list milestones for a repository
@ -36,11 +38,19 @@ func ListMilestones(ctx *context.APIContext) {
// in: query // in: query
// description: Milestone state, Recognised values are open, closed and all. Defaults to "open" // description: Milestone state, Recognised values are open, closed and all. Defaults to "open"
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/MilestoneList" // "$ref": "#/responses/MilestoneList"
milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state"))) milestones, err := models.GetMilestonesByRepoID(ctx.Repo.Repository.ID, api.StateType(ctx.Query("state")), utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetMilestonesByRepoID", err) ctx.Error(http.StatusInternalServerError, "GetMilestonesByRepoID", err)
return return

@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/routers/api/v1/utils"
issue_service "code.gitea.io/gitea/services/issue" issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull" pull_service "code.gitea.io/gitea/services/pull"
) )
@ -41,10 +42,6 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions)
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: Page number
// type: integer
// - name: state // - name: state
// in: query // in: query
// description: "State of pull request: open or closed (optional)" // description: "State of pull request: open or closed (optional)"
@ -68,12 +65,22 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions)
// items: // items:
// type: integer // type: integer
// format: int64 // format: int64
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/PullRequestList" // "$ref": "#/responses/PullRequestList"
listOptions := utils.GetListOptions(ctx)
prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{ prs, maxResults, err := models.PullRequests(ctx.Repo.Repository.ID, &models.PullRequestsOptions{
Page: ctx.QueryInt("page"), ListOptions: listOptions,
State: ctx.QueryTrim("state"), State: ctx.QueryTrim("state"),
SortType: ctx.QueryTrim("sort"), SortType: ctx.QueryTrim("sort"),
Labels: ctx.QueryStrings("labels"), Labels: ctx.QueryStrings("labels"),
@ -106,7 +113,7 @@ func ListPullRequests(ctx *context.APIContext, form api.ListPullRequestsOptions)
apiPrs[i] = convert.ToAPIPullRequest(prs[i]) apiPrs[i] = convert.ToAPIPullRequest(prs[i])
} }
ctx.SetLinkHeader(int(maxResults), models.ItemsPerPage) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.JSON(http.StatusOK, &apiPrs) ctx.JSON(http.StatusOK, &apiPrs)
} }

@ -9,8 +9,8 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
releaseservice "code.gitea.io/gitea/services/release" releaseservice "code.gitea.io/gitea/services/release"
) )
@ -59,20 +59,6 @@ func GetRelease(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, release.APIFormat()) ctx.JSON(http.StatusOK, release.APIFormat())
} }
func getPagesInfo(ctx *context.APIContext) (int, int) {
page := ctx.QueryInt("page")
if page == 0 {
page = 1
}
perPage := ctx.QueryInt("per_page")
if perPage == 0 {
perPage = setting.API.DefaultPagingNum
} else if perPage > setting.API.MaxResponseItems {
perPage = setting.API.MaxResponseItems
}
return page, perPage
}
// ListReleases list a repository's releases // ListReleases list a repository's releases
func ListReleases(ctx *context.APIContext) { func ListReleases(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases // swagger:operation GET /repos/{owner}/{repo}/releases repository repoListReleases
@ -91,23 +77,32 @@ func ListReleases(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: per_page
// in: query
// description: items count every page wants to load
// type: integer
// deprecated: true
// - name: page // - name: page
// in: query // in: query
// description: page wants to load // description: page number of results to return (1-based)
// type: integer // type: integer
// - name: per_page // - name: limit
// in: query // in: query
// description: items count every page wants to load // description: page size of results, maximum page size is 50
// type: integer // type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/ReleaseList" // "$ref": "#/responses/ReleaseList"
listOptions := utils.GetListOptions(ctx)
if ctx.QueryInt("per_page") != 0 {
listOptions.PageSize = ctx.QueryInt("per_page")
}
page, limit := getPagesInfo(ctx)
releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{ releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
ListOptions: listOptions,
IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite, IncludeDrafts: ctx.Repo.AccessMode >= models.AccessModeWrite,
IncludeTags: false, IncludeTags: false,
}, page, limit) })
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err) ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
return return

@ -16,7 +16,6 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@ -27,6 +26,7 @@ import (
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/api/v1/utils"
mirror_service "code.gitea.io/gitea/services/mirror" mirror_service "code.gitea.io/gitea/services/mirror"
repo_service "code.gitea.io/gitea/services/repository" repo_service "code.gitea.io/gitea/services/repository"
) )
@ -91,14 +91,6 @@ func Search(ctx *context.APIContext) {
// in: query // in: query
// description: include template repositories this user has access to (defaults to true) // description: include template repositories this user has access to (defaults to true)
// type: boolean // type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// - name: mode // - name: mode
// in: query // in: query
// description: type of repository to search for. Supported values are // description: type of repository to search for. Supported values are
@ -119,6 +111,14 @@ func Search(ctx *context.APIContext) {
// description: sort order, either "asc" (ascending) or "desc" (descending). // description: sort order, either "asc" (ascending) or "desc" (descending).
// Default is "asc", ignored if "sort" is not specified. // Default is "asc", ignored if "sort" is not specified.
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/SearchResults" // "$ref": "#/responses/SearchResults"
@ -126,12 +126,11 @@ func Search(ctx *context.APIContext) {
// "$ref": "#/responses/validationError" // "$ref": "#/responses/validationError"
opts := &models.SearchRepoOptions{ opts := &models.SearchRepoOptions{
ListOptions: utils.GetListOptions(ctx),
Actor: ctx.User, Actor: ctx.User,
Keyword: strings.Trim(ctx.Query("q"), " "), Keyword: strings.Trim(ctx.Query("q"), " "),
OwnerID: ctx.QueryInt64("uid"), OwnerID: ctx.QueryInt64("uid"),
PriorityOwnerID: ctx.QueryInt64("priority_owner_id"), PriorityOwnerID: ctx.QueryInt64("priority_owner_id"),
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
TopicOnly: ctx.QueryBool("topic"), TopicOnly: ctx.QueryBool("topic"),
Collaborate: util.OptionalBoolNone, Collaborate: util.OptionalBoolNone,
Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")), Private: ctx.IsSigned && (ctx.Query("private") == "" || ctx.QueryBool("private")),
@ -214,7 +213,7 @@ func Search(ctx *context.APIContext) {
results[i] = repo.APIFormat(accessMode) results[i] = repo.APIFormat(accessMode)
} }
ctx.SetLinkHeader(int(count), setting.API.MaxResponseItems) ctx.SetLinkHeader(int(count), opts.PageSize)
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count)) ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", count))
ctx.JSON(http.StatusOK, api.SearchResults{ ctx.JSON(http.StatusOK, api.SearchResults{
OK: true, OK: true,

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListStargazers list a repository's stargazers // ListStargazers list a repository's stargazers
@ -30,11 +31,19 @@ func ListStargazers(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
stargazers, err := ctx.Repo.Repository.GetStargazers(-1) stargazers, err := ctx.Repo.Repository.GetStargazers(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetStargazers", err) ctx.Error(http.StatusInternalServerError, "GetStargazers", err)
return return

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/repofiles" "code.gitea.io/gitea/modules/repofiles"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// NewCommitStatus creates a new CommitStatus // NewCommitStatus creates a new CommitStatus
@ -89,11 +90,6 @@ func GetCommitStatuses(ctx *context.APIContext) {
// description: sha of the commit // description: sha of the commit
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results
// type: integer
// required: false
// - name: sort // - name: sort
// in: query // in: query
// description: type of sort // description: type of sort
@ -106,6 +102,14 @@ func GetCommitStatuses(ctx *context.APIContext) {
// type: string // type: string
// enum: [pending, success, error, failure, warning] // enum: [pending, success, error, failure, warning]
// required: false // required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/StatusList" // "$ref": "#/responses/StatusList"
@ -138,11 +142,6 @@ func GetCommitStatusesByRef(ctx *context.APIContext) {
// description: name of branch/tag/commit // description: name of branch/tag/commit
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results
// type: integer
// required: false
// - name: sort // - name: sort
// in: query // in: query
// description: type of sort // description: type of sort
@ -155,6 +154,14 @@ func GetCommitStatusesByRef(ctx *context.APIContext) {
// type: string // type: string
// enum: [pending, success, error, failure, warning] // enum: [pending, success, error, failure, warning]
// required: false // required: false
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/StatusList" // "$ref": "#/responses/StatusList"
@ -202,7 +209,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
repo := ctx.Repo.Repository repo := ctx.Repo.Repository
statuses, _, err := models.GetCommitStatuses(repo, sha, &models.CommitStatusOptions{ statuses, _, err := models.GetCommitStatuses(repo, sha, &models.CommitStatusOptions{
Page: ctx.QueryInt("page"), ListOptions: utils.GetListOptions(ctx),
SortType: ctx.QueryTrim("sort"), SortType: ctx.QueryTrim("sort"),
State: ctx.QueryTrim("state"), State: ctx.QueryTrim("state"),
}) })

@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListSubscribers list a repo's subscribers (i.e. watchers) // ListSubscribers list a repo's subscribers (i.e. watchers)
@ -30,11 +31,19 @@ func ListSubscribers(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
subscribers, err := ctx.Repo.Repository.GetWatchers(0) subscribers, err := ctx.Repo.Repository.GetWatchers(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetWatchers", err) ctx.Error(http.StatusInternalServerError, "GetWatchers", err)
return return

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListTopics returns list of current topics for repo // ListTopics returns list of current topics for repo
@ -33,11 +34,20 @@ func ListTopics(ctx *context.APIContext) {
// description: name of the repo // description: name of the repo
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TopicNames" // "$ref": "#/responses/TopicNames"
topics, err := models.FindTopics(&models.FindTopicOptions{ topics, err := models.FindTopics(&models.FindTopicOptions{
ListOptions: utils.GetListOptions(ctx),
RepoID: ctx.Repo.Repository.ID, RepoID: ctx.Repo.Repository.ID,
}) })
if err != nil { if err != nil {
@ -231,7 +241,7 @@ func DeleteTopic(ctx *context.APIContext) {
} }
// TopicSearch search for creating topic // TopicSearch search for creating topic
func TopicSearch(ctx *context.Context) { func TopicSearch(ctx *context.APIContext) {
// swagger:operation GET /topics/search repository topicSearch // swagger:operation GET /topics/search repository topicSearch
// --- // ---
// summary: search topics via keyword // summary: search topics via keyword
@ -243,6 +253,14 @@ func TopicSearch(ctx *context.Context) {
// description: keywords to search // description: keywords to search
// required: true // required: true
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/TopicListResponse" // "$ref": "#/responses/TopicListResponse"
@ -256,9 +274,17 @@ func TopicSearch(ctx *context.Context) {
kw := ctx.Query("q") kw := ctx.Query("q")
listOptions := utils.GetListOptions(ctx)
if listOptions.Page < 1 {
listOptions.Page = 1
}
if listOptions.PageSize < 1 {
listOptions.PageSize = 10
}
topics, err := models.FindTopics(&models.FindTopicOptions{ topics, err := models.FindTopics(&models.FindTopicOptions{
Keyword: kw, Keyword: kw,
Limit: 10, ListOptions: listOptions,
}) })
if err != nil { if err != nil {
log.Error("SearchTopics failed: %v", err) log.Error("SearchTopics failed: %v", err)

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// ListAccessTokens list all the access tokens // ListAccessTokens list all the access tokens
@ -26,11 +27,19 @@ func ListAccessTokens(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/AccessTokenList" // "$ref": "#/responses/AccessTokenList"
tokens, err := models.ListAccessTokens(ctx.User.ID) tokens, err := models.ListAccessTokens(ctx.User.ID, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err)
return return

@ -1,4 +1,5 @@
// Copyright 2015 The Gogs Authors. All rights reserved. // Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -11,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
func responseAPIUsers(ctx *context.APIContext, users []*models.User) { func responseAPIUsers(ctx *context.APIContext, users []*models.User) {
@ -22,7 +24,7 @@ func responseAPIUsers(ctx *context.APIContext, users []*models.User) {
} }
func listUserFollowers(ctx *context.APIContext, u *models.User) { func listUserFollowers(ctx *context.APIContext, u *models.User) {
users, err := u.GetFollowers(ctx.QueryInt("page")) users, err := u.GetFollowers(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err) ctx.Error(http.StatusInternalServerError, "GetUserFollowers", err)
return return
@ -35,6 +37,15 @@ func ListMyFollowers(ctx *context.APIContext) {
// swagger:operation GET /user/followers user userCurrentListFollowers // swagger:operation GET /user/followers user userCurrentListFollowers
// --- // ---
// summary: List the authenticated user's followers // summary: List the authenticated user's followers
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:
@ -57,6 +68,14 @@ func ListFollowers(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"
@ -69,7 +88,7 @@ func ListFollowers(ctx *context.APIContext) {
} }
func listUserFollowing(ctx *context.APIContext, u *models.User) { func listUserFollowing(ctx *context.APIContext, u *models.User) {
users, err := u.GetFollowing(ctx.QueryInt("page")) users, err := u.GetFollowing(utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetFollowing", err) ctx.Error(http.StatusInternalServerError, "GetFollowing", err)
return return
@ -82,6 +101,15 @@ func ListMyFollowing(ctx *context.APIContext) {
// swagger:operation GET /user/following user userCurrentListFollowing // swagger:operation GET /user/following user userCurrentListFollowing
// --- // ---
// summary: List the users that the authenticated user is following // summary: List the users that the authenticated user is following
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:
@ -104,6 +132,14 @@ func ListFollowing(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/UserList" // "$ref": "#/responses/UserList"

@ -11,10 +11,11 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
func listGPGKeys(ctx *context.APIContext, uid int64) { func listGPGKeys(ctx *context.APIContext, uid int64, listOptions models.ListOptions) {
keys, err := models.ListGPGKeys(uid) keys, err := models.ListGPGKeys(uid, listOptions)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err) ctx.Error(http.StatusInternalServerError, "ListGPGKeys", err)
return return
@ -41,6 +42,14 @@ func ListGPGKeys(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/GPGKeyList" // "$ref": "#/responses/GPGKeyList"
@ -49,7 +58,7 @@ func ListGPGKeys(ctx *context.APIContext) {
if ctx.Written() { if ctx.Written() {
return return
} }
listGPGKeys(ctx, user.ID) listGPGKeys(ctx, user.ID, utils.GetListOptions(ctx))
} }
//ListMyGPGKeys get the GPG key list of the authenticated user //ListMyGPGKeys get the GPG key list of the authenticated user
@ -57,13 +66,22 @@ func ListMyGPGKeys(ctx *context.APIContext) {
// swagger:operation GET /user/gpg_keys user userCurrentListGPGKeys // swagger:operation GET /user/gpg_keys user userCurrentListGPGKeys
// --- // ---
// summary: List the authenticated user's GPG keys // summary: List the authenticated user's GPG keys
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/GPGKeyList" // "$ref": "#/responses/GPGKeyList"
listGPGKeys(ctx, ctx.User.ID) listGPGKeys(ctx, ctx.User.ID, utils.GetListOptions(ctx))
} }
//GetGPGKey get the GPG key based on a id //GetGPGKey get the GPG key based on a id

@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/repo" "code.gitea.io/gitea/routers/api/v1/repo"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// appendPrivateInformation appends the owner and key type information to api.PublicKey // appendPrivateInformation appends the owner and key type information to api.PublicKey
@ -79,7 +80,7 @@ func listPublicKeys(ctx *context.APIContext, user *models.User) {
} }
} else { } else {
// Use ListPublicKeys // Use ListPublicKeys
keys, err = models.ListPublicKeys(user.ID) keys, err = models.ListPublicKeys(user.ID, utils.GetListOptions(ctx))
} }
if err != nil { if err != nil {
@ -109,6 +110,14 @@ func ListMyPublicKeys(ctx *context.APIContext) {
// in: query // in: query
// description: fingerprint of the key // description: fingerprint of the key
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:
@ -135,6 +144,14 @@ func ListPublicKeys(ctx *context.APIContext) {
// in: query // in: query
// description: fingerprint of the key // description: fingerprint of the key
// type: string // type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/PublicKeyList" // "$ref": "#/responses/PublicKeyList"

@ -10,11 +10,16 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// listUserRepos - List the repositories owned by the given user. // listUserRepos - List the repositories owned by the given user.
func listUserRepos(ctx *context.APIContext, u *models.User, private bool) { func listUserRepos(ctx *context.APIContext, u *models.User, private bool) {
repos, err := models.GetUserRepositories(u.ID, private, 1, u.NumRepos, "") repos, err := models.GetUserRepositories(&models.SearchRepoOptions{
Actor: u,
Private: private,
ListOptions: utils.GetListOptions(ctx),
})
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err) ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err)
return return
@ -47,6 +52,14 @@ func ListUserRepos(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
@ -66,11 +79,24 @@ func ListMyRepos(ctx *context.APIContext) {
// summary: List the repos that the authenticated user owns or has access to // summary: List the repos that the authenticated user owns or has access to
// produces: // produces:
// - application/json // - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
ownRepos, err := models.GetUserRepositories(ctx.User.ID, true, 1, ctx.User.NumRepos, "") ownRepos, err := models.GetUserRepositories(&models.SearchRepoOptions{
Actor: ctx.User,
Private: true,
ListOptions: utils.GetListOptions(ctx),
})
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err) ctx.Error(http.StatusInternalServerError, "GetUserRepositories", err)
return return
@ -106,6 +132,14 @@ func ListOrgRepos(ctx *context.APIContext) {
// description: name of the organization // description: name of the organization
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"

@ -1,4 +1,5 @@
// Copyright 2016 The Gogs Authors. All rights reserved. // Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -10,12 +11,13 @@ import (
"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// getStarredRepos returns the repos that the user with the specified userID has // getStarredRepos returns the repos that the user with the specified userID has
// starred // starred
func getStarredRepos(user *models.User, private bool) ([]*api.Repository, error) { func getStarredRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) {
starredRepos, err := models.GetStarredRepos(user.ID, private) starredRepos, err := models.GetStarredRepos(user.ID, private, listOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -44,13 +46,21 @@ func GetStarredRepos(ctx *context.APIContext) {
// description: username of user // description: username of user
// type: string // type: string
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
user := GetUserByParams(ctx) user := GetUserByParams(ctx)
private := user.ID == ctx.User.ID private := user.ID == ctx.User.ID
repos, err := getStarredRepos(user, private) repos, err := getStarredRepos(user, private, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) ctx.Error(http.StatusInternalServerError, "getStarredRepos", err)
} }
@ -62,13 +72,22 @@ func GetMyStarredRepos(ctx *context.APIContext) {
// swagger:operation GET /user/starred user userCurrentListStarred // swagger:operation GET /user/starred user userCurrentListStarred
// --- // ---
// summary: The repos that the authenticated user has starred // summary: The repos that the authenticated user has starred
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// produces: // produces:
// - application/json // - application/json
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
repos, err := getStarredRepos(ctx.User, true) repos, err := getStarredRepos(ctx.User, true, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getStarredRepos", err) ctx.Error(http.StatusInternalServerError, "getStarredRepos", err)
} }

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -12,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
"github.com/unknwon/com" "github.com/unknwon/com"
) )
@ -33,9 +35,13 @@ func Search(ctx *context.APIContext) {
// description: ID of the user to search for // description: ID of the user to search for
// type: integer // type: integer
// format: int64 // format: int64
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit // - name: limit
// in: query // in: query
// description: maximum number of users to return // description: page size of results, maximum page size is 50
// type: integer // type: integer
// responses: // responses:
// "200": // "200":
@ -54,7 +60,7 @@ func Search(ctx *context.APIContext) {
Keyword: strings.Trim(ctx.Query("q"), " "), Keyword: strings.Trim(ctx.Query("q"), " "),
UID: com.StrTo(ctx.Query("uid")).MustInt64(), UID: com.StrTo(ctx.Query("uid")).MustInt64(),
Type: models.UserTypeIndividual, Type: models.UserTypeIndividual,
PageSize: com.StrTo(ctx.Query("limit")).MustInt(), ListOptions: utils.GetListOptions(ctx),
} }
users, _, err := models.SearchUsers(opts) users, _, err := models.SearchUsers(opts)

@ -11,12 +11,13 @@ import (
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
) )
// getWatchedRepos returns the repos that the user with the specified userID is // getWatchedRepos returns the repos that the user with the specified userID is
// watching // watching
func getWatchedRepos(user *models.User, private bool) ([]*api.Repository, error) { func getWatchedRepos(user *models.User, private bool, listOptions models.ListOptions) ([]*api.Repository, error) {
watchedRepos, err := models.GetWatchedRepos(user.ID, private) watchedRepos, err := models.GetWatchedRepos(user.ID, private, listOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -45,13 +46,21 @@ func GetWatchedRepos(ctx *context.APIContext) {
// in: path // in: path
// description: username of the user // description: username of the user
// required: true // required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
user := GetUserByParams(ctx) user := GetUserByParams(ctx)
private := user.ID == ctx.User.ID private := user.ID == ctx.User.ID
repos, err := getWatchedRepos(user, private) repos, err := getWatchedRepos(user, private, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
} }
@ -65,11 +74,20 @@ func GetMyWatchedRepos(ctx *context.APIContext) {
// summary: List repositories watched by the authenticated user // summary: List repositories watched by the authenticated user
// produces: // produces:
// - application/json // - application/json
// parameters:
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses: // responses:
// "200": // "200":
// "$ref": "#/responses/RepositoryList" // "$ref": "#/responses/RepositoryList"
repos, err := getWatchedRepos(ctx.User, true) repos, err := getWatchedRepos(ctx.User, true, utils.GetListOptions(ctx))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err) ctx.Error(http.StatusInternalServerError, "getWatchedRepos", err)
} }

@ -8,7 +8,9 @@ import (
"strings" "strings"
"time" "time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
) )
// UserID user ID of authenticated user, or 0 if not authenticated // UserID user ID of authenticated user, or 0 if not authenticated
@ -44,3 +46,11 @@ func GetQueryBeforeSince(ctx *context.APIContext) (before, since int64, err erro
} }
return before, since, nil return before, since, nil
} }
// GetListOptions returns list options using the page and limit parameters
func GetListOptions(ctx *context.APIContext) models.ListOptions {
return models.ListOptions{
Page: ctx.QueryInt("page"),
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
}
}

@ -137,9 +137,11 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
ctx.Data["TopicOnly"] = topicOnly ctx.Data["TopicOnly"] = topicOnly
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
Actor: ctx.User, ListOptions: models.ListOptions{
Page: page, Page: page,
PageSize: opts.PageSize, PageSize: opts.PageSize,
},
Actor: ctx.User,
OrderBy: orderBy, OrderBy: orderBy,
Private: opts.Private, Private: opts.Private,
Keyword: keyword, Keyword: keyword,
@ -251,7 +253,7 @@ func ExploreUsers(ctx *context.Context) {
RenderUserSearch(ctx, &models.SearchUserOptions{ RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeIndividual, Type: models.UserTypeIndividual,
PageSize: setting.UI.ExplorePagingNum, ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum},
IsActive: util.OptionalBoolTrue, IsActive: util.OptionalBoolTrue,
Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate}, Visible: []structs.VisibleType{structs.VisibleTypePublic, structs.VisibleTypeLimited, structs.VisibleTypePrivate},
}, tplExploreUsers) }, tplExploreUsers)
@ -271,7 +273,7 @@ func ExploreOrganizations(ctx *context.Context) {
RenderUserSearch(ctx, &models.SearchUserOptions{ RenderUserSearch(ctx, &models.SearchUserOptions{
Type: models.UserTypeOrganization, Type: models.UserTypeOrganization,
PageSize: setting.UI.ExplorePagingNum, ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum},
Visible: visibleTypes, Visible: visibleTypes,
}, tplExploreOrganizations) }, tplExploreOrganizations)
} }

@ -76,14 +76,16 @@ func Home(ctx *context.Context) {
err error err error
) )
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
Page: page,
},
Keyword: keyword, Keyword: keyword,
OwnerID: org.ID, OwnerID: org.ID,
OrderBy: orderBy, OrderBy: orderBy,
Private: ctx.IsSigned, Private: ctx.IsSigned,
Actor: ctx.User, Actor: ctx.User,
Page: page,
IsProfile: true, IsProfile: true,
PageSize: setting.UI.User.RepoPagingNum,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
}) })
if err != nil { if err != nil {
@ -94,7 +96,7 @@ func Home(ctx *context.Context) {
var opts = models.FindOrgMembersOpts{ var opts = models.FindOrgMembersOpts{
OrgID: org.ID, OrgID: org.ID,
PublicOnly: true, PublicOnly: true,
Limit: 25, ListOptions: models.ListOptions{Page: 1, PageSize: 25},
} }
if ctx.User != nil { if ctx.User != nil {
@ -106,7 +108,7 @@ func Home(ctx *context.Context) {
opts.PublicOnly = !isMember && !ctx.User.IsAdmin opts.PublicOnly = !isMember && !ctx.User.IsAdmin
} }
members, _, err := models.FindOrgMembers(opts) members, _, err := models.FindOrgMembers(&opts)
if err != nil { if err != nil {
ctx.ServerError("FindOrgMembers", err) ctx.ServerError("FindOrgMembers", err)
return return

@ -1,4 +1,5 @@
// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors.
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -51,9 +52,9 @@ func Members(ctx *context.Context) {
} }
pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.MembersPagingNum, page, 5)
opts.Start = (page - 1) * setting.UI.MembersPagingNum opts.ListOptions.Page = (page - 1) * setting.UI.MembersPagingNum
opts.Limit = setting.UI.MembersPagingNum opts.ListOptions.PageSize = setting.UI.MembersPagingNum
members, membersIsPublic, err := models.FindOrgMembers(opts) members, membersIsPublic, err := models.FindOrgMembers(&opts)
if err != nil { if err != nil {
ctx.ServerError("GetMembers", err) ctx.ServerError("GetMembers", err)
return return

@ -155,7 +155,7 @@ func Webhooks(ctx *context.Context) {
ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks" ctx.Data["BaseLink"] = ctx.Org.OrgLink + "/settings/hooks"
ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc") ctx.Data["Description"] = ctx.Tr("org.settings.hooks_desc")
ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID) ws, err := models.GetWebhooksByOrgID(ctx.Org.Organization.ID, models.ListOptions{})
if err != nil { if err != nil {
ctx.ServerError("GetWebhooksByOrgId", err) ctx.ServerError("GetWebhooksByOrgId", err)
return return

@ -38,7 +38,7 @@ func Teams(ctx *context.Context) {
ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeams"] = true
for _, t := range org.Teams { for _, t := range org.Teams {
if err := t.GetMembers(); err != nil { if err := t.GetMembers(&models.SearchMembersOptions{}); err != nil {
ctx.ServerError("GetMembers", err) ctx.ServerError("GetMembers", err)
return return
} }
@ -246,7 +246,7 @@ func TeamMembers(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Team.Name ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamMembers"] = true ctx.Data["PageIsOrgTeamMembers"] = true
if err := ctx.Org.Team.GetMembers(); err != nil { if err := ctx.Org.Team.GetMembers(&models.SearchMembersOptions{}); err != nil {
ctx.ServerError("GetMembers", err) ctx.ServerError("GetMembers", err)
return return
} }
@ -258,7 +258,7 @@ func TeamRepositories(ctx *context.Context) {
ctx.Data["Title"] = ctx.Org.Team.Name ctx.Data["Title"] = ctx.Org.Team.Name
ctx.Data["PageIsOrgTeams"] = true ctx.Data["PageIsOrgTeams"] = true
ctx.Data["PageIsOrgTeamRepos"] = true ctx.Data["PageIsOrgTeamRepos"] = true
if err := ctx.Org.Team.GetRepositories(); err != nil { if err := ctx.Org.Team.GetRepositories(&models.SearchTeamOptions{}); err != nil {
ctx.ServerError("GetRepositories", err) ctx.ServerError("GetRepositories", err)
return return
} }

@ -58,8 +58,13 @@ func Commits(ctx *context.Context) {
page = 1 page = 1
} }
pageSize := ctx.QueryInt("limit")
if pageSize <= 0 {
pageSize = git.CommitsRangeSize
}
// Both `git log branchName` and `git log commitId` work. // Both `git log branchName` and `git log commitId` work.
commits, err := ctx.Repo.Commit.CommitsByRange(page) commits, err := ctx.Repo.Commit.CommitsByRange(page, pageSize)
if err != nil { if err != nil {
ctx.ServerError("CommitsByRange", err) ctx.ServerError("CommitsByRange", err)
return return

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save