Browse Source

Add units to team (#947)

* add units to team

* fix lint

* finish team setting backend

* finished permission controll on routes

* fix import blank line

* add unit check on ssh/http pull and push and fix test failed

* fix fixtures data

* remove unused code
Lunny Xiao 3 years ago
parent
commit
fd6034aaf2

+ 8 - 0
cmd/serv.go

@@ -143,8 +143,10 @@ func runServ(c *cli.Context) error {
143 143
 	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
144 144
 
145 145
 	isWiki := false
146
+	unitType := models.UnitTypeCode
146 147
 	if strings.HasSuffix(reponame, ".wiki") {
147 148
 		isWiki = true
149
+		unitType = models.UnitTypeWiki
148 150
 		reponame = reponame[:len(reponame)-5]
149 151
 	}
150 152
 
@@ -248,6 +250,12 @@ func runServ(c *cli.Context) error {
248 250
 					user.Name, requestedMode, repoPath)
249 251
 			}
250 252
 
253
+			if !repo.CheckUnitUser(user.ID, unitType) {
254
+				fail("You do not have allowed for this action",
255
+					"User %s does not have allowed access to repository %s 's code",
256
+					user.Name, repoPath)
257
+			}
258
+
251 259
 			os.Setenv(models.EnvPusherName, user.Name)
252 260
 			os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
253 261
 		}

+ 8 - 0
models/fixtures/repo_unit.yml

@@ -12,4 +12,12 @@
12 12
   type: 2
13 13
   index: 0
14 14
   config: "{}"
15
+  created_unix: 946684810
16
+
17
+-
18
+  id: 3
19
+  repo_id: 1
20
+  type: 7
21
+  index: 0
22
+  config: "{}"
15 23
   created_unix: 946684810

+ 4 - 0
models/fixtures/team.yml

@@ -6,6 +6,7 @@
6 6
   authorize: 4 # owner
7 7
   num_repos: 2
8 8
   num_members: 1
9
+  unit_types: '[1,2,3,4,5,6,7,8,9]'
9 10
 
10 11
 -
11 12
   id: 2
@@ -15,6 +16,7 @@
15 16
   authorize: 2 # write
16 17
   num_repos: 1
17 18
   num_members: 2
19
+  unit_types: '[1,2,3,4,5,6,7,8,9]'
18 20
 
19 21
 -
20 22
   id: 3
@@ -24,6 +26,7 @@
24 26
   authorize: 4 # owner
25 27
   num_repos: 0
26 28
   num_members: 1
29
+  unit_types: '[1,2,3,4,5,6,7,8,9]'
27 30
 
28 31
 -
29 32
   id: 4
@@ -33,3 +36,4 @@
33 36
   authorize: 4 # owner
34 37
   num_repos: 0
35 38
   num_members: 1
39
+  unit_types: '[1,2,3,4,5,6,7,8,9]'

+ 2 - 0
models/migrations/migrations.go

@@ -112,6 +112,8 @@ var migrations = []Migration{
112 112
 	NewMigration("add primary key to external login user", addExternalLoginUserPK),
113 113
 	// 31 -> 32
114 114
 	NewMigration("add field for login source synchronization", addLoginSourceSyncEnabledColumn),
115
+	// v32 -> v33
116
+	NewMigration("add units for team", addUnitsToRepoTeam),
115 117
 }
116 118
 
117 119
 // Migrate database to current version

+ 1 - 1
models/migrations/v31.go

@@ -1,4 +1,4 @@
1
-// Copyright 2017 The Gogs Authors. All rights reserved.
1
+// Copyright 2017 The Gitea Authors. All rights reserved.
2 2
 // Use of this source code is governed by a MIT-style
3 3
 // license that can be found in the LICENSE file.
4 4
 

+ 23 - 0
models/migrations/v32.go

