2018-06-21 19:00:13 +03:00
// Copyright 2018 The Gitea Authors. All rights reserved.
2016-03-26 01:04:02 +03:00
// Copyright 2016 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package models
import (
"errors"
"fmt"
2019-04-07 16:44:34 +03:00
"sort"
2016-03-26 01:04:02 +03:00
"strings"
2017-12-21 10:43:26 +03:00
"code.gitea.io/gitea/modules/log"
2019-01-27 12:25:21 +03:00
"code.gitea.io/gitea/modules/setting"
2018-02-23 11:42:02 +03:00
"github.com/go-xorm/xorm"
2019-10-01 08:32:28 +03:00
"xorm.io/builder"
2016-03-26 01:04:02 +03:00
)
2016-11-28 04:30:08 +03:00
const ownerTeamName = "Owners"
2016-03-26 01:04:02 +03:00
// Team represents a organization team.
type Team struct {
ID int64 ` xorm:"pk autoincr" `
OrgID int64 ` xorm:"INDEX" `
LowerName string
Name string
Description string
Authorize AccessMode
Repos [ ] * Repository ` xorm:"-" `
Members [ ] * User ` xorm:"-" `
NumRepos int
NumMembers int
2018-06-21 19:00:13 +03:00
Units [ ] * TeamUnit ` xorm:"-" `
2017-05-18 17:54:24 +03:00
}
2019-10-01 08:32:28 +03:00
// SearchTeamOptions holds the search options
type SearchTeamOptions struct {
UserID int64
Keyword string
OrgID int64
IncludeDesc bool
PageSize int
Page int
}
// SearchTeam search for teams. Caller is responsible to check permissions.
func SearchTeam ( opts * SearchTeamOptions ) ( [ ] * Team , int64 , error ) {
if opts . Page <= 0 {
opts . Page = 1
}
if opts . PageSize == 0 {
// Default limit
opts . PageSize = 10
}
var cond = builder . NewCond ( )
if len ( opts . Keyword ) > 0 {
lowerKeyword := strings . ToLower ( opts . Keyword )
var keywordCond builder . Cond = builder . Like { "lower_name" , lowerKeyword }
if opts . IncludeDesc {
keywordCond = keywordCond . Or ( builder . Like { "LOWER(description)" , lowerKeyword } )
}
cond = cond . And ( keywordCond )
}
cond = cond . And ( builder . Eq { "org_id" : opts . OrgID } )
sess := x . NewSession ( )
defer sess . Close ( )
count , err := sess .
Where ( cond ) .
Count ( new ( Team ) )
if err != nil {
return nil , 0 , err
}
sess = sess . Where ( cond )
if opts . PageSize == - 1 {
opts . PageSize = int ( count )
} else {
sess = sess . Limit ( opts . PageSize , ( opts . Page - 1 ) * opts . PageSize )
}
teams := make ( [ ] * Team , 0 , opts . PageSize )
if err = sess .
OrderBy ( "lower_name" ) .
Find ( & teams ) ; err != nil {
return nil , 0 , err
}
return teams , count , nil
}
2019-04-22 23:40:51 +03:00
// ColorFormat provides a basic color format for a Team
func ( t * Team ) ColorFormat ( s fmt . State ) {
log . ColorFprintf ( s , "%d:%s (OrgID: %d) %-v" ,
log . NewColoredIDValue ( t . ID ) ,
t . Name ,
log . NewColoredIDValue ( t . OrgID ) ,
t . Authorize )
}
2019-03-20 00:50:06 +03:00
// GetUnits return a list of available units for a team
func ( t * Team ) GetUnits ( ) error {
return t . getUnits ( x )
}
2018-06-21 19:00:13 +03:00
func ( t * Team ) getUnits ( e Engine ) ( err error ) {
if t . Units != nil {
return nil
2017-05-18 17:54:24 +03:00
}
2018-06-21 19:00:13 +03:00
t . Units , err = getUnitsByTeamID ( e , t . ID )
return err
2016-03-26 01:04:02 +03:00
}
2018-11-10 22:45:32 +03:00
// GetUnitNames returns the team units names
func ( t * Team ) GetUnitNames ( ) ( res [ ] string ) {
for _ , u := range t . Units {
res = append ( res , Units [ u . Type ] . NameKey )
}
return
}
2017-09-14 11:16:22 +03:00
// HasWriteAccess returns true if team has at least write level access mode.
func ( t * Team ) HasWriteAccess ( ) bool {
return t . Authorize >= AccessModeWrite
}
2016-03-26 01:04:02 +03:00
// IsOwnerTeam returns true if team is owner team.
func ( t * Team ) IsOwnerTeam ( ) bool {
2016-11-28 04:30:08 +03:00
return t . Name == ownerTeamName
2016-03-26 01:04:02 +03:00
}
2016-11-28 04:30:08 +03:00
// IsMember returns true if given user is a member of team.
2016-11-28 11:33:08 +03:00
func ( t * Team ) IsMember ( userID int64 ) bool {
2017-12-21 10:43:26 +03:00
isMember , err := IsTeamMember ( t . OrgID , t . ID , userID )
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "IsMember: %v" , err )
2017-12-21 10:43:26 +03:00
return false
}
return isMember
2016-03-26 01:04:02 +03:00
}
2017-02-23 04:36:15 +03:00
func ( t * Team ) getRepositories ( e Engine ) error {
2017-02-16 07:07:58 +03:00
return e . Join ( "INNER" , "team_repo" , "repository.id = team_repo.repo_id" ) .
2019-04-07 16:44:34 +03:00
Where ( "team_repo.team_id=?" , t . ID ) .
OrderBy ( "repository.name" ) .
Find ( & t . Repos )
2016-03-26 01:04:02 +03:00
}
// GetRepositories returns all repositories in team of organization.
func ( t * Team ) GetRepositories ( ) error {
return t . getRepositories ( x )
}
func ( t * Team ) getMembers ( e Engine ) ( err error ) {
t . Members , err = getTeamMembers ( e , t . ID )
return err
}
// GetMembers returns all members in team of organization.
func ( t * Team ) GetMembers ( ) ( err error ) {
return t . getMembers ( x )
}
// AddMember adds new membership of the team to the organization,
// the user will have membership to the organization automatically when needed.
2016-11-28 11:33:08 +03:00
func ( t * Team ) AddMember ( userID int64 ) error {
2017-02-24 09:25:09 +03:00
return AddTeamMember ( t , userID )
2016-03-26 01:04:02 +03:00
}
// RemoveMember removes member from team of organization.
2016-11-28 11:33:08 +03:00
func ( t * Team ) RemoveMember ( userID int64 ) error {
2017-02-24 09:25:09 +03:00
return RemoveTeamMember ( t , userID )
2016-03-26 01:04:02 +03:00
}
func ( t * Team ) hasRepository ( e Engine , repoID int64 ) bool {
return hasTeamRepo ( e , t . OrgID , t . ID , repoID )
}
// HasRepository returns true if given repository belong to team.
func ( t * Team ) HasRepository ( repoID int64 ) bool {
return t . hasRepository ( x , repoID )
}
func ( t * Team ) addRepository ( e Engine , repo * Repository ) ( err error ) {
if err = addTeamRepo ( e , t . OrgID , t . ID , repo . ID ) ; err != nil {
return err
}
2017-12-31 06:08:08 +03:00
if _ , err = e . Incr ( "num_repos" ) . ID ( t . ID ) . Update ( new ( Team ) ) ; err != nil {
2016-03-26 01:04:02 +03:00
return fmt . Errorf ( "update team: %v" , err )
}
2017-12-31 06:08:08 +03:00
t . NumRepos ++
2016-03-26 01:04:02 +03:00
if err = repo . recalculateTeamAccesses ( e , 0 ) ; err != nil {
return fmt . Errorf ( "recalculateAccesses: %v" , err )
}
2019-01-27 12:25:21 +03:00
// Make all team members watch this repo if enabled in global settings
if setting . Service . AutoWatchNewRepos {
if err = t . getMembers ( e ) ; err != nil {
return fmt . Errorf ( "getMembers: %v" , err )
}
for _ , u := range t . Members {
if err = watchRepo ( e , u . ID , repo . ID , true ) ; err != nil {
return fmt . Errorf ( "watchRepo: %v" , err )
}
2016-03-26 01:04:02 +03:00
}
}
2019-01-27 12:25:21 +03:00
2016-03-26 01:04:02 +03:00
return nil
}
// AddRepository adds new repository to team of organization.
func ( t * Team ) AddRepository ( repo * Repository ) ( err error ) {
if repo . OwnerID != t . OrgID {
return errors . New ( "Repository does not belong to organization" )
} else if t . HasRepository ( repo . ID ) {
return nil
}
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2016-03-26 01:04:02 +03:00
if err = sess . Begin ( ) ; err != nil {
return err
}
if err = t . addRepository ( sess , repo ) ; err != nil {
return err
}
return sess . Commit ( )
}
func ( t * Team ) removeRepository ( e Engine , repo * Repository , recalculate bool ) ( err error ) {
if err = removeTeamRepo ( e , t . ID , repo . ID ) ; err != nil {
return err
}
t . NumRepos --
2017-10-05 07:43:04 +03:00
if _ , err = e . ID ( t . ID ) . Cols ( "num_repos" ) . Update ( t ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
// Don't need to recalculate when delete a repository from organization.
if recalculate {
if err = repo . recalculateTeamAccesses ( e , t . ID ) ; err != nil {
return err
}
}
2017-03-15 03:51:46 +03:00
teamUsers , err := getTeamUsersByTeamID ( e , t . ID )
if err != nil {
return fmt . Errorf ( "getTeamUsersByTeamID: %v" , err )
2016-03-26 01:04:02 +03:00
}
2017-03-21 03:55:00 +03:00
for _ , teamUser := range teamUsers {
2018-11-28 14:26:14 +03:00
has , err := hasAccess ( e , teamUser . UID , repo )
2016-03-26 01:04:02 +03:00
if err != nil {
return err
} else if has {
continue
}
2017-03-15 03:51:46 +03:00
if err = watchRepo ( e , teamUser . UID , repo . ID , false ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2018-06-19 22:44:33 +03:00
// Remove all IssueWatches a user has subscribed to in the repositories
if err := removeIssueWatchersByRepoID ( e , teamUser . UID , repo . ID ) ; err != nil {
return err
}
2016-03-26 01:04:02 +03:00
}
return nil
}
// RemoveRepository removes repository from team of organization.
func ( t * Team ) RemoveRepository ( repoID int64 ) error {
if ! t . HasRepository ( repoID ) {
return nil
}
repo , err := GetRepositoryByID ( repoID )
if err != nil {
return err
}
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2016-03-26 01:04:02 +03:00
if err = sess . Begin ( ) ; err != nil {
return err
}
if err = t . removeRepository ( sess , repo , true ) ; err != nil {
return err
}
return sess . Commit ( )
}
2017-08-02 11:46:54 +03:00
// UnitEnabled returns if the team has the given unit type enabled
func ( t * Team ) UnitEnabled ( tp UnitType ) bool {
2018-10-25 13:55:16 +03:00
return t . unitEnabled ( x , tp )
}
func ( t * Team ) unitEnabled ( e Engine , tp UnitType ) bool {
if err := t . getUnits ( e ) ; err != nil {
2019-10-10 19:45:11 +03:00
log . Warn ( "Error loading team (ID: %d) units: %s" , t . ID , err . Error ( ) )
2017-05-18 17:54:24 +03:00
}
2018-06-21 19:00:13 +03:00
for _ , unit := range t . Units {
if unit . Type == tp {
2017-05-18 17:54:24 +03:00
return true
}
}
return false
}
2016-11-28 04:30:08 +03:00
// IsUsableTeamName tests if a name could be as team name
2017-02-23 04:36:15 +03:00
func IsUsableTeamName ( name string ) error {
switch name {
case "new" :
return ErrNameReserved { name }
default :
return nil
2016-11-06 12:07:03 +03:00
}
}
2016-03-26 01:04:02 +03:00
// NewTeam creates a record of new team.
// It's caller's responsibility to assign organization ID.
2016-11-06 12:07:03 +03:00
func NewTeam ( t * Team ) ( err error ) {
2016-03-26 01:04:02 +03:00
if len ( t . Name ) == 0 {
return errors . New ( "empty team name" )
}
2016-11-06 12:07:03 +03:00
if err = IsUsableTeamName ( t . Name ) ; err != nil {
return err
}
2017-10-05 07:43:04 +03:00
has , err := x . ID ( t . OrgID ) . Get ( new ( User ) )
2016-03-26 01:04:02 +03:00
if err != nil {
return err
2019-06-12 22:41:28 +03:00
}
if ! has {
2017-07-06 16:30:19 +03:00
return ErrOrgNotExist { t . OrgID , "" }
2016-03-26 01:04:02 +03:00
}
t . LowerName = strings . ToLower ( t . Name )
2016-11-10 18:16:32 +03:00
has , err = x .
Where ( "org_id=?" , t . OrgID ) .
And ( "lower_name=?" , t . LowerName ) .
Get ( new ( Team ) )
2016-03-26 01:04:02 +03:00
if err != nil {
return err
2019-06-12 22:41:28 +03:00
}
if has {
2016-03-26 01:04:02 +03:00
return ErrTeamAlreadyExist { t . OrgID , t . LowerName }
}
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
if _ , err = sess . Insert ( t ) ; err != nil {
2019-06-12 22:41:28 +03:00
errRollback := sess . Rollback ( )
if errRollback != nil {
log . Error ( "NewTeam sess.Rollback: %v" , errRollback )
}
2016-03-26 01:04:02 +03:00
return err
}
2018-06-21 19:00:13 +03:00
// insert units for team
if len ( t . Units ) > 0 {
for _ , unit := range t . Units {
unit . TeamID = t . ID
}
if _ , err = sess . Insert ( & t . Units ) ; err != nil {
2019-06-12 22:41:28 +03:00
errRollback := sess . Rollback ( )
if errRollback != nil {
log . Error ( "NewTeam sess.Rollback: %v" , errRollback )
}
2018-06-21 19:00:13 +03:00
return err
}
}
2016-03-26 01:04:02 +03:00
// Update organization number of teams.
if _ , err = sess . Exec ( "UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?" , t . OrgID ) ; err != nil {
2019-06-12 22:41:28 +03:00
errRollback := sess . Rollback ( )
if errRollback != nil {
log . Error ( "NewTeam sess.Rollback: %v" , errRollback )
}
2016-03-26 01:04:02 +03:00
return err
}
return sess . Commit ( )
}
2016-11-28 04:30:08 +03:00
func getTeam ( e Engine , orgID int64 , name string ) ( * Team , error ) {
2016-03-26 01:04:02 +03:00
t := & Team {
2016-11-28 04:30:08 +03:00
OrgID : orgID ,
2016-03-26 01:04:02 +03:00
LowerName : strings . ToLower ( name ) ,
}
has , err := e . Get ( t )
if err != nil {
return nil , err
} else if ! has {
2019-09-23 23:08:03 +03:00
return nil , ErrTeamNotExist { orgID , 0 , name }
2016-03-26 01:04:02 +03:00
}
return t , nil
}
// GetTeam returns team by given team name and organization.
2016-11-28 04:30:08 +03:00
func GetTeam ( orgID int64 , name string ) ( * Team , error ) {
return getTeam ( x , orgID , name )
2016-03-26 01:04:02 +03:00
}
2019-08-02 19:06:28 +03:00
// getOwnerTeam returns team by given team name and organization.
func getOwnerTeam ( e Engine , orgID int64 ) ( * Team , error ) {
return getTeam ( e , orgID , ownerTeamName )
}
2016-11-28 04:30:08 +03:00
func getTeamByID ( e Engine , teamID int64 ) ( * Team , error ) {
2016-03-26 01:04:02 +03:00
t := new ( Team )
2017-10-05 07:43:04 +03:00
has , err := e . ID ( teamID ) . Get ( t )
2016-03-26 01:04:02 +03:00
if err != nil {
return nil , err
} else if ! has {
2019-09-23 23:08:03 +03:00
return nil , ErrTeamNotExist { 0 , teamID , "" }
2016-03-26 01:04:02 +03:00
}
return t , nil
}
// GetTeamByID returns team by given ID.
2016-11-28 04:30:08 +03:00
func GetTeamByID ( teamID int64 ) ( * Team , error ) {
return getTeamByID ( x , teamID )
2016-03-26 01:04:02 +03:00
}
// UpdateTeam updates information of team.
func UpdateTeam ( t * Team , authChanged bool ) ( err error ) {
if len ( t . Name ) == 0 {
return errors . New ( "empty team name" )
}
if len ( t . Description ) > 255 {
t . Description = t . Description [ : 255 ]
}
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2016-03-26 01:04:02 +03:00
if err = sess . Begin ( ) ; err != nil {
return err
}
t . LowerName = strings . ToLower ( t . Name )
2017-01-27 19:11:41 +03:00
has , err := sess .
2016-11-10 18:16:32 +03:00
Where ( "org_id=?" , t . OrgID ) .
And ( "lower_name=?" , t . LowerName ) .
And ( "id!=?" , t . ID ) .
Get ( new ( Team ) )
2016-03-26 01:04:02 +03:00
if err != nil {
return err
} else if has {
return ErrTeamAlreadyExist { t . OrgID , t . LowerName }
}
2017-10-05 07:43:04 +03:00
if _ , err = sess . ID ( t . ID ) . AllCols ( ) . Update ( t ) ; err != nil {
2016-03-26 01:04:02 +03:00
return fmt . Errorf ( "update: %v" , err )
}
2018-11-10 22:45:32 +03:00
// update units for team
if len ( t . Units ) > 0 {
for _ , unit := range t . Units {
unit . TeamID = t . ID
}
// Delete team-unit.
if _ , err := sess .
Where ( "team_id=?" , t . ID ) .
Delete ( new ( TeamUnit ) ) ; err != nil {
return err
}
if _ , err = sess . Insert ( & t . Units ) ; err != nil {
2019-06-12 22:41:28 +03:00
errRollback := sess . Rollback ( )
if errRollback != nil {
log . Error ( "UpdateTeam sess.Rollback: %v" , errRollback )
}
2018-11-10 22:45:32 +03:00
return err
}
}
2016-03-26 01:04:02 +03:00
// Update access for team members if needed.
if authChanged {
if err = t . getRepositories ( sess ) ; err != nil {
2017-02-23 04:36:15 +03:00
return fmt . Errorf ( "getRepositories: %v" , err )
2016-03-26 01:04:02 +03:00
}
for _ , repo := range t . Repos {
if err = repo . recalculateTeamAccesses ( sess , 0 ) ; err != nil {
return fmt . Errorf ( "recalculateTeamAccesses: %v" , err )
}
}
}
return sess . Commit ( )
}
// DeleteTeam deletes given team.
// It's caller's responsibility to assign organization ID.
func DeleteTeam ( t * Team ) error {
if err := t . GetRepositories ( ) ; err != nil {
return err
}
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2017-03-01 04:09:49 +03:00
if err := sess . Begin ( ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2018-06-19 22:44:33 +03:00
if err := t . getMembers ( sess ) ; err != nil {
return err
}
2016-03-26 01:04:02 +03:00
// Delete all accesses.
for _ , repo := range t . Repos {
2017-03-01 04:09:49 +03:00
if err := repo . recalculateTeamAccesses ( sess , t . ID ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2018-06-19 22:44:33 +03:00
// Remove watches from all users and now unaccessible repos
for _ , user := range t . Members {
2018-11-28 14:26:14 +03:00
has , err := hasAccess ( sess , user . ID , repo )
2018-06-19 22:44:33 +03:00
if err != nil {
return err
} else if has {
continue
}
if err = watchRepo ( sess , user . ID , repo . ID , false ) ; err != nil {
return err
}
// Remove all IssueWatches a user has subscribed to in the repositories
if err = removeIssueWatchersByRepoID ( sess , user . ID , repo . ID ) ; err != nil {
return err
}
}
2016-03-26 01:04:02 +03:00
}
2017-02-23 04:36:15 +03:00
// Delete team-repo
2017-03-01 04:09:49 +03:00
if _ , err := sess .
2017-02-23 04:36:15 +03:00
Where ( "team_id=?" , t . ID ) .
Delete ( new ( TeamRepo ) ) ; err != nil {
return err
}
2016-03-26 01:04:02 +03:00
// Delete team-user.
2017-03-01 04:09:49 +03:00
if _ , err := sess .
Where ( "org_id=?" , t . OrgID ) .
2016-11-10 18:16:32 +03:00
Where ( "team_id=?" , t . ID ) .
Delete ( new ( TeamUser ) ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2018-06-21 19:00:13 +03:00
// Delete team-unit.
if _ , err := sess .
Where ( "team_id=?" , t . ID ) .
Delete ( new ( TeamUnit ) ) ; err != nil {
return err
}
2016-03-26 01:04:02 +03:00
// Delete team.
2017-10-05 07:43:04 +03:00
if _ , err := sess . ID ( t . ID ) . Delete ( new ( Team ) ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
// Update organization number of teams.
2017-03-01 04:09:49 +03:00
if _ , err := sess . Exec ( "UPDATE `user` SET num_teams=num_teams-1 WHERE id=?" , t . OrgID ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
return sess . Commit ( )
}
// ___________ ____ ___
// \__ ___/___ _____ _____ | | \______ ___________
// | |_/ __ \\__ \ / \| | / ___// __ \_ __ \
// | |\ ___/ / __ \| Y Y \ | /\___ \\ ___/| | \/
// |____| \___ >____ /__|_| /______//____ >\___ >__|
// \/ \/ \/ \/ \/
// TeamUser represents an team-user relation.
type TeamUser struct {
ID int64 ` xorm:"pk autoincr" `
OrgID int64 ` xorm:"INDEX" `
TeamID int64 ` xorm:"UNIQUE(s)" `
2016-11-28 04:30:08 +03:00
UID int64 ` xorm:"UNIQUE(s)" `
2016-03-26 01:04:02 +03:00
}
2017-12-21 10:43:26 +03:00
func isTeamMember ( e Engine , orgID , teamID , userID int64 ) ( bool , error ) {
return e .
2016-11-10 18:16:32 +03:00
Where ( "org_id=?" , orgID ) .
And ( "team_id=?" , teamID ) .
2016-11-28 11:33:08 +03:00
And ( "uid=?" , userID ) .
2017-12-21 10:43:26 +03:00
Table ( "team_user" ) .
Exist ( )
2016-03-26 01:04:02 +03:00
}
// IsTeamMember returns true if given user is a member of team.
2017-12-21 10:43:26 +03:00
func IsTeamMember ( orgID , teamID , userID int64 ) ( bool , error ) {
2016-11-28 11:33:08 +03:00
return isTeamMember ( x , orgID , teamID , userID )
2016-03-26 01:04:02 +03:00
}
2017-03-15 03:51:46 +03:00
func getTeamUsersByTeamID ( e Engine , teamID int64 ) ( [ ] * TeamUser , error ) {
2016-03-26 01:04:02 +03:00
teamUsers := make ( [ ] * TeamUser , 0 , 10 )
2017-03-15 03:51:46 +03:00
return teamUsers , e .
2016-11-10 18:16:32 +03:00
Where ( "team_id=?" , teamID ) .
2017-03-15 03:51:46 +03:00
Find ( & teamUsers )
}
func getTeamMembers ( e Engine , teamID int64 ) ( _ [ ] * User , err error ) {
teamUsers , err := getTeamUsersByTeamID ( e , teamID )
if err != nil {
2016-03-26 01:04:02 +03:00
return nil , fmt . Errorf ( "get team-users: %v" , err )
}
2017-03-15 03:51:46 +03:00
members := make ( [ ] * User , len ( teamUsers ) )
for i , teamUser := range teamUsers {
member , err := getUserByID ( e , teamUser . UID )
if err != nil {
return nil , fmt . Errorf ( "get user '%d': %v" , teamUser . UID , err )
2016-03-26 01:04:02 +03:00
}
2017-03-15 03:51:46 +03:00
members [ i ] = member
2016-03-26 01:04:02 +03:00
}
2019-04-07 16:44:34 +03:00
sort . Slice ( members , func ( i , j int ) bool {
return members [ i ] . DisplayName ( ) < members [ j ] . DisplayName ( )
} )
2016-03-26 01:04:02 +03:00
return members , nil
}
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers ( teamID int64 ) ( [ ] * User , error ) {
return getTeamMembers ( x , teamID )
}
2019-01-17 03:39:50 +03:00
func getUserTeams ( e Engine , userID int64 ) ( teams [ ] * Team , err error ) {
return teams , e .
Join ( "INNER" , "team_user" , "team_user.team_id = team.id" ) .
Where ( "team_user.uid=?" , userID ) .
Find ( & teams )
}
func getUserOrgTeams ( e Engine , orgID , userID int64 ) ( teams [ ] * Team , err error ) {
2017-02-16 07:06:23 +03:00
return teams , e .
Join ( "INNER" , "team_user" , "team_user.team_id = team.id" ) .
Where ( "team.org_id = ?" , orgID ) .
And ( "team_user.uid=?" , userID ) .
Find ( & teams )
2016-03-26 01:04:02 +03:00
}
2018-11-09 09:16:52 +03:00
func getUserRepoTeams ( e Engine , orgID , userID , repoID int64 ) ( teams [ ] * Team , err error ) {
return teams , e .
Join ( "INNER" , "team_user" , "team_user.team_id = team.id" ) .
Join ( "INNER" , "team_repo" , "team_repo.team_id = team.id" ) .
Where ( "team.org_id = ?" , orgID ) .
And ( "team_user.uid=?" , userID ) .
And ( "team_repo.repo_id=?" , repoID ) .
Find ( & teams )
}
2019-01-17 03:39:50 +03:00
// GetUserOrgTeams returns all teams that user belongs to in given organization.
func GetUserOrgTeams ( orgID , userID int64 ) ( [ ] * Team , error ) {
return getUserOrgTeams ( x , orgID , userID )
}
// GetUserTeams returns all teams that user belongs across all organizations.
func GetUserTeams ( userID int64 ) ( [ ] * Team , error ) {
return getUserTeams ( x , userID )
2016-03-26 01:04:02 +03:00
}
// AddTeamMember adds new membership of given team to given organization,
// the user will have membership to given organization automatically when needed.
2017-02-24 09:25:09 +03:00
func AddTeamMember ( team * Team , userID int64 ) error {
2017-12-21 10:43:26 +03:00
isAlreadyMember , err := IsTeamMember ( team . OrgID , team . ID , userID )
if err != nil || isAlreadyMember {
return err
2016-03-26 01:04:02 +03:00
}
2017-02-24 09:25:09 +03:00
if err := AddOrgUser ( team . OrgID , userID ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
// Get team and its repositories.
2017-02-24 09:25:09 +03:00
if err := team . GetRepositories ( ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2017-02-24 09:25:09 +03:00
if err := sess . Begin ( ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2017-02-24 09:25:09 +03:00
if _ , err := sess . Insert ( & TeamUser {
2016-11-28 11:33:08 +03:00
UID : userID ,
2017-02-24 09:25:09 +03:00
OrgID : team . OrgID ,
TeamID : team . ID ,
} ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
2017-12-31 06:08:08 +03:00
} else if _ , err := sess . Incr ( "num_members" ) . ID ( team . ID ) . Update ( new ( Team ) ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2017-12-31 06:08:08 +03:00
team . NumMembers ++
2016-03-26 01:04:02 +03:00
// Give access to team repositories.
2017-02-24 09:25:09 +03:00
for _ , repo := range team . Repos {
if err := repo . recalculateTeamAccesses ( sess , 0 ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2019-01-27 12:25:21 +03:00
if setting . Service . AutoWatchNewRepos {
if err = watchRepo ( sess , userID , repo . ID , true ) ; err != nil {
return err
}
2018-06-19 22:44:33 +03:00
}
2016-03-26 01:04:02 +03:00
}
return sess . Commit ( )
}
2018-02-23 11:42:02 +03:00
func removeTeamMember ( e * xorm . Session , team * Team , userID int64 ) error {
2017-12-21 10:43:26 +03:00
isMember , err := isTeamMember ( e , team . OrgID , team . ID , userID )
if err != nil || ! isMember {
return err
2016-03-26 01:04:02 +03:00
}
// Check if the user to delete is the last member in owner team.
2017-02-24 09:25:09 +03:00
if team . IsOwnerTeam ( ) && team . NumMembers == 1 {
2016-11-28 11:33:08 +03:00
return ErrLastOrgOwner { UID : userID }
2016-03-26 01:04:02 +03:00
}
2017-02-24 09:25:09 +03:00
team . NumMembers --
2016-03-26 01:04:02 +03:00
2017-02-24 09:25:09 +03:00
if err := team . getRepositories ( e ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2017-02-24 09:25:09 +03:00
if _ , err := e . Delete ( & TeamUser {
2016-11-28 11:33:08 +03:00
UID : userID ,
2017-02-24 09:25:09 +03:00
OrgID : team . OrgID ,
TeamID : team . ID ,
} ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
2016-11-10 18:16:32 +03:00
} else if _ , err = e .
2017-10-05 07:43:04 +03:00
ID ( team . ID ) .
2017-09-25 07:59:27 +03:00
Cols ( "num_members" ) .
2017-02-24 09:25:09 +03:00
Update ( team ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
// Delete access to team repositories.
2017-02-24 09:25:09 +03:00
for _ , repo := range team . Repos {
2017-03-01 04:09:49 +03:00
if err := repo . recalculateTeamAccesses ( e , 0 ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
2018-06-19 22:44:33 +03:00
// Remove watches from now unaccessible
2018-11-28 14:26:14 +03:00
has , err := hasAccess ( e , userID , repo )
2018-06-19 22:44:33 +03:00
if err != nil {
return err
} else if has {
continue
}
if err = watchRepo ( e , userID , repo . ID , false ) ; err != nil {
return err
}
// Remove all IssueWatches a user has subscribed to in the repositories
if err := removeIssueWatchersByRepoID ( e , userID , repo . ID ) ; err != nil {
return err
}
2016-03-26 01:04:02 +03:00
}
2018-02-23 11:42:02 +03:00
// Check if the user is a member of any team in the organization.
if count , err := e . Count ( & TeamUser {
UID : userID ,
OrgID : team . OrgID ,
} ) ; err != nil {
return err
} else if count == 0 {
return removeOrgUser ( e , team . OrgID , userID )
}
2016-03-26 01:04:02 +03:00
return nil
}
// RemoveTeamMember removes member from given team of given organization.
2017-02-24 09:25:09 +03:00
func RemoveTeamMember ( team * Team , userID int64 ) error {
2016-03-26 01:04:02 +03:00
sess := x . NewSession ( )
2017-06-21 03:57:05 +03:00
defer sess . Close ( )
2016-03-26 01:04:02 +03:00
if err := sess . Begin ( ) ; err != nil {
return err
}
2017-02-24 09:25:09 +03:00
if err := removeTeamMember ( sess , team , userID ) ; err != nil {
2016-03-26 01:04:02 +03:00
return err
}
return sess . Commit ( )
}
2017-09-14 11:16:22 +03:00
// IsUserInTeams returns if a user in some teams
func IsUserInTeams ( userID int64 , teamIDs [ ] int64 ) ( bool , error ) {
return x . Where ( "uid=?" , userID ) . In ( "team_id" , teamIDs ) . Exist ( new ( TeamUser ) )
}
2018-12-11 14:28:37 +03:00
// UsersInTeamsCount counts the number of users which are in userIDs and teamIDs
2019-08-07 07:22:41 +03:00
func UsersInTeamsCount ( userIDs [ ] int64 , teamIDs [ ] int64 ) ( int64 , error ) {
var ids [ ] int64
if err := x . In ( "uid" , userIDs ) . In ( "team_id" , teamIDs ) .
Table ( "team_user" ) .
Cols ( "uid" ) . GroupBy ( "uid" ) . Find ( & ids ) ; err != nil {
2018-12-11 14:28:37 +03:00
return 0 , err
}
2019-08-07 07:22:41 +03:00
return int64 ( len ( ids ) ) , nil
2018-12-11 14:28:37 +03:00
}
2016-03-26 01:04:02 +03:00
// ___________ __________
// \__ ___/___ _____ _____\______ \ ____ ______ ____
// | |_/ __ \\__ \ / \| _// __ \\____ \ / _ \
// | |\ ___/ / __ \| Y Y \ | \ ___/| |_> > <_> )
// |____| \___ >____ /__|_| /____|_ /\___ > __/ \____/
// \/ \/ \/ \/ \/|__|
// TeamRepo represents an team-repository relation.
type TeamRepo struct {
ID int64 ` xorm:"pk autoincr" `
OrgID int64 ` xorm:"INDEX" `
TeamID int64 ` xorm:"UNIQUE(s)" `
RepoID int64 ` xorm:"UNIQUE(s)" `
}
func hasTeamRepo ( e Engine , orgID , teamID , repoID int64 ) bool {
2016-11-10 18:16:32 +03:00
has , _ := e .
Where ( "org_id=?" , orgID ) .
And ( "team_id=?" , teamID ) .
And ( "repo_id=?" , repoID ) .
Get ( new ( TeamRepo ) )
2016-03-26 01:04:02 +03:00
return has
}
// HasTeamRepo returns true if given repository belongs to team.
func HasTeamRepo ( orgID , teamID , repoID int64 ) bool {
return hasTeamRepo ( x , orgID , teamID , repoID )
}
func addTeamRepo ( e Engine , orgID , teamID , repoID int64 ) error {
_ , err := e . InsertOne ( & TeamRepo {
OrgID : orgID ,
TeamID : teamID ,
RepoID : repoID ,
} )
return err
}
func removeTeamRepo ( e Engine , teamID , repoID int64 ) error {
_ , err := e . Delete ( & TeamRepo {
TeamID : teamID ,
RepoID : repoID ,
} )
return err
}
2017-09-14 11:16:22 +03:00
// GetTeamsWithAccessToRepo returns all teams in an organization that have given access level to the repository.
func GetTeamsWithAccessToRepo ( orgID , repoID int64 , mode AccessMode ) ( [ ] * Team , error ) {
teams := make ( [ ] * Team , 0 , 5 )
return teams , x . Where ( "team.authorize >= ?" , mode ) .
Join ( "INNER" , "team_repo" , "team_repo.team_id = team.id" ) .
And ( "team_repo.org_id = ?" , orgID ) .
And ( "team_repo.repo_id = ?" , repoID ) .
Find ( & teams )
}
2018-06-21 19:00:13 +03:00
// ___________ ____ ___ .__ __
// \__ ___/___ _____ _____ | | \____ |__|/ |_
// | |_/ __ \\__ \ / \| | / \| \ __\
// | |\ ___/ / __ \| Y Y \ | / | \ || |
// |____| \___ >____ /__|_| /______/|___| /__||__|
// \/ \/ \/ \/
// TeamUnit describes all units of a repository
type TeamUnit struct {
ID int64 ` xorm:"pk autoincr" `
OrgID int64 ` xorm:"INDEX" `
TeamID int64 ` xorm:"UNIQUE(s)" `
Type UnitType ` xorm:"UNIQUE(s)" `
}
// Unit returns Unit
func ( t * TeamUnit ) Unit ( ) Unit {
return Units [ t . Type ]
}
func getUnitsByTeamID ( e Engine , teamID int64 ) ( units [ ] * TeamUnit , err error ) {
return units , e . Where ( "team_id = ?" , teamID ) . Find ( & units )
}
// UpdateTeamUnits updates a teams's units
func UpdateTeamUnits ( team * Team , units [ ] TeamUnit ) ( err error ) {
sess := x . NewSession ( )
defer sess . Close ( )
if err = sess . Begin ( ) ; err != nil {
return err
}
if _ , err = sess . Where ( "team_id = ?" , team . ID ) . Delete ( new ( TeamUnit ) ) ; err != nil {
return err
}
if _ , err = sess . Insert ( units ) ; err != nil {
2019-06-12 22:41:28 +03:00
errRollback := sess . Rollback ( )
if errRollback != nil {
log . Error ( "UpdateTeamUnits sess.Rollback: %v" , errRollback )
}
2018-06-21 19:00:13 +03:00
return err
}
return sess . Commit ( )
}