From ed2ca46f30e1e53a1ccb1dea386ba2b04366659b Mon Sep 17 00:00:00 2001 From: Animesh Kumar <83393501+kmranimesh@users.noreply.github.com> Date: Wed, 11 Feb 2026 00:16:39 +0530 Subject: [PATCH 1/7] feat: add pagination, search, and description to org teams page Add pagination and keyword search to the organization teams list page. Display team descriptions in team cards. - Refactor Teams() handler to use SearchTeam() with ListOptions for server-side pagination and keyword filtering - Add search input with the existing 'search.team_kind' locale string - Show team description in an attached segment below the team name - Add standard pagination widget at the bottom of the list Fixes go-gitea/gitea#34482 --- routers/web/org/teams.go | 33 +++++++++++++++++++++++++++++++-- templates/org/team/teams.tmpl | 20 +++++++++++++++++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 0ec7cfddc5..d72484d728 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -54,13 +54,42 @@ func Teams(ctx *context.Context) { ctx.Data["Title"] = org.FullName ctx.Data["PageIsOrgTeams"] = true - for _, t := range ctx.Org.Teams { + page := max(ctx.FormInt("page"), 1) + keyword := ctx.FormTrim("q") + + opts := &org_model.SearchTeamOptions{ + Keyword: keyword, + OrgID: org.ID, + IncludeDesc: true, + ListOptions: db.ListOptions{ + Page: page, + PageSize: setting.UI.MembersPagingNum, + }, + } + + if !ctx.Org.IsOwner { + opts.UserID = ctx.Doer.ID + } + + teams, count, err := org_model.SearchTeam(ctx, opts) + if err != nil { + ctx.ServerError("SearchTeam", err) + return + } + + for _, t := range teams { if err := t.LoadMembers(ctx); err != nil { ctx.ServerError("GetMembers", err) return } } - ctx.Data["Teams"] = ctx.Org.Teams + + pager := context.NewPagination(int(count), setting.UI.MembersPagingNum, page, 5) + pager.AddParamFromRequest(ctx.Req) + ctx.Data["Page"] = pager + ctx.Data["Teams"] = teams + ctx.Data["Keyword"] = keyword + ctx.Data["Total"] = count ctx.HTML(http.StatusOK, tplTeams) } diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index 50f4f7e97d..85ace44bc0 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -7,9 +7,19 @@
{{svg "octicon-plus"}} {{ctx.Locale.Tr "org.create_new_team"}}
-
{{end}} + + +
+
{{range .Teams}}
@@ -30,6 +40,11 @@ {{end}}
+ {{if .Description}} +
+

{{.Description}}

