mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	#1575 Limit repo creation
This commit is contained in:
		@@ -5,7 +5,7 @@ Gogs - Go Git Service [
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
##### Current version: 0.7.38 Beta
 | 
					##### Current version: 0.7.39 Beta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| Web | UI  | Preview  |
 | 
					| Web | UI  | Preview  |
 | 
				
			||||||
|:-------------:|:-------:|:-------:|
 | 
					|:-------------:|:-------:|:-------:|
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,7 +86,7 @@ func checkVersion() {
 | 
				
			|||||||
		{"github.com/go-macaron/i18n", i18n.Version, "0.2.0"},
 | 
							{"github.com/go-macaron/i18n", i18n.Version, "0.2.0"},
 | 
				
			||||||
		{"github.com/go-macaron/session", session.Version, "0.1.6"},
 | 
							{"github.com/go-macaron/session", session.Version, "0.1.6"},
 | 
				
			||||||
		{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
 | 
							{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"},
 | 
				
			||||||
		{"gopkg.in/ini.v1", ini.Version, "1.8.1"},
 | 
							{"gopkg.in/ini.v1", ini.Version, "1.8.3"},
 | 
				
			||||||
		{"gopkg.in/macaron.v1", macaron.Version, "0.8.0"},
 | 
							{"gopkg.in/macaron.v1", macaron.Version, "0.8.0"},
 | 
				
			||||||
		{"github.com/gogits/git-shell", git.Version, "0.1.0"},
 | 
							{"github.com/gogits/git-shell", git.Version, "0.1.0"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,6 +15,8 @@ SCRIPT_TYPE = bash
 | 
				
			|||||||
ANSI_CHARSET = 
 | 
					ANSI_CHARSET = 
 | 
				
			||||||
; Force every new repository to be private
 | 
					; Force every new repository to be private
 | 
				
			||||||
FORCE_PRIVATE = false
 | 
					FORCE_PRIVATE = false
 | 
				
			||||||
 | 
					; Global maximum creation limit of repository per user, 0 means no limit
 | 
				
			||||||
 | 
					MAX_CREATION_LIMIT = 0
 | 
				
			||||||
; Patch test queue length, make it as large as possible
 | 
					; Patch test queue length, make it as large as possible
 | 
				
			||||||
PULL_REQUEST_QUEUE_LENGTH = 10000
 | 
					PULL_REQUEST_QUEUE_LENGTH = 10000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -359,6 +359,7 @@ watchers = Watchers
 | 
				
			|||||||
stargazers = Stargazers
 | 
					stargazers = Stargazers
 | 
				
			||||||
forks = Forks
 | 
					forks = Forks
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					form.reach_limit_of_creation = The owner has reached maximum creation limit of %d repositories.
 | 
				
			||||||
form.name_reserved = Repository name '%s' is reserved.
 | 
					form.name_reserved = Repository name '%s' is reserved.
 | 
				
			||||||
form.name_pattern_not_allowed = Repository name pattern '%s' is not allowed.
 | 
					form.name_pattern_not_allowed = Repository name pattern '%s' is not allowed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -855,6 +856,8 @@ users.auth_login_name = Authentication Login Name
 | 
				
			|||||||
users.password_helper = Leave it empty to remain unchanged.
 | 
					users.password_helper = Leave it empty to remain unchanged.
 | 
				
			||||||
users.update_profile_success = Account profile has been updated successfully.
 | 
					users.update_profile_success = Account profile has been updated successfully.
 | 
				
			||||||
users.edit_account = Edit Account
 | 
					users.edit_account = Edit Account
 | 
				
			||||||
 | 
					users.max_repo_creation = Maximum Repository Creation Limit
 | 
				
			||||||
 | 
					users.max_repo_creation_desc = (Set 0 to use gloabl default limit)
 | 
				
			||||||
users.is_activated = This account is activated
 | 
					users.is_activated = This account is activated
 | 
				
			||||||
users.is_admin = This account has administrator permissions
 | 
					users.is_admin = This account has administrator permissions
 | 
				
			||||||
users.allow_git_hook = This account has permissions to create Git hooks
 | 
					users.allow_git_hook = This account has permissions to create Git hooks
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								gogs.go
									
									
									
									
									
								
							@@ -18,7 +18,7 @@ import (
 | 
				
			|||||||
	"github.com/gogits/gogs/modules/setting"
 | 
						"github.com/gogits/gogs/modules/setting"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const APP_VER = "0.7.38.1210 Beta"
 | 
					const APP_VER = "0.7.39.1210 Beta"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	runtime.GOMAXPROCS(runtime.NumCPU())
 | 
						runtime.GOMAXPROCS(runtime.NumCPU())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -107,6 +107,19 @@ func (err ErrUserHasOrgs) Error() string {
 | 
				
			|||||||
	return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
 | 
						return fmt.Sprintf("user still has membership of organizations [uid: %d]", err.UID)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ErrReachLimitOfRepo struct {
 | 
				
			||||||
 | 
						Limit int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func IsErrReachLimitOfRepo(err error) bool {
 | 
				
			||||||
 | 
						_, ok := err.(ErrReachLimitOfRepo)
 | 
				
			||||||
 | 
						return ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err ErrReachLimitOfRepo) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//  __      __.__ __   .__
 | 
					//  __      __.__ __   .__
 | 
				
			||||||
// /  \    /  \__|  | _|__|
 | 
					// /  \    /  \__|  | _|__|
 | 
				
			||||||
// \   \/\/   /  |  |/ /  |
 | 
					// \   \/\/   /  |  |/ /  |
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -900,6 +900,10 @@ func createRepository(e *xorm.Session, u *User, repo *Repository) (err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CreateRepository creates a repository for given user or organization.
 | 
					// CreateRepository creates a repository for given user or organization.
 | 
				
			||||||
func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) {
 | 
					func CreateRepository(u *User, opts CreateRepoOptions) (_ *Repository, err error) {
 | 
				
			||||||
 | 
						if !u.CanCreateRepo() {
 | 
				
			||||||
 | 
							return nil, ErrReachLimitOfRepo{u.MaxRepoCreation}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo := &Repository{
 | 
						repo := &Repository{
 | 
				
			||||||
		OwnerID:      u.Id,
 | 
							OwnerID:      u.Id,
 | 
				
			||||||
		Owner:        u,
 | 
							Owner:        u,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -75,6 +75,8 @@ type User struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// Remember visibility choice for convenience, true for private
 | 
						// Remember visibility choice for convenience, true for private
 | 
				
			||||||
	LastRepoVisibility bool
 | 
						LastRepoVisibility bool
 | 
				
			||||||
 | 
						// Maximum repository creation limit, 0 means use gloabl default
 | 
				
			||||||
 | 
						MaxRepoCreation int `xorm:"NOT NULL"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Permissions.
 | 
						// Permissions.
 | 
				
			||||||
	IsActive         bool
 | 
						IsActive         bool
 | 
				
			||||||
@@ -101,6 +103,12 @@ type User struct {
 | 
				
			|||||||
	Members     []*User `xorm:"-"`
 | 
						Members     []*User `xorm:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *User) BeforeUpdate() {
 | 
				
			||||||
 | 
						if u.MaxRepoCreation < 0 {
 | 
				
			||||||
 | 
							u.MaxRepoCreation = 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (u *User) AfterSet(colName string, _ xorm.Cell) {
 | 
					func (u *User) AfterSet(colName string, _ xorm.Cell) {
 | 
				
			||||||
	switch colName {
 | 
						switch colName {
 | 
				
			||||||
	case "full_name":
 | 
						case "full_name":
 | 
				
			||||||
@@ -116,6 +124,20 @@ func (u *User) HasForkedRepo(repoID int64) bool {
 | 
				
			|||||||
	return has
 | 
						return has
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *User) RepoCreationNum() int {
 | 
				
			||||||
 | 
						if u.MaxRepoCreation == 0 {
 | 
				
			||||||
 | 
							return setting.Repository.MaxCreationLimit
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.MaxRepoCreation
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (u *User) CanCreateRepo() bool {
 | 
				
			||||||
 | 
						if u.MaxRepoCreation == 0 {
 | 
				
			||||||
 | 
							return u.NumRepos < setting.Repository.MaxCreationLimit
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return u.NumRepos < u.MaxRepoCreation
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CanEditGitHook returns true if user can edit Git hooks.
 | 
					// CanEditGitHook returns true if user can edit Git hooks.
 | 
				
			||||||
func (u *User) CanEditGitHook() bool {
 | 
					func (u *User) CanEditGitHook() bool {
 | 
				
			||||||
	return u.IsAdmin || u.AllowGitHook
 | 
						return u.IsAdmin || u.AllowGitHook
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ type AdminEditUserForm struct {
 | 
				
			|||||||
	Password         string `binding:"MaxSize(255)"`
 | 
						Password         string `binding:"MaxSize(255)"`
 | 
				
			||||||
	Website          string `binding:"MaxSize(50)"`
 | 
						Website          string `binding:"MaxSize(50)"`
 | 
				
			||||||
	Location         string `binding:"MaxSize(50)"`
 | 
						Location         string `binding:"MaxSize(50)"`
 | 
				
			||||||
 | 
						MaxRepoCreation  int
 | 
				
			||||||
	Active           bool
 | 
						Active           bool
 | 
				
			||||||
	Admin            bool
 | 
						Admin            bool
 | 
				
			||||||
	AllowGitHook     bool
 | 
						AllowGitHook     bool
 | 
				
			||||||
 
 | 
				
			|||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -98,6 +98,7 @@ var (
 | 
				
			|||||||
	Repository struct {
 | 
						Repository struct {
 | 
				
			||||||
		AnsiCharset            string
 | 
							AnsiCharset            string
 | 
				
			||||||
		ForcePrivate           bool
 | 
							ForcePrivate           bool
 | 
				
			||||||
 | 
							MaxCreationLimit       int
 | 
				
			||||||
		PullRequestQueueLength int
 | 
							PullRequestQueueLength int
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	RepoRootPath string
 | 
						RepoRootPath string
 | 
				
			||||||
@@ -379,9 +380,9 @@ func NewContext() {
 | 
				
			|||||||
		RepoRootPath = path.Clean(RepoRootPath)
 | 
							RepoRootPath = path.Clean(RepoRootPath)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
 | 
						ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash")
 | 
				
			||||||
	Repository.AnsiCharset = sec.Key("ANSI_CHARSET").String()
 | 
						if err = Cfg.Section("repository").MapTo(&Repository); err != nil {
 | 
				
			||||||
	Repository.ForcePrivate = sec.Key("FORCE_PRIVATE").MustBool()
 | 
							log.Fatal(4, "Fail to map Repository settings: %v", err)
 | 
				
			||||||
	Repository.PullRequestQueueLength = sec.Key("PULL_REQUEST_QUEUE_LENGTH").MustInt(10000)
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// UI settings.
 | 
						// UI settings.
 | 
				
			||||||
	sec = Cfg.Section("ui")
 | 
						sec = Cfg.Section("ui")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -210,6 +210,7 @@ func EditUserPost(ctx *middleware.Context, form auth.AdminEditUserForm) {
 | 
				
			|||||||
	u.Email = form.Email
 | 
						u.Email = form.Email
 | 
				
			||||||
	u.Website = form.Website
 | 
						u.Website = form.Website
 | 
				
			||||||
	u.Location = form.Location
 | 
						u.Location = form.Location
 | 
				
			||||||
 | 
						u.MaxRepoCreation = form.MaxRepoCreation
 | 
				
			||||||
	u.IsActive = form.Active
 | 
						u.IsActive = form.Active
 | 
				
			||||||
	u.IsAdmin = form.Admin
 | 
						u.IsAdmin = form.Admin
 | 
				
			||||||
	u.AllowGitHook = form.AllowGitHook
 | 
						u.AllowGitHook = form.AllowGitHook
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,8 +78,10 @@ func Create(ctx *middleware.Context) {
 | 
				
			|||||||
	ctx.HTML(200, CREATE)
 | 
						ctx.HTML(200, CREATE)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handleCreateError(ctx *middleware.Context, err error, name string, tpl base.TplName, form interface{}) {
 | 
					func handleCreateError(ctx *middleware.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
 | 
				
			||||||
	switch {
 | 
						switch {
 | 
				
			||||||
 | 
						case models.IsErrReachLimitOfRepo(err):
 | 
				
			||||||
 | 
							ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form)
 | 
				
			||||||
	case models.IsErrRepoAlreadyExist(err):
 | 
						case models.IsErrRepoAlreadyExist(err):
 | 
				
			||||||
		ctx.Data["Err_RepoName"] = true
 | 
							ctx.Data["Err_RepoName"] = true
 | 
				
			||||||
		ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
 | 
							ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
 | 
				
			||||||
@@ -133,7 +135,7 @@ func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handleCreateError(ctx, err, "CreatePost", CREATE, &form)
 | 
						handleCreateError(ctx, ctxUser, err, "CreatePost", CREATE, &form)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Migrate(ctx *middleware.Context) {
 | 
					func Migrate(ctx *middleware.Context) {
 | 
				
			||||||
@@ -216,7 +218,7 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	handleCreateError(ctx, err, "MigratePost", MIGRATE, &form)
 | 
						handleCreateError(ctx, ctxUser, err, "MigratePost", MIGRATE, &form)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Action(ctx *middleware.Context) {
 | 
					func Action(ctx *middleware.Context) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1 @@
 | 
				
			|||||||
0.7.38.1210 Beta
 | 
					0.7.39.1210 Beta
 | 
				
			||||||
@@ -57,6 +57,16 @@
 | 
				
			|||||||
							<input id="location" name="location" value="{{.User.Location}}">
 | 
												<input id="location" name="location" value="{{.User.Location}}">
 | 
				
			||||||
						</div>
 | 
											</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<div class="inline field {{if .Err_MaxRepoCreation}}error{{end}}">
 | 
				
			||||||
 | 
												<label for="max_repo_creation">{{.i18n.Tr "admin.users.max_repo_creation"}}</label>
 | 
				
			||||||
 | 
												<input id="max_repo_creation" name="max_repo_creation" type="number" value="{{.User.MaxRepoCreation}}">
 | 
				
			||||||
 | 
												<p class="help">{{.i18n.Tr "admin.users.max_repo_creation_desc"}}</p>
 | 
				
			||||||
 | 
											</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											<div class="ui divider"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						<div class="inline field">
 | 
											<div class="inline field">
 | 
				
			||||||
							<div class="ui checkbox">
 | 
												<div class="ui checkbox">
 | 
				
			||||||
								<label><strong>{{.i18n.Tr "admin.users.is_activated"}}</strong></label>
 | 
													<label><strong>{{.i18n.Tr "admin.users.is_activated"}}</strong></label>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user