hide issues from org private repos w/o team assignment (#4034)
This commit is contained in:
		
							parent
							
								
									46d19c4676
								
							
						
					
					
						commit
						0b3ea42847
					
				
					 24 changed files with 545 additions and 72 deletions
				
			
		|  | @ -67,9 +67,9 @@ func TestAPISearchRepo(t *testing.T) { | ||||||
| 		expectedResults | 		expectedResults | ||||||
| 	}{ | 	}{ | ||||||
| 		{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ | 		{name: "RepositoriesMax50", requestURL: "/api/v1/repos/search?limit=50", expectedResults: expectedResults{ | ||||||
| 			nil:   {count: 15}, | 			nil:   {count: 16}, | ||||||
| 			user:  {count: 15}, | 			user:  {count: 16}, | ||||||
| 			user2: {count: 15}}, | 			user2: {count: 16}}, | ||||||
| 		}, | 		}, | ||||||
| 		{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ | 		{name: "RepositoriesMax10", requestURL: "/api/v1/repos/search?limit=10", expectedResults: expectedResults{ | ||||||
| 			nil:   {count: 10}, | 			nil:   {count: 10}, | ||||||
|  |  | ||||||
|  | @ -22,8 +22,12 @@ func TestAccessLevel(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | 	user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||||
| 	user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) | 	user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) | ||||||
| 	repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) | 	// A public repository owned by User 2
 | ||||||
| 	repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) | 	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||||||
|  | 	assert.False(t, repo1.IsPrivate) | ||||||
|  | 	// A private repository owned by Org 3
 | ||||||
|  | 	repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) | ||||||
|  | 	assert.True(t, repo2.IsPrivate) | ||||||
| 
 | 
 | ||||||
| 	level, err := AccessLevel(user1.ID, repo1) | 	level, err := AccessLevel(user1.ID, repo1) | ||||||
| 	assert.NoError(t, err) | 	assert.NoError(t, err) | ||||||
|  | @ -47,8 +51,12 @@ func TestHasAccess(t *testing.T) { | ||||||
| 
 | 
 | ||||||
| 	user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | 	user1 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||||
| 	user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) | 	user2 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) | ||||||
| 	repo1 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 2, IsPrivate: false}).(*Repository) | 	// A public repository owned by User 2
 | ||||||
| 	repo2 := AssertExistsAndLoadBean(t, &Repository{OwnerID: 3, IsPrivate: true}).(*Repository) | 	repo1 := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository) | ||||||
|  | 	assert.False(t, repo1.IsPrivate) | ||||||
|  | 	// A private repository owned by Org 3
 | ||||||
|  | 	repo2 := AssertExistsAndLoadBean(t, &Repository{ID: 3}).(*Repository) | ||||||
|  | 	assert.True(t, repo2.IsPrivate) | ||||||
| 
 | 
 | ||||||
