Support pagination of organizations on user settings pages (#16083)

* Add pagination for user setting orgs
* Use FindOrgs instead of GetOrgsByUserID
* Remove unnecessary functions and fix test
* remove unnecessary code
This commit is contained in:
Lunny Xiao 2021-11-22 21:51:45 +08:00 committed by GitHub
parent ed23a6c397
commit c2ab19888f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 108 additions and 34 deletions

View file

@ -116,7 +116,10 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca
Name: username, Name: username,
}).(*models.User) }).(*models.User)
orgs, err := models.GetOrgsByUserID(user.ID, true) orgs, err := models.FindOrgs(models.FindOrgOptions{
UserID: user.ID,
IncludePrivate: true,
})
assert.NoError(t, err) assert.NoError(t, err)
calcOrgCounts := map[string]int{} calcOrgCounts := map[string]int{}

View file

@ -440,24 +440,6 @@ func getUsersWhoCanCreateOrgRepo(e db.Engine, orgID int64) ([]*User, error) {
And("team_user.org_id = ?", orgID).Asc("`user`.name").Find(&users) And("team_user.org_id = ?", orgID).Asc("`user`.name").Find(&users)
} }
func getOrgsByUserID(e db.Engine, userID int64, showAll bool) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
sess := e.Where("`org_user`.uid=?", userID)
if !showAll {
sess = sess.And("`org_user`.is_public=?", true)
}
return orgs, sess.
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").
Asc("`user`.name").
Find(&orgs)
}
// GetOrgsByUserID returns a list of organizations that the given user ID
// has joined.
func GetOrgsByUserID(userID int64, showAll bool) ([]*Organization, error) {
return getOrgsByUserID(db.GetEngine(db.DefaultContext), userID, showAll)
}
// MinimalOrg represents a simple orgnization with only needed columns // MinimalOrg represents a simple orgnization with only needed columns
type MinimalOrg = Organization type MinimalOrg = Organization
@ -519,6 +501,51 @@ func GetUserOrgsList(user *User) ([]*MinimalOrg, error) {
return orgs, nil return orgs, nil
} }
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
UserID int64
IncludePrivate bool
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
var cond = builder.Eq{"uid": userID}
if !includePrivate {
cond["is_public"] = true
}
return builder.Select("org_id").From("org_user").Where(cond)
}
func (opts FindOrgOptions) toConds() builder.Cond {
var cond = builder.NewCond()
if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludePrivate)))
}
if !opts.IncludePrivate {
cond = cond.And(builder.Eq{"`user`.visibility": structs.VisibleTypePublic})
}
return cond
}
// FindOrgs returns a list of organizations according given conditions
func FindOrgs(opts FindOrgOptions) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
sess := db.GetEngine(db.DefaultContext).
Where(opts.toConds()).
Asc("`user`.name")
if opts.Page > 0 && opts.PageSize > 0 {
sess.Limit(opts.PageSize, opts.PageSize*(opts.Page-1))
}
return orgs, sess.Find(&orgs)
}
// CountOrgs returns total count organizations according options
func CountOrgs(opts FindOrgOptions) (int64, error) {
return db.GetEngine(db.DefaultContext).
Where(opts.toConds()).
Count(new(User))
}
func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*User, error) { func getOwnedOrgsByUserID(sess db.Engine, userID int64) ([]*User, error) {
orgs := make([]*User, 0, 10) orgs := make([]*User, 0, 10)
return orgs, sess. return orgs, sess.

View file

@ -307,18 +307,31 @@ func TestIsPublicMembership(t *testing.T) {
test(unittest.NonexistentID, unittest.NonexistentID, false) test(unittest.NonexistentID, unittest.NonexistentID, false)
} }
func TestGetOrgsByUserID(t *testing.T) { func TestFindOrgs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
orgs, err := GetOrgsByUserID(4, true) orgs, err := FindOrgs(FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
assert.NoError(t, err) assert.NoError(t, err)
if assert.Len(t, orgs, 1) { if assert.Len(t, orgs, 1) {
assert.EqualValues(t, 3, orgs[0].ID) assert.EqualValues(t, 3, orgs[0].ID)
} }
orgs, err = GetOrgsByUserID(4, false) orgs, err = FindOrgs(FindOrgOptions{
UserID: 4,
IncludePrivate: false,
})
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, orgs, 0) assert.Len(t, orgs, 0)
total, err := CountOrgs(FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
assert.NoError(t, err)
assert.EqualValues(t, 1, total)
} }
func TestGetOwnedOrgsByUserID(t *testing.T) { func TestGetOwnedOrgsByUserID(t *testing.T) {

View file

@ -12,7 +12,6 @@ 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/modules/util"
"code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web"
"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/routers/api/v1/utils"
@ -20,25 +19,31 @@ import (
) )
func listUserOrgs(ctx *context.APIContext, u *models.User) { func listUserOrgs(ctx *context.APIContext, u *models.User) {
listOptions := utils.GetListOptions(ctx) listOptions := utils.GetListOptions(ctx)
showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == u.ID) showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == u.ID)
orgs, err := models.GetOrgsByUserID(u.ID, showPrivate) var opts = models.FindOrgOptions{
ListOptions: listOptions,
UserID: u.ID,
IncludePrivate: showPrivate,
}
orgs, err := models.FindOrgs(opts)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetOrgsByUserID", err) ctx.Error(http.StatusInternalServerError, "FindOrgs", err)
return
}
maxResults, err := models.CountOrgs(opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "CountOrgs", err)
return return
} }
maxResults := len(orgs)
orgs, _ = util.PaginateSlice(orgs, listOptions.Page, listOptions.PageSize).([]*models.Organization)
apiOrgs := make([]*api.Organization, len(orgs)) apiOrgs := make([]*api.Organization, len(orgs))
for i := range orgs { for i := range orgs {
apiOrgs[i] = convert.ToOrganization(orgs[i]) apiOrgs[i] = convert.ToOrganization(orgs[i])
} }
ctx.SetLinkHeader(maxResults, listOptions.PageSize) ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.SetTotalCountHeader(int64(maxResults)) ctx.SetTotalCountHeader(int64(maxResults))
ctx.JSON(http.StatusOK, &apiOrgs) ctx.JSON(http.StatusOK, &apiOrgs)
} }

