mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-26 12:27:06 +00: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
	 Unknwon
					Unknwon