mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-15 20:25:18 +02:00
Closes #37670. Today, org members in Gitea only see teams they're a member of. In larger orgs that hurts onboarding and discoverability — there's no way to look up which team owns what without asking around. GitHub solves this with a per-team visibility setting; this PR brings the same model to Gitea. ## What changes - Every team gets a `visibility` setting: - `private` *(default)* — only team members and org owners can see the team. Same as today's behavior. - `limited` — listable by any member of the organization. Members and the repos the team has access to are visible too. Non-org-members still see nothing. - `public` — listable by any signed-in user. - The Owners team visibility is fixed and cannot be changed via settings. - Existing teams default to `private`, so this is a no-op for anyone who doesn't change anything. ## API - `Team`, `CreateTeamOption`, `EditTeamOption` all gain a `visibility` field (string enum: `private` | `limited` | `public`). - `GET /orgs/{org}/teams` and `/orgs/{org}/teams/search` now apply the same visibility rules as the web UI: - site admins and org owners still see every team - other org members see their own teams plus any `limited` or `public` team - `private` teams are no longer leaked through these endpoints - Swagger/OpenAPI specs regenerated. ## UI View from admin2 (not an owner): <img width="1669" height="726" src="https://github.com/user-attachments/assets/daf4bccb-644b-4426-b178-71963aeaf73b" /> View from admin (owner): <img width="2559" height="863" src="https://github.com/user-attachments/assets/4f22cebc-e9df-4fd2-8ed4-724d31fadb7a" /> --------- Signed-off-by: bircni <bircni@icloud.com> Co-authored-by: TheFox0x7 <thefox0x7@gmail.com> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
76 lines
3.5 KiB
Handlebars
76 lines
3.5 KiB
Handlebars
{{template "base/head" .}}
|
|
<div role="main" aria-label="{{.Title}}" class="page-content organization teams">
|
|
{{template "org/header" .}}
|
|
<div class="ui container">
|
|
{{template "base/alert" .}}
|
|
{{if .IsOrganizationOwner}}
|
|
<div class="flex-text-block">
|
|
<div class="tw-flex-1">{{ctx.Locale.Tr "org.teams.manage_team_member_prompt"}}</div>
|
|
<a class="ui primary button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus"}} {{ctx.Locale.Tr "org.create_new_team"}}</a>
|
|
</div>
|
|
{{end}}
|
|
|
|
<form class="ui form ignore-dirty tw-my-4" method="get" action="{{$.Link}}">
|
|
<div class="ui fluid action input">
|
|
<input type="search" name="q" value="{{$.Keyword}}" placeholder="{{ctx.Locale.Tr "search.team_kind"}}" maxlength="255" spellcheck="false">
|
|
<button class="ui button" type="submit">{{svg "octicon-search"}}</button>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="ui two column stackable grid">
|
|
{{range $team := $.OrgListTeams}}
|
|
<div class="column team-item-box">
|
|
<div class="ui top attached header muted-links flex-left-right team-item-header">
|
|
<div class="flex-text-inline">
|
|
<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}"><strong>{{.Name}}</strong></a>
|
|
{{if .IsPrivate}}
|
|
<span class="ui mini label" data-tooltip-content="{{ctx.Locale.Tr "org.teams.visibility_private_helper"}}">{{ctx.Locale.Tr "org.teams.visibility_private"}}</span>
|
|
{{else if .IsLimited}}
|
|
<span class="ui mini label" data-tooltip-content="{{ctx.Locale.Tr "org.teams.visibility_limited_helper"}}">{{ctx.Locale.Tr "org.teams.visibility_limited"}}</span>
|
|
{{else if .IsPublic}}
|
|
<span class="ui mini label" data-tooltip-content="{{ctx.Locale.Tr "org.teams.visibility_public_helper"}}">{{ctx.Locale.Tr "org.teams.visibility_public"}}</span>
|
|
{{end}}
|
|
</div>
|
|
<div class="flex-text-block tw-flex-wrap">
|
|
<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}">{{.NumMembers}} {{ctx.Locale.Tr "org.lower_members"}}</a>
|
|
·
|
|
<a href="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/repositories">{{.NumRepos}} {{ctx.Locale.Tr "org.lower_repositories"}}</a>
|
|
{{if .IsMember ctx $.SignedUser.ID}}
|
|
<button class="ui red mini compact button show-modal" data-modal="#org-member-leave-team"
|
|
data-modal-form.action="{{$.OrgLink}}/teams/{{.LowerName | PathEscape}}/action/leave?uid={{$.SignedUser.ID}}"
|
|
data-modal-to-leave-team-name="{{.Name}}"
|
|
>{{ctx.Locale.Tr "org.teams.leave"}}</button>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
{{if $team.Description}}
|
|
<div class="ui attached header team-item-description">
|
|
{{if $team.Description}}{{$team.Description}}{{end}}
|
|
</div>
|
|
{{end}}
|
|
<div class="ui attached segment">
|
|
<div class="flex-text-block tw-flex-wrap">
|
|
{{range .Members}}
|
|
{{template "shared/user/avatarlink" dict "user" . "size" 32 "tooltip" true}}
|
|
{{else}}
|
|
<a class="flex-text-inline tw-h-[32px]" href="{{$.OrgLink}}/teams/{{$team.LowerName | PathEscape}}">{{ctx.Locale.Tr "org.teams.add_team_member"}}</a>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
{{template "base/paginate" .}}
|
|
</div>
|
|
</div>
|
|
<div class="ui mini modal" id="org-member-leave-team">
|
|
<div class="header">
|
|
{{ctx.Locale.Tr "org.teams.leave"}}
|
|
</div>
|
|
<form class="content ui form form-fetch-action" method="post">
|
|
<p>{{ctx.Locale.Tr "org.teams.leave.detail" (HTMLFormat `<span class="%s"></span>` "to-leave-team-name")}}</p>
|
|
{{template "base/modal_actions_confirm" .}}
|
|
</form>
|
|
</div>
|
|
{{template "base/footer" .}}
|