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 @@
-
{{end}}
+
+
+
+
{{range .Teams}}
@@ -30,6 +40,11 @@
{{end}}
+ {{if .Description}}
+
+ {{end}}
{{range .Members}}
{{template "shared/user/avatarlink" dict "user" .}}
@@ -41,6 +56,8 @@
{{end}}
+
+ {{template "base/paginate" .}}
@@ -53,3 +70,4 @@
{{template "base/modal_actions_confirm" .}}
{{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) {