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>
This commit is contained in:
David Svantesson 2020-01-17 08:34:37 +01:00 committed by Lauris BH
parent 36943e56d6
commit 3c07d03c03
14 changed files with 315 additions and 140 deletions

View file

@ -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,
})
}
}
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,
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)
}
} 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
}
units = append(units, models.RepoUnit{
RepoID: repo.ID,
Type: models.UnitTypePullRequests,
Config: config,
})
}
if err := models.UpdateRepositoryUnits(repo, units); err != nil {
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()
}
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,
})
} else if !*opts.HasPullRequests && !models.UnitTypePullRequests.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, models.UnitTypePullRequests)
}
}
if err := models.UpdateRepositoryUnits(repo, units, deleteUnitTypes); err != nil {
ctx.Error(http.StatusInternalServerError, "UpdateRepositoryUnits", err)
return err
}

View file

@ -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: tp,
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: 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
}

View file

@ -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.

View file

@ -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
}