diff options
author | David Svantesson <davidsvantesson@gmail.com> | 2020-01-17 08:34:37 +0100 |
---|---|---|
committer | Lauris BH <lauris@nix.lv> | 2020-01-17 09:34:37 +0200 |
commit | 3c07d03c0388d3b86138572401281b51f2db9282 (patch) | |
tree | 06eecf8b818ee8721a5dbfdd688eb5f45e5bef51 | |
parent | 36943e56d66a2d711a6b0c27219ce91a3ddc020a (diff) | |
download | gitea-3c07d03c0388d3b86138572401281b51f2db9282.tar.gz gitea-3c07d03c0388d3b86138572401281b51f2db9282.zip |
Add setting to set default and global disabled repository units. (#8788)
* Add possibility to global disable repo units.
* Add Default Repo Unit app.ini setting.
* Hide units
* Hide disabled repo units
* Minor fixes
* Indicate disabled units in team settings.
Co-authored-by: Lauris BH <lauris@nix.lv>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: zeripath <art27@cantab.net>
-rw-r--r-- | custom/conf/app.ini.sample | 7 | ||||
-rw-r--r-- | models/repo.go | 11 | ||||
-rw-r--r-- | models/repo_unit.go | 13 | ||||
-rw-r--r-- | models/unit.go | 79 | ||||
-rw-r--r-- | models/user.go | 3 | ||||
-rw-r--r-- | modules/setting/repository.go | 4 | ||||
-rw-r--r-- | options/locale/locale_en-US.ini | 2 | ||||
-rw-r--r-- | routers/api/v1/repo/repo.go | 140 | ||||
-rw-r--r-- | routers/repo/setting.go | 125 | ||||
-rw-r--r-- | routers/routes/routes.go | 5 | ||||
-rw-r--r-- | routers/user/home.go | 18 | ||||
-rw-r--r-- | templates/base/head_navbar.tmpl | 6 | ||||
-rw-r--r-- | templates/org/team/new.tmpl | 6 | ||||
-rw-r--r-- | templates/repo/settings/options.tmpl | 28 |
14 files changed, 311 insertions, 136 deletions
diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 29e147add8..7e7dbbf5f3 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -42,6 +42,13 @@ DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false ; Allow users to push local repositories to Gitea and have them automatically created for a user or an org ENABLE_PUSH_CREATE_USER = false ENABLE_PUSH_CREATE_ORG = false +; Comma separated list of globally disabled repo units. Allowed values: repo.issues, repo.ext_issues, repo.pulls, repo.wiki, repo.ext_wiki +DISABLED_REPO_UNITS = +; Comma separated list of default repo units. Allowed values: repo.code, repo.releases, repo.issues, repo.pulls, repo.wiki. +; Note: Code and Releases can currently not be deactivated. If you specify default repo units you should still list them for future compatibility. +; External wiki and issue tracker can't be enabled by default as it requires additional settings. +; Disabled repo units will not be added to new repositories regardless if it is in the default list. +DEFAULT_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki [repository.editor] ; List of file extensions for which lines should be wrapped in the CodeMirror editor diff --git a/models/repo.go b/models/repo.go index 2c9dafefc9..6c89dbcbbb 100644 --- a/models/repo.go +++ b/models/repo.go @@ -128,6 +128,7 @@ func loadRepoConfig() { // NewRepoContext creates a new repository context func NewRepoContext() { loadRepoConfig() + loadUnitConfig() RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp")) } @@ -393,6 +394,7 @@ func (repo *Repository) getUnits(e Engine) (err error) { } repo.Units, err = getUnitsByRepoID(e, repo.ID) + log.Trace("repo.Units: %-+v", repo.Units) return err } @@ -1442,14 +1444,19 @@ func UpdateRepositoryUpdatedTime(repoID int64, updateTime time.Time) error { } // UpdateRepositoryUnits updates a repository's units -func UpdateRepositoryUnits(repo *Repository, units []RepoUnit) (err error) { +func UpdateRepositoryUnits(repo *Repository, units []RepoUnit, deleteUnitTypes []UnitType) (err error) { sess := x.NewSession() defer sess.Close() if err = sess.Begin(); err != nil { return err } - if _, err = sess.Where("repo_id = ?", repo.ID).Delete(new(RepoUnit)); err != nil { + // Delete existing settings of units before adding again + for _, u := range units { + deleteUnitTypes = append(deleteUnitTypes, u.Type) + } + + if _, err = sess.Where("repo_id = ?", repo.ID).In("type", deleteUnitTypes).Delete(new(RepoUnit)); err != nil { return err } diff --git a/models/repo_unit.go b/models/repo_unit.go index a6162a65e5..ec680c395e 100644 --- a/models/repo_unit.go +++ b/models/repo_unit.go @@ -170,5 +170,16 @@ func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig { } func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) { - return units, e.Where("repo_id = ?", repoID).Find(&units) + var tmpUnits []*RepoUnit + if err := e.Where("repo_id = ?", repoID).Find(&tmpUnits); err != nil { + return nil, err + } + + for _, u := range tmpUnits { + if !u.Type.UnitGlobalDisabled() { + units = append(units, u) + } + } + + return units, nil } diff --git a/models/unit.go b/models/unit.go index 9f5c8d3cbb..bd2e6b13a6 100644 --- a/models/unit.go +++ b/models/unit.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" ) // UnitType is Unit's Type @@ -78,13 +79,89 @@ var ( UnitTypeWiki, } + // NotAllowedDefaultRepoUnits contains units that can't be default + NotAllowedDefaultRepoUnits = []UnitType{ + UnitTypeExternalWiki, + UnitTypeExternalTracker, + } + // MustRepoUnits contains the units could not be disabled currently MustRepoUnits = []UnitType{ UnitTypeCode, UnitTypeReleases, } + + // DisabledRepoUnits contains the units that have been globally disabled + DisabledRepoUnits = []UnitType{} ) +func loadUnitConfig() { + setDefaultRepoUnits := FindUnitTypes(setting.Repository.DefaultRepoUnits...) + // Default repo units set if setting is not empty + if len(setDefaultRepoUnits) > 0 { + // MustRepoUnits required as default + DefaultRepoUnits = make([]UnitType, len(MustRepoUnits)) + copy(DefaultRepoUnits, MustRepoUnits) + for _, defaultU := range setDefaultRepoUnits { + if !defaultU.CanBeDefault() { + log.Warn("Not allowed as default unit: %s", defaultU.String()) + continue + } + // MustRepoUnits already added + if defaultU.CanDisable() { + DefaultRepoUnits = append(DefaultRepoUnits, defaultU) + } + } + } + + DisabledRepoUnits = FindUnitTypes(setting.Repository.DisabledRepoUnits...) + // Check that must units are not disabled + for i, disabledU := range DisabledRepoUnits { + if !disabledU.CanDisable() { + log.Warn("Not allowed to global disable unit %s", disabledU.String()) + DisabledRepoUnits = append(DisabledRepoUnits[:i], DisabledRepoUnits[i+1:]...) + } + } + // Remove disabled units from default units + for _, disabledU := range DisabledRepoUnits { + for i, defaultU := range DefaultRepoUnits { + if defaultU == disabledU { + DefaultRepoUnits = append(DefaultRepoUnits[:i], DefaultRepoUnits[i+1:]...) + } + } + } +} + +// UnitGlobalDisabled checks if unit type is global disabled +func (u UnitType) UnitGlobalDisabled() bool { + for _, ud := range DisabledRepoUnits { + if u == ud { + return true + } + } + return false +} + +// CanDisable checks if this unit type can be disabled. +func (u *UnitType) CanDisable() bool { + for _, mu := range MustRepoUnits { + if *u == mu { + return false + } + } + return true +} + +// CanBeDefault checks if the unit type can be a default repo unit +func (u *UnitType) CanBeDefault() bool { + for _, nadU := range NotAllowedDefaultRepoUnits { + if *u == nadU { + return false + } + } + return true +} + // Unit is a section of one repository type Unit struct { Type UnitType @@ -96,7 +173,7 @@ type Unit struct { // CanDisable returns if this unit could be disabled. func (u *Unit) CanDisable() bool { - return true + return u.Type.CanDisable() } // IsLessThan compares order of two units diff --git a/models/user.go b/models/user.go index d7129fb09a..4a4af3547a 100644 --- a/models/user.go +++ b/models/user.go @@ -622,6 +622,7 @@ func (u *User) GetRepositories(page, pageSize int) (err error) { } // GetRepositoryIDs returns repositories IDs where user owned and has unittypes +// Caller shall check that units is not globally disabled func (u *User) GetRepositoryIDs(units ...UnitType) ([]int64, error) { var ids []int64 @@ -636,6 +637,7 @@ func (u *User) GetRepositoryIDs(units ...UnitType) ([]int64, error) { } // GetOrgRepositoryIDs returns repositories IDs where user's team owned and has unittypes +// Caller shall check that units is not globally disabled func (u *User) GetOrgRepositoryIDs(units ...UnitType) ([]int64, error) { var ids []int64 @@ -656,6 +658,7 @@ func (u *User) GetOrgRepositoryIDs(units ...UnitType) ([]int64, error) { } // GetAccessRepoIDs returns all repositories IDs where user's or user is a team member organizations +// Caller shall check that units is not globally disabled func (u *User) GetAccessRepoIDs(units ...UnitType) ([]int64, error) { ids, err := u.GetRepositoryIDs(units...) if err != nil { diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 06797e891b..807b29b2d8 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -37,6 +37,8 @@ var ( DefaultCloseIssuesViaCommitsInAnyBranch bool EnablePushCreateUser bool EnablePushCreateOrg bool + DisabledRepoUnits []string + DefaultRepoUnits []string // Repository editor settings Editor struct { @@ -98,6 +100,8 @@ var ( DefaultCloseIssuesViaCommitsInAnyBranch: false, EnablePushCreateUser: false, EnablePushCreateOrg: false, + DisabledRepoUnits: []string{}, + DefaultRepoUnits: []string{}, // Repository editor settings Editor: struct { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9a4f0535e8..0f3e9f4a3d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -636,6 +636,7 @@ stargazers = Stargazers forks = Forks pick_reaction = Pick your reaction reactions_more = and %d more +unit_disabled = The site administrator has disabled this repository section. template.items = Template Items template.git_content = Git Content (Default Branch) @@ -1613,6 +1614,7 @@ team_desc_helper = Describe the purpose or role of the team. team_access_desc = Repository access team_permission_desc = Permission team_unit_desc = Allow Access to Repository Sections +team_unit_disabled = (Disabled) form.name_reserved = The organization name '%s' is reserved. form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name. diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 9ae0c4af4e..a13f6ebe0d 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -757,25 +757,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { repo := ctx.Repo.Repository var units []models.RepoUnit + var deleteUnitTypes []models.UnitType - for _, tp := range models.MustRepoUnits { - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: tp, - Config: new(models.UnitConfig), - }) - } - - if opts.HasIssues == nil { - // If HasIssues setting not touched, rewrite existing repo unit - if unit, err := repo.GetUnit(models.UnitTypeIssues); err == nil { - units = append(units, *unit) - } else if unit, err := repo.GetUnit(models.UnitTypeExternalTracker); err == nil { - units = append(units, *unit) - } - } else if *opts.HasIssues { - if opts.ExternalTracker != nil { - + if opts.HasIssues != nil { + if *opts.HasIssues && opts.ExternalTracker != nil && !models.UnitTypeExternalTracker.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) { err := fmt.Errorf("External tracker URL not valid") @@ -797,7 +782,8 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle, }, }) - } else { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeIssues) + } else if *opts.HasIssues && opts.ExternalTracker == nil && !models.UnitTypeIssues.UnitGlobalDisabled() { // Default to built-in tracker var config *models.IssuesConfig @@ -823,19 +809,19 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { Type: models.UnitTypeIssues, Config: config, }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalTracker) + } else if !*opts.HasIssues { + if !models.UnitTypeExternalTracker.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalTracker) + } + if !models.UnitTypeIssues.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeIssues) + } } } - if opts.HasWiki == nil { - // If HasWiki setting not touched, rewrite existing repo unit - if unit, err := repo.GetUnit(models.UnitTypeWiki); err == nil { - units = append(units, *unit) - } else if unit, err := repo.GetUnit(models.UnitTypeExternalWiki); err == nil { - units = append(units, *unit) - } - } else if *opts.HasWiki { - if opts.ExternalWiki != nil { - + if opts.HasWiki != nil { + if *opts.HasWiki && opts.ExternalWiki != nil && !models.UnitTypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { err := fmt.Errorf("External wiki URL not valid") @@ -850,64 +836,72 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { ExternalWikiURL: opts.ExternalWiki.ExternalWikiURL, }, }) - } else { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeWiki) + } else if *opts.HasWiki && opts.ExternalWiki == nil && !models.UnitTypeWiki.UnitGlobalDisabled() { config := &models.UnitConfig{} units = append(units, models.RepoUnit{ RepoID: repo.ID, Type: models.UnitTypeWiki, Config: config, }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalWiki) + } else if !*opts.HasWiki { + if !models.UnitTypeExternalWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalWiki) + } + if !models.UnitTypeWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeWiki) + } } } - if opts.HasPullRequests == nil { - // If HasPullRequest setting not touched, rewrite existing repo unit - if unit, err := repo.GetUnit(models.UnitTypePullRequests); err == nil { - units = append(units, *unit) - } - } else if *opts.HasPullRequests { - // We do allow setting individual PR settings through the API, so - // we get the config settings and then set them - // if those settings were provided in the opts. - unit, err := repo.GetUnit(models.UnitTypePullRequests) - var config *models.PullRequestsConfig - if err != nil { - // Unit type doesn't exist so we make a new config file with default values - config = &models.PullRequestsConfig{ - IgnoreWhitespaceConflicts: false, - AllowMerge: true, - AllowRebase: true, - AllowRebaseMerge: true, - AllowSquash: true, + if opts.HasPullRequests != nil { + if *opts.HasPullRequests && !models.UnitTypePullRequests.UnitGlobalDisabled() { + // We do allow setting individual PR settings through the API, so + // we get the config settings and then set them + // if those settings were provided in the opts. + unit, err := repo.GetUnit(models.UnitTypePullRequests) + var config *models.PullRequestsConfig + if err != nil { + // Unit type doesn't exist so we make a new config file with default values + config = &models.PullRequestsConfig{ + IgnoreWhitespaceConflicts: false, + AllowMerge: true, + AllowRebase: true, + AllowRebaseMerge: true, + AllowSquash: true, + } + } else { + config = unit.PullRequestsConfig() } - } else { - config = unit.PullRequestsConfig() - } - if opts.IgnoreWhitespaceConflicts != nil { - config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts - } - if opts.AllowMerge != nil { - config.AllowMerge = *opts.AllowMerge - } - if opts.AllowRebase != nil { - config.AllowRebase = *opts.AllowRebase - } - if opts.AllowRebaseMerge != nil { - config.AllowRebaseMerge = *opts.AllowRebaseMerge - } - if opts.AllowSquash != nil { - config.AllowSquash = *opts.AllowSquash - } + if opts.IgnoreWhitespaceConflicts != nil { + config.IgnoreWhitespaceConflicts = *opts.IgnoreWhitespaceConflicts + } + if opts.AllowMerge != nil { + config.AllowMerge = *opts.AllowMerge + } + if opts.AllowRebase != nil { + config.AllowRebase = *opts.AllowRebase + } + if opts.AllowRebaseMerge != nil { + config.AllowRebaseMerge = *opts.AllowRebaseMerge + } + if opts.AllowSquash != nil { + config.AllowSquash = *opts.AllowSquash + } - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: models.UnitTypePullRequests, - Config: config, - }) + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: models.UnitTypePullRequests, + Config: config, + }) + } else if !*opts.HasPullRequests && !models.UnitTypePullRequests.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypePullRequests) + } } - if err := models.UpdateRepositoryUnits(repo, units); err != nil { + if err := models.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err) return err } diff --git a/routers/repo/setting.go b/routers/repo/setting.go index b2330f4ebc..6ad0b4a967 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -205,78 +205,85 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { case "advanced": var units []models.RepoUnit + var deleteUnitTypes []models.UnitType // This section doesn't require repo_name/RepoName to be set in the form, don't show it // as an error on the UI for this action ctx.Data["Err_RepoName"] = nil - for _, tp := range models.MustRepoUnits { + if form.EnableWiki && form.EnableExternalWiki && !models.UnitTypeExternalWiki.UnitGlobalDisabled() { + if !validation.IsValidExternalURL(form.ExternalWikiURL) { + ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) + ctx.Redirect(repo.Link() + "/settings") + return + } + + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: models.UnitTypeExternalWiki, + Config: &models.ExternalWikiConfig{ + ExternalWikiURL: form.ExternalWikiURL, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeWiki) + } else if form.EnableWiki && !form.EnableExternalWiki && !models.UnitTypeWiki.UnitGlobalDisabled() { units = append(units, models.RepoUnit{ RepoID: repo.ID, - Type: tp, + Type: models.UnitTypeWiki, Config: new(models.UnitConfig), }) - } - - if form.EnableWiki { - if form.EnableExternalWiki { - if !validation.IsValidExternalURL(form.ExternalWikiURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: models.UnitTypeExternalWiki, - Config: &models.ExternalWikiConfig{ - ExternalWikiURL: form.ExternalWikiURL, - }, - }) - } else { - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: models.UnitTypeWiki, - Config: new(models.UnitConfig), - }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalWiki) + } else { + if !models.UnitTypeExternalWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalWiki) + } + if !models.UnitTypeWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeWiki) } } - if form.EnableIssues { - if form.EnableExternalTracker { - if !validation.IsValidExternalURL(form.ExternalTrackerURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { - ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: models.UnitTypeExternalTracker, - Config: &models.ExternalTrackerConfig{ - ExternalTrackerURL: form.ExternalTrackerURL, - ExternalTrackerFormat: form.TrackerURLFormat, - ExternalTrackerStyle: form.TrackerIssueStyle, - }, - }) - } else { - units = append(units, models.RepoUnit{ - RepoID: repo.ID, - Type: models.UnitTypeIssues, - Config: &models.IssuesConfig{ - EnableTimetracker: form.EnableTimetracker, - AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, - EnableDependencies: form.EnableIssueDependencies, - }, - }) + if form.EnableIssues && form.EnableExternalTracker && !models.UnitTypeExternalTracker.UnitGlobalDisabled() { + if !validation.IsValidExternalURL(form.ExternalTrackerURL) { + ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) + ctx.Redirect(repo.Link() + "/settings") + return + } + if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { + ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error")) + ctx.Redirect(repo.Link() + "/settings") + return + } + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: models.UnitTypeExternalTracker, + Config: &models.ExternalTrackerConfig{ + ExternalTrackerURL: form.ExternalTrackerURL, + ExternalTrackerFormat: form.TrackerURLFormat, + ExternalTrackerStyle: form.TrackerIssueStyle, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeIssues) + } else if form.EnableIssues && !form.EnableExternalTracker && !models.UnitTypeIssues.UnitGlobalDisabled() { + units = append(units, models.RepoUnit{ + RepoID: repo.ID, + Type: models.UnitTypeIssues, + Config: &models.IssuesConfig{ + EnableTimetracker: form.EnableTimetracker, + AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, + EnableDependencies: form.EnableIssueDependencies, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalTracker) + } else { + if !models.UnitTypeExternalTracker.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeExternalTracker) + } + if !models.UnitTypeIssues.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeIssues) } } - if form.EnablePulls { + if form.EnablePulls && !models.UnitTypePullRequests.UnitGlobalDisabled() { units = append(units, models.RepoUnit{ RepoID: repo.ID, Type: models.UnitTypePullRequests, @@ -288,9 +295,11 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { AllowSquash: form.PullsAllowSquash, }, }) + } else if !models.UnitTypePullRequests.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, models.UnitTypePullRequests) } - if err := models.UpdateRepositoryUnits(repo, units); err != nil { + if err := models.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil { ctx.ServerError("UpdateRepositoryUnits", err) return } diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 7e81f55de6..74bddc79e5 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -261,6 +261,11 @@ func RegisterRoutes(m *macaron.Macaron) { } m.Use(user.GetNotificationCount) + m.Use(func(ctx *context.Context) { + ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled() + ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled() + ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled() + }) // FIXME: not all routes need go through same middlewares. // Especially some AJAX requests, we can reduce middleware number to improve performance. diff --git a/routers/user/home.go b/routers/user/home.go index 822452f1ca..0d78b17dad 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -158,6 +158,12 @@ func Dashboard(ctx *context.Context) { // Milestones render the user milestones page func Milestones(ctx *context.Context) { + if models.UnitTypeIssues.UnitGlobalDisabled() && models.UnitTypePullRequests.UnitGlobalDisabled() { + log.Debug("Milestones overview page not available as both issues and pull requests are globally disabled") + ctx.Status(404) + return + } + ctx.Data["Title"] = ctx.Tr("milestones") ctx.Data["PageIsMilestonesDashboard"] = true @@ -335,10 +341,22 @@ func Issues(ctx *context.Context) { isPullList := ctx.Params(":type") == "pulls" unitType := models.UnitTypeIssues if isPullList { + if models.UnitTypePullRequests.UnitGlobalDisabled() { + log.Debug("Pull request overview page not available as it is globally disabled.") + ctx.Status(404) + return + } + ctx.Data["Title"] = ctx.Tr("pull_requests") ctx.Data["PageIsPulls"] = true unitType = models.UnitTypePullRequests } else { + if models.UnitTypeIssues.UnitGlobalDisabled() { + log.Debug("Issues overview page not available as it is globally disabled.") + ctx.Status(404) + return + } + ctx.Data["Title"] = ctx.Tr("issues") ctx.Data["PageIsIssues"] = true } diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index a09b4b832e..5f1b9405d9 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -10,9 +10,15 @@ {{if .IsSigned}} <a class="item {{if .PageIsDashboard}}active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "dashboard"}}</a> + {{if not .UnitIssuesGlobalDisabled}} <a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> + {{end}} + {{if not .UnitPullsGlobalDisabled}} <a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a> + {{end}} + {{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}} {{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}} + {{end}} <a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "explore"}}</a> {{else if .IsLandingPageHome}} <a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/">{{.i18n.Tr "home"}}</a> diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index c38fa4d940..228f86824a 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -81,10 +81,14 @@ <label>{{.i18n.Tr "org.team_unit_desc"}}</label> <br> {{range $t, $unit := $.Units}} + {{if $unit.Type.UnitGlobalDisabled}} + <div class="field poping up" data-content="{{$.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="field"> + {{end}} <div class="ui toggle checkbox"> <input type="checkbox" class="hidden" name="units" value="{{$unit.Type.Value}}"{{if or (eq $.Team.ID 0) ($.Team.UnitEnabled $unit.Type)}} checked{{end}}> - <label>{{$.i18n.Tr $unit.NameKey}}</label> + <label>{{$.i18n.Tr $unit.NameKey}}{{if $unit.Type.UnitGlobalDisabled}} {{$.i18n.Tr "org.team_unit_disabled"}}{{end}}</label> <span class="help">{{$.i18n.Tr $unit.DescKey}}</span> </div> </div> diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 6f96ff7f47..c674fcf7f9 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -144,20 +144,32 @@ {{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} <div class="inline field"> <label>{{.i18n.Tr "repo.wiki"}}</label> + {{if and (.UnitTypeWiki.UnitGlobalDisabled) (.UnitTypeExternalWiki.UnitGlobalDisabled)}} + <div class="ui checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui checkbox"> + {{end}} <input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if $isWikiEnabled}}checked{{end}}> <label>{{.i18n.Tr "repo.settings.wiki_desc"}}</label> </div> </div> <div class="field {{if not $isWikiEnabled}}disabled{{end}}" id="wiki_box"> <div class="field"> + {{if .UnitTypeWiki.UnitGlobalDisabled}} + <div class="ui radio checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui radio checkbox"> + {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.UnitTypeExternalWiki)}}checked{{end}}/> <label>{{.i18n.Tr "repo.settings.use_internal_wiki"}}</label> </div> </div> <div class="field"> + {{if .UnitTypeExternalWiki.UnitGlobalDisabled}} + <div class="ui radio checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui radio checkbox"> + {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.UnitTypeExternalWiki}}checked{{end}}/> <label>{{.i18n.Tr "repo.settings.use_external_wiki"}}</label> </div> @@ -174,14 +186,22 @@ {{$isIssuesEnabled := or (.Repository.UnitEnabled $.UnitTypeIssues) (.Repository.UnitEnabled $.UnitTypeExternalTracker)}} <div class="inline field"> <label>{{.i18n.Tr "repo.issues"}}</label> + {{if and (.UnitTypeIssues.UnitGlobalDisabled) (.UnitTypeExternalTracker.UnitGlobalDisabled)}} + <div class="ui checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui checkbox"> + {{end}} <input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if $isIssuesEnabled}}checked{{end}}> <label>{{.i18n.Tr "repo.settings.issues_desc"}}</label> </div> </div> <div class="field {{if not $isIssuesEnabled}}disabled{{end}}" id="issue_box"> <div class="field"> + {{if .UnitTypeIssues.UnitGlobalDisabled}} + <div class="ui radio checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui radio checkbox"> + {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.UnitTypeExternalTracker)}}checked{{end}}/> <label>{{.i18n.Tr "repo.settings.use_internal_issue_tracker"}}</label> </div> @@ -209,7 +229,11 @@ </div> </div> <div class="field"> + {{if .UnitTypeExternalTracker.UnitGlobalDisabled}} + <div class="ui radio checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui radio checkbox"> + {{end}} <input class="hidden enable-system-radio" tabindex="0" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.UnitTypeExternalTracker}}checked{{end}}/> <label>{{.i18n.Tr "repo.settings.use_external_issue_tracker"}}</label> </div> @@ -251,7 +275,11 @@ {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} <div class="inline field"> <label>{{.i18n.Tr "repo.pulls"}}</label> + {{if .UnitTypePullRequests.UnitGlobalDisabled}} + <div class="ui checkbox poping up disabled" data-content="{{.i18n.Tr "repo.unit_disabled"}}"> + {{else}} <div class="ui checkbox"> + {{end}} <input class="enable-system" name="enable_pulls" type="checkbox" data-target="#pull_box" {{if $pullRequestEnabled}}checked{{end}}> <label>{{.i18n.Tr "repo.settings.pulls_desc"}}</label> </div> |