@@ -0,0 +1,23 @@
1
+// Copyright 2017 The Gitea Authors. All rights reserved.
2
+// Use of this source code is governed by a MIT-style
3
+// license that can be found in the LICENSE file.
4
+
5
+package migrations
6
+
7
+import "github.com/go-xorm/xorm"
8
+
9
+func addUnitsToRepoTeam(x *xorm.Engine) error {
10
+	type Team struct {
11
+		UnitTypes []int `xorm:"json"`
12
+	}
13
+
14
+	err := x.Sync(new(Team))
15
+	if err != nil {
16
+		return err
17
+	}
18
+
19
+	_, err = x.Update(&Team{
20
+		UnitTypes: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
21
+	})
22
+	return err
23
+}

+ 22 - 0
models/org_team.go

@@ -24,6 +24,15 @@ type Team struct {
24 24
 	Members     []*User       `xorm:"-"`
25 25
 	NumRepos    int
26 26
 	NumMembers  int
27
+	UnitTypes   []UnitType `xorm:"json"`
28
+}
29
+
30
+// GetUnitTypes returns unit types the team owned, empty means all the unit types
31
+func (t *Team) GetUnitTypes() []UnitType {
32
+	if len(t.UnitTypes) == 0 {
33
+		return allRepUnitTypes
34
+	}
35
+	return t.UnitTypes
27 36
 }
28 37
 
29 38
 // IsOwnerTeam returns true if team is owner team.
@@ -183,6 +192,19 @@ func (t *Team) RemoveRepository(repoID int64) error {
183 192
 	return sess.Commit()
184 193
 }
185 194
 
195
+// EnableUnit returns if the team enables unit type t
196
+func (t *Team) EnableUnit(tp UnitType) bool {
197
+	if len(t.UnitTypes) == 0 {
198
+		return true
199
+	}
200
+	for _, u := range t.UnitTypes {
201
+		if u == tp {
202
+			return true
203
+		}
204
+	}
205
+	return false
206
+}
207
+
186 208
 // IsUsableTeamName tests if a name could be as team name
187 209
 func IsUsableTeamName(name string) error {
188 210
 	switch name {

+ 56 - 2
models/repo.go

@@ -329,8 +329,61 @@ func (repo *Repository) getUnits(e Engine) (err error) {
329 329
 	return err
330 330
 }
331 331
 
332
-func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
333
-	return units, e.Where("repo_id = ?", repoID).Find(&units)
332
+// CheckUnitUser check whether user could visit the unit of this repository
333
+func (repo *Repository) CheckUnitUser(userID int64, unitType UnitType) bool {
334
+	if err := repo.getUnitsByUserID(x, userID); err != nil {
335
+		return false
336
+	}
337
+
338
+	for _, unit := range repo.Units {
339
+		if unit.Type == unitType {
340
+			return true
341
+		}
342
+	}
343
+	return false
344
+}
345
+
346
+// LoadUnitsByUserID loads units according userID's permissions
347
+func (repo *Repository) LoadUnitsByUserID(userID int64) error {
348
+	return repo.getUnitsByUserID(x, userID)
349
+}
350
+
351
+func (repo *Repository) getUnitsByUserID(e Engine, userID int64) (err error) {
352
+	if repo.Units != nil {
353
+		return nil
354
+	}
355
+
356
+	err = repo.getUnits(e)
357
+	if err != nil {
358
+		return err
359
+	}
360
+
361
+	if !repo.Owner.IsOrganization() || userID == 0 {
362
+		return nil
363
+	}
364
+
365
+	teams, err := getUserTeams(e, repo.OwnerID, userID)
366
+	if err != nil {
367
+		return err
368
+	}
369
+
370
+	var allTypes = make(map[UnitType]struct{}, len(allRepUnitTypes))
371
+	for _, team := range teams {
372
+		for _, unitType := range team.UnitTypes {
373
+			allTypes[unitType] = struct{}{}
374
+		}
375
+	}
376
+
377
+	// unique
378
+	var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units))
379
+	for _, u := range repo.Units {
380
+		if _, ok := allTypes[u.Type]; ok {
381
+			newRepoUnits = append(newRepoUnits, u)
382
+		}
383
+	}
384
+
385
+	repo.Units = newRepoUnits
386
+	return nil
334 387
 }