| 	for _, accessMode := range accessModes { | 	for _, accessMode := range accessModes { | ||||||
| 		has, err := HasAccess(user1.ID, repo1, accessMode) | 		has, err := HasAccess(user1.ID, repo1, accessMode) | ||||||
|  |  | ||||||
|  | @ -351,7 +351,7 @@ | ||||||
|   is_mirror: true |   is_mirror: true | ||||||
|   num_forks: 1 |   num_forks: 1 | ||||||
|   is_fork: false |   is_fork: false | ||||||
|    | 
 | ||||||
| - | - | ||||||
|   id: 29 |   id: 29 | ||||||
|   fork_id: 27 |   fork_id: 27 | ||||||
|  | @ -365,7 +365,7 @@ | ||||||
|   num_closed_pulls: 0 |   num_closed_pulls: 0 | ||||||
|   is_mirror: false |   is_mirror: false | ||||||
|   is_fork: true |   is_fork: true | ||||||
|    | 
 | ||||||
| - | - | ||||||
|   id: 30 |   id: 30 | ||||||
|   fork_id: 28 |   fork_id: 28 | ||||||
|  | @ -389,3 +389,14 @@ | ||||||
|   num_forks: 0 |   num_forks: 0 | ||||||
|   num_issues: 0 |   num_issues: 0 | ||||||
|   is_mirror: false |   is_mirror: false | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 32 | ||||||
|  |   owner_id: 3 | ||||||
|  |   lower_name: repo21 | ||||||
|  |   name: repo21 | ||||||
|  |   is_private: false | ||||||
|  |   num_stars: 0 | ||||||
|  |   num_forks: 0 | ||||||
|  |   num_issues: 0 | ||||||
|  |   is_mirror: false | ||||||
|  |  | ||||||
|  | @ -4,9 +4,8 @@ | ||||||
|   lower_name: owners |   lower_name: owners | ||||||
|   name: Owners |   name: Owners | ||||||
|   authorize: 4 # owner |   authorize: 4 # owner | ||||||
|   num_repos: 2 |   num_repos: 3 | ||||||
|   num_members: 1 |   num_members: 1 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
| 
 | 
 | ||||||
| - | - | ||||||
|   id: 2 |   id: 2 | ||||||
|  | @ -16,7 +15,6 @@ | ||||||
|   authorize: 2 # write |   authorize: 2 # write | ||||||
|   num_repos: 1 |   num_repos: 1 | ||||||
|   num_members: 2 |   num_members: 2 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
| 
 | 
 | ||||||
| - | - | ||||||
|   id: 3 |   id: 3 | ||||||
|  | @ -26,7 +24,6 @@ | ||||||
|   authorize: 4 # owner |   authorize: 4 # owner | ||||||
|   num_repos: 0 |   num_repos: 0 | ||||||
|   num_members: 1 |   num_members: 1 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
| 
 | 
 | ||||||
| - | - | ||||||
|   id: 4 |   id: 4 | ||||||
|  | @ -36,7 +33,6 @@ | ||||||
|   authorize: 4 # owner |   authorize: 4 # owner | ||||||
|   num_repos: 0 |   num_repos: 0 | ||||||
|   num_members: 1 |   num_members: 1 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
| 
 | 
 | ||||||
| - | - | ||||||
|   id: 5 |   id: 5 | ||||||
|  | @ -46,7 +42,6 @@ | ||||||
|   authorize: 4 # owner |   authorize: 4 # owner | ||||||
|   num_repos: 2 |   num_repos: 2 | ||||||
|   num_members: 2 |   num_members: 2 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
| 
 | 
 | ||||||
| - | - | ||||||
|   id: 6 |   id: 6 | ||||||
|  | @ -56,4 +51,3 @@ | ||||||
|   authorize: 4 # owner |   authorize: 4 # owner | ||||||
|   num_repos: 2 |   num_repos: 2 | ||||||
|   num_members: 1 |   num_members: 1 | ||||||
|   unit_types: '[1,2,3,4,5,6,7]' |  | ||||||
|  | @ -33,9 +33,15 @@ | ||||||
|   org_id: 19 |   org_id: 19 | ||||||
|   team_id: 6 |   team_id: 6 | ||||||
|   repo_id: 27 |   repo_id: 27 | ||||||
|    | 
 | ||||||
| - | - | ||||||
|   id: 7 |   id: 7 | ||||||
|   org_id: 19 |   org_id: 19 | ||||||
|   team_id: 6 |   team_id: 6 | ||||||
|   repo_id: 28 |   repo_id: 28 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 8 | ||||||
|  |   org_id: 3 | ||||||
|  |   team_id: 1 | ||||||
|  |   repo_id: 32 | ||||||
|  |  | ||||||
							
								
								
									
										209
									
								
								models/fixtures/team_unit.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								models/fixtures/team_unit.yml
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,209 @@ | ||||||
|  | - | ||||||
|  |   id: 1 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 2 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 3 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 4 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 5 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 6 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 7 | ||||||
|  |   team_id: 1 | ||||||
|  |   type: 7 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 8 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 9 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 10 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 11 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 12 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 13 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 14 | ||||||
|  |   team_id: 2 | ||||||
|  |   type: 7 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 15 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 16 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 17 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 18 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 19 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 20 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 21 | ||||||
|  |   team_id: 3 | ||||||
|  |   type: 7 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 22 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 23 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 24 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 25 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 26 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 27 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 28 | ||||||
|  |   team_id: 4 | ||||||
|  |   type: 7 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 29 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 30 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 31 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 32 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 33 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 34 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 35 | ||||||
|  |   team_id: 5 | ||||||
|  |   type: 7 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 36 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 1 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 37 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 2 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 38 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 3 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 39 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 4 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 40 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 5 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 41 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 6 | ||||||
|  | 
 | ||||||
|  | - | ||||||
|  |   id: 42 | ||||||
|  |   team_id: 6 | ||||||
|  |   type: 7 | ||||||
|  | @ -45,7 +45,7 @@ | ||||||
|   is_admin: false |   is_admin: false | ||||||
|   avatar: avatar3 |   avatar: avatar3 | ||||||
|   avatar_email: user3@example.com |   avatar_email: user3@example.com | ||||||
|   num_repos: 2 |   num_repos: 3 | ||||||
|   num_members: 2 |   num_members: 2 | ||||||
|   num_teams: 2 |   num_teams: 2 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -190,6 +190,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("remove stale watches", removeStaleWatches), | 	NewMigration("remove stale watches", removeStaleWatches), | ||||||
| 	// v68 -> V69
 | 	// v68 -> V69
 | ||||||
| 	NewMigration("Reformat and remove incorrect topics", reformatAndRemoveIncorrectTopics), | 	NewMigration("Reformat and remove incorrect topics", reformatAndRemoveIncorrectTopics), | ||||||
|  | 	// v69 -> v70
 | ||||||
|  | 	NewMigration("move team units to team_unit table", moveTeamUnitsToTeamUnitTable), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Migrate database to current version
 | // Migrate database to current version
 | ||||||
|  |  | ||||||
|  | @ -25,10 +25,15 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) { | ||||||
| 		Created     time.Time       `xorm:"-"` | 		Created     time.Time       `xorm:"-"` | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	type Team struct { | ||||||
|  | 		ID        int64 | ||||||
|  | 		UnitTypes []int `xorm:"json"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Update team unit types
 | 	// Update team unit types
 | ||||||
| 	const batchSize = 100 | 	const batchSize = 100 | ||||||
| 	for start := 0; ; start += batchSize { | 	for start := 0; ; start += batchSize { | ||||||
| 		teams := make([]*models.Team, 0, batchSize) | 		teams := make([]*Team, 0, batchSize) | ||||||
| 		if err := x.Limit(batchSize, start).Find(&teams); err != nil { | 		if err := x.Limit(batchSize, start).Find(&teams); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  | @ -36,7 +41,7 @@ func removeCommitsUnitType(x *xorm.Engine) (err error) { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		for _, team := range teams { | 		for _, team := range teams { | ||||||
| 			ut := make([]models.UnitType, 0, len(team.UnitTypes)) | 			ut := make([]int, 0, len(team.UnitTypes)) | ||||||
| 			for _, u := range team.UnitTypes { | 			for _, u := range team.UnitTypes { | ||||||
| 				if u < V16UnitTypeCommits { | 				if u < V16UnitTypeCommits { | ||||||
| 					ut = append(ut, u) | 					ut = append(ut, u) | ||||||
|  |  | ||||||
							
								
								
									
										80
									
								
								models/migrations/v69.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								models/migrations/v69.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,80 @@ | ||||||
|  | // Copyright 2018 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 ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-xorm/xorm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func moveTeamUnitsToTeamUnitTable(x *xorm.Engine) error { | ||||||
|  | 	// Team see models/team.go
 | ||||||
|  | 	type Team struct { | ||||||
|  | 		ID        int64 | ||||||
|  | 		OrgID     int64 | ||||||
|  | 		UnitTypes []int `xorm:"json"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// TeamUnit see models/org_team.go
 | ||||||
|  | 	type TeamUnit struct { | ||||||
|  | 		ID     int64 `xorm:"pk autoincr"` | ||||||
|  | 		OrgID  int64 `xorm:"INDEX"` | ||||||
|  | 		TeamID int64 `xorm:"UNIQUE(s)"` | ||||||
|  | 		Type   int   `xorm:"UNIQUE(s)"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := x.Sync2(new(TeamUnit)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sess := x.NewSession() | ||||||
|  | 	defer sess.Close() | ||||||
|  | 
 | ||||||
|  | 	if err := sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// Update team unit types
 | ||||||
|  | 	const batchSize = 100 | ||||||
|  | 	for start := 0; ; start += batchSize { | ||||||
|  | 		teams := make([]*Team, 0, batchSize) | ||||||
|  | 		if err := x.Limit(batchSize, start).Find(&teams); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		if len(teams) == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		for _, team := range teams { | ||||||
|  | 			var unitTypes []int | ||||||
|  | 			if len(team.UnitTypes) == 0 { | ||||||
|  | 				unitTypes = allUnitTypes | ||||||
|  | 			} else { | ||||||
|  | 				unitTypes = team.UnitTypes | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// insert units for team
 | ||||||
|  | 			var units = make([]TeamUnit, 0, len(unitTypes)) | ||||||
|  | 			for _, tp := range unitTypes { | ||||||
|  | 				units = append(units, TeamUnit{ | ||||||
|  | 					OrgID:  team.OrgID, | ||||||
|  | 					TeamID: team.ID, | ||||||
|  | 					Type:   tp, | ||||||
|  | 				}) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if _, err := sess.Insert(&units); err != nil { | ||||||
|  | 				return fmt.Errorf("Insert team units: %v", err) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := dropTableColumns(sess, "team", "unit_types"); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	return sess.Commit() | ||||||
|  | } | ||||||
|  | @ -122,6 +122,7 @@ func init() { | ||||||
| 		new(Reaction), | 		new(Reaction), | ||||||
| 		new(IssueAssignees), | 		new(IssueAssignees), | ||||||
| 		new(U2FRegistration), | 		new(U2FRegistration), | ||||||
|  | 		new(TeamUnit), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	gonicNames := []string{"SSL", "UID"} | 	gonicNames := []string{"SSL", "UID"} | ||||||
|  |  | ||||||
|  | @ -119,7 +119,17 @@ func createOrUpdateIssueNotifications(e Engine, issue *Issue, notificationAuthor | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	issue.loadRepo(e) | ||||||
|  | 
 | ||||||
| 	for _, watch := range watches { | 	for _, watch := range watches { | ||||||
|  | 		issue.Repo.Units = nil | ||||||
|  | 		if issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypePullRequests) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if !issue.IsPull && !issue.Repo.CheckUnitUser(watch.UserID, false, UnitTypeIssues) { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if err := notifyUser(watch.UserID); err != nil { | 		if err := notifyUser(watch.UserID); err != nil { | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -154,12 +154,26 @@ func CreateOrganization(org, owner *User) (err error) { | ||||||
| 		Name:       ownerTeamName, | 		Name:       ownerTeamName, | ||||||
| 		Authorize:  AccessModeOwner, | 		Authorize:  AccessModeOwner, | ||||||
| 		NumMembers: 1, | 		NumMembers: 1, | ||||||
| 		UnitTypes:  allRepUnitTypes, |  | ||||||
| 	} | 	} | ||||||
| 	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) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// insert units for team
 | ||||||
|  | 	var units = make([]TeamUnit, 0, len(allRepUnitTypes)) | ||||||
|  | 	for _, tp := range allRepUnitTypes { | ||||||
|  | 		units = append(units, TeamUnit{ | ||||||
|  | 			OrgID:  org.ID, | ||||||
|  | 			TeamID: t.ID, | ||||||
|  | 			Type:   tp, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err = sess.Insert(&units); err != nil { | ||||||
|  | 		sess.Rollback() | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if _, err = sess.Insert(&TeamUser{ | 	if _, err = sess.Insert(&TeamUser{ | ||||||
| 		UID:    owner.ID, | 		UID:    owner.ID, | ||||||
| 		OrgID:  org.ID, | 		OrgID:  org.ID, | ||||||
|  | @ -238,6 +252,7 @@ func deleteOrg(e *xorm.Session, u *User) error { | ||||||
| 		&Team{OrgID: u.ID}, | 		&Team{OrgID: u.ID}, | ||||||
| 		&OrgUser{OrgID: u.ID}, | 		&OrgUser{OrgID: u.ID}, | ||||||
| 		&TeamUser{OrgID: u.ID}, | 		&TeamUser{OrgID: u.ID}, | ||||||
|  | 		&TeamUnit{OrgID: u.ID}, | ||||||
| 	); err != nil { | 	); err != nil { | ||||||
| 		return fmt.Errorf("deleteBeans: %v", err) | 		return fmt.Errorf("deleteBeans: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -1,3 +1,4 @@ | ||||||
|  | // Copyright 2018 The Gitea Authors. All rights reserved.
 | ||||||
| // Copyright 2016 The Gogs Authors. All rights reserved.
 | // Copyright 2016 The Gogs 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.
 | ||||||
|  | @ -10,7 +11,6 @@ import ( | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 
 |  | ||||||
| 	"github.com/go-xorm/xorm" | 	"github.com/go-xorm/xorm" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -28,15 +28,16 @@ type Team struct { | ||||||
| 	Members     []*User       `xorm:"-"` | 	Members     []*User       `xorm:"-"` | ||||||
| 	NumRepos    int | 	NumRepos    int | ||||||
| 	NumMembers  int | 	NumMembers  int | ||||||
| 	UnitTypes   []UnitType `xorm:"json"` | 	Units       []*TeamUnit `xorm:"-"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetUnitTypes returns unit types the team owned, empty means all the unit types
 | func (t *Team) getUnits(e Engine) (err error) { | ||||||
| func (t *Team) GetUnitTypes() []UnitType { | 	if t.Units != nil { | ||||||
| 	if len(t.UnitTypes) == 0 { | 		return nil | ||||||
| 		return allRepUnitTypes |  | ||||||
| 	} | 	} | ||||||
| 	return t.UnitTypes | 
 | ||||||
|  | 	t.Units, err = getUnitsByTeamID(e, t.ID) | ||||||
|  | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // HasWriteAccess returns true if team has at least write level access mode.
 | // HasWriteAccess returns true if team has at least write level access mode.
 | ||||||
|  | @ -214,11 +215,12 @@ func (t *Team) RemoveRepository(repoID int64) error { | ||||||
| 
 | 
 | ||||||
| // UnitEnabled returns if the team has the given unit type enabled
 | // UnitEnabled returns if the team has the given unit type enabled
 | ||||||
| func (t *Team) UnitEnabled(tp UnitType) bool { | func (t *Team) UnitEnabled(tp UnitType) bool { | ||||||
| 	if len(t.UnitTypes) == 0 { | 	if err := t.getUnits(x); err != nil { | ||||||
| 		return true | 		log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error()) | ||||||
| 	} | 	} | ||||||
| 	for _, u := range t.UnitTypes { | 
 | ||||||
| 		if u == tp { | 	for _, unit := range t.Units { | ||||||
|  | 		if unit.Type == tp { | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | @ -275,6 +277,17 @@ func NewTeam(t *Team) (err error) { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// 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 { | ||||||
|  | 			sess.Rollback() | ||||||
|  | 			return 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 { | ||||||
| 		sess.Rollback() | 		sess.Rollback() | ||||||
|  | @ -424,6 +437,13 @@ func DeleteTeam(t *Team) error { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Delete team-unit.
 | ||||||
|  | 	if _, err := sess. | ||||||
|  | 		Where("team_id=?", t.ID). | ||||||
|  | 		Delete(new(TeamUnit)); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Delete team.
 | 	// Delete team.
 | ||||||
| 	if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil { | 	if _, err := sess.ID(t.ID).Delete(new(Team)); err != nil { | ||||||
| 		return err | 		return err | ||||||
|  | @ -695,3 +715,47 @@ func GetTeamsWithAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, er | ||||||
| 		And("team_repo.repo_id = ?", repoID). | 		And("team_repo.repo_id = ?", repoID). | ||||||
| 		Find(&teams) | 		Find(&teams) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ___________                    ____ ___      .__  __
 | ||||||
|  | // \__    ___/___ _____    _____ |    |   \____ |__|/  |_
 | ||||||
|  | //   |    |_/ __ \\__  \  /     \|    |   /    \|  \   __\
 | ||||||
|  | //   |    |\  ___/ / __ \|  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 { | ||||||
|  | 		sess.Rollback() | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return sess.Commit() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -489,8 +489,8 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) { | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		assert.EqualValues(t, expectedCount, count) | 		assert.EqualValues(t, expectedCount, count) | ||||||
| 	} | 	} | ||||||
| 	testSuccess(2, 2) | 	testSuccess(2, 3) | ||||||
| 	testSuccess(4, 1) | 	testSuccess(4, 2) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAccessibleReposEnv_RepoIDs(t *testing.T) { | func TestAccessibleReposEnv_RepoIDs(t *testing.T) { | ||||||
|  | @ -503,8 +503,8 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) { | ||||||
| 		assert.NoError(t, err) | 		assert.NoError(t, err) | ||||||
| 		assert.Equal(t, expectedRepoIDs, repoIDs) | 		assert.Equal(t, expectedRepoIDs, repoIDs) | ||||||
| 	} | 	} | ||||||
| 	testSuccess(2, 1, 100, []int64{3, 5}) | 	testSuccess(2, 1, 100, []int64{3, 5, 32}) | ||||||
| 	testSuccess(4, 0, 100, []int64{3}) | 	testSuccess(4, 0, 100, []int64{3, 32}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAccessibleReposEnv_Repos(t *testing.T) { | func TestAccessibleReposEnv_Repos(t *testing.T) { | ||||||
|  | @ -522,8 +522,8 @@ func TestAccessibleReposEnv_Repos(t *testing.T) { | ||||||
| 		} | 		} | ||||||
| 		assert.Equal(t, expectedRepos, repos) | 		assert.Equal(t, expectedRepos, repos) | ||||||
| 	} | 	} | ||||||
| 	testSuccess(2, []int64{3, 5}) | 	testSuccess(2, []int64{3, 5, 32}) | ||||||
| 	testSuccess(4, []int64{3}) | 	testSuccess(4, []int64{3, 32}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { | func TestAccessibleReposEnv_MirrorRepos(t *testing.T) { | ||||||
|  |  | ||||||
|  | @ -365,22 +365,14 @@ func (repo *Repository) getUnitsByUserID(e Engine, userID int64, isAdmin bool) ( | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var allTypes = make(map[UnitType]struct{}, len(allRepUnitTypes)) |  | ||||||
| 	for _, team := range teams { |  | ||||||
| 		// Administrators can not be limited
 |  | ||||||
| 		if team.Authorize >= AccessModeAdmin { |  | ||||||
| 			return nil |  | ||||||
| 		} |  | ||||||
| 		for _, unitType := range team.UnitTypes { |  | ||||||
| 			allTypes[unitType] = struct{}{} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// unique
 | 	// unique
 | ||||||
| 	var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units)) | 	var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units)) | ||||||
| 	for _, u := range repo.Units { | 	for _, u := range repo.Units { | ||||||
| 		if _, ok := allTypes[u.Type]; ok { | 		for _, team := range teams { | ||||||
| 			newRepoUnits = append(newRepoUnits, u) | 			if team.UnitEnabled(u.Type) { | ||||||
|  | 				newRepoUnits = append(newRepoUnits, u) | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -147,10 +147,10 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||||
| 			count: 14}, | 			count: 14}, | ||||||
| 		{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", | 		{name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, AllPublic: true}, | ||||||
| 			count: 15}, | 			count: 16}, | ||||||
| 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | ||||||
| 			count: 19}, | 			count: 20}, | ||||||
| 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", | 		{name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", | ||||||
| 			opts:  &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | 			opts:  &SearchRepoOptions{Keyword: "test", Page: 1, PageSize: 10, OwnerID: 15, Private: true, AllPublic: true}, | ||||||
| 			count: 13}, | 			count: 13}, | ||||||
|  | @ -159,7 +159,7 @@ func TestSearchRepositoryByName(t *testing.T) { | ||||||
| 			count: 11}, | 			count: 11}, | ||||||
| 		{name: "AllPublic/PublicRepositoriesOfOrganization", | 		{name: "AllPublic/PublicRepositoriesOfOrganization", | ||||||
| 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, | 			opts:  &SearchRepoOptions{Page: 1, PageSize: 10, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse}, | ||||||
| 			count: 15}, | 			count: 16}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, testCase := range testCases { | 	for _, testCase := range testCases { | ||||||
|  |  | ||||||
|  | @ -109,6 +109,23 @@ func notifyWatchers(e Engine, act *Action) error { | ||||||
| 
 | 
 | ||||||
| 		act.ID = 0 | 		act.ID = 0 | ||||||
| 		act.UserID = watches[i].UserID | 		act.UserID = watches[i].UserID | ||||||
|  | 		act.Repo.Units = nil | ||||||
|  | 
 | ||||||
|  | 		switch act.OpType { | ||||||
|  | 		case ActionCommitRepo, ActionPushTag, ActionDeleteTag, ActionDeleteBranch: | ||||||
|  | 			if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypeCode) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		case ActionCreateIssue, ActionCommentIssue, ActionCloseIssue, ActionReopenIssue: | ||||||
|  | 			if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypeIssues) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		case ActionCreatePullRequest, ActionMergePullRequest, ActionClosePullRequest, ActionReopenPullRequest: | ||||||
|  | 			if !act.Repo.CheckUnitUser(act.UserID, false, UnitTypePullRequests) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if _, err = e.InsertOne(act); err != nil { | 		if _, err = e.InsertOne(act); err != nil { | ||||||
| 			return fmt.Errorf("insert new action: %v", err) | 			return fmt.Errorf("insert new action: %v", err) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -546,28 +546,46 @@ func (u *User) GetRepositories(page, pageSize int) (err error) { | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetRepositoryIDs returns repositories IDs where user owned
 | // GetRepositoryIDs returns repositories IDs where user owned and has unittypes
 | ||||||
| func (u *User) GetRepositoryIDs() ([]int64, error) { | func (u *User) GetRepositoryIDs(units ...UnitType) ([]int64, error) { | ||||||
| 	var ids []int64 | 	var ids []int64 | ||||||
| 	return ids, x.Table("repository").Cols("id").Where("owner_id = ?", u.ID).Find(&ids) | 
 | ||||||
|  | 	sess := x.Table("repository").Cols("repository.id") | ||||||
|  | 
 | ||||||
|  | 	if len(units) > 0 { | ||||||
|  | 		sess = sess.Join("INNER", "repo_unit", "repository.id = repo_unit.repo_id") | ||||||
|  | 		sess = sess.In("repo_unit.type", units) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ids, sess.Where("owner_id = ?", u.ID).Find(&ids) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetOrgRepositoryIDs returns repositories IDs where user's team owned
 | // GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes
 | ||||||
| func (u *User) GetOrgRepositoryIDs() ([]int64, error) { | func (u *User) GetOrgRepositoryIDs(units ...UnitType) ([]int64, error) { | ||||||
| 	var ids []int64 | 	var ids []int64 | ||||||
| 	return ids, x.Table("repository"). | 
 | ||||||
|  | 	sess := x.Table("repository"). | ||||||
| 		Cols("repository.id"). | 		Cols("repository.id"). | ||||||
| 		Join("INNER", "team_user", "repository.owner_id = team_user.org_id AND team_user.uid = ?", u.ID). | 		Join("INNER", "team_user", "repository.owner_id = team_user.org_id"). | ||||||
|  | 		Join("INNER", "team_repo", "repository.is_private != ? OR (team_user.team_id = team_repo.team_id AND repository.id = team_repo.repo_id)", true) | ||||||
|  | 
 | ||||||
|  | 	if len(units) > 0 { | ||||||
|  | 		sess = sess.Join("INNER", "team_unit", "team_unit.team_id = team_user.team_id") | ||||||
|  | 		sess = sess.In("team_unit.type", units) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ids, sess. | ||||||
|  | 		Where("team_user.uid = ?", u.ID). | ||||||
| 		GroupBy("repository.id").Find(&ids) | 		GroupBy("repository.id").Find(&ids) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
 | // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations
 | ||||||
| func (u *User) GetAccessRepoIDs() ([]int64, error) { | func (u *User) GetAccessRepoIDs(units ...UnitType) ([]int64, error) { | ||||||
| 	ids, err := u.GetRepositoryIDs() | 	ids, err := u.GetRepositoryIDs(units...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	ids2, err := u.GetOrgRepositoryIDs() | 	ids2, err := u.GetOrgRepositoryIDs(units...) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -159,3 +159,25 @@ func BenchmarkHashPassword(b *testing.B) { | ||||||
| 		u.HashPassword(pass) | 		u.HashPassword(pass) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestGetOrgRepositoryIDs(t *testing.T) { | ||||||
|  | 	assert.NoError(t, PrepareTestDatabase()) | ||||||
|  | 	user2 := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User) | ||||||
|  | 	user4 := AssertExistsAndLoadBean(t, &User{ID: 4}).(*User) | ||||||
|  | 	user5 := AssertExistsAndLoadBean(t, &User{ID: 5}).(*User) | ||||||
|  | 
 | ||||||
|  | 	accessibleRepos, err := user2.GetOrgRepositoryIDs() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	// User 2's team has access to private repos 3, 5, repo 32 is a public repo of the organization
 | ||||||
|  | 	assert.Equal(t, []int64{3, 5, 32}, accessibleRepos) | ||||||
|  | 
 | ||||||
|  | 	accessibleRepos, err = user4.GetOrgRepositoryIDs() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	// User 4's team has access to private repo 3, repo 32 is a public repo of the organization
 | ||||||
|  | 	assert.Equal(t, []int64{3, 32}, accessibleRepos) | ||||||
|  | 
 | ||||||
|  | 	accessibleRepos, err = user5.GetOrgRepositoryIDs() | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 	// User 5's team has no access to any repo
 | ||||||
|  | 	assert.Len(t, accessibleRepos, 0) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -182,7 +182,14 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | ||||||
| 		Authorize:   models.ParseAccessMode(form.Permission), | 		Authorize:   models.ParseAccessMode(form.Permission), | ||||||
| 	} | 	} | ||||||
| 	if t.Authorize < models.AccessModeAdmin { | 	if t.Authorize < models.AccessModeAdmin { | ||||||
| 		t.UnitTypes = form.Units | 		var units = make([]*models.TeamUnit, 0, len(form.Units)) | ||||||
|  | 		for _, tp := range form.Units { | ||||||
|  | 			units = append(units, &models.TeamUnit{ | ||||||
|  | 				OrgID: ctx.Org.Organization.ID, | ||||||
|  | 				Type:  tp, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 		t.Units = units | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	ctx.Data["Team"] = t | 	ctx.Data["Team"] = t | ||||||
|  | @ -264,9 +271,17 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) { | ||||||
| 	} | 	} | ||||||
| 	t.Description = form.Description | 	t.Description = form.Description | ||||||
| 	if t.Authorize < models.AccessModeAdmin { | 	if t.Authorize < models.AccessModeAdmin { | ||||||
| 		t.UnitTypes = form.Units | 		var units = make([]models.TeamUnit, 0, len(form.Units)) | ||||||
|  | 		for _, tp := range form.Units { | ||||||
|  | 			units = append(units, models.TeamUnit{ | ||||||
|  | 				OrgID:  t.OrgID, | ||||||
|  | 				TeamID: t.ID, | ||||||
|  | 				Type:   tp, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 		models.UpdateTeamUnits(t, units) | ||||||
| 	} else { | 	} else { | ||||||
| 		t.UnitTypes = nil | 		models.UpdateTeamUnits(t, nil) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if ctx.HasError() { | 	if ctx.HasError() { | ||||||
|  |  | ||||||
|  | @ -203,7 +203,11 @@ func Issues(ctx *context.Context) { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		userRepoIDs, err = ctxUser.GetAccessRepoIDs() | 		unitType := models.UnitTypeIssues | ||||||
|  | 		if isPullList { | ||||||
|  | 			unitType = models.UnitTypePullRequests | ||||||
|  | 		} | ||||||
|  | 		userRepoIDs, err = ctxUser.GetAccessRepoIDs(unitType) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ctx.ServerError("ctxUser.GetAccessRepoIDs", err) | 			ctx.ServerError("ctxUser.GetAccessRepoIDs", err) | ||||||
| 			return | 			return | ||||||
|  |  | ||||||
|  | @ -26,8 +26,8 @@ func TestIssues(t *testing.T) { | ||||||
| 	Issues(ctx) | 	Issues(ctx) | ||||||
| 	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) | 	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) | ||||||
| 
 | 
 | ||||||
| 	assert.EqualValues(t, map[int64]int64{1: 1, 2: 1}, ctx.Data["Counts"]) | 	assert.EqualValues(t, map[int64]int64{1: 1}, ctx.Data["Counts"]) | ||||||
| 	assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) | 	assert.EqualValues(t, true, ctx.Data["IsShowClosed"]) | ||||||
| 	assert.Len(t, ctx.Data["Issues"], 1) | 	assert.Len(t, ctx.Data["Issues"], 1) | ||||||
| 	assert.Len(t, ctx.Data["Repos"], 2) | 	assert.Len(t, ctx.Data["Repos"], 1) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -57,7 +57,7 @@ | ||||||
| 							{{range $t, $unit := $.Units}} | 							{{range $t, $unit := $.Units}} | ||||||
| 							<div class="field"> | 							<div class="field"> | ||||||
| 								<div class="ui toggle checkbox"> | 								<div class="ui toggle checkbox"> | ||||||
| 									<input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if $.Team.UnitEnabled $unit.Type}} checked{{end}}> | 									<input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if or (eq $.Team.ID 0) ($.Team.UnitEnabled $unit.Type)}} checked{{end}}> | ||||||
| 									<label>{{$.i18n.Tr $unit.NameKey}}</label> | 									<label>{{$.i18n.Tr $unit.NameKey}}</label> | ||||||
| 									<span class="help">{{$.i18n.Tr $unit.DescKey}}</span> | 									<span class="help">{{$.i18n.Tr $unit.DescKey}}</span> | ||||||
| 								</div> | 								</div> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue