diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample index 991a2a3e6b4b1..2a5a70e65ca43 100644 --- a/custom/conf/app.ini.sample +++ b/custom/conf/app.ini.sample @@ -39,6 +39,10 @@ ACCESS_CONTROL_ALLOW_ORIGIN = USE_COMPAT_SSH_URI = false ; Close issues as long as a commit on any branch marks it as fixed DEFAULT_CLOSE_ISSUES_VIA_COMMITS_IN_ANY_BRANCH = false +; Set this to false to completely disable the wiki function +ENABLE_WIKI = true +; Set this to false to completely disable the issue function +ENABLE_ISSUES = true [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 86370821d3bb0..646d6aec88198 100644 --- a/models/repo.go +++ b/models/repo.go @@ -1278,6 +1278,9 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err var units = make([]RepoUnit, 0, len(DefaultRepoUnits)) for _, tp := range DefaultRepoUnits { if tp == UnitTypeIssues { + if !setting.Repository.EnableIssues { + continue + } units = append(units, RepoUnit{ RepoID: repo.ID, Type: tp, @@ -1287,6 +1290,10 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err EnableDependencies: setting.Service.DefaultEnableDependencies, }, }) + } else if tp == UnitTypeWiki { + if !setting.Repository.EnableWiki { + continue + } } else if tp == UnitTypePullRequests { units = append(units, RepoUnit{ RepoID: repo.ID, diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 98e3d6e82624d..e5a2c212d4b37 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -64,6 +64,10 @@ var ( Issue struct { LockReasons []string } `ini:"repository.issue"` + + // global feature flags + EnableWiki bool + EnableIssues bool }{ AnsiCharset: "", ForcePrivate: false, @@ -121,6 +125,9 @@ var ( }{ LockReasons: strings.Split("Too heated,Off-topic,Spam,Resolved", ","), }, + + EnableWiki: true, + EnableIssues: true, } RepoRootPath string ScriptType = "bash" @@ -147,6 +154,10 @@ func newRepository() { } ScriptType = sec.Key("SCRIPT_TYPE").MustString("bash") + // feature flags + Repository.EnableIssues = sec.Key("ENABLE_ISSUES").MustBool(true) + Repository.EnableWiki = sec.Key("ENABLE_WIKI").MustBool(true) + if err = Cfg.Section("repository").MapTo(&Repository); err != nil { log.Fatal("Failed to map Repository settings: %v", err) } else if err = Cfg.Section("repository.editor").MapTo(&Repository.Editor); err != nil { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0df7cdf7e0d09..b34af85797a70 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -945,6 +945,7 @@ issues.review.review = Review issues.review.reviewers = Reviewers issues.review.show_outdated = Show outdated issues.review.hide_outdated = Hide outdated +issues.disabled = Issues are disabled for this installation. pulls.desc = Enable merge requests and code reviews. pulls.new = New Pull Request @@ -1044,6 +1045,7 @@ wiki.page_already_exists = A wiki page with the same name already exists. wiki.reserved_page = The wiki page name '%s' is reserved. wiki.pages = Pages wiki.last_updated = Last updated %s +wiki.disabled = The wiki function has been disabled for this installation. activity = Activity activity.period.filter_label = Period: diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index cd308fce51824..9605921f2af70 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -454,7 +454,7 @@ func mustAllowPulls(ctx *context.APIContext) { } func mustEnableIssuesOrPulls(ctx *context.APIContext) { - if !ctx.Repo.CanRead(models.UnitTypeIssues) && + if (!setting.Repository.EnableIssues || !ctx.Repo.CanRead(models.UnitTypeIssues)) && !(ctx.Repo.Repository.CanEnablePulls() && ctx.Repo.CanRead(models.UnitTypePullRequests)) { if ctx.Repo.Repository.CanEnablePulls() && log.IsTrace() { if ctx.IsSigned { diff --git a/routers/repo/activity.go b/routers/repo/activity.go index e170a91299915..b62b9516a603a 100644 --- a/routers/repo/activity.go +++ b/routers/repo/activity.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/setting" ) const ( @@ -46,7 +47,7 @@ func Activity(ctx *context.Context) { var err error if ctx.Data["Activity"], err = models.GetActivityStats(ctx.Repo.Repository, timeFrom, ctx.Repo.CanRead(models.UnitTypeReleases), - ctx.Repo.CanRead(models.UnitTypeIssues), + setting.Repository.EnableIssues && ctx.Repo.CanRead(models.UnitTypeIssues), ctx.Repo.CanRead(models.UnitTypePullRequests), ctx.Repo.CanRead(models.UnitTypeCode)); err != nil { ctx.ServerError("GetActivityStats", err) diff --git a/routers/repo/issue.go b/routers/repo/issue.go index 72e0357e6cf87..b40251b96d086 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -73,8 +73,9 @@ func MustAllowUserComment(ctx *context.Context) { // MustEnableIssues check if repository enable internal issues func MustEnableIssues(ctx *context.Context) { - if !ctx.Repo.CanRead(models.UnitTypeIssues) && - !ctx.Repo.CanRead(models.UnitTypeExternalTracker) { + if !setting.Repository.EnableIssues || + (!ctx.Repo.CanRead(models.UnitTypeIssues) && + !ctx.Repo.CanRead(models.UnitTypeExternalTracker)) { ctx.NotFound("MustEnableIssues", nil) return } @@ -987,7 +988,7 @@ func GetActionIssue(ctx *context.Context) *models.Issue { func checkIssueRights(ctx *context.Context, issue *models.Issue) { if issue.IsPull && !ctx.Repo.CanRead(models.UnitTypePullRequests) || - !issue.IsPull && !ctx.Repo.CanRead(models.UnitTypeIssues) { + !issue.IsPull && (!setting.Repository.EnableIssues || !ctx.Repo.CanRead(models.UnitTypeIssues)) { ctx.NotFound("IssueOrPullRequestUnitNotAllowed", nil) } } @@ -1012,7 +1013,7 @@ func getActionIssues(ctx *context.Context) []*models.Issue { return nil } // Check access rights for all issues - issueUnitEnabled := ctx.Repo.CanRead(models.UnitTypeIssues) + issueUnitEnabled := setting.Repository.EnableIssues && ctx.Repo.CanRead(models.UnitTypeIssues) prUnitEnabled := ctx.Repo.CanRead(models.UnitTypePullRequests) for _, issue := range issues { if issue.IsPull && !prUnitEnabled || !issue.IsPull && !issueUnitEnabled { diff --git a/routers/repo/issue_label_test.go b/routers/repo/issue_label_test.go index bf625112581e3..becb8d8ce6d44 100644 --- a/routers/repo/issue_label_test.go +++ b/routers/repo/issue_label_test.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" @@ -133,6 +134,7 @@ func TestUpdateIssueLabel_Clear(t *testing.T) { } func TestUpdateIssueLabel_Toggle(t *testing.T) { + setting.Repository.EnableIssues = true for _, testCase := range []struct { Action string IssueIDs []int64 diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 757295069e90c..f9be941287199 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -218,7 +218,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { }) } - if form.EnableWiki { + if form.EnableWiki && setting.Repository.EnableWiki { if form.EnableExternalWiki { if !validation.IsValidExternalURL(form.ExternalWikiURL) { ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) @@ -242,7 +242,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } } - if form.EnableIssues { + if form.EnableIssues && setting.Repository.EnableIssues { if form.EnableExternalTracker { if !validation.IsValidExternalURL(form.ExternalTrackerURL) { ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go index 0d965f2183937..5331442b1a306 100644 --- a/routers/repo/wiki.go +++ b/routers/repo/wiki.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" ) @@ -32,6 +33,10 @@ const ( // MustEnableWiki check if wiki is enabled, if external then redirect func MustEnableWiki(ctx *context.Context) { + if !setting.Repository.EnableWiki { + ctx.NotFound("MustEnableWiki", nil) + return + } if !ctx.Repo.CanRead(models.UnitTypeWiki) && !ctx.Repo.CanRead(models.UnitTypeExternalWiki) { if log.IsTrace() { diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 6169aa563c099..d6202ade7a0d1 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -253,6 +253,10 @@ func RegisterRoutes(m *macaron.Macaron) { } m.Use(user.GetNotificationCount) + m.Use(func(ctx *context.Context) { + ctx.Data["GlobalEnableWiki"] = setting.Repository.EnableWiki + ctx.Data["GlobalEnableIssues"] = setting.Repository.EnableIssues + }) // FIXME: not all routes need go through same middlewares. // Especially some AJAX requests, we can reduce middleware number to improve performance. @@ -677,7 +681,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:username/:reponame", func() { m.Group("/issues", func() { m.Combo("/new").Get(context.RepoRef(), repo.NewIssue). - Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost) + Post(bindIgnErr(auth.CreateIssueForm{}), repo.NewIssuePost, repo.MustEnableIssues) }, context.RepoMustNotBeArchived(), reqRepoIssueReader) // 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. @@ -707,7 +711,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) - }, context.RepoMustNotBeArchived()) + }, context.RepoMustNotBeArchived(), repo.MustEnableIssues) m.Group("/comments/:id", func() { m.Post("", repo.UpdateCommentContent) m.Post("/delete", repo.DeleteComment) diff --git a/routers/user/home.go b/routers/user/home.go index 9ccd5bdb2681c..181f85ba328c9 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -308,7 +308,7 @@ func Issues(ctx *context.Context) { ctx.ServerError("GetUserRepoPermission", fmt.Errorf("[%d]%v", repoID, err)) return } - if !perm.CanRead(models.UnitTypeIssues) { + if !setting.Repository.EnableIssues || !perm.CanRead(models.UnitTypeIssues) { if log.IsTrace() { log.Trace("Permission Denied: User %-v cannot read %-v of repo %-v\n"+ "User in repo has Permissions: %-+v", diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 30316e7e5b074..8dd0dd46b3d15 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -10,7 +10,9 @@ {{if .IsSigned}} {{.i18n.Tr "dashboard"}} - {{.i18n.Tr "issues"}} + {{if .GlobalEnableIssues}} + {{.i18n.Tr "issues"}} + {{end}} {{.i18n.Tr "pull_requests"}} {{.i18n.Tr "explore"}} {{else if .IsLandingPageHome}} diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index f264a9b55956c..af4ee976525d4 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -55,13 +55,13 @@ {{end}} - {{if .Permission.CanRead $.UnitTypeIssues}} + {{if and .GlobalEnableIssues (.Permission.CanRead $.UnitTypeIssues)}} {{.i18n.Tr "repo.issues"}} {{.Repository.NumOpenIssues}} {{end}} - {{if .Permission.CanRead $.UnitTypeExternalTracker}} + {{if and .GlobalEnableIssues (.Permission.CanRead $.UnitTypeExternalTracker)}} {{.i18n.Tr "repo.issues"}} @@ -79,7 +79,7 @@ {{end}} - {{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}} + {{if and .GlobalEnableWiki (or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki))}} {{.i18n.Tr "repo.wiki"}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index c6d715acbee88..2f864cb9933fc 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -109,164 +109,185 @@ {{.i18n.Tr "repo.settings.advanced_settings"}}
-
- {{.CsrfTokenHtml}} - - - {{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} + {{if and (not .GlobalEnableWiki) (not .GlobalEnableIssues) (not .Repository.CanEnablePulls)}}
- -
- - -
-
-
-
-
- - -
-
-
-
- - -
-
-
- - -

{{.i18n.Tr "repo.settings.external_wiki_url_desc"}}

-
+
- -
- - {{$isIssuesEnabled := or (.Repository.UnitEnabled $.UnitTypeIssues) (.Repository.UnitEnabled $.UnitTypeExternalTracker)}}
- -
- - -
+
-
-
-
- - + {{else}} + + {{.CsrfTokenHtml}} + + + {{if .GlobalEnableWiki}} + {{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} +
+ +
+ + +
-
-
- {{if .Repository.CanEnableTimetracker}} +
-
- - -
-
-
-
- - +
+ +
- {{end}}
-
- - +
+ +
-
-
-
- - +
+ + +

{{.i18n.Tr "repo.settings.external_wiki_url_desc"}}

+
-
-
-
- - -

{{.i18n.Tr "repo.settings.external_tracker_url_desc"}}

+ {{else}} +
+
-
- - -

{{.i18n.Tr "repo.settings.tracker_url_format_desc" | Str2html}}

+ {{end}} + +
+ + {{if .GlobalEnableIssues}} + {{$isIssuesEnabled := or (.Repository.UnitEnabled $.UnitTypeIssues) (.Repository.UnitEnabled $.UnitTypeExternalTracker)}} +
+ +
+ + +
-
- +
- {{$externalTracker := (.Repository.MustGetUnit $.UnitTypeExternalTracker)}} - {{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} - - + +
+
+ {{if .Repository.CanEnableTimetracker}} +
+
+ + +
+
+
+
+ + +
+
+ {{end}} +
+
+ + +
+
+
- - + + +
+
+
+
+ + +

{{.i18n.Tr "repo.settings.external_tracker_url_desc"}}

+
+
+ + +

{{.i18n.Tr "repo.settings.tracker_url_format_desc" | Str2html}}

+
+
+ +
+
+ {{$externalTracker := (.Repository.MustGetUnit $.UnitTypeExternalTracker)}} + {{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} + + +
+
+
+
+ + +
+
-
-
- - {{if .Repository.CanEnablePulls}} -
- {{$pullRequestEnabled := .Repository.UnitEnabled $.UnitTypePullRequests}} - {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} -
- -
- - + {{else}} +
+
-
-
-
+ {{end}} + + {{if .Repository.CanEnablePulls}} +
+ {{$pullRequestEnabled := .Repository.UnitEnabled $.UnitTypePullRequests}} + {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} +
+
- - + +
-
-
- - +
+
+
+ + +
-
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
-
-
-
- - +
+
+ + +
+
+
+
+ + +
-
- {{end}} + {{end}} -
-
- -
- +
+
+ +
+ + {{end}}
{{if .IsAdmin}}