335 388
 
336 389
 // EnableUnit if this repository enabled some unit
@@ -1595,6 +1648,7 @@ func DeleteRepository(uid, repoID int64) error {
1595 1648
 		&Release{RepoID: repoID},
1596 1649
 		&Collaboration{RepoID: repoID},
1597 1650
 		&PullRequest{BaseRepoID: repoID},
1651
+		&RepoUnit{RepoID: repoID},
1598 1652
 	); err != nil {
1599 1653
 		return fmt.Errorf("deleteBeans: %v", err)
1600 1654
 	}

+ 8 - 0
models/repo_unit.go

@@ -135,3 +135,11 @@ func (r *RepoUnit) ExternalWikiConfig() *ExternalWikiConfig {
135 135
 func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
136 136
 	return r.Config.(*ExternalTrackerConfig)
137 137
 }
138
+
139
+func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
140
+	return units, e.Where("repo_id = ?", repoID).Find(&units)
141
+}
142
+
143
+func getUnitsByRepoIDAndIDs(e Engine, repoID int64, types []UnitType) (units []*RepoUnit, err error) {
144
+	return units, e.Where("repo_id = ?", repoID).In("`type`", types).Find(&units)
145
+}

+ 50 - 30
models/unit.go

@@ -20,6 +20,40 @@ const (
20 20
 	UnitTypeExternalTracker                     // 9 ExternalTracker
21 21
 )
22 22
 
23
+var (
24
+	// allRepUnitTypes contains all the unit types
25
+	allRepUnitTypes = []UnitType{
26
+		UnitTypeCode,
27
+		UnitTypeIssues,
28
+		UnitTypePullRequests,
29
+		UnitTypeCommits,
30
+		UnitTypeReleases,
31
+		UnitTypeWiki,
32
+		UnitTypeSettings,
33
+		UnitTypeExternalWiki,
34
+		UnitTypeExternalTracker,
35
+	}
36
+
37
+	// defaultRepoUnits contains the default unit types
38
+	defaultRepoUnits = []UnitType{
39
+		UnitTypeCode,
40
+		UnitTypeIssues,
41
+		UnitTypePullRequests,
42
+		UnitTypeCommits,
43
+		UnitTypeReleases,
44
+		UnitTypeWiki,
45
+		UnitTypeSettings,
46
+	}
47
+
48
+	// MustRepoUnits contains the units could be disabled currently
49
+	MustRepoUnits = []UnitType{
50
+		UnitTypeCode,
51
+		UnitTypeCommits,
52
+		UnitTypeReleases,
53
+		UnitTypeSettings,
54
+	}
55
+)
56
+
23 57
 // Unit is a tab page of one repository
24 58
 type Unit struct {
25 59
 	Type    UnitType
@@ -29,13 +63,18 @@ type Unit struct {
29 63
 	Idx     int
30 64
 }
31 65
 
66
+// CanDisable returns if this unit could be disabled.
67
+func (u *Unit) CanDisable() bool {
68
+	return u.Type != UnitTypeSettings
69
+}
70
+
32 71
 // Enumerate all the units
33 72
 var (
34 73
 	UnitCode = Unit{
35 74
 		UnitTypeCode,
36 75
 		"repo.code",
37 76
 		"/",
38
-		"repo.code_desc",
77
+		"repo.code.desc",
39 78
 		0,
40 79
 	}
41 80
 
@@ -43,15 +82,15 @@ var (
43 82
 		UnitTypeIssues,
44 83
 		"repo.issues",
45 84
 		"/issues",
46
-		"repo.issues_desc",
85
+		"repo.issues.desc",
47 86
 		1,
48 87
 	}
49 88
 
50 89
 	UnitExternalTracker = Unit{
51 90
 		UnitTypeExternalTracker,
52
-		"repo.issues",
91
+		"repo.ext_issues",
53 92
 		"/issues",
54
-		"repo.issues_desc",
93
+		"repo.ext_issues.desc",
55 94
 		1,
56 95
 	}
57 96
 
@@ -59,7 +98,7 @@ var (
59 98
 		UnitTypePullRequests,
60 99
 		"repo.pulls",
61 100
 		"/pulls",
62
-		"repo.pulls_desc",
101
+		"repo.pulls.desc",
63 102
 		2,
64 103
 	}
65 104
 
@@ -67,7 +106,7 @@ var (
67 106
 		UnitTypeCommits,
68 107
 		"repo.commits",
69 108
 		"/commits/master",
70
-		"repo.commits_desc",
109
+		"repo.commits.desc",
71 110
 		3,
72 111
 	}
73 112
 
@@ -75,7 +114,7 @@ var (
75 114
 		UnitTypeReleases,
76 115
 		"repo.releases",
77 116
 		"/releases",
78
-		"repo.releases_desc",
117
+		"repo.releases.desc",
79 118
 		4,
80 119
 	}
81 120
 
@@ -83,15 +122,15 @@ var (
83 122
 		UnitTypeWiki,
84 123
 		"repo.wiki",
85 124
 		"/wiki",
86
-		"repo.wiki_desc",
125
+		"repo.wiki.desc",
87 126
 		5,
88 127
 	}
89 128
 
90 129
 	UnitExternalWiki = Unit{
91 130
 		UnitTypeExternalWiki,
92
-		"repo.wiki",
131
+		"repo.ext_wiki",
93 132
 		"/wiki",
94
-		"repo.wiki_desc",
133
+		"repo.ext_wiki.desc",
95 134
 		5,
96 135
 	}
97 136
 
@@ -99,29 +138,10 @@ var (
99 138
 		UnitTypeSettings,
100 139
 		"repo.settings",
101 140
 		"/settings",
102
-		"repo.settings_desc",
141
+		"repo.settings.desc",
103 142
 		6,
104 143
 	}
105 144
 
106
-	// defaultRepoUnits contains all the default unit types
107
-	defaultRepoUnits = []UnitType{
108
-		UnitTypeCode,
109
-		UnitTypeIssues,
110
-		UnitTypePullRequests,
111
-		UnitTypeCommits,
112
-		UnitTypeReleases,
113
-		UnitTypeWiki,
114
-		UnitTypeSettings,
115
-	}
116
-
117
-	// MustRepoUnits contains the units could be disabled currently
118
-	MustRepoUnits = []UnitType{
119
-		UnitTypeCode,
120
-		UnitTypeCommits,
121
-		UnitTypeReleases,
122
-		UnitTypeSettings,
123
-	}
124
-
125 145
 	// Units contains all the units
126 146
 	Units = map[UnitType]Unit{
127 147
 		UnitTypeCode:            UnitCode,

+ 3 - 0
modules/auth/org.go

@@ -5,6 +5,8 @@
5 5
 package auth
6 6
 
7 7
 import (
8
+	"code.gitea.io/gitea/models"
9
+
8 10
 	"github.com/go-macaron/binding"
9 11
 	"gopkg.in/macaron.v1"
10 12
 )
@@ -53,6 +55,7 @@ type CreateTeamForm struct {
53 55
 	TeamName    string `binding:"Required;AlphaDashDot;MaxSize(30)"`
54 56
 	Description string `binding:"MaxSize(255)"`
55 57
 	Permission  string
58
+	Units       []models.UnitType
56 59
 }
57 60
 
58 61
 // Validate validates the fields

+ 31 - 0
modules/context/repo.go

@@ -493,6 +493,37 @@ func RequireRepoWriter() macaron.Handler {
493 493
 	}
494 494
 }
495 495
 
496
+// LoadRepoUnits loads repsitory's units, it should be called after repository and user loaded
497
+func LoadRepoUnits() macaron.Handler {
498
+	return func(ctx *Context) {
499
+		var userID int64
500
+		if ctx.User != nil {
501
+			userID = ctx.User.ID
502
+		}
503
+		err := ctx.Repo.Repository.LoadUnitsByUserID(userID)
504
+		if err != nil {
505
+			ctx.Handle(500, "LoadUnitsByUserID", err)
506
+			return
507
+		}
508
+	}
509
+}
510
+
511
+// CheckUnit will check whether
512
+func CheckUnit(unitType models.UnitType) macaron.Handler {
513
+	return func(ctx *Context) {
514
+		var find bool
515
+		for _, unit := range ctx.Repo.Repository.Units {
516
+			if unit.Type == unitType {
517
+				find = true
518
+				break
519
+			}
520
+		}
521
+		if !find {
522
+			ctx.Handle(404, "CheckUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitType))
523
+		}
524
+	}
525
+}
526
+
496 527
 // GitHookService checks if repository Git hooks service has been enabled.
497 528
 func GitHookService() macaron.Handler {
498 529
 	return func(ctx *Context) {

+ 18 - 0
options/locale/locale_en-US.ini

@@ -505,6 +505,7 @@ push_exist_repo = Pushing an existing repository from the command line
505 505
 bare_message = This repository does not contain any content.
506 506
 
507 507
 code = Code
508
+code.desc = Code is your program source
508 509
 branch = Branch
509 510
 tree = Tree
510 511
 filter_branch_and_tag = Filter branch or tag
@@ -565,6 +566,7 @@ editor.unable_to_upload_files = Failed to upload files to '%s' with error: %v
565 566
 editor.upload_files_to_dir = Upload files to '%s'
566 567
 editor.cannot_commit_to_protected_branch = Can not commit to protected branch '%s'.
567 568
 
569
+commits.desc = Commits show the history submited
568 570
 commits.commits = Commits
569 571
 commits.search = Search commits
570 572
 commits.find = Search
@@ -575,6 +577,10 @@ commits.date = Date
575 577
 commits.older = Older
576 578
 commits.newer = Newer
577 579
 
580
+ext_issues = Ext Issues
581
+ext_issues.desc = Ext Issues link to an external issues management
582
+
583
+issues.desc = Issues management your tasks for this repository
578 584
 issues.new = New Issue
579 585
 issues.new.labels = Labels
580 586
 issues.new.no_label = No Label
@@ -678,6 +684,7 @@ issues.attachment.download = `Click to download "%s"`
678 684
 issues.subscribe = Subscribe
679 685
 issues.unsubscribe = Unsubscribe
680 686
 
687
+pulls.desc = Pulls management your code review and merge requests
681 688
 pulls.new = New Pull Request
682 689
 pulls.compare_changes = Compare Changes
683 690
 pulls.compare_changes_desc = Compare two branches and make a pull request for changes.
@@ -734,9 +741,13 @@ milestones.filter_sort.most_complete = Most complete
734 741
 milestones.filter_sort.most_issues = Most issues
735 742
 milestones.filter_sort.least_issues = Least issues
736 743
 
744
+ext_wiki = Ext Wiki
745
+ext_wiki.desc = Ext Wiki links to an external wiki system
746
+
737 747
 wiki = Wiki
738 748
 wiki.welcome = Welcome to the project wiki
739 749
 wiki.welcome_desc = A wiki allows you and your collaborators to easily document your project.
750
+wiki.desc = Wiki is a collection of your documents
740 751
 wiki.create_first_page = Create the first page
741 752
 wiki.page = Page
742 753
 wiki.filter_page = Filter page
@@ -753,6 +764,7 @@ wiki.pages = Pages
753 764
 wiki.last_updated = Last updated %s
754 765
 
755 766
 settings = Settings
767
+settings.desc = Settings management your settings for repository
756 768
 settings.options = Options
757 769
 settings.collaboration = Collaboration
758 770
 settings.collaboration.admin = Admin
@@ -910,6 +922,7 @@ diff.view_file = View File
910 922
 diff.file_suppressed = File diff suppressed because it is too large
911 923
 diff.too_many_files = Some files were not shown because too many files changed in this diff
912 924
 
925
+releases.desc = Releases manage your milestone versions
913 926
 release.releases = Releases
914 927
 release.new_release = New Release
915 928
 release.draft = Draft
@@ -968,6 +981,7 @@ team_desc = Description
968 981
 team_name_helper = You will use this name to mention this team in conversations.
969 982
 team_desc_helper = What is this team for?
970 983
 team_permission_desc = What permissions should this team have?
984
+team_unit_desc = Which units should this team have?
971 985
 
972 986
 form.name_reserved = Organization name '%s' is reserved.
973 987
 form.name_pattern_not_allowed = Organization name pattern '%s' is not allowed.
@@ -1406,3 +1420,7 @@ error.no_committer_account = No account linked to committer's email
1406 1420
 error.no_gpg_keys_found = "No known key found for this signature in database"
1407 1421
 error.not_signed_commit = "Not a signed commit"
1408 1422
 error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer account"
1423
+
1424
+[units]
1425
+error.no_unit_allowed_repo = Cannot find any unit allowed on this repository
1426
+error.unit_not_allowed = You have not allowed to visit this repository unit

+ 4 - 0
routers/org/teams.go

@@ -156,6 +156,7 @@ func NewTeam(ctx *context.Context) {
156 156
 	ctx.Data["PageIsOrgTeams"] = true
157 157
 	ctx.Data["PageIsOrgTeamsNew"] = true
158 158
 	ctx.Data["Team"] = &models.Team{}
159
+	ctx.Data["Units"] = models.Units
159 160
 	ctx.HTML(200, tplTeamNew)
160 161
 }
161 162
 
@@ -170,6 +171,7 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
170 171
 		Name:        form.TeamName,
171 172
 		Description: form.Description,
172 173
 		Authorize:   models.ParseAccessMode(form.Permission),
174
+		UnitTypes:   form.Units,
173 175
 	}
174 176
 	ctx.Data["Team"] = t
175 177
 
@@ -220,6 +222,7 @@ func EditTeam(ctx *context.Context) {
220 222
 	ctx.Data["PageIsOrgTeams"] = true
221 223
 	ctx.Data["team_name"] = ctx.Org.Team.Name
222 224
 	ctx.Data["desc"] = ctx.Org.Team.Description
225
+	ctx.Data["Units"] = models.Units
223 226
 	ctx.HTML(200, tplTeamNew)
224 227
 }
225 228
 
@@ -258,6 +261,7 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
258 261
 		}
259 262
 	}
260 263
 	t.Description = form.Description
264
+	t.UnitTypes = form.Units
261 265
 	if err := models.UpdateTeam(t, isAuthChanged); err != nil {
262 266
 		ctx.Data["Err_TeamName"] = true
263 267
 		switch {

+ 8 - 0
routers/repo/http.go

@@ -77,8 +77,10 @@ func HTTP(ctx *context.Context) {
77 77
 	}
78 78
 
79 79
 	isWiki := false
80
+	var unitType = models.UnitTypeCode
80 81
 	if strings.HasSuffix(reponame, ".wiki") {
81 82
 		isWiki = true
83
+		unitType = models.UnitTypeWiki
82 84
 		reponame = reponame[:len(reponame)-5]
83 85
 	}
84 86
 
@@ -204,6 +206,12 @@ func HTTP(ctx *context.Context) {
204 206
 			}
205 207
 		}
206 208
 
209
+		if !repo.CheckUnitUser(authUser.ID, unitType) {
210
+			ctx.HandleText(http.StatusForbidden, fmt.Sprintf("User %s does not have allowed access to repository %s 's code",
211
+				authUser.Name, repo.RepoPath()))
212
+			return
213
+		}
214
+
207 215
 		environ = []string{
208 216
 			models.EnvRepoUsername + "=" + username,
209 217
 			models.EnvRepoName + "=" + reponame,

+ 19 - 0
routers/repo/view.go

@@ -252,6 +252,25 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
252 252
 
253 253
 // Home render repository home page
254 254
 func Home(ctx *context.Context) {
255
+	if len(ctx.Repo.Repository.Units) > 0 {
256
+		tp := ctx.Repo.Repository.Units[0].Type
257
+		if tp == models.UnitTypeCode {
258
+			renderCode(ctx)
259
+			return
260
+		}
261
+
262
+		unit, ok := models.Units[tp]
263
+		if ok {
264
+			ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/%s%s",
265
+				ctx.Repo.Repository.FullName(), unit.URI))
266
+			return
267
+		}
268
+	}
269
+
270
+	ctx.Handle(404, "Home", fmt.Errorf(ctx.Tr("units.error.no_unit_allowed_repo")))
271
+}
272
+
273
+func renderCode(ctx *context.Context) {
255 274
 	ctx.Data["PageIsViewCode"] = true
256 275
 
257 276
 	if ctx.Repo.Repository.IsBare {

+ 42 - 36
routers/routes/routes.go

@@ -445,10 +445,11 @@ func RegisterRoutes(m *macaron.Macaron) {
445 445
 
446 446
 		}, func(ctx *context.Context) {
447 447
 			ctx.Data["PageIsSettings"] = true
448
-		}, context.UnitTypes())
448
+		}, context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeSettings))
449 449
 	}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