View file

@ -167,9 +167,12 @@ func Profile(ctx *context.Context) {
showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID) showPrivate := ctx.IsSigned && (ctx.User.IsAdmin || ctx.User.ID == ctxUser.ID)
orgs, err := models.GetOrgsByUserID(ctxUser.ID, showPrivate) orgs, err := models.FindOrgs(models.FindOrgOptions{
UserID: ctxUser.ID,
IncludePrivate: showPrivate,
})
if err != nil { if err != nil {
ctx.ServerError("GetOrgsByUserIDDesc", err) ctx.ServerError("FindOrgs", err)
return return
} }

View file

@ -214,12 +214,34 @@ func DeleteAvatar(ctx *context.Context) {
func Organization(ctx *context.Context) { func Organization(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsOrganization"] = true ctx.Data["PageIsSettingsOrganization"] = true
orgs, err := models.GetOrgsByUserID(ctx.User.ID, ctx.IsSigned)
opts := models.FindOrgOptions{
ListOptions: db.ListOptions{
PageSize: setting.UI.Admin.UserPagingNum,
Page: ctx.FormInt("page"),
},
UserID: ctx.User.ID,
IncludePrivate: ctx.IsSigned,
}
if opts.Page <= 0 {
opts.Page = 1
}
orgs, err := models.FindOrgs(opts)
if err != nil { if err != nil {
ctx.ServerError("GetOrgsByUserID", err) ctx.ServerError("FindOrgs", err)
return
}
total, err := models.CountOrgs(opts)
if err != nil {
ctx.ServerError("CountOrgs", err)
return return
} }
ctx.Data["Orgs"] = orgs ctx.Data["Orgs"] = orgs
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplSettingsOrganization) ctx.HTML(http.StatusOK, tplSettingsOrganization)
} }

View file

@ -29,6 +29,7 @@
</div> </div>
{{end}} {{end}}
</div> </div>
{{template "base/paginate" .}}
{{else}} {{else}}
{{.i18n.Tr "settings.orgs_none"}} {{.i18n.Tr "settings.orgs_none"}}
{{end}} {{end}}