+
+ {{end}}
{{range .Members}} {{template "shared/user/avatarlink" dict "user" .}} @@ -41,6 +56,8 @@
{{end}} + + {{template "base/paginate" .}} {{template "base/footer" .}} + From be33fd7749abc81415bbc5ed001d799363a87547 Mon Sep 17 00:00:00 2001 From: Animesh Kumar <83393501+kmranimesh@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:52:17 +0530 Subject: [PATCH 2/7] Refactor team visibility logic and update search input attributes --- models/organization/org.go | 24 ++++++++++++++++++++++++ routers/web/org/teams.go | 8 +++++++- services/context/org.go | 21 ++++++--------------- templates/org/team/teams.tmpl | 2 +- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index b4d28f5405..e60de91691 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -596,3 +596,27 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder { "team_user.uid": userID, }) } + +// CanUserSeeAllTeams returns true if user can see all teams in organization +func (org *Organization) CanUserSeeAllTeams(ctx context.Context, userID int64) (bool, error) { + isOwner, err := org.IsOwnedBy(ctx, userID) + if err != nil { + return false, err + } + if isOwner { + return true, nil + } + + teams, err := org.GetUserTeams(ctx, userID) + if err != nil { + return false, err + } + + for _, team := range teams { + if team.IncludesAllRepositories && team.HasAdminAccess() { + return true, nil + } + } + + return false, nil +} diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index d72484d728..6330c1b180 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -67,7 +67,13 @@ func Teams(ctx *context.Context) { }, } - if !ctx.Org.IsOwner { + + canSeeAllTeams, err := ctx.Org.Organization.CanUserSeeAllTeams(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("CanUserSeeAllTeams", err) + return + } + if !canSeeAllTeams { opts.UserID = ctx.Doer.ID } diff --git a/services/context/org.go b/services/context/org.go index 4c64ff72a9..78e6b1c3e0 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -171,24 +171,15 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) { return } + // Team. if ctx.Org.IsMember { - shouldSeeAllTeams := false - if ctx.Org.IsOwner { - shouldSeeAllTeams = true - } else { - teams, err := org.GetUserTeams(ctx, ctx.Doer.ID) - if err != nil { - ctx.ServerError("GetUserTeams", err) - return - } - for _, team := range teams { - if team.IncludesAllRepositories && team.HasAdminAccess() { - shouldSeeAllTeams = true - break - } - } + shouldSeeAllTeams, err := org.CanUserSeeAllTeams(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("CanUserSeeAllTeams", err) + return } + if shouldSeeAllTeams { ctx.Org.Teams, err = org.LoadTeams(ctx) if err != nil { diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index 85ace44bc0..2c97f8ce3b 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -12,7 +12,7 @@ {{template "base/footer" .}} - From 824e52cb263ce6f6dd729bcb0a67fd3b3b6ba8b5 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 13 Feb 2026 17:43:11 +0100 Subject: [PATCH 5/7] Apply suggestion from @silverwind Signed-off-by: silverwind --- templates/org/team/teams.tmpl | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index 34475b276c..351da54c38 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -56,7 +56,6 @@ {{end}} - {{template "base/paginate" .}} From f5773c16e8ddf27220cc0673ac1f38f900bc09e0 Mon Sep 17 00:00:00 2001 From: Animesh Kumar <83393501+kmranimesh@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:49:59 +0530 Subject: [PATCH 6/7] Fix CanUserSeeAllTeams to handle Site Admins correctly --- models/organization/org.go | 9 ++++++--- routers/web/org/teams.go | 2 +- services/context/org.go | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index e60de91691..9f174372a7 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -598,8 +598,11 @@ func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder { } // CanUserSeeAllTeams returns true if user can see all teams in organization -func (org *Organization) CanUserSeeAllTeams(ctx context.Context, userID int64) (bool, error) { - isOwner, err := org.IsOwnedBy(ctx, userID) +func (org *Organization) CanUserSeeAllTeams(ctx context.Context, user *user_model.User) (bool, error) { + if user.IsAdmin { + return true, nil + } + isOwner, err := org.IsOwnedBy(ctx, user.ID) if err != nil { return false, err } @@ -607,7 +610,7 @@ func (org *Organization) CanUserSeeAllTeams(ctx context.Context, userID int64) ( return true, nil } - teams, err := org.GetUserTeams(ctx, userID) + teams, err := org.GetUserTeams(ctx, user.ID) if err != nil { return false, err } diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 1446b848e2..6ec20a79ed 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -67,7 +67,7 @@ func Teams(ctx *context.Context) { }, } - canSeeAllTeams, err := ctx.Org.Organization.CanUserSeeAllTeams(ctx, ctx.Doer.ID) + canSeeAllTeams, err := ctx.Org.Organization.CanUserSeeAllTeams(ctx, ctx.Doer) if err != nil { ctx.ServerError("CanUserSeeAllTeams", err) return diff --git a/services/context/org.go b/services/context/org.go index 69c327ad7c..70df8533c9 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -173,7 +173,7 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) { // Team. if ctx.Org.IsMember { - shouldSeeAllTeams, err := org.CanUserSeeAllTeams(ctx, ctx.Doer.ID) + shouldSeeAllTeams, err := org.CanUserSeeAllTeams(ctx, ctx.Doer) if err != nil { ctx.ServerError("CanUserSeeAllTeams", err) return From 6f58072ac0a4dcc33cba164ae202b8875d6152bc Mon Sep 17 00:00:00 2001 From: Animesh Kumar <83393501+kmranimesh@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:30:02 +0530 Subject: [PATCH 7/7] optimize team loading and CanUserSeeAllTeams query --- models/organization/org.go | 20 ++++++-------- models/organization/team_list.go | 13 +++++++++ routers/web/org/home.go | 16 +++++++++++ routers/web/user/home.go | 16 +++++++++++ services/context/org.go | 46 +++++++++++++++++++++++--------- 5 files changed, 86 insertions(+), 25 deletions(-) diff --git a/models/organization/org.go b/models/organization/org.go index 9f174372a7..0c60c0682d 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -610,16 +610,12 @@ func (org *Organization) CanUserSeeAllTeams(ctx context.Context, user *user_mode return true, nil } - teams, err := org.GetUserTeams(ctx, user.ID) - if err != nil { - return false, err - } - - for _, team := range teams { - if team.IncludesAllRepositories && team.HasAdminAccess() { - return true, nil - } - } - - return false, nil + return db.GetEngine(ctx). + Table("team"). + Join("INNER", "team_user", "team_user.team_id = team.id"). + Where("team_user.uid = ?", user.ID). + And("team.org_id = ?", org.ID). + And("team.includes_all_repositories = ?", true). + And("team.authorize >= ?", perm.AccessModeAdmin). + Exist(new(Team)) } diff --git a/models/organization/team_list.go b/models/organization/team_list.go index 0274f9c5ba..9cfb7ebd8e 100644 --- a/models/organization/team_list.go +++ b/models/organization/team_list.go @@ -96,6 +96,19 @@ func SearchTeam(ctx context.Context, opts *SearchTeamOptions) (TeamList, int64, return teams, count, nil } +// CountTeam counts teams matching the given options without loading full team objects. +func CountTeam(ctx context.Context, opts *SearchTeamOptions) (int64, error) { + sess := db.GetEngine(ctx) + + cond := opts.toCond() + + if opts.UserID > 0 { + sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id") + } + + return sess.Where(cond).Count(new(Team)) +} + // GetRepoTeams gets the list of teams that has access to the repository func GetRepoTeams(ctx context.Context, orgID, repoID int64) (teams TeamList, err error) { return teams, db.GetEngine(ctx). diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 63ae6c683b..50181ae2a2 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -99,6 +99,22 @@ func home(ctx *context.Context, viewRepositories bool) { return } ctx.Data["Members"] = members + if ctx.Org.IsMember && ctx.Org.Teams == nil { + shouldSeeAllTeams, err := ctx.Org.Organization.CanUserSeeAllTeams(ctx, ctx.Doer) + if err != nil { + ctx.ServerError("CanUserSeeAllTeams", err) + return + } + if shouldSeeAllTeams { + ctx.Org.Teams, err = ctx.Org.Organization.LoadTeams(ctx) + } else { + ctx.Org.Teams, err = ctx.Org.Organization.GetUserTeams(ctx, ctx.Doer.ID) + } + if err != nil { + ctx.ServerError("LoadTeams", err) + return + } + } ctx.Data["Teams"] = ctx.Org.Teams ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull ctx.Data["ShowMemberAndTeamTab"] = ctx.Org.IsMember || len(members) > 0 diff --git a/routers/web/user/home.go b/routers/web/user/home.go index afdba9a75f..7999f683a1 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -60,6 +60,22 @@ func prepareDashboardContextUserOrgTeams(ctx *context.Context) *user_model.User orgName := ctx.PathParam("org") if len(orgName) > 0 { ctxUser = ctx.Org.Organization.AsUser() + if ctx.Org.IsMember && ctx.Org.Teams == nil { + shouldSeeAllTeams, err := ctx.Org.Organization.CanUserSeeAllTeams(ctx, ctx.Doer) + if err != nil { + ctx.ServerError("CanUserSeeAllTeams", err) + return nil + } + if shouldSeeAllTeams { + ctx.Org.Teams, err = ctx.Org.Organization.LoadTeams(ctx) + } else { + ctx.Org.Teams, err = ctx.Org.Organization.GetUserTeams(ctx, ctx.Doer.ID) + } + if err != nil { + ctx.ServerError("LoadTeams", err) + return nil + } + } ctx.Data["Teams"] = ctx.Org.Teams } ctx.Data["ContextUser"] = ctxUser diff --git a/services/context/org.go b/services/context/org.go index 70df8533c9..c5d94efe7d 100644 --- a/services/context/org.go +++ b/services/context/org.go @@ -179,24 +179,44 @@ func OrgAssignment(orgAssignmentOpts OrgAssignmentOptions) func(ctx *Context) { return } - if shouldSeeAllTeams { - ctx.Org.Teams, err = org.LoadTeams(ctx) - if err != nil { - ctx.ServerError("LoadTeams", err) - return - } - } else { - ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID) - if err != nil { - ctx.ServerError("GetUserTeams", err) - return - } + countOpts := &organization.SearchTeamOptions{ + OrgID: org.ID, } - ctx.Data["NumTeams"] = len(ctx.Org.Teams) + if !shouldSeeAllTeams { + countOpts.UserID = ctx.Doer.ID + } + numTeams, err := organization.CountTeam(ctx, countOpts) + if err != nil { + ctx.ServerError("CountTeam", err) + return + } + ctx.Data["NumTeams"] = numTeams } teamName := ctx.PathParam("team") if len(teamName) > 0 { + if ctx.Org.Teams == nil && ctx.Org.IsMember { + shouldSeeAllTeams, err := org.CanUserSeeAllTeams(ctx, ctx.Doer) + if err != nil { + ctx.ServerError("CanUserSeeAllTeams", err) + return + } + + if shouldSeeAllTeams { + ctx.Org.Teams, err = org.LoadTeams(ctx) + if err != nil { + ctx.ServerError("LoadTeams", err) + return + } + } else { + ctx.Org.Teams, err = org.GetUserTeams(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("GetUserTeams", err) + return + } + } + } + teamExists := false for _, team := range ctx.Org.Teams { if strings.EqualFold(team.LowerName, teamName) {