450 450
 
451 451
 	m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
452
+
452 453
 	m.Group("/:username/:reponame", func() {
453 454
 		// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
454 455
 		// So they can apply their own enable/disable logic on routers.
@@ -486,28 +487,6 @@ func RegisterRoutes(m *macaron.Macaron) {
486 487
 			m.Get("/:id/:action", repo.ChangeMilestonStatus)
487 488
 			m.Post("/delete", repo.DeleteMilestone)
488 489
 		}, reqRepoWriter, context.RepoRef())
489
-		m.Group("/releases", func() {
490
-			m.Get("/new", repo.NewRelease)
491
-			m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
492
-			m.Post("/delete", repo.DeleteRelease)
493
-		}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef())
494
-		m.Group("/releases", func() {
495
-			m.Get("/edit/*", repo.EditRelease)
496
-			m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
497
-		}, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) {
498
-			var err error
499
-			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
500
-			if err != nil {
501
-				ctx.Handle(500, "GetBranchCommit", err)
502
-				return
503
-			}
504
-			ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
505
-			if err != nil {
506
-				ctx.Handle(500, "CommitsCount", err)
507
-				return
508
-			}
509
-			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
510
-		})
511 490
 
512 491
 		m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists).
513 492
 			Get(repo.CompareAndPullRequest).
