mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-04 01:34:27 +00:00 
			
		
		
		
	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
This commit is contained in:
		@@ -143,8 +143,10 @@ func runServ(c *cli.Context) error {
 | 
			
		||||
	reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git"))
 | 
			
		||||
 | 
			
		||||
	isWiki := false
 | 
			
		||||
	unitType := models.UnitTypeCode
 | 
			
		||||
	if strings.HasSuffix(reponame, ".wiki") {
 | 
			
		||||
		isWiki = true
 | 
			
		||||
		unitType = models.UnitTypeWiki
 | 
			
		||||
		reponame = reponame[:len(reponame)-5]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -248,6 +250,12 @@ func runServ(c *cli.Context) error {
 | 
			
		||||
					user.Name, requestedMode, repoPath)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if !repo.CheckUnitUser(user.ID, unitType) {
 | 
			
		||||
				fail("You do not have allowed for this action",
 | 
			
		||||
					"User %s does not have allowed access to repository %s 's code",
 | 
			
		||||
					user.Name, repoPath)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			os.Setenv(models.EnvPusherName, user.Name)
 | 
			
		||||
			os.Setenv(models.EnvPusherID, fmt.Sprintf("%d", user.ID))
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,4 +12,12 @@
 | 
			
		||||
  type: 2
 | 
			
		||||
  index: 0
 | 
			
		||||
  config: "{}"
 | 
			
		||||
  created_unix: 946684810
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 3
 | 
			
		||||
  repo_id: 1
 | 
			
		||||
  type: 7
 | 
			
		||||
  index: 0
 | 
			
		||||
  config: "{}"
 | 
			
		||||
  created_unix: 946684810
 | 
			
		||||
@@ -6,6 +6,7 @@
 | 
			
		||||
  authorize: 4 # owner
 | 
			
		||||
  num_repos: 2
 | 
			
		||||
  num_members: 1
 | 
			
		||||
  unit_types: '[1,2,3,4,5,6,7,8,9]'
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 2
 | 
			
		||||
@@ -15,6 +16,7 @@
 | 
			
		||||
  authorize: 2 # write
 | 
			
		||||
  num_repos: 1
 | 
			
		||||
  num_members: 2
 | 
			
		||||
  unit_types: '[1,2,3,4,5,6,7,8,9]'
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 3
 | 
			
		||||
@@ -24,6 +26,7 @@
 | 
			
		||||
  authorize: 4 # owner
 | 
			
		||||
  num_repos: 0
 | 
			
		||||
  num_members: 1
 | 
			
		||||
  unit_types: '[1,2,3,4,5,6,7,8,9]'
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
  id: 4
 | 
			
		||||
@@ -33,3 +36,4 @@
 | 
			
		||||
  authorize: 4 # owner
 | 
			
		||||
  num_repos: 0
 | 
			
		||||
  num_members: 1
 | 
			
		||||
  unit_types: '[1,2,3,4,5,6,7,8,9]'
 | 
			
		||||
@@ -112,6 +112,8 @@ var migrations = []Migration{
 | 
			
		||||
	NewMigration("add primary key to external login user", addExternalLoginUserPK),
 | 
			
		||||
	// 31 -> 32
 | 
			
		||||
	NewMigration("add field for login source synchronization", addLoginSourceSyncEnabledColumn),
 | 
			
		||||
	// v32 -> v33
 | 
			
		||||
	NewMigration("add units for team", addUnitsToRepoTeam),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Migrate database to current version
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
// Copyright 2017 The Gogs Authors. All rights reserved.
 | 
			
		||||
// Copyright 2017 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.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								models/migrations/v32.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								models/migrations/v32.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
// Copyright 2017 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 "github.com/go-xorm/xorm"
 | 
			
		||||
 | 
			
		||||
func addUnitsToRepoTeam(x *xorm.Engine) error {
 | 
			
		||||
	type Team struct {
 | 
			
		||||
		UnitTypes []int `xorm:"json"`
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := x.Sync(new(Team))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = x.Update(&Team{
 | 
			
		||||
		UnitTypes: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
@@ -24,6 +24,15 @@ type Team struct {
 | 
			
		||||
	Members     []*User       `xorm:"-"`
 | 
			
		||||
	NumRepos    int
 | 
			
		||||
	NumMembers  int
 | 
			
		||||
	UnitTypes   []UnitType `xorm:"json"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetUnitTypes returns unit types the team owned, empty means all the unit types
 | 
			
		||||
func (t *Team) GetUnitTypes() []UnitType {
 | 
			
		||||
	if len(t.UnitTypes) == 0 {
 | 
			
		||||
		return allRepUnitTypes
 | 
			
		||||
	}
 | 
			
		||||
	return t.UnitTypes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsOwnerTeam returns true if team is owner team.
 | 
			
		||||
@@ -183,6 +192,19 @@ func (t *Team) RemoveRepository(repoID int64) error {
 | 
			
		||||
	return sess.Commit()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableUnit returns if the team enables unit type t
 | 
			
		||||
func (t *Team) EnableUnit(tp UnitType) bool {
 | 
			
		||||
	if len(t.UnitTypes) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	for _, u := range t.UnitTypes {
 | 
			
		||||
		if u == tp {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsUsableTeamName tests if a name could be as team name
 | 
			
		||||
func IsUsableTeamName(name string) error {
 | 
			
		||||
	switch name {
 | 
			
		||||
 
 | 
			
		||||
@@ -329,8 +329,61 @@ func (repo *Repository) getUnits(e Engine) (err error) {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
 | 
			
		||||
	return units, e.Where("repo_id = ?", repoID).Find(&units)
 | 
			
		||||
// CheckUnitUser check whether user could visit the unit of this repository
 | 
			
		||||
func (repo *Repository) CheckUnitUser(userID int64, unitType UnitType) bool {
 | 
			
		||||
	if err := repo.getUnitsByUserID(x, userID); err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, unit := range repo.Units {
 | 
			
		||||
		if unit.Type == unitType {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadUnitsByUserID loads units according userID's permissions
 | 
			
		||||
func (repo *Repository) LoadUnitsByUserID(userID int64) error {
 | 
			
		||||
	return repo.getUnitsByUserID(x, userID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (repo *Repository) getUnitsByUserID(e Engine, userID int64) (err error) {
 | 
			
		||||
	if repo.Units != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = repo.getUnits(e)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !repo.Owner.IsOrganization() || userID == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	teams, err := getUserTeams(e, repo.OwnerID, userID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var allTypes = make(map[UnitType]struct{}, len(allRepUnitTypes))
 | 
			
		||||
	for _, team := range teams {
 | 
			
		||||
		for _, unitType := range team.UnitTypes {
 | 
			
		||||
			allTypes[unitType] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// unique
 | 
			
		||||
	var newRepoUnits = make([]*RepoUnit, 0, len(repo.Units))
 | 
			
		||||
	for _, u := range repo.Units {
 | 
			
		||||
		if _, ok := allTypes[u.Type]; ok {
 | 
			
		||||
			newRepoUnits = append(newRepoUnits, u)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	repo.Units = newRepoUnits
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EnableUnit if this repository enabled some unit
 | 
			
		||||
@@ -1595,6 +1648,7 @@ func DeleteRepository(uid, repoID int64) error {
 | 
			
		||||
		&Release{RepoID: repoID},
 | 
			
		||||
		&Collaboration{RepoID: repoID},
 | 
			
		||||
		&PullRequest{BaseRepoID: repoID},
 | 
			
		||||
		&RepoUnit{RepoID: repoID},
 | 
			
		||||
	); err != nil {
 | 
			
		||||
		return fmt.Errorf("deleteBeans: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -135,3 +135,11 @@ func (r *RepoUnit) ExternalWikiConfig() *ExternalWikiConfig {
 | 
			
		||||
func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
 | 
			
		||||
	return r.Config.(*ExternalTrackerConfig)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
 | 
			
		||||
	return units, e.Where("repo_id = ?", repoID).Find(&units)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUnitsByRepoIDAndIDs(e Engine, repoID int64, types []UnitType) (units []*RepoUnit, err error) {
 | 
			
		||||
	return units, e.Where("repo_id = ?", repoID).In("`type`", types).Find(&units)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										168
									
								
								models/unit.go
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								models/unit.go
									
									
									
									
									
								
							@@ -20,90 +20,21 @@ const (
 | 
			
		||||
	UnitTypeExternalTracker                     // 9 ExternalTracker
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Unit is a tab page of one repository
 | 
			
		||||
type Unit struct {
 | 
			
		||||
	Type    UnitType
 | 
			
		||||
	NameKey string
 | 
			
		||||
	URI     string
 | 
			
		||||
	DescKey string
 | 
			
		||||
	Idx     int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enumerate all the units
 | 
			
		||||
var (
 | 
			
		||||
	UnitCode = Unit{
 | 
			
		||||
	// allRepUnitTypes contains all the unit types
 | 
			
		||||
	allRepUnitTypes = []UnitType{
 | 
			
		||||
		UnitTypeCode,
 | 
			
		||||
		"repo.code",
 | 
			
		||||
		"/",
 | 
			
		||||
		"repo.code_desc",
 | 
			
		||||
		0,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitIssues = Unit{
 | 
			
		||||
		UnitTypeIssues,
 | 
			
		||||
		"repo.issues",
 | 
			
		||||
		"/issues",
 | 
			
		||||
		"repo.issues_desc",
 | 
			
		||||
		1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitExternalTracker = Unit{
 | 
			
		||||
		UnitTypeExternalTracker,
 | 
			
		||||
		"repo.issues",
 | 
			
		||||
		"/issues",
 | 
			
		||||
		"repo.issues_desc",
 | 
			
		||||
		1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitPullRequests = Unit{
 | 
			
		||||
		UnitTypePullRequests,
 | 
			
		||||
		"repo.pulls",
 | 
			
		||||
		"/pulls",
 | 
			
		||||
		"repo.pulls_desc",
 | 
			
		||||
		2,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitCommits = Unit{
 | 
			
		||||
		UnitTypeCommits,
 | 
			
		||||
		"repo.commits",
 | 
			
		||||
		"/commits/master",
 | 
			
		||||
		"repo.commits_desc",
 | 
			
		||||
		3,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitReleases = Unit{
 | 
			
		||||
		UnitTypeReleases,
 | 
			
		||||
		"repo.releases",
 | 
			
		||||
		"/releases",
 | 
			
		||||
		"repo.releases_desc",
 | 
			
		||||
		4,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitWiki = Unit{
 | 
			
		||||
		UnitTypeWiki,
 | 
			
		||||
		"repo.wiki",
 | 
			
		||||
		"/wiki",
 | 
			
		||||
		"repo.wiki_desc",
 | 
			
		||||
		5,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitExternalWiki = Unit{
 | 
			
		||||
		UnitTypeExternalWiki,
 | 
			
		||||
		"repo.wiki",
 | 
			
		||||
		"/wiki",
 | 
			
		||||
		"repo.wiki_desc",
 | 
			
		||||
		5,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitSettings = Unit{
 | 
			
		||||
		UnitTypeSettings,
 | 
			
		||||
		"repo.settings",
 | 
			
		||||
		"/settings",
 | 
			
		||||
		"repo.settings_desc",
 | 
			
		||||
		6,
 | 
			
		||||
		UnitTypeExternalWiki,
 | 
			
		||||
		UnitTypeExternalTracker,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// defaultRepoUnits contains all the default unit types
 | 
			
		||||
	// defaultRepoUnits contains the default unit types
 | 
			
		||||
	defaultRepoUnits = []UnitType{
 | 
			
		||||
		UnitTypeCode,
 | 
			
		||||
		UnitTypeIssues,
 | 
			
		||||
@@ -121,6 +52,95 @@ var (
 | 
			
		||||
		UnitTypeReleases,
 | 
			
		||||
		UnitTypeSettings,
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Unit is a tab page of one repository
 | 
			
		||||
type Unit struct {
 | 
			
		||||
	Type    UnitType
 | 
			
		||||
	NameKey string
 | 
			
		||||
	URI     string
 | 
			
		||||
	DescKey string
 | 
			
		||||
	Idx     int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CanDisable returns if this unit could be disabled.
 | 
			
		||||
func (u *Unit) CanDisable() bool {
 | 
			
		||||
	return u.Type != UnitTypeSettings
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Enumerate all the units
 | 
			
		||||
var (
 | 
			
		||||
	UnitCode = Unit{
 | 
			
		||||
		UnitTypeCode,
 | 
			
		||||
		"repo.code",
 | 
			
		||||
		"/",
 | 
			
		||||
		"repo.code.desc",
 | 
			
		||||
		0,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitIssues = Unit{
 | 
			
		||||
		UnitTypeIssues,
 | 
			
		||||
		"repo.issues",
 | 
			
		||||
		"/issues",
 | 
			
		||||
		"repo.issues.desc",
 | 
			
		||||
		1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitExternalTracker = Unit{
 | 
			
		||||
		UnitTypeExternalTracker,
 | 
			
		||||
		"repo.ext_issues",
 | 
			
		||||
		"/issues",
 | 
			
		||||
		"repo.ext_issues.desc",
 | 
			
		||||
		1,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitPullRequests = Unit{
 | 
			
		||||
		UnitTypePullRequests,
 | 
			
		||||
		"repo.pulls",
 | 
			
		||||
		"/pulls",
 | 
			
		||||
		"repo.pulls.desc",
 | 
			
		||||
		2,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitCommits = Unit{
 | 
			
		||||
		UnitTypeCommits,
 | 
			
		||||
		"repo.commits",
 | 
			
		||||
		"/commits/master",
 | 
			
		||||
		"repo.commits.desc",
 | 
			
		||||
		3,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitReleases = Unit{
 | 
			
		||||
		UnitTypeReleases,
 | 
			
		||||
		"repo.releases",
 | 
			
		||||
		"/releases",
 | 
			
		||||
		"repo.releases.desc",
 | 
			
		||||
		4,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitWiki = Unit{
 | 
			
		||||
		UnitTypeWiki,
 | 
			
		||||
		"repo.wiki",
 | 
			
		||||
		"/wiki",
 | 
			
		||||
		"repo.wiki.desc",
 | 
			
		||||
		5,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitExternalWiki = Unit{
 | 
			
		||||
		UnitTypeExternalWiki,
 | 
			
		||||
		"repo.ext_wiki",
 | 
			
		||||
		"/wiki",
 | 
			
		||||
		"repo.ext_wiki.desc",
 | 
			
		||||
		5,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	UnitSettings = Unit{
 | 
			
		||||
		UnitTypeSettings,
 | 
			
		||||
		"repo.settings",
 | 
			
		||||
		"/settings",
 | 
			
		||||
		"repo.settings.desc",
 | 
			
		||||
		6,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Units contains all the units
 | 
			
		||||
	Units = map[UnitType]Unit{
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@
 | 
			
		||||
package auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"code.gitea.io/gitea/models"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-macaron/binding"
 | 
			
		||||
	"gopkg.in/macaron.v1"
 | 
			
		||||
)
 | 
			
		||||
@@ -53,6 +55,7 @@ type CreateTeamForm struct {
 | 
			
		||||
	TeamName    string `binding:"Required;AlphaDashDot;MaxSize(30)"`
 | 
			
		||||
	Description string `binding:"MaxSize(255)"`
 | 
			
		||||
	Permission  string
 | 
			
		||||
	Units       []models.UnitType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates the fields
 | 
			
		||||
 
 | 
			
		||||
@@ -493,6 +493,37 @@ func RequireRepoWriter() macaron.Handler {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadRepoUnits loads repsitory's units, it should be called after repository and user loaded
 | 
			
		||||
func LoadRepoUnits() macaron.Handler {
 | 
			
		||||
	return func(ctx *Context) {
 | 
			
		||||
		var userID int64
 | 
			
		||||
		if ctx.User != nil {
 | 
			
		||||
			userID = ctx.User.ID
 | 
			
		||||
		}
 | 
			
		||||
		err := ctx.Repo.Repository.LoadUnitsByUserID(userID)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			ctx.Handle(500, "LoadUnitsByUserID", err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckUnit will check whether
 | 
			
		||||
func CheckUnit(unitType models.UnitType) macaron.Handler {
 | 
			
		||||
	return func(ctx *Context) {
 | 
			
		||||
		var find bool
 | 
			
		||||
		for _, unit := range ctx.Repo.Repository.Units {
 | 
			
		||||
			if unit.Type == unitType {
 | 
			
		||||
				find = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if !find {
 | 
			
		||||
			ctx.Handle(404, "CheckUnit", fmt.Errorf("%s: %v", ctx.Tr("units.error.unit_not_allowed"), unitType))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GitHookService checks if repository Git hooks service has been enabled.
 | 
			
		||||
func GitHookService() macaron.Handler {
 | 
			
		||||
	return func(ctx *Context) {
 | 
			
		||||
 
 | 
			
		||||
@@ -505,6 +505,7 @@ push_exist_repo = Pushing an existing repository from the command line
 | 
			
		||||
bare_message = This repository does not contain any content.
 | 
			
		||||
 | 
			
		||||
code = Code
 | 
			
		||||
code.desc = Code is your program source
 | 
			
		||||
branch = Branch
 | 
			
		||||
tree = Tree
 | 
			
		||||
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
 | 
			
		||||
editor.upload_files_to_dir = Upload files to '%s'
 | 
			
		||||
editor.cannot_commit_to_protected_branch = Can not commit to protected branch '%s'.
 | 
			
		||||
 | 
			
		||||
commits.desc = Commits show the history submited
 | 
			
		||||
commits.commits = Commits
 | 
			
		||||
commits.search = Search commits
 | 
			
		||||
commits.find = Search
 | 
			
		||||
@@ -575,6 +577,10 @@ commits.date = Date
 | 
			
		||||
commits.older = Older
 | 
			
		||||
commits.newer = Newer
 | 
			
		||||
 | 
			
		||||
ext_issues = Ext Issues
 | 
			
		||||
ext_issues.desc = Ext Issues link to an external issues management
 | 
			
		||||
 | 
			
		||||
issues.desc = Issues management your tasks for this repository
 | 
			
		||||
issues.new = New Issue
 | 
			
		||||
issues.new.labels = Labels
 | 
			
		||||
issues.new.no_label = No Label
 | 
			
		||||
@@ -678,6 +684,7 @@ issues.attachment.download = `Click to download "%s"`
 | 
			
		||||
issues.subscribe = Subscribe
 | 
			
		||||
issues.unsubscribe = Unsubscribe
 | 
			
		||||
 | 
			
		||||
pulls.desc = Pulls management your code review and merge requests
 | 
			
		||||
pulls.new = New Pull Request
 | 
			
		||||
pulls.compare_changes = Compare Changes
 | 
			
		||||
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
 | 
			
		||||
milestones.filter_sort.most_issues = Most issues
 | 
			
		||||
milestones.filter_sort.least_issues = Least issues
 | 
			
		||||
 | 
			
		||||
ext_wiki = Ext Wiki
 | 
			
		||||
ext_wiki.desc = Ext Wiki links to an external wiki system
 | 
			
		||||
 | 
			
		||||
wiki = Wiki
 | 
			
		||||
wiki.welcome = Welcome to the project wiki
 | 
			
		||||
wiki.welcome_desc = A wiki allows you and your collaborators to easily document your project.
 | 
			
		||||
wiki.desc = Wiki is a collection of your documents
 | 
			
		||||
wiki.create_first_page = Create the first page
 | 
			
		||||
wiki.page = Page
 | 
			
		||||
wiki.filter_page = Filter page
 | 
			
		||||
@@ -753,6 +764,7 @@ wiki.pages = Pages
 | 
			
		||||
wiki.last_updated = Last updated %s
 | 
			
		||||
 | 
			
		||||
settings = Settings
 | 
			
		||||
settings.desc = Settings management your settings for repository
 | 
			
		||||
settings.options = Options
 | 
			
		||||
settings.collaboration = Collaboration
 | 
			
		||||
settings.collaboration.admin = Admin
 | 
			
		||||
@@ -910,6 +922,7 @@ diff.view_file = View File
 | 
			
		||||
diff.file_suppressed = File diff suppressed because it is too large
 | 
			
		||||
diff.too_many_files = Some files were not shown because too many files changed in this diff
 | 
			
		||||
 | 
			
		||||
releases.desc = Releases manage your milestone versions
 | 
			
		||||
release.releases = Releases
 | 
			
		||||
release.new_release = New Release
 | 
			
		||||
release.draft = Draft
 | 
			
		||||
@@ -968,6 +981,7 @@ team_desc = Description
 | 
			
		||||
team_name_helper = You will use this name to mention this team in conversations.
 | 
			
		||||
team_desc_helper = What is this team for?
 | 
			
		||||
team_permission_desc = What permissions should this team have?
 | 
			
		||||
team_unit_desc = Which units should this team have?
 | 
			
		||||
 | 
			
		||||
form.name_reserved = Organization name '%s' is reserved.
 | 
			
		||||
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
 | 
			
		||||
error.no_gpg_keys_found = "No known key found for this signature in database"
 | 
			
		||||
error.not_signed_commit = "Not a signed commit"
 | 
			
		||||
error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer account"
 | 
			
		||||
 | 
			
		||||
[units]
 | 
			
		||||
error.no_unit_allowed_repo = Cannot find any unit allowed on this repository
 | 
			
		||||
error.unit_not_allowed = You have not allowed to visit this repository unit
 | 
			
		||||
@@ -156,6 +156,7 @@ func NewTeam(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["PageIsOrgTeams"] = true
 | 
			
		||||
	ctx.Data["PageIsOrgTeamsNew"] = true
 | 
			
		||||
	ctx.Data["Team"] = &models.Team{}
 | 
			
		||||
	ctx.Data["Units"] = models.Units
 | 
			
		||||
	ctx.HTML(200, tplTeamNew)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -170,6 +171,7 @@ func NewTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
			
		||||
		Name:        form.TeamName,
 | 
			
		||||
		Description: form.Description,
 | 
			
		||||
		Authorize:   models.ParseAccessMode(form.Permission),
 | 
			
		||||
		UnitTypes:   form.Units,
 | 
			
		||||
	}
 | 
			
		||||
	ctx.Data["Team"] = t
 | 
			
		||||
 | 
			
		||||
@@ -220,6 +222,7 @@ func EditTeam(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["PageIsOrgTeams"] = true
 | 
			
		||||
	ctx.Data["team_name"] = ctx.Org.Team.Name
 | 
			
		||||
	ctx.Data["desc"] = ctx.Org.Team.Description
 | 
			
		||||
	ctx.Data["Units"] = models.Units
 | 
			
		||||
	ctx.HTML(200, tplTeamNew)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -258,6 +261,7 @@ func EditTeamPost(ctx *context.Context, form auth.CreateTeamForm) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	t.Description = form.Description
 | 
			
		||||
	t.UnitTypes = form.Units
 | 
			
		||||
	if err := models.UpdateTeam(t, isAuthChanged); err != nil {
 | 
			
		||||
		ctx.Data["Err_TeamName"] = true
 | 
			
		||||
		switch {
 | 
			
		||||
 
 | 
			
		||||
@@ -77,8 +77,10 @@ func HTTP(ctx *context.Context) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isWiki := false
 | 
			
		||||
	var unitType = models.UnitTypeCode
 | 
			
		||||
	if strings.HasSuffix(reponame, ".wiki") {
 | 
			
		||||
		isWiki = true
 | 
			
		||||
		unitType = models.UnitTypeWiki
 | 
			
		||||
		reponame = reponame[:len(reponame)-5]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -204,6 +206,12 @@ func HTTP(ctx *context.Context) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !repo.CheckUnitUser(authUser.ID, unitType) {
 | 
			
		||||
			ctx.HandleText(http.StatusForbidden, fmt.Sprintf("User %s does not have allowed access to repository %s 's code",
 | 
			
		||||
				authUser.Name, repo.RepoPath()))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		environ = []string{
 | 
			
		||||
			models.EnvRepoUsername + "=" + username,
 | 
			
		||||
			models.EnvRepoName + "=" + reponame,
 | 
			
		||||
 
 | 
			
		||||
@@ -252,6 +252,25 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 | 
			
		||||
 | 
			
		||||
// Home render repository home page
 | 
			
		||||
func Home(ctx *context.Context) {
 | 
			
		||||
	if len(ctx.Repo.Repository.Units) > 0 {
 | 
			
		||||
		tp := ctx.Repo.Repository.Units[0].Type
 | 
			
		||||
		if tp == models.UnitTypeCode {
 | 
			
		||||
			renderCode(ctx)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		unit, ok := models.Units[tp]
 | 
			
		||||
		if ok {
 | 
			
		||||
			ctx.Redirect(setting.AppSubURL + fmt.Sprintf("/%s%s",
 | 
			
		||||
				ctx.Repo.Repository.FullName(), unit.URI))
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.Handle(404, "Home", fmt.Errorf(ctx.Tr("units.error.no_unit_allowed_repo")))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func renderCode(ctx *context.Context) {
 | 
			
		||||
	ctx.Data["PageIsViewCode"] = true
 | 
			
		||||
 | 
			
		||||
	if ctx.Repo.Repository.IsBare {
 | 
			
		||||
 
 | 
			
		||||
@@ -445,10 +445,11 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
 | 
			
		||||
		}, func(ctx *context.Context) {
 | 
			
		||||
			ctx.Data["PageIsSettings"] = true
 | 
			
		||||
		}, context.UnitTypes())
 | 
			
		||||
		}, context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeSettings))
 | 
			
		||||
	}, reqSignIn, context.RepoAssignment(), reqRepoAdmin, context.RepoRef())
 | 
			
		||||
 | 
			
		||||
	m.Get("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), repo.Action)
 | 
			
		||||
 | 
			
		||||
	m.Group("/:username/:reponame", func() {
 | 
			
		||||
		// FIXME: should use different URLs but mostly same logic for comments of issue and pull reuqest.
 | 
			
		||||
		// So they can apply their own enable/disable logic on routers.
 | 
			
		||||
@@ -486,28 +487,6 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
			m.Get("/:id/:action", repo.ChangeMilestonStatus)
 | 
			
		||||
			m.Post("/delete", repo.DeleteMilestone)
 | 
			
		||||
		}, reqRepoWriter, context.RepoRef())
 | 
			
		||||
		m.Group("/releases", func() {
 | 
			
		||||
			m.Get("/new", repo.NewRelease)
 | 
			
		||||
			m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
 | 
			
		||||
			m.Post("/delete", repo.DeleteRelease)
 | 
			
		||||
		}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef())
 | 
			
		||||
		m.Group("/releases", func() {
 | 
			
		||||
			m.Get("/edit/*", repo.EditRelease)
 | 
			
		||||
			m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
 | 
			
		||||
		}, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) {
 | 
			
		||||
			var err error
 | 
			
		||||
			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Handle(500, "GetBranchCommit", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Handle(500, "CommitsCount", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		m.Combo("/compare/*", repo.MustAllowPulls, repo.SetEditorconfigIfExists).
 | 
			
		||||
			Get(repo.CompareAndPullRequest).
 | 
			
		||||
@@ -539,16 +518,42 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}, reqSignIn, context.RepoAssignment(), context.UnitTypes())
 | 
			
		||||
	}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeIssues))
 | 
			
		||||
 | 
			
		||||
	// Releases
 | 
			
		||||
	m.Group("/:username/:reponame", func() {
 | 
			
		||||
		m.Group("/releases", func() {
 | 
			
		||||
			m.Get("/", repo.MustBeNotBare, repo.Releases)
 | 
			
		||||
			m.Get("/new", repo.NewRelease)
 | 
			
		||||
			m.Post("/new", bindIgnErr(auth.NewReleaseForm{}), repo.NewReleasePost)
 | 
			
		||||
			m.Post("/delete", repo.DeleteRelease)
 | 
			
		||||
		}, repo.MustBeNotBare, reqRepoWriter, context.RepoRef())
 | 
			
		||||
		m.Group("/releases", func() {
 | 
			
		||||
			m.Get("/edit/*", repo.EditRelease)
 | 
			
		||||
			m.Post("/edit/*", bindIgnErr(auth.EditReleaseForm{}), repo.EditReleasePost)
 | 
			
		||||
		}, repo.MustBeNotBare, reqRepoWriter, func(ctx *context.Context) {
 | 
			
		||||
			var err error
 | 
			
		||||
			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Handle(500, "GetBranchCommit", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Repo.CommitsCount, err = ctx.Repo.Commit.CommitsCount()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				ctx.Handle(500, "CommitsCount", err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
 | 
			
		||||
		})
 | 
			
		||||
	}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits(), context.CheckUnit(models.UnitTypeReleases))
 | 
			
		||||
 | 
			
		||||
	m.Group("/:username/:reponame", func() {
 | 
			
		||||
		m.Group("", func() {
 | 
			
		||||
			m.Get("/releases", repo.MustBeNotBare, repo.Releases)
 | 
			
		||||
			m.Get("/^:type(issues|pulls)$", repo.RetrieveLabels, repo.Issues)
 | 
			
		||||
			m.Get("/^:type(issues|pulls)$/:index", repo.ViewIssue)
 | 
			
		||||
			m.Get("/labels/", repo.RetrieveLabels, repo.Labels)
 | 
			
		||||
			m.Get("/milestones", repo.Milestones)
 | 
			
		||||
		}, context.RepoRef())
 | 
			
		||||
		}, context.RepoRef(), context.CheckUnit(models.UnitTypeIssues))
 | 
			
		||||
 | 
			
		||||
		// m.Get("/branches", repo.Branches)
 | 
			
		||||
		m.Post("/branches/:name/delete", reqSignIn, reqRepoWriter, repo.MustBeNotBare, repo.DeleteBranchPost)
 | 
			
		||||
@@ -564,20 +569,20 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
					Post(bindIgnErr(auth.NewWikiForm{}), repo.EditWikiPost)
 | 
			
		||||
				m.Post("/:page/delete", repo.DeleteWikiPagePost)
 | 
			
		||||
			}, reqSignIn, reqRepoWriter)
 | 
			
		||||
		}, repo.MustEnableWiki, context.RepoRef())
 | 
			
		||||
		}, repo.MustEnableWiki, context.RepoRef(), context.CheckUnit(models.UnitTypeWiki))
 | 
			
		||||
 | 
			
		||||
		m.Group("/wiki", func() {
 | 
			
		||||
			m.Get("/raw/*", repo.WikiRaw)
 | 
			
		||||
			m.Get("/*", repo.WikiRaw)
 | 
			
		||||
		}, repo.MustEnableWiki)
 | 
			
		||||
		}, repo.MustEnableWiki, context.CheckUnit(models.UnitTypeWiki), context.CheckUnit(models.UnitTypeWiki))
 | 
			
		||||
 | 
			
		||||
		m.Get("/archive/*", repo.MustBeNotBare, repo.Download)
 | 
			
		||||
		m.Get("/archive/*", repo.MustBeNotBare, repo.Download, context.CheckUnit(models.UnitTypeCode))
 | 
			
		||||
 | 
			
		||||
		m.Group("/pulls/:index", func() {
 | 
			
		||||
			m.Get("/commits", context.RepoRef(), repo.ViewPullCommits)
 | 
			
		||||
			m.Get("/files", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ViewPullFiles)
 | 
			
		||||
			m.Post("/merge", reqRepoWriter, repo.MergePullRequest)
 | 
			
		||||
		}, repo.MustAllowPulls)
 | 
			
		||||
		}, repo.MustAllowPulls, context.CheckUnit(models.UnitTypePullRequests))
 | 
			
		||||
 | 
			
		||||
		m.Group("", func() {
 | 
			
		||||
			m.Get("/src/*", repo.SetEditorconfigIfExists, repo.Home)
 | 
			
		||||
@@ -586,21 +591,22 @@ func RegisterRoutes(m *macaron.Macaron) {
 | 
			
		||||
			m.Get("/graph", repo.Graph)
 | 
			
		||||
			m.Get("/commit/:sha([a-f0-9]{7,40})$", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.Diff)
 | 
			
		||||
			m.Get("/forks", repo.Forks)
 | 
			
		||||
		}, context.RepoRef())
 | 
			
		||||
		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff)
 | 
			
		||||
		}, context.RepoRef(), context.CheckUnit(models.UnitTypeCode))
 | 
			
		||||
		m.Get("/commit/:sha([a-f0-9]{7,40})\\.:ext(patch|diff)", repo.MustBeNotBare, repo.RawDiff, context.CheckUnit(models.UnitTypeCode))
 | 
			
		||||
 | 
			
		||||
		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.MustBeNotBare, repo.CompareDiff)
 | 
			
		||||
	}, ignSignIn, context.RepoAssignment(), context.UnitTypes())
 | 
			
		||||
		m.Get("/compare/:before([a-z0-9]{40})\\.\\.\\.:after([a-z0-9]{40})", repo.SetEditorconfigIfExists,
 | 
			
		||||
			repo.SetDiffViewStyle, repo.MustBeNotBare, repo.CompareDiff, context.CheckUnit(models.UnitTypeCode))
 | 
			
		||||
	}, ignSignIn, context.RepoAssignment(), context.UnitTypes(), context.LoadRepoUnits())
 | 
			
		||||
	m.Group("/:username/:reponame", func() {
 | 
			
		||||
		m.Get("/stars", repo.Stars)
 | 
			
		||||
		m.Get("/watchers", repo.Watchers)
 | 
			
		||||
	}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 | 
			
		||||
	}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
 | 
			
		||||
 | 
			
		||||
	m.Group("/:username", func() {
 | 
			
		||||
		m.Group("/:reponame", func() {
 | 
			
		||||
			m.Get("", repo.SetEditorconfigIfExists, repo.Home)
 | 
			
		||||
			m.Get("\\.git$", repo.SetEditorconfigIfExists, repo.Home)
 | 
			
		||||
		}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes())
 | 
			
		||||
		}, ignSignIn, context.RepoAssignment(), context.RepoRef(), context.UnitTypes(), context.LoadRepoUnits())
 | 
			
		||||
 | 
			
		||||
		m.Group("/:reponame", func() {
 | 
			
		||||
			m.Group("/info/lfs", func() {
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,21 @@
 | 
			
		||||
						<div class="ui divider"></div>
 | 
			
		||||
					{{end}}
 | 
			
		||||
 | 
			
		||||
					<div class="required grouped field">
 | 
			
		||||
							<label>{{.i18n.Tr "org.team_unit_desc"}}</label>
 | 
			
		||||
							<br>
 | 
			
		||||
							{{range $t, $unit := $.Units}}
 | 
			
		||||
							<div class="field">
 | 
			
		||||
								<div class="ui toggle checkbox">
 | 
			
		||||
									<input type="checkbox" class="hidden" name="units" value="{{$unit.Type}}"{{if $.Team.EnableUnit $unit.Type}} checked{{end}}>
 | 
			
		||||
									<label>{{$.i18n.Tr $unit.NameKey}}</label>
 | 
			
		||||
									<span class="help">{{$.i18n.Tr $unit.DescKey}}</span>
 | 
			
		||||
								</div>
 | 
			
		||||
							</div>
 | 
			
		||||
							{{end}}
 | 
			
		||||
						</div>
 | 
			
		||||
						<div class="ui divider"></div>
 | 
			
		||||
 | 
			
		||||
					<div class="field">
 | 
			
		||||
						{{if .PageIsOrgTeamsNew}}
 | 
			
		||||
							<button class="ui green button">{{.i18n.Tr "org.create_new_team"}}</button>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user