2014-06-29 16:30:41 -04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2019-11-06 10:37:14 +01:00
// Copyright 2019 The Gitea Authors. All rights reserved.
2022-11-27 13:20:29 -05:00
// SPDX-License-Identifier: MIT
2014-06-29 16:30:41 -04:00
2014-06-27 21:33:49 +08:00
package org
import (
2024-03-04 09:16:03 +01:00
"errors"
2023-04-14 04:06:10 +09:00
"fmt"
2019-06-12 21:41:28 +02:00
"net/http"
2021-11-16 18:18:25 +00:00
"net/url"
2014-08-26 18:11:15 +08:00
"path"
2022-01-05 11:37:00 +08:00
"strconv"
2017-11-26 16:01:48 +08:00
"strings"
2014-08-26 18:11:15 +08:00
2022-04-08 02:59:56 +08:00
"code.gitea.io/gitea/models/db"
2022-10-19 14:40:28 +02:00
org_model "code.gitea.io/gitea/models/organization"
2021-11-28 19:58:28 +08:00
"code.gitea.io/gitea/models/perm"
2021-12-10 09:27:50 +08:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-10 03:57:58 +08:00
unit_model "code.gitea.io/gitea/models/unit"
2021-11-24 17:49:20 +08:00
user_model "code.gitea.io/gitea/models/user"
2016-11-10 17:24:48 +01:00
"code.gitea.io/gitea/modules/log"
2022-10-19 14:40:28 +02:00
"code.gitea.io/gitea/modules/setting"
2024-12-22 23:33:19 +08:00
"code.gitea.io/gitea/modules/templates"
2021-01-26 23:36:53 +08:00
"code.gitea.io/gitea/modules/web"
2023-09-06 10:49:36 +02:00
shared_user "code.gitea.io/gitea/routers/web/shared/user"
2024-02-27 15:12:22 +08:00
"code.gitea.io/gitea/services/context"
2022-12-29 03:57:15 +01:00
"code.gitea.io/gitea/services/convert"
2021-04-06 20:44:05 +01:00
"code.gitea.io/gitea/services/forms"
2022-08-25 10:31:57 +08:00
org_service "code.gitea.io/gitea/services/org"
2023-09-08 12:51:15 +08:00
repo_service "code.gitea.io/gitea/services/repository"
2014-06-27 21:33:49 +08:00
)
2014-06-29 16:30:41 -04:00
const (
2016-11-18 11:03:03 +08:00
// tplTeams template path for teams list page
2024-12-22 23:33:19 +08:00
tplTeams templates . TplName = "org/team/teams"
2016-11-18 11:03:03 +08:00
// tplTeamNew template path for create new team page
2024-12-22 23:33:19 +08:00
tplTeamNew templates . TplName = "org/team/new"
2016-11-18 11:03:03 +08:00
// tplTeamMembers template path for showing team members page
2024-12-22 23:33:19 +08:00
tplTeamMembers templates . TplName = "org/team/members"
2016-11-18 11:03:03 +08:00
// tplTeamRepositories template path for showing team repositories page
2024-12-22 23:33:19 +08:00
tplTeamRepositories templates . TplName = "org/team/repositories"
2022-10-19 14:40:28 +02:00
// tplTeamInvite template path for team invites page
2024-12-22 23:33:19 +08:00
tplTeamInvite templates . TplName = "org/team/invite"
2014-06-29 16:30:41 -04:00
)
2016-11-18 11:03:03 +08:00
// Teams render teams list page
2016-03-11 11:56:52 -05:00
func Teams ( ctx * context . Context ) {
2025-04-11 03:12:55 +08:00
if _ , err := shared_user . RenderUserOrgHeader ( ctx ) ; err != nil {
ctx . ServerError ( "RenderUserOrgHeader" , err )
return
}
2014-08-16 16:21:17 +08:00
org := ctx . Org . Organization
ctx . Data [ "Title" ] = org . FullName
ctx . Data [ "PageIsOrgTeams" ] = true
2014-06-29 16:30:41 -04:00
2021-11-19 19:41:40 +08:00
for _ , t := range ctx . Org . Teams {
2022-12-03 10:48:26 +08:00
if err := t . LoadMembers ( ctx ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetMembers" , err )
2014-06-29 16:30:41 -04:00
return
}
}
2021-11-19 19:41:40 +08:00
ctx . Data [ "Teams" ] = ctx . Org . Teams
2014-06-29 16:30:41 -04:00
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeams )
2014-06-27 21:33:49 +08:00
}
2016-11-18 11:03:03 +08:00
// TeamsAction response for join, leave, remove, add operations to team
2016-03-11 11:56:52 -05:00
func TeamsAction ( ctx * context . Context ) {
2021-08-11 02:31:13 +02:00
page := ctx . FormString ( "page" )
2014-08-16 16:21:17 +08:00
var err error
2024-12-24 21:47:45 +08:00
switch ctx . PathParam ( "action" ) {
2014-08-16 16:21:17 +08:00
case "join" :
2014-08-24 21:09:05 +08:00
if ! ctx . Org . IsOwner {
2025-02-17 14:13:17 +08:00
ctx . HTTPError ( http . StatusNotFound )
2014-08-24 21:09:05 +08:00
return
}
2024-11-27 13:12:26 -08:00
err = org_service . AddTeamMember ( ctx , ctx . Org . Team , ctx . Doer )
2014-08-16 16:21:17 +08:00
case "leave" :
2024-11-27 13:12:26 -08:00
err = org_service . RemoveTeamMember ( ctx , ctx . Org . Team , ctx . Doer )
2021-08-27 03:57:40 +01:00
if err != nil {
2022-10-19 14:40:28 +02:00
if org_model . IsErrLastOrgOwner ( err ) {
2021-08-27 03:57:40 +01:00
ctx . Flash . Error ( ctx . Tr ( "form.last_org_owner" ) )
} else {
2024-12-24 21:47:45 +08:00
log . Error ( "Action(%s): %v" , ctx . PathParam ( "action" ) , err )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2021-08-27 03:57:40 +01:00
"ok" : false ,
"err" : err . Error ( ) ,
} )
return
}
}
2023-08-12 16:02:22 +09:00
checkIsOrgMemberAndRedirect ( ctx , ctx . Org . OrgLink + "/teams/" )
2021-08-27 03:57:40 +01:00
return
2014-08-24 21:09:05 +08:00
case "remove" :
if ! ctx . Org . IsOwner {
2025-02-17 14:13:17 +08:00
ctx . HTTPError ( http . StatusNotFound )
2014-08-24 21:09:05 +08:00
return
}
2022-10-19 14:40:28 +02:00
2024-03-04 09:16:03 +01:00
user , _ := user_model . GetUserByID ( ctx , ctx . FormInt64 ( "uid" ) )
if user == nil {
2022-10-19 14:40:28 +02:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams" )
return
}
2024-11-27 13:12:26 -08:00
err = org_service . RemoveTeamMember ( ctx , ctx . Org . Team , user )
2021-08-27 03:57:40 +01:00
if err != nil {
2022-10-19 14:40:28 +02:00
if org_model . IsErrLastOrgOwner ( err ) {
2021-08-27 03:57:40 +01:00
ctx . Flash . Error ( ctx . Tr ( "form.last_org_owner" ) )
} else {
2024-12-24 21:47:45 +08:00
log . Error ( "Action(%s): %v" , ctx . PathParam ( "action" ) , err )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2021-08-27 03:57:40 +01:00
"ok" : false ,
"err" : err . Error ( ) ,
} )
return
}
}
2023-08-12 16:02:22 +09:00
checkIsOrgMemberAndRedirect ( ctx , ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) )
2021-08-27 03:57:40 +01:00
return
2014-08-24 21:09:05 +08:00
case "add" :
if ! ctx . Org . IsOwner {
2025-02-17 14:13:17 +08:00
ctx . HTTPError ( http . StatusNotFound )
2014-08-24 21:09:05 +08:00
return
}
2024-02-01 18:10:16 +01:00
uname := strings . ToLower ( ctx . FormString ( "uname" ) )
2021-11-24 17:49:20 +08:00
var u * user_model . User
2022-05-20 22:08:52 +08:00
u , err = user_model . GetUserByName ( ctx , uname )
2014-08-24 21:09:05 +08:00
if err != nil {
2021-11-24 17:49:20 +08:00
if user_model . IsErrUserNotExist ( err ) {
2022-10-19 14:40:28 +02:00
if setting . MailService != nil && user_model . ValidateEmail ( uname ) == nil {
2022-10-20 05:23:31 +02:00
if err := org_service . CreateTeamInvite ( ctx , ctx . Doer , ctx . Org . Team , uname ) ; err != nil {
2022-10-19 14:40:28 +02:00
if org_model . IsErrTeamInviteAlreadyExist ( err ) {
ctx . Flash . Error ( ctx . Tr ( "form.duplicate_invite_to_team" ) )
} else if org_model . IsErrUserEmailAlreadyAdded ( err ) {
ctx . Flash . Error ( ctx . Tr ( "org.teams.add_duplicate_users" ) )
} else {
ctx . ServerError ( "CreateTeamInvite" , err )
return
}
}
} else {
ctx . Flash . Error ( ctx . Tr ( "form.user_not_exist" ) )
}
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) )
2014-08-24 21:09:05 +08:00
} else {
2022-10-19 14:40:28 +02:00
ctx . ServerError ( "GetUserByName" , err )
2014-08-24 21:09:05 +08:00
}
return
}
2017-05-26 21:12:02 +08:00
if u . IsOrganization ( ) {
ctx . Flash . Error ( ctx . Tr ( "form.cannot_add_org_to_team" ) )
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) )
2017-05-26 21:12:02 +08:00
return
}
2023-10-03 12:30:41 +02:00
if ctx . Org . Team . IsMember ( ctx , u . ID ) {
2018-08-19 19:49:19 +01:00
ctx . Flash . Error ( ctx . Tr ( "org.teams.add_duplicate_users" ) )
} else {
2024-11-27 13:12:26 -08:00
err = org_service . AddTeamMember ( ctx , ctx . Org . Team , u )
2018-08-19 19:49:19 +01:00
}
2022-10-19 14:40:28 +02:00
page = "team"
case "remove_invite" :
if ! ctx . Org . IsOwner {
2025-02-17 14:13:17 +08:00
ctx . HTTPError ( http . StatusNotFound )
2022-10-19 14:40:28 +02:00
return
}
iid := ctx . FormInt64 ( "iid" )
if iid == 0 {
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) )
return
}
if err := org_model . RemoveInviteByID ( ctx , iid , ctx . Org . Team . ID ) ; err != nil {
2024-12-24 21:47:45 +08:00
log . Error ( "Action(%s): %v" , ctx . PathParam ( "action" ) , err )
2022-10-19 14:40:28 +02:00
ctx . ServerError ( "RemoveInviteByID" , err )
return
}
2014-08-24 21:09:05 +08:00
page = "team"
2014-07-02 16:42:16 -04:00
}
2014-08-16 16:21:17 +08:00
if err != nil {
2022-10-19 14:40:28 +02:00
if org_model . IsErrLastOrgOwner ( err ) {
2014-08-24 21:09:05 +08:00
ctx . Flash . Error ( ctx . Tr ( "form.last_org_owner" ) )
2024-03-04 09:16:03 +01:00
} else if errors . Is ( err , user_model . ErrBlockedUser ) {
ctx . Flash . Error ( ctx . Tr ( "org.teams.members.blocked_user" ) )
2014-08-24 21:09:05 +08:00
} else {
2024-12-24 21:47:45 +08:00
log . Error ( "Action(%s): %v" , ctx . PathParam ( "action" ) , err )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2014-08-24 21:09:05 +08:00
"ok" : false ,
"err" : err . Error ( ) ,
} )
return
}
}
switch page {
case "team" :
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) )
2017-03-09 19:18:49 +08:00
case "home" :
2021-11-19 19:41:40 +08:00
ctx . Redirect ( ctx . Org . Organization . AsUser ( ) . HomeLink ( ) )
2014-08-24 21:09:05 +08:00
default :
ctx . Redirect ( ctx . Org . OrgLink + "/teams" )
2014-07-02 16:42:16 -04:00
}
2014-08-16 16:21:17 +08:00
}
2014-07-02 16:42:16 -04:00
2023-08-12 16:02:22 +09:00
func checkIsOrgMemberAndRedirect ( ctx * context . Context , defaultRedirect string ) {
if isOrgMember , err := org_model . IsOrganizationMember ( ctx , ctx . Org . Organization . ID , ctx . Doer . ID ) ; err != nil {
ctx . ServerError ( "IsOrganizationMember" , err )
return
} else if ! isOrgMember {
if ctx . Org . Organization . Visibility . IsPrivate ( ) {
defaultRedirect = setting . AppSubURL + "/"
} else {
defaultRedirect = ctx . Org . Organization . HomeLink ( )
}
}
ctx . JSONRedirect ( defaultRedirect )
}
2016-11-18 11:03:03 +08:00
// TeamsRepoAction operate team's repository
2016-03-11 11:56:52 -05:00
func TeamsRepoAction ( ctx * context . Context ) {
2014-08-26 18:11:15 +08:00
if ! ctx . Org . IsOwner {
2025-02-17 14:13:17 +08:00
ctx . HTTPError ( http . StatusNotFound )
2014-08-26 18:11:15 +08:00
return
}
var err error
2024-12-24 21:47:45 +08:00
action := ctx . PathParam ( "action" )
2019-11-16 11:36:13 -06:00
switch action {
2014-08-26 18:11:15 +08:00
case "add" :
2021-08-11 02:31:13 +02:00
repoName := path . Base ( ctx . FormString ( "repo_name" ) )
2021-12-10 09:27:50 +08:00
var repo * repo_model . Repository
2023-10-11 06:24:07 +02:00
repo , err = repo_model . GetRepositoryByName ( ctx , ctx . Org . Organization . ID , repoName )
2014-08-26 18:11:15 +08:00
if err != nil {
2021-12-10 09:27:50 +08:00
if repo_model . IsErrRepoNotExist ( err ) {
2014-10-10 05:06:12 -04:00
ctx . Flash . Error ( ctx . Tr ( "org.teams.add_nonexistent_repo" ) )
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) + "/repositories" )
2014-10-10 05:06:12 -04:00
return
}
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetRepositoryByName" , err )
2014-08-26 18:11:15 +08:00
return
}
2024-11-27 13:12:26 -08:00
err = repo_service . TeamAddRepository ( ctx , ctx . Org . Team , repo )
2014-08-26 18:11:15 +08:00
case "remove" :
2023-09-08 12:51:15 +08:00
err = repo_service . RemoveRepositoryFromTeam ( ctx , ctx . Org . Team , ctx . FormInt64 ( "repoid" ) )
2019-11-09 01:39:37 +01:00
case "addall" :
2024-11-27 13:12:26 -08:00
err = repo_service . AddAllRepositoriesToTeam ( ctx , ctx . Org . Team )
2019-11-09 01:39:37 +01:00
case "removeall" :
2024-11-27 13:12:26 -08:00
err = repo_service . RemoveAllRepositoriesFromTeam ( ctx , ctx . Org . Team )
2014-08-26 18:11:15 +08:00
}
if err != nil {
2024-12-24 21:47:45 +08:00
log . Error ( "Action(%s): '%s' %v" , ctx . PathParam ( "action" ) , ctx . Org . Team . Name , err )
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "TeamsRepoAction" , err )
2014-08-26 18:11:15 +08:00
return
}
2019-11-09 01:39:37 +01:00
2019-11-16 11:36:13 -06:00
if action == "addall" || action == "removeall" {
2023-07-26 14:04:01 +08:00
ctx . JSONRedirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) + "/repositories" )
2019-11-16 11:36:13 -06:00
return
}
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( ctx . Org . Team . LowerName ) + "/repositories" )
2014-08-26 18:11:15 +08:00
}
2016-11-18 11:03:03 +08:00
// NewTeam render create new team page
2016-03-11 11:56:52 -05:00
func NewTeam ( ctx * context . Context ) {
2025-04-11 03:12:55 +08:00
if _ , err := shared_user . RenderUserOrgHeader ( ctx ) ; err != nil {
ctx . ServerError ( "RenderUserOrgHeader" , err )
return
}
2014-08-16 16:21:17 +08:00
ctx . Data [ "Title" ] = ctx . Org . Organization . FullName
ctx . Data [ "PageIsOrgTeams" ] = true
ctx . Data [ "PageIsOrgTeamsNew" ] = true
2022-10-19 14:40:28 +02:00
ctx . Data [ "Team" ] = & org_model . Team { }
2021-11-10 03:57:58 +08:00
ctx . Data [ "Units" ] = unit_model . Units
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamNew )
2014-07-02 16:42:16 -04:00
}
2025-04-08 12:15:15 +08:00
// FIXME: TEAM-UNIT-PERMISSION: this design is not right, when a new unit is added in the future,
2025-06-24 21:24:09 +08:00
// The existing teams won't inherit the correct admin permission for the new unit.
// The full history is like this:
// 1. There was only "team", no "team unit", so "team.authorize" was used to determine the team permission.
// 2. Later, "team unit" was introduced, then the usage of "team.authorize" became inconsistent, and causes various bugs.
// - Sometimes, "team.authorize" is used to determine the team permission, e.g. admin, owner
// - Sometimes, "team unit" is used not really used and "team unit" is used.
// - Some functions like `GetTeamsWithAccessToAnyRepoUnit` use both.
//
// 3. After introducing "team unit" and more unclear changes, it becomes difficult to maintain team permissions.
// - Org owner need to click the permission for each unit, but can't just set a common "write" permission for all units.
//
// Ideally, "team.authorize=write" should mean the team has write access to all units including newly (future) added ones.
2023-04-14 04:06:10 +09:00
func getUnitPerms ( forms url . Values , teamPermission perm . AccessMode ) map [ unit_model . Type ] perm . AccessMode {
2022-01-05 11:37:00 +08:00
unitPerms := make ( map [ unit_model . Type ] perm . AccessMode )
2023-04-14 04:06:10 +09:00
for _ , ut := range unit_model . AllRepoUnitTypes {
2025-06-24 21:24:09 +08:00
// Default access mode is none
2023-04-14 04:06:10 +09:00
unitPerms [ ut ] = perm . AccessModeNone
v , ok := forms [ fmt . Sprintf ( "unit_%d" , ut ) ]
if ok {
vv , _ := strconv . Atoi ( v [ 0 ] )
if teamPermission >= perm . AccessModeAdmin {
unitPerms [ ut ] = teamPermission
// Don't allow `TypeExternal{Tracker,Wiki}` to influence this as they can only be set to READ perms.
if ut == unit_model . TypeExternalTracker || ut == unit_model . TypeExternalWiki {
unitPerms [ ut ] = perm . AccessModeRead
}
} else {
unitPerms [ ut ] = perm . AccessMode ( vv )
if unitPerms [ ut ] >= perm . AccessModeAdmin {
unitPerms [ ut ] = perm . AccessModeWrite
}
2022-01-05 11:37:00 +08:00
}
}
}
return unitPerms
}
2016-11-18 11:03:03 +08:00
// NewTeamPost response for create new team
2021-01-26 23:36:53 +08:00
func NewTeamPost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateTeamForm )
2022-01-05 11:37:00 +08:00
includesAllRepositories := form . RepoAccess == "all"
2025-04-08 12:15:15 +08:00
teamPermission := perm . ParseAccessMode ( form . Permission , perm . AccessModeNone , perm . AccessModeAdmin )
unitPerms := getUnitPerms ( ctx . Req . Form , teamPermission )
2014-07-02 16:42:16 -04:00
2022-10-19 14:40:28 +02:00
t := & org_model . Team {
2019-11-06 10:37:14 +01:00
OrgID : ctx . Org . Organization . ID ,
Name : form . TeamName ,
Description : form . Description ,
2025-04-08 12:15:15 +08:00
AccessMode : teamPermission ,
2019-11-06 10:37:14 +01:00
IncludesAllRepositories : includesAllRepositories ,
2019-11-20 12:27:49 +01:00
CanCreateOrgRepo : form . CanCreateOrgRepo ,
2014-07-02 16:42:16 -04:00
}
2018-08-21 18:02:32 +01:00
2023-04-14 04:06:10 +09:00
units := make ( [ ] * org_model . TeamUnit , 0 , len ( unitPerms ) )
for tp , perm := range unitPerms {
units = append ( units , & org_model . TeamUnit {
OrgID : ctx . Org . Organization . ID ,
Type : tp ,
AccessMode : perm ,
} )
2017-07-17 05:04:43 +03:00
}
2023-04-14 04:06:10 +09:00
t . Units = units
2017-07-17 05:04:43 +03:00
2022-01-05 11:37:00 +08:00
ctx . Data [ "Title" ] = ctx . Org . Organization . FullName
ctx . Data [ "PageIsOrgTeams" ] = true
ctx . Data [ "PageIsOrgTeamsNew" ] = true
ctx . Data [ "Units" ] = unit_model . Units
2016-01-29 17:06:14 -05:00
ctx . Data [ "Team" ] = t
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamNew )
2016-01-29 17:06:14 -05:00
return
}
2022-01-05 11:37:00 +08:00
if t . AccessMode < perm . AccessModeAdmin && len ( unitPerms ) == 0 {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "form.team_no_units_error" ) , tplTeamNew , & form )
2017-07-17 05:04:43 +03:00
return
}
2024-11-27 13:12:26 -08:00
if err := org_service . NewTeam ( ctx , t ) ; err != nil {
2016-01-29 17:06:14 -05:00
ctx . Data [ "Err_TeamName" ] = true
switch {
2022-10-19 14:40:28 +02:00
case org_model . IsErrTeamAlreadyExist ( err ) :
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "form.team_name_been_taken" ) , tplTeamNew , & form )
2014-08-16 16:21:17 +08:00
default :
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "NewTeam" , err )
2014-07-02 16:42:16 -04:00
}
return
}
2016-01-29 17:06:14 -05:00
log . Trace ( "Team created: %s/%s" , ctx . Org . Organization . Name , t . Name )
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( t . LowerName ) )
2014-06-27 21:33:49 +08:00
}
2014-06-27 22:04:04 +08:00
2016-11-18 11:03:03 +08:00
// TeamMembers render team members page
2016-03-11 11:56:52 -05:00
func TeamMembers ( ctx * context . Context ) {
2025-04-11 03:12:55 +08:00
if _ , err := shared_user . RenderUserOrgHeader ( ctx ) ; err != nil {
ctx . ServerError ( "RenderUserOrgHeader" , err )
return
}
2014-08-24 21:09:05 +08:00
ctx . Data [ "Title" ] = ctx . Org . Team . Name
ctx . Data [ "PageIsOrgTeams" ] = true
2018-12-09 14:42:11 +08:00
ctx . Data [ "PageIsOrgTeamMembers" ] = true
2023-09-07 09:44:47 +08:00
2022-12-03 10:48:26 +08:00
if err := ctx . Org . Team . LoadMembers ( ctx ) ; err != nil {
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "GetMembers" , err )
2014-08-24 21:09:05 +08:00
return
}
2022-02-23 22:07:05 +00:00
ctx . Data [ "Units" ] = unit_model . Units
2022-10-19 14:40:28 +02:00
invites , err := org_model . GetInvitesByTeamID ( ctx , ctx . Org . Team . ID )
if err != nil {
ctx . ServerError ( "GetInvitesByTeamID" , err )
return
}
ctx . Data [ "Invites" ] = invites
ctx . Data [ "IsEmailInviteEnabled" ] = setting . MailService != nil
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamMembers )
2014-08-24 21:09:05 +08:00
}
2016-11-18 11:03:03 +08:00
// TeamRepositories show the repositories of team
2016-03-11 11:56:52 -05:00
func TeamRepositories ( ctx * context . Context ) {
2025-04-11 03:12:55 +08:00
if _ , err := shared_user . RenderUserOrgHeader ( ctx ) ; err != nil {
ctx . ServerError ( "RenderUserOrgHeader" , err )
return
}
2014-08-26 18:11:15 +08:00
ctx . Data [ "Title" ] = ctx . Org . Team . Name
ctx . Data [ "PageIsOrgTeams" ] = true
2018-12-09 14:42:11 +08:00
ctx . Data [ "PageIsOrgTeamRepos" ] = true
2023-09-07 09:44:47 +08:00
2024-12-17 19:44:16 -08:00
repos , err := repo_model . GetTeamRepositories ( ctx , & repo_model . SearchTeamRepoOptions {
TeamID : ctx . Org . Team . ID ,
} )
if err != nil {
ctx . ServerError ( "GetTeamRepositories" , err )
2014-08-26 18:11:15 +08:00
return
}
2022-02-23 22:07:05 +00:00
ctx . Data [ "Units" ] = unit_model . Units
2024-12-17 19:44:16 -08:00
ctx . Data [ "TeamRepos" ] = repos
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamRepositories )
2014-08-26 18:11:15 +08:00
}
2022-04-08 02:59:56 +08:00
// SearchTeam api for searching teams
func SearchTeam ( ctx * context . Context ) {
listOptions := db . ListOptions {
Page : ctx . FormInt ( "page" ) ,
PageSize : convert . ToCorrectPageSize ( ctx . FormInt ( "limit" ) ) ,
}
2022-10-19 14:40:28 +02:00
opts := & org_model . SearchTeamOptions {
2022-08-21 18:24:05 +02:00
// UserID is not set because the router already requires the doer to be an org admin. Thus, we don't need to restrict to teams that the user belongs in
2022-04-08 02:59:56 +08:00
Keyword : ctx . FormTrim ( "q" ) ,
OrgID : ctx . Org . Organization . ID ,
IncludeDesc : ctx . FormString ( "include_desc" ) == "" || ctx . FormBool ( "include_desc" ) ,
ListOptions : listOptions ,
}
2023-09-25 15:17:37 +02:00
teams , maxResults , err := org_model . SearchTeam ( ctx , opts )
2022-04-08 02:59:56 +08:00
if err != nil {
log . Error ( "SearchTeam failed: %v" , err )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusInternalServerError , map [ string ] any {
2022-04-08 02:59:56 +08:00
"ok" : false ,
"error" : "SearchTeam internal failure" ,
} )
return
}
2023-02-15 21:37:34 +08:00
apiTeams , err := convert . ToTeams ( ctx , teams , false )
2022-05-13 19:27:58 +02:00
if err != nil {
log . Error ( "convert ToTeams failed: %v" , err )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusInternalServerError , map [ string ] any {
2022-05-13 19:27:58 +02:00
"ok" : false ,
"error" : "SearchTeam failed to get units" ,
} )
return
2022-04-08 02:59:56 +08:00
}
ctx . SetTotalCountHeader ( maxResults )
2023-07-04 20:36:08 +02:00
ctx . JSON ( http . StatusOK , map [ string ] any {
2022-04-08 02:59:56 +08:00
"ok" : true ,
"data" : apiTeams ,
} )
}
2016-11-18 11:03:03 +08:00
// EditTeam render team edit page
2016-03-11 11:56:52 -05:00
func EditTeam ( ctx * context . Context ) {
2025-04-11 03:12:55 +08:00
if _ , err := shared_user . RenderUserOrgHeader ( ctx ) ; err != nil {
ctx . ServerError ( "RenderUserOrgHeader" , err )
return
}
2014-08-24 21:09:05 +08:00
ctx . Data [ "Title" ] = ctx . Org . Organization . FullName
ctx . Data [ "PageIsOrgTeams" ] = true
2023-04-14 04:06:10 +09:00
if err := ctx . Org . Team . LoadUnits ( ctx ) ; err != nil {
ctx . ServerError ( "LoadUnits" , err )
return
}
ctx . Data [ "Team" ] = ctx . Org . Team
2021-11-10 03:57:58 +08:00
ctx . Data [ "Units" ] = unit_model . Units
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamNew )
2014-06-27 22:04:04 +08:00
}
2014-07-07 18:13:42 +08:00
2016-11-18 11:03:03 +08:00
// EditTeamPost response for modify team information
2021-01-26 23:36:53 +08:00
func EditTeamPost ( ctx * context . Context ) {
2021-04-06 20:44:05 +01:00
form := web . GetForm ( ctx ) . ( * forms . CreateTeamForm )
2014-08-24 21:09:05 +08:00
t := ctx . Org . Team
2025-04-08 12:15:15 +08:00
teamPermission := perm . ParseAccessMode ( form . Permission , perm . AccessModeNone , perm . AccessModeAdmin )
unitPerms := getUnitPerms ( ctx . Req . Form , teamPermission )
2022-01-05 11:37:00 +08:00
isAuthChanged := false
isIncludeAllChanged := false
includesAllRepositories := form . RepoAccess == "all"
2014-08-24 21:09:05 +08:00
ctx . Data [ "Title" ] = ctx . Org . Organization . FullName
2014-08-23 20:24:02 +08:00
ctx . Data [ "PageIsOrgTeams" ] = true
2016-01-29 17:06:14 -05:00
ctx . Data [ "Team" ] = t
2021-11-10 03:57:58 +08:00
ctx . Data [ "Units" ] = unit_model . Units
2014-08-24 21:09:05 +08:00
if ! t . IsOwnerTeam ( ) {
t . Name = form . TeamName
2025-04-08 12:15:15 +08:00
if t . AccessMode != teamPermission {
2014-08-24 21:09:05 +08:00
isAuthChanged = true
2025-04-08 12:15:15 +08:00
t . AccessMode = teamPermission
2014-08-24 21:09:05 +08:00
}
2019-11-06 10:37:14 +01:00
if t . IncludesAllRepositories != includesAllRepositories {
isIncludeAllChanged = true
t . IncludesAllRepositories = includesAllRepositories
}
2022-08-18 16:58:21 +08:00
t . CanCreateOrgRepo = form . CanCreateOrgRepo
} else {
t . CanCreateOrgRepo = true
2014-08-24 21:09:05 +08:00
}
2022-08-18 16:58:21 +08:00
2014-08-24 21:09:05 +08:00
t . Description = form . Description
2023-04-14 04:06:10 +09:00
units := make ( [ ] * org_model . TeamUnit , 0 , len ( unitPerms ) )
for tp , perm := range unitPerms {
units = append ( units , & org_model . TeamUnit {
OrgID : t . OrgID ,
TeamID : t . ID ,
Type : tp ,
AccessMode : perm ,
} )
2017-07-17 05:04:43 +03:00
}
2023-04-14 04:06:10 +09:00
t . Units = units
2017-07-17 05:04:43 +03:00
if ctx . HasError ( ) {
2021-04-05 17:30:52 +02:00
ctx . HTML ( http . StatusOK , tplTeamNew )
2017-07-17 05:04:43 +03:00
return
}
2022-01-05 11:37:00 +08:00
if t . AccessMode < perm . AccessModeAdmin && len ( unitPerms ) == 0 {
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "form.team_no_units_error" ) , tplTeamNew , & form )
2017-07-17 05:04:43 +03:00
return
}
2024-11-27 13:12:26 -08:00
if err := org_service . UpdateTeam ( ctx , t , isAuthChanged , isIncludeAllChanged ) ; err != nil {
2016-01-29 17:06:14 -05:00
ctx . Data [ "Err_TeamName" ] = true
switch {
2022-10-19 14:40:28 +02:00
case org_model . IsErrTeamAlreadyExist ( err ) :
2026-02-27 20:38:44 +08:00
ctx . RenderWithErrDeprecated ( ctx . Tr ( "form.team_name_been_taken" ) , tplTeamNew , & form )
2016-01-29 17:06:14 -05:00
default :
2018-01-10 22:34:17 +01:00
ctx . ServerError ( "UpdateTeam" , err )
2014-08-24 21:09:05 +08:00
}
return
}
2021-11-16 18:18:25 +00:00
ctx . Redirect ( ctx . Org . OrgLink + "/teams/" + url . PathEscape ( t . LowerName ) )
2014-08-24 21:09:05 +08:00
}
2016-11-18 11:03:03 +08:00
// DeleteTeam response for the delete team request
2016-03-11 11:56:52 -05:00
func DeleteTeam ( ctx * context . Context ) {
2024-11-27 13:12:26 -08:00
if err := org_service . DeleteTeam ( ctx , ctx . Org . Team ) ; err != nil {
2015-11-22 01:32:09 -05:00
ctx . Flash . Error ( "DeleteTeam: " + err . Error ( ) )
} else {
ctx . Flash . Success ( ctx . Tr ( "org.teams.delete_team_success" ) )
2014-08-24 21:09:05 +08:00
}
2015-11-22 01:32:09 -05:00
2023-07-26 14:04:01 +08:00
ctx . JSONRedirect ( ctx . Org . OrgLink + "/teams" )
2014-07-07 18:13:42 +08:00
}
2022-10-19 14:40:28 +02:00
// TeamInvite renders the team invite page
func TeamInvite ( ctx * context . Context ) {
invite , org , team , inviter , err := getTeamInviteFromContext ( ctx )
if err != nil {
if org_model . IsErrTeamInviteNotFound ( err ) {
2025-02-17 14:13:17 +08:00
ctx . NotFound ( err )
2022-10-19 14:40:28 +02:00
} else {
ctx . ServerError ( "getTeamInviteFromContext" , err )
}
return
}
ctx . Data [ "Title" ] = ctx . Tr ( "org.teams.invite_team_member" , team . Name )
ctx . Data [ "Invite" ] = invite
ctx . Data [ "Organization" ] = org
ctx . Data [ "Team" ] = team
ctx . Data [ "Inviter" ] = inviter
ctx . HTML ( http . StatusOK , tplTeamInvite )
}
// TeamInvitePost handles the team invitation
func TeamInvitePost ( ctx * context . Context ) {
invite , org , team , _ , err := getTeamInviteFromContext ( ctx )
if err != nil {
if org_model . IsErrTeamInviteNotFound ( err ) {
2025-02-17 14:13:17 +08:00
ctx . NotFound ( err )
2022-10-19 14:40:28 +02:00
} else {
ctx . ServerError ( "getTeamInviteFromContext" , err )
}
return
}
2024-11-27 13:12:26 -08:00
if err := org_service . AddTeamMember ( ctx , team , ctx . Doer ) ; err != nil {
2022-10-19 14:40:28 +02:00
ctx . ServerError ( "AddTeamMember" , err )
return
}
if err := org_model . RemoveInviteByID ( ctx , invite . ID , team . ID ) ; err != nil {
log . Error ( "RemoveInviteByID: %v" , err )
}
ctx . Redirect ( org . OrganisationLink ( ) + "/teams/" + url . PathEscape ( team . LowerName ) )
}
func getTeamInviteFromContext ( ctx * context . Context ) ( * org_model . TeamInvite , * org_model . Organization , * org_model . Team , * user_model . User , error ) {
2024-06-19 06:32:45 +08:00
invite , err := org_model . GetInviteByToken ( ctx , ctx . PathParam ( "token" ) )
2022-10-19 14:40:28 +02:00
if err != nil {
return nil , nil , nil , nil , err
}
2022-12-03 10:48:26 +08:00
inviter , err := user_model . GetUserByID ( ctx , invite . InviterID )
2022-10-19 14:40:28 +02:00
if err != nil {
return nil , nil , nil , nil , err
}
team , err := org_model . GetTeamByID ( ctx , invite . TeamID )
if err != nil {
return nil , nil , nil , nil , err
}
2022-12-03 10:48:26 +08:00
org , err := user_model . GetUserByID ( ctx , team . OrgID )
2022-10-19 14:40:28 +02:00
if err != nil {
return nil , nil , nil , nil , err
}
return invite , org_model . OrgFromUser ( org ) , team , inviter , nil
}