@@ -539,16 +518,42 @@ func RegisterRoutes(m *macaron.Macaron) {
539 518
 				return
540 519
 			}
541 520
 		})
542
-	}, reqSignIn, context.RepoAssignment(), context.UnitTypes())
521
+	}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeIssues))
522
+
523
+	// Releases
524
+	m.Group("/:username/:reponame", func() {
525
+		m.Group("/releases", func() {
526
+			m.Get("/", repo.MustBeNotBare, repo.Releases)
527
+			m.Get("/new", repo.NewRelease)
528
+			m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
529
+			m.Post("/delete", repo.DeleteRelease)
530
+		}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef())
531
+		m.Group("/releases", func() {
532
+			m.Get("/edit/*", repo.EditRelease)
533
+			m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
534
+		}, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) {
535
+			var err error
536
+			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
537
+			if err != nil {
538
+				ctx.Handle(500, "GetBranchCommit", err)
539
+				return
540
+			}
541
+			ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
542
+			if err != nil {
543
+				ctx.Handle(500, "CommitsCount", err)
544
+				return
545
+			}
546
+			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
547
+		})
548
+	}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases))
543 549
 
544 550
 	m.Group("/:username/:reponame", func() {
545 551
 		m.Group("", func() {
546
-			m.Get("/releases", repo.MustBeNotBare, repo.Releases)
547 552
 			m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues)
548 553
 			m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
549 554
 			m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
550 555
 			m.Get("/milestones", repo.Milestones)
551
-		}, context.RepoRef())
556
+		}, context.RepoRef(), context.CheckUnit(models.UnitTypeIssues))
552 557
 
