Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support pagination of organizations on user settings pages #16083

Merged
merged 16 commits into from
Nov 22, 2021
5 changes: 4 additions & 1 deletion integrations/org_count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ func doCheckOrgCounts(username string, orgCounts map[string]int, strict bool, ca
Name: username,
}).(*models.User)

orgs, err := models.GetOrgsByUserID(user.ID, true)
orgs, err := models.FindOrgs(models.FindOrgOptions{
UserID: user.ID,
IncludePrivate: true,
})
assert.NoError(t, err)

calcOrgCounts := map[string]int{}
Expand Down
63 changes: 45 additions & 18 deletions models/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,24 +440,6 @@ func getUsersWhoCanCreateOrgRepo(e db.Engine, orgID int64) ([]*User, error) {
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
type MinimalOrg = Organization

Expand Down Expand Up @@ -519,6 +501,51 @@ func GetUserOrgsList(user *User) ([]*MinimalOrg, error) {
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) {
orgs := make([]*User, 0, 10)
return orgs, sess.
Expand Down
19 changes: 16 additions & 3 deletions models/org_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,18 +307,31 @@ func TestIsPublicMembership(t *testing.T) {
test(unittest.NonexistentID, unittest.NonexistentID, false)
}

func TestGetOrgsByUserID(t *testing.T) {
func TestFindOrgs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

orgs, err := GetOrgsByUserID(4, true)
orgs, err := FindOrgs(FindOrgOptions{
UserID: 4,
IncludePrivate: true,
})
assert.NoError(t, err)
if assert.Len(t, orgs, 1) {
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.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) {
Expand Down
21 changes: 13 additions & 8 deletions routers/api/v1/org/org.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,38 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
"code.gitea.io/gitea/services/org"
)

func listUserOrgs(ctx *context.APIContext, u *models.User) {

listOptions := utils.GetListOptions(ctx)
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 {
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
}

maxResults := len(orgs)
orgs, _ = util.PaginateSlice(orgs, listOptions.Page, listOptions.PageSize).([]*models.Organization)

apiOrgs := make([]*api.Organization, len(orgs))
for i := range orgs {
apiOrgs[i] = convert.ToOrganization(orgs[i])
}

ctx.SetLinkHeader(maxResults, listOptions.PageSize)
ctx.SetLinkHeader(int(maxResults), listOptions.PageSize)
ctx.SetTotalCountHeader(int64(maxResults))
ctx.JSON(http.StatusOK, &apiOrgs)
}
Expand Down
7 changes: 5 additions & 2 deletions routers/web/user/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,12 @@ func Profile(ctx *context.Context) {

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 {
ctx.ServerError("GetOrgsByUserIDDesc", err)
ctx.ServerError("FindOrgs", err)
return
}

Expand Down
26 changes: 24 additions & 2 deletions routers/web/user/setting/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,34 @@ func DeleteAvatar(ctx *context.Context) {
func Organization(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
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 {
ctx.ServerError("FindOrgs", err)
return
}
total, err := models.CountOrgs(opts)
if err != nil {
ctx.ServerError("GetOrgsByUserID", err)
ctx.ServerError("CountOrgs", err)
return
}
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)
}

Expand Down
1 change: 1 addition & 0 deletions templates/user/settings/organization.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
</div>
{{end}}
</div>
{{template "base/paginate" .}}
{{else}}
{{.i18n.Tr "settings.orgs_none"}}
{{end}}
Expand Down