mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Add team option to grant rights for all organization repositories (#8688)
* Add field IsAllRepositories to team * Add AllRepositories to team UI * Manage team with access to all repositories * Add field IsAllRepositories to team API * put backticks around table/column names * rename IsAllRepositories to IncludesAllRepositories * do not reload slice if already loaded * add repo to teams with access to all repositories when changing repo owner * improve tests for teams with access to all repositories * Merge branch 'master' * Change code for adding all repositories Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * fmt after merge * Change code in API EditTeam similar to EditTeamPost web interface Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * Clarify that all repositories will be added Signed-off-by: David Svantesson <davidsvantesson@gmail.com> * All repositories option under Permissions headline * New setting group 'Repository access' * Move check IncludeAllRepositories to removeRepository. * Revert "Move check IncludeAllRepositories to removeRepository." and add comment instead. This reverts commit 753b7d205be260b8be465b5291a02975a81f3093. * Clarify help text what options do.
This commit is contained in:
		
				
					committed by
					
						
						Lauris BH
					
				
			
			
				
	
			
			
			
						parent
						
							0109229928
						
					
				
				
					commit
					72aa5a20ec
				
			@@ -57,35 +57,42 @@ func TestAPITeam(t *testing.T) {
 | 
				
			|||||||
	teamToCreate := &api.CreateTeamOption{
 | 
						teamToCreate := &api.CreateTeamOption{
 | 
				
			||||||
		Name:                    "team1",
 | 
							Name:                    "team1",
 | 
				
			||||||
		Description:             "team one",
 | 
							Description:             "team one",
 | 
				
			||||||
 | 
							IncludesAllRepositories: true,
 | 
				
			||||||
		Permission:              "write",
 | 
							Permission:              "write",
 | 
				
			||||||
		Units:                   []string{"repo.code", "repo.issues"},
 | 
							Units:                   []string{"repo.code", "repo.issues"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
 | 
						req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", org.Name, token), teamToCreate)
 | 
				
			||||||
	resp = session.MakeRequest(t, req, http.StatusCreated)
 | 
						resp = session.MakeRequest(t, req, http.StatusCreated)
 | 
				
			||||||
	DecodeJSON(t, resp, &apiTeam)
 | 
						DecodeJSON(t, resp, &apiTeam)
 | 
				
			||||||
	checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.Permission, teamToCreate.Units)
 | 
						checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
 | 
				
			||||||
	checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.Permission, teamToCreate.Units)
 | 
							teamToCreate.Permission, teamToCreate.Units)
 | 
				
			||||||
 | 
						checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
 | 
				
			||||||
 | 
							teamToCreate.Permission, teamToCreate.Units)
 | 
				
			||||||
	teamID := apiTeam.ID
 | 
						teamID := apiTeam.ID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Edit team.
 | 
						// Edit team.
 | 
				
			||||||
	teamToEdit := &api.EditTeamOption{
 | 
						teamToEdit := &api.EditTeamOption{
 | 
				
			||||||
		Name:                    "teamone",
 | 
							Name:                    "teamone",
 | 
				
			||||||
		Description:             "team 1",
 | 
							Description:             "team 1",
 | 
				
			||||||
 | 
							IncludesAllRepositories: false,
 | 
				
			||||||
		Permission:              "admin",
 | 
							Permission:              "admin",
 | 
				
			||||||
		Units:                   []string{"repo.code", "repo.pulls", "repo.releases"},
 | 
							Units:                   []string{"repo.code", "repo.pulls", "repo.releases"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit)
 | 
						req = NewRequestWithJSON(t, "PATCH", fmt.Sprintf("/api/v1/teams/%d?token=%s", teamID, token), teamToEdit)
 | 
				
			||||||
	resp = session.MakeRequest(t, req, http.StatusOK)
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
	DecodeJSON(t, resp, &apiTeam)
 | 
						DecodeJSON(t, resp, &apiTeam)
 | 
				
			||||||
	checkTeamResponse(t, &apiTeam, teamToEdit.Name, teamToEdit.Description, teamToEdit.Permission, teamToEdit.Units)
 | 
						checkTeamResponse(t, &apiTeam, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories,
 | 
				
			||||||
	checkTeamBean(t, apiTeam.ID, teamToEdit.Name, teamToEdit.Description, teamToEdit.Permission, teamToEdit.Units)
 | 
							teamToEdit.Permission, teamToEdit.Units)
 | 
				
			||||||
 | 
						checkTeamBean(t, apiTeam.ID, teamToEdit.Name, teamToEdit.Description, teamToEdit.IncludesAllRepositories,
 | 
				
			||||||
 | 
							teamToEdit.Permission, teamToEdit.Units)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Read team.
 | 
						// Read team.
 | 
				
			||||||
	teamRead := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
 | 
						teamRead := models.AssertExistsAndLoadBean(t, &models.Team{ID: teamID}).(*models.Team)
 | 
				
			||||||
	req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
 | 
						req = NewRequestf(t, "GET", "/api/v1/teams/%d?token="+token, teamID)
 | 
				
			||||||
	resp = session.MakeRequest(t, req, http.StatusOK)
 | 
						resp = session.MakeRequest(t, req, http.StatusOK)
 | 
				
			||||||
	DecodeJSON(t, resp, &apiTeam)
 | 
						DecodeJSON(t, resp, &apiTeam)
 | 
				
			||||||
	checkTeamResponse(t, &apiTeam, teamRead.Name, teamRead.Description, teamRead.Authorize.String(), teamRead.GetUnitNames())
 | 
						checkTeamResponse(t, &apiTeam, teamRead.Name, teamRead.Description, teamRead.IncludesAllRepositories,
 | 
				
			||||||
 | 
							teamRead.Authorize.String(), teamRead.GetUnitNames())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Delete team.
 | 
						// Delete team.
 | 
				
			||||||
	req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
 | 
						req = NewRequestf(t, "DELETE", "/api/v1/teams/%d?token="+token, teamID)
 | 
				
			||||||
@@ -93,19 +100,20 @@ func TestAPITeam(t *testing.T) {
 | 
				
			|||||||
	models.AssertNotExistsBean(t, &models.Team{ID: teamID})
 | 
						models.AssertNotExistsBean(t, &models.Team{ID: teamID})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, permission string, units []string) {
 | 
					func checkTeamResponse(t *testing.T, apiTeam *api.Team, name, description string, includesAllRepositories bool, permission string, units []string) {
 | 
				
			||||||
	assert.Equal(t, name, apiTeam.Name, "name")
 | 
						assert.Equal(t, name, apiTeam.Name, "name")
 | 
				
			||||||
	assert.Equal(t, description, apiTeam.Description, "description")
 | 
						assert.Equal(t, description, apiTeam.Description, "description")
 | 
				
			||||||
 | 
						assert.Equal(t, includesAllRepositories, apiTeam.IncludesAllRepositories, "includesAllRepositories")
 | 
				
			||||||
	assert.Equal(t, permission, apiTeam.Permission, "permission")
 | 
						assert.Equal(t, permission, apiTeam.Permission, "permission")
 | 
				
			||||||
	sort.StringSlice(units).Sort()
 | 
						sort.StringSlice(units).Sort()
 | 
				
			||||||
	sort.StringSlice(apiTeam.Units).Sort()
 | 
						sort.StringSlice(apiTeam.Units).Sort()
 | 
				
			||||||
	assert.EqualValues(t, units, apiTeam.Units, "units")
 | 
						assert.EqualValues(t, units, apiTeam.Units, "units")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func checkTeamBean(t *testing.T, id int64, name, description string, permission string, units []string) {
 | 
					func checkTeamBean(t *testing.T, id int64, name, description string, includesAllRepositories bool, permission string, units []string) {
 | 
				
			||||||
	team := models.AssertExistsAndLoadBean(t, &models.Team{ID: id}).(*models.Team)
 | 
						team := models.AssertExistsAndLoadBean(t, &models.Team{ID: id}).(*models.Team)
 | 
				
			||||||
	assert.NoError(t, team.GetUnits(), "GetUnits")
 | 
						assert.NoError(t, team.GetUnits(), "GetUnits")
 | 
				
			||||||
	checkTeamResponse(t, convert.ToTeam(team), name, description, permission, units)
 | 
						checkTeamResponse(t, convert.ToTeam(team), name, description, includesAllRepositories, permission, units)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type TeamSearchResults struct {
 | 
					type TeamSearchResults struct {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -264,6 +264,8 @@ var migrations = []Migration{
 | 
				
			|||||||
	NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches),
 | 
						NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches),
 | 
				
			||||||
	// v104 -> v105
 | 
						// v104 -> v105
 | 
				
			||||||
	NewMigration("remove unnecessary columns from label", removeLabelUneededCols),
 | 
						NewMigration("remove unnecessary columns from label", removeLabelUneededCols),
 | 
				
			||||||
 | 
						// v105 -> v106
 | 
				
			||||||
 | 
						NewMigration("add includes_all_repositories to teams", addTeamIncludesAllRepositories),
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Migrate database to current version
 | 
					// Migrate database to current version
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										25
									
								
								models/migrations/v105.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								models/migrations/v105.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					// Copyright 2019 The Gitea 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 migrations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"xorm.io/xorm"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func addTeamIncludesAllRepositories(x *xorm.Engine) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type Team struct {
 | 
				
			||||||
 | 
							ID                      int64 `xorm:"pk autoincr"`
 | 
				
			||||||
 | 
							IncludesAllRepositories bool  `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := x.Sync2(new(Team)); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
 | 
				
			||||||
 | 
							true, "Owners")
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -48,6 +48,9 @@ func (org *User) GetOwnerTeam() (*Team, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (org *User) getTeams(e Engine) error {
 | 
					func (org *User) getTeams(e Engine) error {
 | 
				
			||||||
 | 
						if org.Teams != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return e.
 | 
						return e.
 | 
				
			||||||
		Where("org_id=?", org.ID).
 | 
							Where("org_id=?", org.ID).
 | 
				
			||||||
		OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
 | 
							OrderBy("CASE WHEN name LIKE '" + ownerTeamName + "' THEN '' ELSE name END").
 | 
				
			||||||
@@ -154,6 +157,7 @@ func CreateOrganization(org, owner *User) (err error) {
 | 
				
			|||||||
		Name:                    ownerTeamName,
 | 
							Name:                    ownerTeamName,
 | 
				
			||||||
		Authorize:               AccessModeOwner,
 | 
							Authorize:               AccessModeOwner,
 | 
				
			||||||
		NumMembers:              1,
 | 
							NumMembers:              1,
 | 
				
			||||||
 | 
							IncludesAllRepositories: true,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if _, err = sess.Insert(t); err != nil {
 | 
						if _, err = sess.Insert(t); err != nil {
 | 
				
			||||||
		return fmt.Errorf("insert owner team: %v", err)
 | 
							return fmt.Errorf("insert owner team: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ type Team struct {
 | 
				
			|||||||
	NumRepos                int
 | 
						NumRepos                int
 | 
				
			||||||
	NumMembers              int
 | 
						NumMembers              int
 | 
				
			||||||
	Units                   []*TeamUnit `xorm:"-"`
 | 
						Units                   []*TeamUnit `xorm:"-"`
 | 
				
			||||||
 | 
						IncludesAllRepositories bool        `xorm:"NOT NULL DEFAULT false"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SearchTeamOptions holds the search options
 | 
					// SearchTeamOptions holds the search options
 | 
				
			||||||
@@ -149,6 +150,9 @@ func (t *Team) IsMember(userID int64) bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Team) getRepositories(e Engine) error {
 | 
					func (t *Team) getRepositories(e Engine) error {
 | 
				
			||||||
 | 
						if t.Repos != nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id").
 | 
						return e.Join("INNER", "team_repo", "repository.id = team_repo.repo_id").
 | 
				
			||||||
		Where("team_repo.team_id=?", t.ID).
 | 
							Where("team_repo.team_id=?", t.ID).
 | 
				
			||||||
		OrderBy("repository.name").
 | 
							OrderBy("repository.name").
 | 
				
			||||||
@@ -220,6 +224,25 @@ func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// addAllRepositories adds all repositories to the team.
 | 
				
			||||||
 | 
					// If the team already has some repositories they will be left unchanged.
 | 
				
			||||||
 | 
					func (t *Team) addAllRepositories(e Engine) error {
 | 
				
			||||||
 | 
						var orgRepos []Repository
 | 
				
			||||||
 | 
						if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("get org repos: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, repo := range orgRepos {
 | 
				
			||||||
 | 
							if !t.hasRepository(e, repo.ID) {
 | 
				
			||||||
 | 
								if err := t.addRepository(e, &repo); err != nil {
 | 
				
			||||||
 | 
									return fmt.Errorf("addRepository: %v", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AddRepository adds new repository to team of organization.
 | 
					// AddRepository adds new repository to team of organization.
 | 
				
			||||||
func (t *Team) AddRepository(repo *Repository) (err error) {
 | 
					func (t *Team) AddRepository(repo *Repository) (err error) {
 | 
				
			||||||
	if repo.OwnerID != t.OrgID {
 | 
						if repo.OwnerID != t.OrgID {
 | 
				
			||||||
@@ -241,6 +264,8 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
 | 
				
			|||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// removeRepository removes a repository from a team and recalculates access
 | 
				
			||||||
 | 
					// Note: Repository shall not be removed from team if it includes all repositories (unless the repository is deleted)
 | 
				
			||||||
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
 | 
					func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
 | 
				
			||||||
	if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
 | 
						if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -284,11 +309,16 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveRepository removes repository from team of organization.
 | 
					// RemoveRepository removes repository from team of organization.
 | 
				
			||||||
 | 
					// If the team shall include all repositories the request is ignored.
 | 
				
			||||||
func (t *Team) RemoveRepository(repoID int64) error {
 | 
					func (t *Team) RemoveRepository(repoID int64) error {
 | 
				
			||||||
	if !t.HasRepository(repoID) {
 | 
						if !t.HasRepository(repoID) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if t.IncludesAllRepositories {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo, err := GetRepositoryByID(repoID)
 | 
						repo, err := GetRepositoryByID(repoID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
@@ -394,6 +424,14 @@ func NewTeam(t *Team) (err error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add all repositories to the team if it has access to all of them.
 | 
				
			||||||
 | 
						if t.IncludesAllRepositories {
 | 
				
			||||||
 | 
							err = t.addAllRepositories(sess)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("addAllRepositories: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Update organization number of teams.
 | 
						// Update organization number of teams.
 | 
				
			||||||
	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
 | 
						if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
 | 
				
			||||||
		errRollback := sess.Rollback()
 | 
							errRollback := sess.Rollback()
 | 
				
			||||||
@@ -446,7 +484,7 @@ func GetTeamByID(teamID int64) (*Team, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UpdateTeam updates information of team.
 | 
					// UpdateTeam updates information of team.
 | 
				
			||||||
func UpdateTeam(t *Team, authChanged bool) (err error) {
 | 
					func UpdateTeam(t *Team, authChanged bool, includeAllChanged bool) (err error) {
 | 
				
			||||||
	if len(t.Name) == 0 {
 | 
						if len(t.Name) == 0 {
 | 
				
			||||||
		return errors.New("empty team name")
 | 
							return errors.New("empty team name")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -511,6 +549,14 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add all repositories to the team if it has access to all of them.
 | 
				
			||||||
 | 
						if includeAllChanged && t.IncludesAllRepositories {
 | 
				
			||||||
 | 
							err = t.addAllRepositories(sess)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return fmt.Errorf("addAllRepositories: %v", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return sess.Commit()
 | 
						return sess.Commit()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,12 @@
 | 
				
			|||||||
package models
 | 
					package models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -206,7 +209,7 @@ func TestUpdateTeam(t *testing.T) {
 | 
				
			|||||||
	team.Name = "newName"
 | 
						team.Name = "newName"
 | 
				
			||||||
	team.Description = strings.Repeat("A long description!", 100)
 | 
						team.Description = strings.Repeat("A long description!", 100)
 | 
				
			||||||
	team.Authorize = AccessModeAdmin
 | 
						team.Authorize = AccessModeAdmin
 | 
				
			||||||
	assert.NoError(t, UpdateTeam(team, true))
 | 
						assert.NoError(t, UpdateTeam(team, true, false))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	team = AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team)
 | 
						team = AssertExistsAndLoadBean(t, &Team{Name: "newName"}).(*Team)
 | 
				
			||||||
	assert.True(t, strings.HasPrefix(team.Description, "A long description!"))
 | 
						assert.True(t, strings.HasPrefix(team.Description, "A long description!"))
 | 
				
			||||||
@@ -225,7 +228,7 @@ func TestUpdateTeam2(t *testing.T) {
 | 
				
			|||||||
	team.LowerName = "owners"
 | 
						team.LowerName = "owners"
 | 
				
			||||||
	team.Name = "Owners"
 | 
						team.Name = "Owners"
 | 
				
			||||||
	team.Description = strings.Repeat("A long description!", 100)
 | 
						team.Description = strings.Repeat("A long description!", 100)
 | 
				
			||||||
	err := UpdateTeam(team, true)
 | 
						err := UpdateTeam(team, true, false)
 | 
				
			||||||
	assert.True(t, IsErrTeamAlreadyExist(err))
 | 
						assert.True(t, IsErrTeamAlreadyExist(err))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CheckConsistencyFor(t, &Team{ID: team.ID})
 | 
						CheckConsistencyFor(t, &Team{ID: team.ID})
 | 
				
			||||||
@@ -374,3 +377,133 @@ func TestUsersInTeamsCount(t *testing.T) {
 | 
				
			|||||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
 | 
						test([]int64{1, 2, 3, 4, 5}, []int64{2, 5}, 2)    // userid 2,4
 | 
				
			||||||
	test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
 | 
						test([]int64{1, 2, 3, 4, 5}, []int64{2, 3, 5}, 3) // userid 2,4,5
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIncludesAllRepositoriesTeams(t *testing.T) {
 | 
				
			||||||
 | 
						assert.NoError(t, PrepareTestDatabase())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testTeamRepositories := func(teamID int64, repoIds []int64) {
 | 
				
			||||||
 | 
							team := AssertExistsAndLoadBean(t, &Team{ID: teamID}).(*Team)
 | 
				
			||||||
 | 
							assert.NoError(t, team.GetRepositories(), "%s: GetRepositories", team.Name)
 | 
				
			||||||
 | 
							assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
 | 
				
			||||||
 | 
							assert.Equal(t, len(repoIds), len(team.Repos), "%s: repo count", team.Name)
 | 
				
			||||||
 | 
							for i, rid := range repoIds {
 | 
				
			||||||
 | 
								if rid > 0 {
 | 
				
			||||||
 | 
									assert.True(t, team.HasRepository(rid), "%s: HasRepository(%d) %d", rid, i)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Get an admin user.
 | 
				
			||||||
 | 
						user, err := GetUserByID(1)
 | 
				
			||||||
 | 
						assert.NoError(t, err, "GetUserByID")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create org.
 | 
				
			||||||
 | 
						org := &User{
 | 
				
			||||||
 | 
							Name:       "All repo",
 | 
				
			||||||
 | 
							IsActive:   true,
 | 
				
			||||||
 | 
							Type:       UserTypeOrganization,
 | 
				
			||||||
 | 
							Visibility: structs.VisibleTypePublic,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.NoError(t, CreateOrganization(org, user), "CreateOrganization")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check Owner team.
 | 
				
			||||||
 | 
						ownerTeam, err := org.GetOwnerTeam()
 | 
				
			||||||
 | 
						assert.NoError(t, err, "GetOwnerTeam")
 | 
				
			||||||
 | 
						assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create repos.
 | 
				
			||||||
 | 
						repoIds := make([]int64, 0)
 | 
				
			||||||
 | 
						for i := 0; i < 3; i++ {
 | 
				
			||||||
 | 
							r, err := CreateRepository(user, org, CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
 | 
				
			||||||
 | 
							assert.NoError(t, err, "CreateRepository %d", i)
 | 
				
			||||||
 | 
							if r != nil {
 | 
				
			||||||
 | 
								repoIds = append(repoIds, r.ID)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Get fresh copy of Owner team after creating repos.
 | 
				
			||||||
 | 
						ownerTeam, err = org.GetOwnerTeam()
 | 
				
			||||||
 | 
						assert.NoError(t, err, "GetOwnerTeam")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create teams and check repositories.
 | 
				
			||||||
 | 
						teams := []*Team{
 | 
				
			||||||
 | 
							ownerTeam,
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								OrgID:                   org.ID,
 | 
				
			||||||
 | 
								Name:                    "team one",
 | 
				
			||||||
 | 
								Authorize:               AccessModeRead,
 | 
				
			||||||
 | 
								IncludesAllRepositories: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								OrgID:                   org.ID,
 | 
				
			||||||
 | 
								Name:                    "team 2",
 | 
				
			||||||
 | 
								Authorize:               AccessModeRead,
 | 
				
			||||||
 | 
								IncludesAllRepositories: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								OrgID:                   org.ID,
 | 
				
			||||||
 | 
								Name:                    "team three",
 | 
				
			||||||
 | 
								Authorize:               AccessModeWrite,
 | 
				
			||||||
 | 
								IncludesAllRepositories: true,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								OrgID:                   org.ID,
 | 
				
			||||||
 | 
								Name:                    "team 4",
 | 
				
			||||||
 | 
								Authorize:               AccessModeWrite,
 | 
				
			||||||
 | 
								IncludesAllRepositories: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						teamRepos := [][]int64{
 | 
				
			||||||
 | 
							repoIds,
 | 
				
			||||||
 | 
							repoIds,
 | 
				
			||||||
 | 
							{},
 | 
				
			||||||
 | 
							repoIds,
 | 
				
			||||||
 | 
							{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for i, team := range teams {
 | 
				
			||||||
 | 
							if i > 0 { // first team is Owner.
 | 
				
			||||||
 | 
								assert.NoError(t, NewTeam(team), "%s: NewTeam", team.Name)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							testTeamRepositories(team.ID, teamRepos[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Update teams and check repositories.
 | 
				
			||||||
 | 
						teams[3].IncludesAllRepositories = false
 | 
				
			||||||
 | 
						teams[4].IncludesAllRepositories = true
 | 
				
			||||||
 | 
						teamRepos[4] = repoIds
 | 
				
			||||||
 | 
						for i, team := range teams {
 | 
				
			||||||
 | 
							assert.NoError(t, UpdateTeam(team, false, true), "%s: UpdateTeam", team.Name)
 | 
				
			||||||
 | 
							testTeamRepositories(team.ID, teamRepos[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create repo and check teams repositories.
 | 
				
			||||||
 | 
						org.Teams = nil // Reset teams to allow their reloading.
 | 
				
			||||||
 | 
						r, err := CreateRepository(user, org, CreateRepoOptions{Name: "repo-last"})
 | 
				
			||||||
 | 
						assert.NoError(t, err, "CreateRepository last")
 | 
				
			||||||
 | 
						if r != nil {
 | 
				
			||||||
 | 
							repoIds = append(repoIds, r.ID)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						teamRepos[0] = repoIds
 | 
				
			||||||
 | 
						teamRepos[1] = repoIds
 | 
				
			||||||
 | 
						teamRepos[4] = repoIds
 | 
				
			||||||
 | 
						for i, team := range teams {
 | 
				
			||||||
 | 
							testTeamRepositories(team.ID, teamRepos[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Remove repo and check teams repositories.
 | 
				
			||||||
 | 
						assert.NoError(t, DeleteRepository(user, org.ID, repoIds[0]), "DeleteRepository")
 | 
				
			||||||
 | 
						teamRepos[0] = repoIds[1:]
 | 
				
			||||||
 | 
						teamRepos[1] = repoIds[1:]
 | 
				
			||||||
 | 
						teamRepos[3] = repoIds[1:3]
 | 
				
			||||||
 | 
						teamRepos[4] = repoIds[1:]
 | 
				
			||||||
 | 
						for i, team := range teams {
 | 
				
			||||||
 | 
							testTeamRepositories(team.ID, teamRepos[i])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Wipe created items.
 | 
				
			||||||
 | 
						for i, rid := range repoIds {
 | 
				
			||||||
 | 
							if i > 0 { // first repo already deleted.
 | 
				
			||||||
 | 
								assert.NoError(t, DeleteRepository(user, org.ID, rid), "DeleteRepository %d", i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						assert.NoError(t, DeleteOrganization(org), "DeleteOrganization")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1447,15 +1447,18 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	u.NumRepos++
 | 
						u.NumRepos++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Give access to all members in owner team.
 | 
						// Give access to all members in teams with access to all repositories.
 | 
				
			||||||
	if u.IsOrganization() {
 | 
						if u.IsOrganization() {
 | 
				
			||||||
		t, err := u.getOwnerTeam(e)
 | 
							if err := u.GetTeams(); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
								return fmt.Errorf("GetTeams: %v", err)
 | 
				
			||||||
			return fmt.Errorf("getOwnerTeam: %v", err)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err = t.addRepository(e, repo); err != nil {
 | 
							for _, t := range u.Teams {
 | 
				
			||||||
 | 
								if t.IncludesAllRepositories {
 | 
				
			||||||
 | 
									if err := t.addRepository(e, repo); err != nil {
 | 
				
			||||||
					return fmt.Errorf("addRepository: %v", err)
 | 
										return fmt.Errorf("addRepository: %v", err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	} else if err = repo.recalculateAccesses(e); err != nil {
 | 
						} else if err = repo.recalculateAccesses(e); err != nil {
 | 
				
			||||||
		// Organization automatically called this in addRepository method.
 | 
							// Organization automatically called this in addRepository method.
 | 
				
			||||||
		return fmt.Errorf("recalculateAccesses: %v", err)
 | 
							return fmt.Errorf("recalculateAccesses: %v", err)
 | 
				
			||||||
@@ -1641,11 +1644,15 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if newOwner.IsOrganization() {
 | 
						if newOwner.IsOrganization() {
 | 
				
			||||||
		t, err := newOwner.getOwnerTeam(sess)
 | 
							if err := newOwner.GetTeams(); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
								return fmt.Errorf("GetTeams: %v", err)
 | 
				
			||||||
			return fmt.Errorf("getOwnerTeam: %v", err)
 | 
							}
 | 
				
			||||||
		} else if err = t.addRepository(sess, repo); err != nil {
 | 
							for _, t := range newOwner.Teams {
 | 
				
			||||||
			return fmt.Errorf("add to owner team: %v", err)
 | 
								if t.IncludesAllRepositories {
 | 
				
			||||||
 | 
									if err := t.addRepository(sess, repo); err != nil {
 | 
				
			||||||
 | 
										return fmt.Errorf("addRepository: %v", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if err = repo.recalculateAccesses(sess); err != nil {
 | 
						} else if err = repo.recalculateAccesses(sess); err != nil {
 | 
				
			||||||
		// Organization called this in addRepository method.
 | 
							// Organization called this in addRepository method.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,6 +62,7 @@ type CreateTeamForm struct {
 | 
				
			|||||||
	Description string `binding:"MaxSize(255)"`
 | 
						Description string `binding:"MaxSize(255)"`
 | 
				
			||||||
	Permission  string
 | 
						Permission  string
 | 
				
			||||||
	Units       []models.UnitType
 | 
						Units       []models.UnitType
 | 
				
			||||||
 | 
						RepoAccess  string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,6 +11,7 @@ type Team struct {
 | 
				
			|||||||
	Name                    string        `json:"name"`
 | 
						Name                    string        `json:"name"`
 | 
				
			||||||
	Description             string        `json:"description"`
 | 
						Description             string        `json:"description"`
 | 
				
			||||||
	Organization            *Organization `json:"organization"`
 | 
						Organization            *Organization `json:"organization"`
 | 
				
			||||||
 | 
						IncludesAllRepositories bool          `json:"includes_all_repositories"`
 | 
				
			||||||
	// enum: none,read,write,admin,owner
 | 
						// enum: none,read,write,admin,owner
 | 
				
			||||||
	Permission string `json:"permission"`
 | 
						Permission string `json:"permission"`
 | 
				
			||||||
	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
						// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
				
			||||||
@@ -22,6 +23,7 @@ type CreateTeamOption struct {
 | 
				
			|||||||
	// required: true
 | 
						// required: true
 | 
				
			||||||
	Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 | 
						Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 | 
				
			||||||
	Description             string `json:"description" binding:"MaxSize(255)"`
 | 
						Description             string `json:"description" binding:"MaxSize(255)"`
 | 
				
			||||||
 | 
						IncludesAllRepositories bool   `json:"includes_all_repositories"`
 | 
				
			||||||
	// enum: read,write,admin
 | 
						// enum: read,write,admin
 | 
				
			||||||
	Permission string `json:"permission"`
 | 
						Permission string `json:"permission"`
 | 
				
			||||||
	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
						// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
				
			||||||
@@ -33,6 +35,7 @@ type EditTeamOption struct {
 | 
				
			|||||||
	// required: true
 | 
						// required: true
 | 
				
			||||||
	Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 | 
						Name                    string `json:"name" binding:"Required;AlphaDashDot;MaxSize(30)"`
 | 
				
			||||||
	Description             string `json:"description" binding:"MaxSize(255)"`
 | 
						Description             string `json:"description" binding:"MaxSize(255)"`
 | 
				
			||||||
 | 
						IncludesAllRepositories bool   `json:"includes_all_repositories"`
 | 
				
			||||||
	// enum: read,write,admin
 | 
						// enum: read,write,admin
 | 
				
			||||||
	Permission string `json:"permission"`
 | 
						Permission string `json:"permission"`
 | 
				
			||||||
	// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
						// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.ext_wiki"]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1515,6 +1515,7 @@ team_name = Team Name
 | 
				
			|||||||
team_desc = Description
 | 
					team_desc = Description
 | 
				
			||||||
team_name_helper = Team names should be short and memorable.
 | 
					team_name_helper = Team names should be short and memorable.
 | 
				
			||||||
team_desc_helper = Describe the purpose or role of the team.
 | 
					team_desc_helper = Describe the purpose or role of the team.
 | 
				
			||||||
 | 
					team_access_desc = Repository access
 | 
				
			||||||
team_permission_desc = Permission
 | 
					team_permission_desc = Permission
 | 
				
			||||||
team_unit_desc = Allow Access to Repository Sections
 | 
					team_unit_desc = Allow Access to Repository Sections
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1588,6 +1589,13 @@ teams.add_nonexistent_repo = "The repository you're trying to add does not exist
 | 
				
			|||||||
teams.add_duplicate_users = User is already a team member.
 | 
					teams.add_duplicate_users = User is already a team member.
 | 
				
			||||||
teams.repos.none = No repositories could be accessed by this team.
 | 
					teams.repos.none = No repositories could be accessed by this team.
 | 
				
			||||||
teams.members.none = No members on this team.
 | 
					teams.members.none = No members on this team.
 | 
				
			||||||
 | 
					teams.specific_repositories = Specific repositories
 | 
				
			||||||
 | 
					teams.specific_repositories_helper = Members will only have access to repositories explicitly added to the team. Selecting this <strong>will not</strong> automatically remove repositories already added with <i>All repositories</i>.
 | 
				
			||||||
 | 
					teams.all_repositories = All repositories
 | 
				
			||||||
 | 
					teams.all_repositories_helper = Team has access to all repositories. Selecting this will <strong>add all existing</strong> repositories to the team.
 | 
				
			||||||
 | 
					teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories.
 | 
				
			||||||
 | 
					teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories.
 | 
				
			||||||
 | 
					teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[admin]
 | 
					[admin]
 | 
				
			||||||
dashboard = Dashboard
 | 
					dashboard = Dashboard
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -230,6 +230,7 @@ func ToTeam(team *models.Team) *api.Team {
 | 
				
			|||||||
		ID:                      team.ID,
 | 
							ID:                      team.ID,
 | 
				
			||||||
		Name:                    team.Name,
 | 
							Name:                    team.Name,
 | 
				
			||||||
		Description:             team.Description,
 | 
							Description:             team.Description,
 | 
				
			||||||
 | 
							IncludesAllRepositories: team.IncludesAllRepositories,
 | 
				
			||||||
		Permission:              team.Authorize.String(),
 | 
							Permission:              team.Authorize.String(),
 | 
				
			||||||
		Units:                   team.GetUnitNames(),
 | 
							Units:                   team.GetUnitNames(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -131,6 +131,7 @@ func CreateTeam(ctx *context.APIContext, form api.CreateTeamOption) {
 | 
				
			|||||||
		OrgID:                   ctx.Org.Organization.ID,
 | 
							OrgID:                   ctx.Org.Organization.ID,
 | 
				
			||||||
		Name:                    form.Name,
 | 
							Name:                    form.Name,
 | 
				
			||||||
		Description:             form.Description,
 | 
							Description:             form.Description,
 | 
				
			||||||
 | 
							IncludesAllRepositories: form.IncludesAllRepositories,
 | 
				
			||||||
		Authorize:               models.ParseAccessMode(form.Permission),
 | 
							Authorize:               models.ParseAccessMode(form.Permission),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -182,11 +183,27 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
 | 
				
			|||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/Team"
 | 
						//     "$ref": "#/responses/Team"
 | 
				
			||||||
	team := ctx.Org.Team
 | 
						team := ctx.Org.Team
 | 
				
			||||||
	team.Name = form.Name
 | 
					 | 
				
			||||||
	team.Description = form.Description
 | 
						team.Description = form.Description
 | 
				
			||||||
	team.Authorize = models.ParseAccessMode(form.Permission)
 | 
					 | 
				
			||||||
	unitTypes := models.FindUnitTypes(form.Units...)
 | 
						unitTypes := models.FindUnitTypes(form.Units...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isAuthChanged := false
 | 
				
			||||||
 | 
						isIncludeAllChanged := false
 | 
				
			||||||
 | 
						if !team.IsOwnerTeam() {
 | 
				
			||||||
 | 
							// Validate permission level.
 | 
				
			||||||
 | 
							auth := models.ParseAccessMode(form.Permission)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							team.Name = form.Name
 | 
				
			||||||
 | 
							if team.Authorize != auth {
 | 
				
			||||||
 | 
								isAuthChanged = true
 | 
				
			||||||
 | 
								team.Authorize = auth
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if team.IncludesAllRepositories != form.IncludesAllRepositories {
 | 
				
			||||||
 | 
								isIncludeAllChanged = true
 | 
				
			||||||
 | 
								team.IncludesAllRepositories = form.IncludesAllRepositories
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if team.Authorize < models.AccessModeOwner {
 | 
						if team.Authorize < models.AccessModeOwner {
 | 
				
			||||||
		var units = make([]*models.TeamUnit, 0, len(form.Units))
 | 
							var units = make([]*models.TeamUnit, 0, len(form.Units))
 | 
				
			||||||
		for _, tp := range unitTypes {
 | 
							for _, tp := range unitTypes {
 | 
				
			||||||
@@ -198,7 +215,7 @@ func EditTeam(ctx *context.APIContext, form api.EditTeamOption) {
 | 
				
			|||||||
		team.Units = units
 | 
							team.Units = units
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.UpdateTeam(team, true); err != nil {
 | 
						if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil {
 | 
				
			||||||
		ctx.Error(500, "EditTeam", err)
 | 
							ctx.Error(500, "EditTeam", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
					// Copyright 2014 The Gogs Authors. All rights reserved.
 | 
				
			||||||
 | 
					// Copyright 2019 The Gitea Authors. All rights reserved.
 | 
				
			||||||
// Use of this source code is governed by a MIT-style
 | 
					// Use of this source code is governed by a MIT-style
 | 
				
			||||||
// license that can be found in the LICENSE file.
 | 
					// license that can be found in the LICENSE file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -180,12 +181,14 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
				
			|||||||
	ctx.Data["PageIsOrgTeams"] = true
 | 
						ctx.Data["PageIsOrgTeams"] = true
 | 
				
			||||||
	ctx.Data["PageIsOrgTeamsNew"] = true
 | 
						ctx.Data["PageIsOrgTeamsNew"] = true
 | 
				
			||||||
	ctx.Data["Units"] = models.Units
 | 
						ctx.Data["Units"] = models.Units
 | 
				
			||||||
 | 
						var includesAllRepositories = (form.RepoAccess == "all")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t := &models.Team{
 | 
						t := &models.Team{
 | 
				
			||||||
		OrgID:                   ctx.Org.Organization.ID,
 | 
							OrgID:                   ctx.Org.Organization.ID,
 | 
				
			||||||
		Name:                    form.TeamName,
 | 
							Name:                    form.TeamName,
 | 
				
			||||||
		Description:             form.Description,
 | 
							Description:             form.Description,
 | 
				
			||||||
		Authorize:               models.ParseAccessMode(form.Permission),
 | 
							Authorize:               models.ParseAccessMode(form.Permission),
 | 
				
			||||||
 | 
							IncludesAllRepositories: includesAllRepositories,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if t.Authorize < models.AccessModeOwner {
 | 
						if t.Authorize < models.AccessModeOwner {
 | 
				
			||||||
@@ -268,6 +271,8 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
				
			|||||||
	ctx.Data["Units"] = models.Units
 | 
						ctx.Data["Units"] = models.Units
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isAuthChanged := false
 | 
						isAuthChanged := false
 | 
				
			||||||
 | 
						isIncludeAllChanged := false
 | 
				
			||||||
 | 
						var includesAllRepositories = (form.RepoAccess == "all")
 | 
				
			||||||
	if !t.IsOwnerTeam() {
 | 
						if !t.IsOwnerTeam() {
 | 
				
			||||||
		// Validate permission level.
 | 
							// Validate permission level.
 | 
				
			||||||
		auth := models.ParseAccessMode(form.Permission)
 | 
							auth := models.ParseAccessMode(form.Permission)
 | 
				
			||||||
@@ -277,6 +282,11 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
				
			|||||||
			isAuthChanged = true
 | 
								isAuthChanged = true
 | 
				
			||||||
			t.Authorize = auth
 | 
								t.Authorize = auth
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if t.IncludesAllRepositories != includesAllRepositories {
 | 
				
			||||||
 | 
								isIncludeAllChanged = true
 | 
				
			||||||
 | 
								t.IncludesAllRepositories = includesAllRepositories
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	t.Description = form.Description
 | 
						t.Description = form.Description
 | 
				
			||||||
	if t.Authorize < models.AccessModeOwner {
 | 
						if t.Authorize < models.AccessModeOwner {
 | 
				
			||||||
@@ -305,7 +315,7 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := models.UpdateTeam(t, isAuthChanged); err != nil {
 | 
						if err := models.UpdateTeam(t, isAuthChanged, isIncludeAllChanged); err != nil {
 | 
				
			||||||
		ctx.Data["Err_TeamName"] = true
 | 
							ctx.Data["Err_TeamName"] = true
 | 
				
			||||||
		switch {
 | 
							switch {
 | 
				
			||||||
		case models.IsErrTeamAlreadyExist(err):
 | 
							case models.IsErrTeamAlreadyExist(err):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,24 @@
 | 
				
			|||||||
						<span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span>
 | 
											<span class="help">{{.i18n.Tr "org.team_desc_helper"}}</span>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
					{{if not (eq .Team.LowerName "owners")}}
 | 
										{{if not (eq .Team.LowerName "owners")}}
 | 
				
			||||||
 | 
											<div class="grouped field">
 | 
				
			||||||
 | 
												<label>{{.i18n.Tr "org.team_access_desc"}}</label>
 | 
				
			||||||
 | 
												<br>
 | 
				
			||||||
 | 
												<div class="field">
 | 
				
			||||||
 | 
													<div class="ui radio checkbox">
 | 
				
			||||||
 | 
														<input type="radio" name="repo_access" value="specific" {{if not .Team.IncludesAllRepositories}}checked{{end}}>
 | 
				
			||||||
 | 
														<label>{{.i18n.Tr "org.teams.specific_repositories"}}</label>
 | 
				
			||||||
 | 
														<span class="help">{{.i18n.Tr "org.teams.specific_repositories_helper"}}</span>
 | 
				
			||||||
 | 
													</div>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
												<div class="field">
 | 
				
			||||||
 | 
													<div class="ui radio checkbox">
 | 
				
			||||||
 | 
														<input type="radio" name="repo_access" value="all" {{if .Team.IncludesAllRepositories}}checked{{end}}>
 | 
				
			||||||
 | 
														<label>{{.i18n.Tr "org.teams.all_repositories"}}</label>
 | 
				
			||||||
 | 
														<span class="help">{{.i18n.Tr "org.teams.all_repositories_helper"}}</span>
 | 
				
			||||||
 | 
													</div>
 | 
				
			||||||
 | 
												</div>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
						<div class="grouped field">
 | 
											<div class="grouped field">
 | 
				
			||||||
							<label>{{.i18n.Tr "org.team_permission_desc"}}</label>
 | 
												<label>{{.i18n.Tr "org.team_permission_desc"}}</label>
 | 
				
			||||||
							<br>
 | 
												<br>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,7 @@
 | 
				
			|||||||
			{{template "org/team/sidebar" .}}
 | 
								{{template "org/team/sidebar" .}}
 | 
				
			||||||
			<div class="ui ten wide column">
 | 
								<div class="ui ten wide column">
 | 
				
			||||||
				{{template "org/team/navbar" .}}
 | 
									{{template "org/team/navbar" .}}
 | 
				
			||||||
				{{$canAddRemove := and $.IsOrganizationOwner (not (eq $.Team.LowerName "owners"))}}
 | 
									{{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}}
 | 
				
			||||||
				{{if $canAddRemove}}
 | 
									{{if $canAddRemove}}
 | 
				
			||||||
					<div class="ui attached segment">
 | 
										<div class="ui attached segment">
 | 
				
			||||||
						<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
 | 
											<form class="ui form" id="add-repo-form" action="{{$.OrgLink}}/teams/{{$.Team.LowerName}}/action/repo/add" method="post">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,12 +22,24 @@
 | 
				
			|||||||
			{{if eq .Team.LowerName "owners"}}
 | 
								{{if eq .Team.LowerName "owners"}}
 | 
				
			||||||
				{{.i18n.Tr "org.teams.owners_permission_desc" | Str2html}}
 | 
									{{.i18n.Tr "org.teams.owners_permission_desc" | Str2html}}
 | 
				
			||||||
			{{else if (eq .Team.Authorize 1)}}
 | 
								{{else if (eq .Team.Authorize 1)}}
 | 
				
			||||||
 | 
									{{if .Team.IncludesAllRepositories}}
 | 
				
			||||||
 | 
										{{.i18n.Tr "org.teams.all_repositories_read_permission_desc" | Str2html}}
 | 
				
			||||||
 | 
									{{else}}
 | 
				
			||||||
					{{.i18n.Tr "org.teams.read_permission_desc" | Str2html}}
 | 
										{{.i18n.Tr "org.teams.read_permission_desc" | Str2html}}
 | 
				
			||||||
 | 
									{{end}}
 | 
				
			||||||
			{{else if (eq .Team.Authorize 2)}}
 | 
								{{else if (eq .Team.Authorize 2)}}
 | 
				
			||||||
 | 
									{{if .Team.IncludesAllRepositories}}
 | 
				
			||||||
 | 
										{{.i18n.Tr "org.teams.all_repositories_write_permission_desc" | Str2html}}
 | 
				
			||||||
 | 
									{{else}}
 | 
				
			||||||
					{{.i18n.Tr "org.teams.write_permission_desc" | Str2html}}
 | 
										{{.i18n.Tr "org.teams.write_permission_desc" | Str2html}}
 | 
				
			||||||
 | 
									{{end}}
 | 
				
			||||||
			{{else if (eq .Team.Authorize 3)}}
 | 
								{{else if (eq .Team.Authorize 3)}}
 | 
				
			||||||
 | 
									{{if .Team.IncludesAllRepositories}}
 | 
				
			||||||
 | 
										{{.i18n.Tr "org.teams.all_repositories_admin_permission_desc" | Str2html}}
 | 
				
			||||||
 | 
									{{else}}
 | 
				
			||||||
					{{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}}
 | 
										{{.i18n.Tr "org.teams.admin_permission_desc" | Str2html}}
 | 
				
			||||||
				{{end}}
 | 
									{{end}}
 | 
				
			||||||
 | 
								{{end}}
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	{{if .IsOrganizationOwner}}
 | 
						{{if .IsOrganizationOwner}}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8242,6 +8242,10 @@
 | 
				
			|||||||
          "type": "string",
 | 
					          "type": "string",
 | 
				
			||||||
          "x-go-name": "Description"
 | 
					          "x-go-name": "Description"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "includes_all_repositories": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "IncludesAllRepositories"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "name": {
 | 
					        "name": {
 | 
				
			||||||
          "type": "string",
 | 
					          "type": "string",
 | 
				
			||||||
          "x-go-name": "Name"
 | 
					          "x-go-name": "Name"
 | 
				
			||||||
@@ -8801,6 +8805,10 @@
 | 
				
			|||||||
          "type": "string",
 | 
					          "type": "string",
 | 
				
			||||||
          "x-go-name": "Description"
 | 
					          "x-go-name": "Description"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "includes_all_repositories": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "IncludesAllRepositories"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "name": {
 | 
					        "name": {
 | 
				
			||||||
          "type": "string",
 | 
					          "type": "string",
 | 
				
			||||||
          "x-go-name": "Name"
 | 
					          "x-go-name": "Name"
 | 
				
			||||||
@@ -10457,6 +10465,10 @@
 | 
				
			|||||||
          "format": "int64",
 | 
					          "format": "int64",
 | 
				
			||||||
          "x-go-name": "ID"
 | 
					          "x-go-name": "ID"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        "includes_all_repositories": {
 | 
				
			||||||
 | 
					          "type": "boolean",
 | 
				
			||||||
 | 
					          "x-go-name": "IncludesAllRepositories"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "name": {
 | 
					        "name": {
 | 
				
			||||||
          "type": "string",
 | 
					          "type": "string",
 | 
				
			||||||
          "x-go-name": "Name"
 | 
					          "x-go-name": "Name"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user