553 558
 		// m.Get("/branches", repo.Branches)
554 559
 		m.Post("/branches/:name/delete", reqSignIn, reqRepoWriter, repo.MustBeNotBare, repo.DeleteBranchPost)
@@ -564,20 +569,20 @@ func RegisterRoutes(m *macaron.Macaron) {
564 569
 					Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
565 570
 				m.Post("/:page/delete", repo.DeleteWikiPagePost)
566 571
 			}, reqSignIn, reqRepoWriter)
567
-		}, repo.MustEnableWiki, context.RepoRef())
572
+		}, repo.MustEnableWiki, context.RepoRef(), context.CheckUnit(models.UnitTypeWiki))
568 573
 
569 574
 		m.Group("/wiki", func() {
570 575
 			m.Get("/raw/*", repo.WikiRaw)
571 576
 			m.Get("/*", repo.WikiRaw)
572
-		}, repo.MustEnableWiki)
577
+		}, repo.MustEnableWiki, context.CheckUnit(models.UnitTypeWiki), context.CheckUnit(models.UnitTypeWiki))
573 578
 
574
-		m.Get("/archive/*", repo.MustBeNotBare, repo.Download)
579
+		m.Get("/archive/*", repo.MustBeNotBare, repo.Download, context.CheckUnit(models.UnitTypeCode))
575 580
 
576 581
 		m.Group("/pulls/:index", func() {
577 582
 			m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
578 583
 			m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
579 584
 			m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
580
-		}, repo.MustAllowPulls)
585
+		}, repo.MustAllowPulls, context.CheckUnit(models.UnitTypePullRequests))
581 586
 
582 587
 		m.Group("", func() {
583 588
 			m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
@@ -586,21 +591,22 @@ func RegisterRoutes(m *macaron.Macaron) {
586 591
 			m.Get("/graph", repo.Graph)
587 592
 			m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
588 593
 			m.Get("/forks", repo.Forks)
589
-		}, context.RepoRef())
590
-		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff)
594
+		}, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
595
+		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff, context.CheckUnit(models.UnitTypeCode))
591 596
 
592
-		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.MustBeNotBare, repo.CompareDiff)
593
-	}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
597
+		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists,
598
+			repo.SetDiffViewStyle, repo.MustBeNotBare, repo.CompareDiff, context.CheckUnit(models.UnitTypeCode))
599
+	}, ignSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits())
594 600
 	m.Group("/:username/:reponame", func() {
595 601
 		m.Get("/stars", repo.Stars)
596 602
 		m.Get("/watchers", repo.Watchers)
597
-	}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
603
+	}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
598 604
 
599 605
 	m.Group("/:username", func() {
600 606
 		m.Group("/:reponame", func() {
601 607
 			m.Get("", repo.SetEditorconfigIfExists, repo.Home)
602 608
 			m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
603
-		}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
609
+		}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
604 610
 
605 611
 		m.Group("/:reponame", func() {
606 612
 			m.Group("/info/lfs", func() {

+ 15 - 0
templates/org/team/new.tmpl

@@ -52,6 +52,21 @@
52 52
 						<div class="ui divider"></div>
53 53
 					{{end}}
54 54
 
55
+					<div class="required grouped field">
56
+							<label>{{.i18n.Tr "org.team_unit_desc"}}</label>
57
+							<br>
58
+							{{range $t, $unit := $.Units}}
59
+							<div class="field">
60
+								<div class="ui toggle checkbox">
61
+									<input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if $.Team.EnableUnit $unit.Type}} checked{{end}}>
62
+									<label>{{$.i18n.Tr $unit.NameKey}}</label>
63
+									<span class="help">{{$.i18n.Tr $unit.DescKey}}</span>
64
+								</div>
65
+							</div>
66
+							{{end}}
67
+						</div>
68
+						<div class="ui divider"></div>
69
+
55 70
 					<div class="field">
56 71
 						{{if .PageIsOrgTeamsNew}}
57 72
 							<button class="ui green button">{{.i18n.Tr "org.create_new_team"}}</button>