From fa73375e139d159caf697897b88068a2c66a48eb Mon Sep 17 00:00:00 2001
From: Gergely Nagy <forgejo@gergo.csillger.hu>
Date: Tue, 23 Jan 2024 10:46:34 +0100
Subject: [PATCH] Split out repository unit settings

This splits out the repository unit settings (formerly "Advanced
settings" under the repository settings page) into their own, separate
page.

The primary reason for this is that the settings page became long and
complicated, with a structure that not always made sense. A secondary
reason is that toggling units on and off should not necessarily be an
"advanced" setting. We want to make doing that easier, and having the
units on their own page helps with that.

This is basically a refactor, there is no new functionality introduced,
just an extra pair of routes for the new page, and the supporting code.

Signed-off-by: Gergely Nagy <forgejo@gergo.csillger.hu>
---
 options/locale/locale_en-US.ini             |   3 +
 routers/web/repo/setting/setting.go         | 378 ++++++++++----------
 routers/web/web.go                          |   2 +
 services/forms/repo_form.go                 |  28 +-
 templates/repo/settings/navbar.tmpl         |  17 +
 templates/repo/settings/options.tmpl        | 319 -----------------
 templates/repo/settings/units.tmpl          |  13 +
 templates/repo/settings/units/issues.tmpl   | 102 ++++++
 templates/repo/settings/units/overview.tmpl |  62 ++++
 templates/repo/settings/units/pulls.tmpl    | 121 +++++++
 templates/repo/settings/units/wiki.tmpl     |  51 +++
 11 files changed, 586 insertions(+), 510 deletions(-)
 create mode 100644 templates/repo/settings/units.tmpl
 create mode 100644 templates/repo/settings/units/issues.tmpl
 create mode 100644 templates/repo/settings/units/overview.tmpl
 create mode 100644 templates/repo/settings/units/pulls.tmpl
 create mode 100644 templates/repo/settings/units/wiki.tmpl

diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 95ce92e882..be2a06b38f 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -2066,6 +2066,9 @@ settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL
 settings.mirror_settings.push_mirror.add = Add Push Mirror
 settings.mirror_settings.push_mirror.edit_sync_time = Edit mirror sync interval
 
+settings.units.units = Repository Units
+settings.units.overview = Overview
+
 settings.sync_mirror = Synchronize Now
 settings.pull_mirror_sync_in_progress = Pulling changes from the remote %s at the moment.
 settings.push_mirror_sync_in_progress = Pushing changes to the remote %s at the moment.
diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go
index 552507e57c..8a429c359c 100644
--- a/routers/web/repo/setting/setting.go
+++ b/routers/web/repo/setting/setting.go
@@ -41,6 +41,7 @@ import (
 
 const (
 	tplSettingsOptions base.TplName = "repo/settings/options"
+	tplSettingsUnits   base.TplName = "repo/settings/units"
 	tplCollaboration   base.TplName = "repo/settings/collaboration"
 	tplBranches        base.TplName = "repo/settings/branches"
 	tplGithooks        base.TplName = "repo/settings/githooks"
@@ -89,6 +90,201 @@ func SettingsCtxData(ctx *context.Context) {
 	ctx.Data["PushMirrors"] = pushMirrors
 }
 
+// Units show a repositorys unit settings page
+func Units(ctx *context.Context) {
+	ctx.Data["Title"] = ctx.Tr("repo.settings.units.units")
+	ctx.Data["PageIsRepoSettingsUnits"] = true
+
+	ctx.HTML(http.StatusOK, tplSettingsUnits)
+}
+
+func UnitsPost(ctx *context.Context) {
+	form := web.GetForm(ctx).(*forms.RepoUnitSettingForm)
+
+	repo := ctx.Repo.Repository
+
+	var repoChanged bool
+	var units []repo_model.RepoUnit
+	var deleteUnitTypes []unit_model.Type
+
+	// 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
+
+	if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch {
+		repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch
+		repoChanged = true
+	}
+
+	if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeCode,
+		})
+	} else if !unit_model.TypeCode.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
+	}
+
+	if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
+		if !validation.IsValidExternalURL(form.ExternalWikiURL) {
+			ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error"))
+			ctx.Redirect(repo.Link() + "/settings/units")
+			return
+		}
+
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeExternalWiki,
+			Config: &repo_model.ExternalWikiConfig{
+				ExternalWikiURL: form.ExternalWikiURL,
+			},
+		})
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
+	} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
+		var wikiPermissions repo_model.UnitAccessMode
+		if form.GloballyWriteableWiki {
+			wikiPermissions = repo_model.UnitAccessModeWrite
+		} else {
+			wikiPermissions = repo_model.UnitAccessModeRead
+		}
+		units = append(units, repo_model.RepoUnit{
+			RepoID:             repo.ID,
+			Type:               unit_model.TypeWiki,
+			Config:             new(repo_model.UnitConfig),
+			DefaultPermissions: wikiPermissions,
+		})
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
+	} else {
+		if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
+			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
+		}
+		if !unit_model.TypeWiki.UnitGlobalDisabled() {
+			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
+		}
+	}
+
+	if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
+		if !validation.IsValidExternalURL(form.ExternalTrackerURL) {
+			ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error"))
+			ctx.Redirect(repo.Link() + "/settings/units")
+			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/units")
+			return
+		}
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeExternalTracker,
+			Config: &repo_model.ExternalTrackerConfig{
+				ExternalTrackerURL:           form.ExternalTrackerURL,
+				ExternalTrackerFormat:        form.TrackerURLFormat,
+				ExternalTrackerStyle:         form.TrackerIssueStyle,
+				ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
+			},
+		})
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
+	} else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeIssues,
+			Config: &repo_model.IssuesConfig{
+				EnableTimetracker:                form.EnableTimetracker,
+				AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
+				EnableDependencies:               form.EnableIssueDependencies,
+			},
+		})
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
+	} else {
+		if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
+			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
+		}
+		if !unit_model.TypeIssues.UnitGlobalDisabled() {
+			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
+		}
+	}
+
+	if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeProjects,
+		})
+	} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
+	}
+
+	if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeReleases,
+		})
+	} else if !unit_model.TypeReleases.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
+	}
+
+	if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypePackages,
+		})
+	} else if !unit_model.TypePackages.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
+	}
+
+	if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypeActions,
+		})
+	} else if !unit_model.TypeActions.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
+	}
+
+	if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
+		units = append(units, repo_model.RepoUnit{
+			RepoID: repo.ID,
+			Type:   unit_model.TypePullRequests,
+			Config: &repo_model.PullRequestsConfig{
+				IgnoreWhitespaceConflicts:     form.PullsIgnoreWhitespace,
+				AllowMerge:                    form.PullsAllowMerge,
+				AllowRebase:                   form.PullsAllowRebase,
+				AllowRebaseMerge:              form.PullsAllowRebaseMerge,
+				AllowSquash:                   form.PullsAllowSquash,
+				AllowManualMerge:              form.PullsAllowManualMerge,
+				AutodetectManualMerge:         form.EnableAutodetectManualMerge,
+				AllowRebaseUpdate:             form.PullsAllowRebaseUpdate,
+				DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
+				DefaultMergeStyle:             repo_model.MergeStyle(form.PullsDefaultMergeStyle),
+				DefaultAllowMaintainerEdit:    form.DefaultAllowMaintainerEdit,
+			},
+		})
+	} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
+		deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
+	}
+
+	if len(units) == 0 {
+		ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
+		ctx.Redirect(ctx.Repo.RepoLink + "/settings/units")
+		return
+	}
+
+	if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
+		ctx.ServerError("UpdateRepositoryUnits", err)
+		return
+	}
+	if repoChanged {
+		if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
+			ctx.ServerError("UpdateRepository", err)
+			return
+		}
+	}
+	log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
+
+	ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
+	ctx.Redirect(ctx.Repo.RepoLink + "/settings/units")
+}
+
 // Settings show a repository's settings page
 func Settings(ctx *context.Context) {
 	ctx.HTML(http.StatusOK, tplSettingsOptions)
@@ -435,188 +631,6 @@ func SettingsPost(ctx *context.Context) {
 		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
 		ctx.Redirect(repo.Link() + "/settings")
 
-	case "advanced":
-		var repoChanged bool
-		var units []repo_model.RepoUnit
-		var deleteUnitTypes []unit_model.Type
-
-		// 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
-
-		if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch {
-			repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch
-			repoChanged = true
-		}
-
-		if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeCode,
-			})
-		} else if !unit_model.TypeCode.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode)
-		}
-
-		if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.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, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeExternalWiki,
-				Config: &repo_model.ExternalWikiConfig{
-					ExternalWikiURL: form.ExternalWikiURL,
-				},
-			})
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
-		} else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() {
-			var wikiPermissions repo_model.UnitAccessMode
-			if form.GloballyWriteableWiki {
-				wikiPermissions = repo_model.UnitAccessModeWrite
-			} else {
-				wikiPermissions = repo_model.UnitAccessModeRead
-			}
-			units = append(units, repo_model.RepoUnit{
-				RepoID:             repo.ID,
-				Type:               unit_model.TypeWiki,
-				Config:             new(repo_model.UnitConfig),
-				DefaultPermissions: wikiPermissions,
-			})
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
-		} else {
-			if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
-				deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
-			}
-			if !unit_model.TypeWiki.UnitGlobalDisabled() {
-				deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
-			}
-		}
-
-		if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.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, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeExternalTracker,
-				Config: &repo_model.ExternalTrackerConfig{
-					ExternalTrackerURL:           form.ExternalTrackerURL,
-					ExternalTrackerFormat:        form.TrackerURLFormat,
-					ExternalTrackerStyle:         form.TrackerIssueStyle,
-					ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern,
-				},
-			})
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
-		} else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeIssues,
-				Config: &repo_model.IssuesConfig{
-					EnableTimetracker:                form.EnableTimetracker,
-					AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime,
-					EnableDependencies:               form.EnableIssueDependencies,
-				},
-			})
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
-		} else {
-			if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
-				deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
-			}
-			if !unit_model.TypeIssues.UnitGlobalDisabled() {
-				deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
-			}
-		}
-
-		if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeProjects,
-			})
-		} else if !unit_model.TypeProjects.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
-		}
-
-		if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeReleases,
-			})
-		} else if !unit_model.TypeReleases.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases)
-		}
-
-		if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypePackages,
-			})
-		} else if !unit_model.TypePackages.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages)
-		}
-
-		if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypeActions,
-			})
-		} else if !unit_model.TypeActions.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions)
-		}
-
-		if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() {
-			units = append(units, repo_model.RepoUnit{
-				RepoID: repo.ID,
-				Type:   unit_model.TypePullRequests,
-				Config: &repo_model.PullRequestsConfig{
-					IgnoreWhitespaceConflicts:     form.PullsIgnoreWhitespace,
-					AllowMerge:                    form.PullsAllowMerge,
-					AllowRebase:                   form.PullsAllowRebase,
-					AllowRebaseMerge:              form.PullsAllowRebaseMerge,
-					AllowSquash:                   form.PullsAllowSquash,
-					AllowManualMerge:              form.PullsAllowManualMerge,
-					AutodetectManualMerge:         form.EnableAutodetectManualMerge,
-					AllowRebaseUpdate:             form.PullsAllowRebaseUpdate,
-					DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge,
-					DefaultMergeStyle:             repo_model.MergeStyle(form.PullsDefaultMergeStyle),
-					DefaultAllowMaintainerEdit:    form.DefaultAllowMaintainerEdit,
-				},
-			})
-		} else if !unit_model.TypePullRequests.UnitGlobalDisabled() {
-			deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
-		}
-
-		if len(units) == 0 {
-			ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit"))
-			ctx.Redirect(ctx.Repo.RepoLink + "/settings")
-			return
-		}
-
-		if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil {
-			ctx.ServerError("UpdateRepositoryUnits", err)
-			return
-		}
-		if repoChanged {
-			if err := repo_service.UpdateRepository(ctx, repo, false); err != nil {
-				ctx.ServerError("UpdateRepository", err)
-				return
-			}
-		}
-		log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name)
-
-		ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success"))
-		ctx.Redirect(ctx.Repo.RepoLink + "/settings")
-
 	case "signing":
 		changed := false
 		trustModel := repo_model.ToTrustModel(form.TrustModel)
diff --git a/routers/web/web.go b/routers/web/web.go
index 1744ddb83a..688fe48160 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -1037,6 +1037,8 @@ func registerRoutes(m *web.Route) {
 				m.Combo("").Get(repo_setting.Settings).
 					Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost)
 			}, repo_setting.SettingsCtxData)
+			m.Combo("/units").Get(repo_setting.Units).
+				Post(web.Bind(forms.RepoUnitSettingForm{}), repo_setting.UnitsPost)
 			m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar)
 			m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar)
 
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index 7cc07532ef..9527916ae0 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -130,6 +130,24 @@ type RepoSettingForm struct {
 	EnablePrune            bool
 
 	// Advanced settings
+	IsArchived bool
+
+	// Signing Settings
+	TrustModel string
+
+	// Admin settings
+	EnableHealthCheck  bool
+	RequestReindexType string
+}
+
+// Validate validates the fields
+func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
+	ctx := context.GetValidateContext(req)
+	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
+}
+
+// RepoUnitSettingForm form for changing repository unit settings
+type RepoUnitSettingForm struct {
 	EnableCode                            bool
 	EnableWiki                            bool
 	GloballyWriteableWiki                 bool
@@ -161,18 +179,10 @@ type RepoSettingForm struct {
 	EnableTimetracker                     bool
 	AllowOnlyContributorsToTrackTime      bool
 	EnableIssueDependencies               bool
-	IsArchived                            bool
-
-	// Signing Settings
-	TrustModel string
-
-	// Admin settings
-	EnableHealthCheck  bool
-	RequestReindexType string
 }
 
 // Validate validates the fields
-func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
+func (f *RepoUnitSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 	ctx := context.GetValidateContext(req)
 	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 }
diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl
index 3bef0fa4c1..62f81a901e 100644
--- a/templates/repo/settings/navbar.tmpl
+++ b/templates/repo/settings/navbar.tmpl
@@ -4,6 +4,23 @@
 		<a class="{{if .PageIsSettingsOptions}}active {{end}}item" href="{{.RepoLink}}/settings">
 			{{ctx.Locale.Tr "repo.settings.options"}}
 		</a>
+		<details class="item toggleable-item" {{if .PageIsRepoSettingsUnits}}open{{end}}>
+			<summary class="item{{if .PageIsRepoSettingsUnits}} active{{end}}">{{ctx.Locale.Tr "repo.settings.units.units"}}</summary>
+			<div class="menu">
+				<a class="item" href="{{.RepoLink}}/settings/units#overview">
+					{{ctx.Locale.Tr "repo.settings.units.overview"}}
+				</a>
+				<a class="item" href="{{.RepoLink}}/settings/units#issues">
+					{{ctx.Locale.Tr "repo.issues"}}
+				</a>
+				<a class="item" href="{{.RepoLink}}/settings/units#pulls">
+					{{ctx.Locale.Tr "repo.pulls"}}
+				</a>
+				<a class="item" href="{{.RepoLink}}/settings/units#wiki">
+					{{ctx.Locale.Tr "repo.wiki"}}
+				</a>
+			</div>
+		</details>
 		<a class="{{if .PageIsSettingsCollaboration}}active {{end}}item" href="{{.RepoLink}}/settings/collaboration">
 			{{ctx.Locale.Tr "repo.settings.collaboration"}}
 		</a>
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 191ac53967..6cfef31060 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -299,325 +299,6 @@
 			</div>
 		{{end}}
 
-		<h4 class="ui top attached header">
-			{{ctx.Locale.Tr "repo.settings.advanced_settings"}}
-		</h4>
-		<div class="ui attached segment">
-			<form class="ui form" method="post">
-				{{.CsrfTokenHtml}}
-				<input type="hidden" name="action" value="advanced">
-
-				{{$isCodeEnabled := .Repository.UnitEnabled $.Context $.UnitTypeCode}}
-				{{$isCodeGlobalDisabled := .UnitTypeCode.UnitGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.code"}}</label>
-					<div class="ui checkbox{{if $isCodeGlobalDisabled}} disabled{{end}}"{{if $isCodeGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
-					</div>
-				</div>
-
-				{{$isWikiEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeWiki) (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}
-				{{$isWikiGlobalDisabled := .UnitTypeWiki.UnitGlobalDisabled}}
-				{{$isExternalWikiGlobalDisabled := .UnitTypeExternalWiki.UnitGlobalDisabled}}
-				{{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.wiki"}}</label>
-					<div class="ui checkbox{{if $isBothWikiGlobalDisabled}} disabled{{end}}"{{if $isBothWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if $isWikiEnabled}}checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.settings.wiki_desc"}}</label>
-					</div>
-				</div>
-				<div class="field{{if not $isWikiEnabled}} disabled{{end}}" id="wiki_box">
-					<div class="field">
-						<div class="ui radio checkbox{{if $isWikiGlobalDisabled}} disabled{{end}}"{{if $isWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.use_internal_wiki"}}</label>
-						</div>
-					</div>
-					{{if (not .Repository.IsPrivate)}}
-					<div class="field {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}">
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="globally_writeable_wiki" type="checkbox" {{if .Permission.IsGloballyWriteable $.UnitTypeWiki}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.wiki_globally_editable"}}</label>
-							</div>
-						</div>
-					</div>
-					{{end}}
-					<div class="field">
-						<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.use_external_wiki"}}</label>
-						</div>
-					</div>
-					<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
-						<label for="external_wiki_url">{{ctx.Locale.Tr "repo.settings.external_wiki_url"}}</label>
-						<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
-						<p class="help">{{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
-					</div>
-				</div>
-
-				<div class="divider"></div>
-
-				{{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeIssues) (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}
-				{{$isIssuesGlobalDisabled := .UnitTypeIssues.UnitGlobalDisabled}}
-				{{$isExternalTrackerGlobalDisabled := .UnitTypeExternalTracker.UnitGlobalDisabled}}
-				{{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.issues"}}</label>
-					<div class="ui checkbox{{if $isIssuesAndExternalGlobalDisabled}} disabled{{end}}"{{if $isIssuesAndExternalGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if $isIssuesEnabled}}checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.settings.issues_desc"}}</label>
-					</div>
-				</div>
-				<div class="field {{if not $isIssuesEnabled}}disabled{{end}}" id="issue_box">
-					<div class="field">
-						<div class="ui radio checkbox{{if $isIssuesGlobalDisabled}} disabled{{end}}"{{if $isIssuesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.use_internal_issue_tracker"}}</label>
-						</div>
-					</div>
-					<div class="field gt-pl-4 {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
-						{{if .Repository.CanEnableTimetracker}}
-							<div class="field">
-								<div class="ui checkbox">
-									<input name="enable_timetracker" class="enable-system" data-target="#only_contributors" type="checkbox" {{if .Repository.IsTimetrackerEnabled $.Context}}checked{{end}}>
-									<label>{{ctx.Locale.Tr "repo.settings.enable_timetracker"}}</label>
-								</div>
-							</div>
-							<div class="field {{if not (.Repository.IsTimetrackerEnabled $.Context)}}disabled{{end}}" id="only_contributors">
-								<div class="ui checkbox">
-									<input name="allow_only_contributors_to_track_time" type="checkbox" {{if .Repository.AllowOnlyContributorsToTrackTime $.Context}}checked{{end}}>
-									<label>{{ctx.Locale.Tr "repo.settings.allow_only_contributors_to_track_time"}}</label>
-								</div>
-							</div>
-						{{end}}
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="enable_issue_dependencies" type="checkbox" {{if (.Repository.IsDependenciesEnabled $.Context)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.issues.dependency.setting"}}</label>
-							</div>
-						</div>
-						<div class="ui checkbox">
-							<input name="enable_close_issues_via_commit_in_any_branch" type="checkbox" {{if .Repository.CloseIssuesViaCommitInAnyBranch}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.admin_enable_close_issues_via_commit_in_any_branch"}}</label>
-						</div>
-					</div>
-					<div class="field">
-						<div class="ui radio checkbox{{if $isExternalTrackerGlobalDisabled}} disabled{{end}}"{{if $isExternalTrackerGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalTracker}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.use_external_issue_tracker"}}</label>
-						</div>
-					</div>
-					<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
-						<div class="field">
-							<label for="external_tracker_url">{{ctx.Locale.Tr "repo.settings.external_tracker_url"}}</label>
-							<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}">
-							<p class="help">{{ctx.Locale.Tr "repo.settings.external_tracker_url_desc"}}</p>
-						</div>
-						<div class="field">
-							<label for="tracker_url_format">{{ctx.Locale.Tr "repo.settings.tracker_url_format"}}</label>
-							<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerFormat}}" placeholder="https://github.com/{user}/{repo}/issues/{index}">
-							<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_url_format_desc" | Str2html}}</p>
-						</div>
-						<div class="inline fields">
-							<label for="issue_style">{{ctx.Locale.Tr "repo.settings.tracker_issue_style"}}</label>
-							<div class="field">
-								<div class="ui radio checkbox">
-								{{$externalTracker := (.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker)}}
-								{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}}
-									<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="numeric" {{if eq $externalTrackerStyle "numeric"}}checked{{end}}>
-									<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">#1234</span></label>
-								</div>
-							</div>
-							<div class="field">
-								<div class="ui radio checkbox">
-									<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="alphanumeric" {{if eq $externalTrackerStyle "alphanumeric"}}checked{{end}}>
-									<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">ABC-123 , DEFG-234</span></label>
-								</div>
-							</div>
-							<div class="field">
-								<div class="ui radio checkbox">
-									<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="regexp" {{if eq $externalTrackerStyle "regexp"}}checked{{end}}>
-									<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp"}} <span class="ui light grey text">(ISSUE-\d+) , ISSUE-(\d+)</span></label>
-								</div>
-							</div>
-						</div>
-						<div class="field {{if ne $externalTrackerStyle "regexp"}}disabled{{end}}" id="tracker-issue-style-regex-box">
-							<label for="external_tracker_regexp_pattern">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern"}}</label>
-							<input id="external_tracker_regexp_pattern" name="external_tracker_regexp_pattern" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerRegexpPattern}}">
-							<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | Str2html}}</p>
-						</div>
-					</div>
-				</div>
-
-				<div class="divider"></div>
-
-				{{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}}
-				{{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
-					<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_projects" type="checkbox" {{if $isProjectsEnabled}}checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
-					</div>
-				</div>
-
-				{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}}
-				{{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.releases"}}</label>
-					<div class="ui checkbox{{if $isReleasesGlobalDisabled}} disabled{{end}}"{{if $isReleasesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_releases" type="checkbox" {{if $isReleasesEnabled}}checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.settings.releases_desc"}}</label>
-					</div>
-				</div>
-
-				{{$isPackagesEnabled := .Repository.UnitEnabled $.Context $.UnitTypePackages}}
-				{{$isPackagesGlobalDisabled := .UnitTypePackages.UnitGlobalDisabled}}
-				<div class="inline field">
-					<label>{{ctx.Locale.Tr "repo.packages"}}</label>
-					<div class="ui checkbox{{if $isPackagesGlobalDisabled}} disabled{{end}}"{{if $isPackagesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-						<input class="enable-system" name="enable_packages" type="checkbox" {{if $isPackagesEnabled}}checked{{end}}>
-						<label>{{ctx.Locale.Tr "repo.settings.packages_desc"}}</label>
-					</div>
-				</div>
-
-				{{if .EnableActions}}
-					{{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}}
-					{{$isActionsGlobalDisabled := .UnitTypeActions.UnitGlobalDisabled}}
-					<div class="inline field">
-						<label>{{ctx.Locale.Tr "actions.actions"}}</label>
-							<div class="ui checkbox{{if $isActionsGlobalDisabled}} disabled{{end}}"{{if $isActionsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system" name="enable_actions" type="checkbox" {{if $isActionsEnabled}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.actions_desc"}}</label>
-						</div>
-					</div>
-				{{end}}
-
-				{{if not .IsMirror}}
-					<div class="divider"></div>
-					{{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}}
-					{{$pullRequestGlobalDisabled := .UnitTypePullRequests.UnitGlobalDisabled}}
-					{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
-					<div class="inline field">
-						<label>{{ctx.Locale.Tr "repo.pulls"}}</label>
-						<div class="ui checkbox{{if $pullRequestGlobalDisabled}} disabled{{end}}"{{if $pullRequestGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
-							<input class="enable-system" name="enable_pulls" type="checkbox" data-target="#pull_box" {{if $pullRequestEnabled}}checked{{end}}>
-							<label>{{ctx.Locale.Tr "repo.settings.pulls_desc"}}</label>
-						</div>
-					</div>
-					<div class="field{{if not $pullRequestEnabled}} disabled{{end}}" id="pull_box">
-						<div class="field">
-							<p>
-								{{ctx.Locale.Tr "repo.settings.merge_style_desc"}}
-							</p>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowMerge)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_rebase" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebase)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_rebase_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseMerge)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_squash" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowSquash)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowManualMerge)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.pulls.merge_manually"}}</label>
-							</div>
-						</div>
-
-						<div class="field">
-							<p>
-								{{ctx.Locale.Tr "repo.settings.default_merge_style_desc"}}
-							</p>
-							<div class="ui dropdown selection">
-								<select name="pulls_default_merge_style">
-									<option value="merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</option>
-									<option value="rebase" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</option>
-									<option value="rebase-merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</option>
-									<option value="squash" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</option>
-								</select>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
-								<div class="default text">
-									{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}
-										{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}
-									{{end}}
-									{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}
-										{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}
-									{{end}}
-									{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}
-										{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}
-									{{end}}
-									{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}
-										{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}
-									{{end}}
-								</div>
-								<div class="menu">
-									<div class="item" data-value="merge">{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</div>
-									<div class="item" data-value="rebase">{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</div>
-									<div class="item" data-value="rebase-merge">{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div>
-									<div class="item" data-value="squash">{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</div>
-								</div>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="default_allow_maintainer_edit" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultAllowMaintainerEdit)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.pulls.default_allow_edits_from_maintainers"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_allow_rebase_update" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseUpdate)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.pulls.allow_rebase_update"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="default_delete_branch_after_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.pulls.default_delete_branch_after_merge"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="enable_autodetect_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AutodetectManualMerge)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.pulls.enable_autodetect_manual_merge"}}</label>
-							</div>
-						</div>
-						<div class="field">
-							<div class="ui checkbox">
-								<input name="pulls_ignore_whitespace" type="checkbox" {{if and $pullRequestEnabled ($prUnit.PullRequestsConfig.IgnoreWhitespaceConflicts)}}checked{{end}}>
-								<label>{{ctx.Locale.Tr "repo.settings.pulls.ignore_whitespace"}}</label>
-							</div>
-						</div>
-					</div>
-				{{end}}
-
-				<div class="divider"></div>
-				<div class="field">
-					<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
-				</div>
-			</form>
-		</div>
-
 		<h4 class="ui top attached header">
 			{{ctx.Locale.Tr "repo.settings.signing_settings"}}
 		</h4>
diff --git a/templates/repo/settings/units.tmpl b/templates/repo/settings/units.tmpl
new file mode 100644
index 0000000000..66ed035964
--- /dev/null
+++ b/templates/repo/settings/units.tmpl
@@ -0,0 +1,13 @@
+{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings options")}}
+<div class="user-main-content twelve wide column">
+	<form class="ui form" method="post" action="{{.RepoLink}}/settings/units">
+		{{.CsrfTokenHtml}}
+		{{template "repo/settings/units/overview" .}}
+		{{template "repo/settings/units/issues" .}}
+		{{if not .IsMirror}}
+			{{template "repo/settings/units/pulls" .}}
+		{{end}}
+		{{template "repo/settings/units/wiki" .}}
+	</form>
+</div>
+{{template "repo/settings/layout_footer" .}}
diff --git a/templates/repo/settings/units/issues.tmpl b/templates/repo/settings/units/issues.tmpl
new file mode 100644
index 0000000000..a09b8edb2e
--- /dev/null
+++ b/templates/repo/settings/units/issues.tmpl
@@ -0,0 +1,102 @@
+<h4 class="ui top attached header" id="issues">
+	{{ctx.Locale.Tr "repo.issues"}}
+</h4>
+<div class="ui attached segment">
+	{{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeIssues) (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}
+	{{$isIssuesGlobalDisabled := .UnitTypeIssues.UnitGlobalDisabled}}
+	{{$isExternalTrackerGlobalDisabled := .UnitTypeExternalTracker.UnitGlobalDisabled}}
+	{{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.issues"}}</label>
+		<div class="ui checkbox{{if $isIssuesAndExternalGlobalDisabled}} disabled{{end}}"{{if $isIssuesAndExternalGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_issues" type="checkbox" data-target="#issue_box" {{if $isIssuesEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.issues_desc"}}</label>
+		</div>
+	</div>
+	<div class="field {{if not $isIssuesEnabled}}disabled{{end}}" id="issue_box">
+		<div class="field">
+			<div class="ui radio checkbox{{if $isIssuesGlobalDisabled}} disabled{{end}}"{{if $isIssuesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+				<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="false" data-context="#internal_issue_box" data-target="#external_issue_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.use_internal_issue_tracker"}}</label>
+			</div>
+		</div>
+		<div class="field gt-pl-4 {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="internal_issue_box">
+			{{if .Repository.CanEnableTimetracker}}
+				<div class="field">
+					<div class="ui checkbox">
+						<input name="enable_timetracker" class="enable-system" data-target="#only_contributors" type="checkbox" {{if .Repository.IsTimetrackerEnabled $.Context}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.enable_timetracker"}}</label>
+					</div>
+				</div>
+				<div class="field {{if not (.Repository.IsTimetrackerEnabled $.Context)}}disabled{{end}}" id="only_contributors">
+					<div class="ui checkbox">
+						<input name="allow_only_contributors_to_track_time" type="checkbox" {{if .Repository.AllowOnlyContributorsToTrackTime $.Context}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.allow_only_contributors_to_track_time"}}</label>
+					</div>
+				</div>
+			{{end}}
+			<div class="field">
+				<div class="ui checkbox">
+					<input name="enable_issue_dependencies" type="checkbox" {{if (.Repository.IsDependenciesEnabled $.Context)}}checked{{end}}>
+					<label>{{ctx.Locale.Tr "repo.issues.dependency.setting"}}</label>
+				</div>
+			</div>
+			<div class="ui checkbox">
+				<input name="enable_close_issues_via_commit_in_any_branch" type="checkbox" {{if .Repository.CloseIssuesViaCommitInAnyBranch}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.admin_enable_close_issues_via_commit_in_any_branch"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui radio checkbox{{if $isExternalTrackerGlobalDisabled}} disabled{{end}}"{{if $isExternalTrackerGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+				<input class="enable-system-radio" name="enable_external_tracker" type="radio" value="true" data-context="#internal_issue_box" data-target="#external_issue_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalTracker}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.use_external_issue_tracker"}}</label>
+			</div>
+		</div>
+		<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}}disabled{{end}}" id="external_issue_box">
+			<div class="field">
+				<label for="external_tracker_url">{{ctx.Locale.Tr "repo.settings.external_tracker_url"}}</label>
+				<input id="external_tracker_url" name="external_tracker_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerURL}}">
+				<p class="help">{{ctx.Locale.Tr "repo.settings.external_tracker_url_desc"}}</p>
+			</div>
+			<div class="field">
+				<label for="tracker_url_format">{{ctx.Locale.Tr "repo.settings.tracker_url_format"}}</label>
+				<input id="tracker_url_format" name="tracker_url_format" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerFormat}}" placeholder="https://github.com/{user}/{repo}/issues/{index}">
+				<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_url_format_desc" | Str2html}}</p>
+			</div>
+			<div class="inline fields">
+				<label for="issue_style">{{ctx.Locale.Tr "repo.settings.tracker_issue_style"}}</label>
+				<div class="field">
+					<div class="ui radio checkbox">
+						{{$externalTracker := (.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker)}}
+						{{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}}
+						<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="numeric" {{if eq $externalTrackerStyle "numeric"}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.numeric"}} <span class="ui light grey text">#1234</span></label>
+					</div>
+				</div>
+				<div class="field">
+					<div class="ui radio checkbox">
+						<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="alphanumeric" {{if eq $externalTrackerStyle "alphanumeric"}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.alphanumeric"}} <span class="ui light grey text">ABC-123 , DEFG-234</span></label>
+					</div>
+				</div>
+				<div class="field">
+					<div class="ui radio checkbox">
+						<input class="js-tracker-issue-style" name="tracker_issue_style" type="radio" value="regexp" {{if eq $externalTrackerStyle "regexp"}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp"}} <span class="ui light grey text">(ISSUE-\d+) , ISSUE-(\d+)</span></label>
+					</div>
+				</div>
+			</div>
+			<div class="field {{if ne $externalTrackerStyle "regexp"}}disabled{{end}}" id="tracker-issue-style-regex-box">
+				<label for="external_tracker_regexp_pattern">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern"}}</label>
+				<input id="external_tracker_regexp_pattern" name="external_tracker_regexp_pattern" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker).ExternalTrackerConfig.ExternalTrackerRegexpPattern}}">
+				<p class="help">{{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | Str2html}}</p>
+			</div>
+		</div>
+	</div>
+
+	<div class="divider"></div>
+
+	<div class="field">
+		<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
+	</div>
+</div>
diff --git a/templates/repo/settings/units/overview.tmpl b/templates/repo/settings/units/overview.tmpl
new file mode 100644
index 0000000000..816b45ce1d
--- /dev/null
+++ b/templates/repo/settings/units/overview.tmpl
@@ -0,0 +1,62 @@
+<h4 class="ui top attached header" id="overview">
+	{{ctx.Locale.Tr "repo.settings.units.overview"}}
+</h4>
+<div class="ui attached segment">
+	{{$isCodeEnabled := .Repository.UnitEnabled $.Context $.UnitTypeCode}}
+	{{$isCodeGlobalDisabled := .UnitTypeCode.UnitGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.code"}}</label>
+		<div class="ui checkbox{{if $isCodeGlobalDisabled}} disabled{{end}}"{{if $isCodeGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_code" type="checkbox"{{if $isCodeEnabled}} checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.code.desc"}}</label>
+		</div>
+	</div>
+
+	{{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}}
+	{{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.project_board"}}</label>
+		<div class="ui checkbox{{if $isProjectsGlobalDisabled}} disabled{{end}}"{{if $isProjectsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_projects" type="checkbox" {{if $isProjectsEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.projects_desc"}}</label>
+		</div>
+	</div>
+
+	{{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}}
+	{{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.releases"}}</label>
+		<div class="ui checkbox{{if $isReleasesGlobalDisabled}} disabled{{end}}"{{if $isReleasesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_releases" type="checkbox" {{if $isReleasesEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.releases_desc"}}</label>
+		</div>
+	</div>
+
+	{{$isPackagesEnabled := .Repository.UnitEnabled $.Context $.UnitTypePackages}}
+	{{$isPackagesGlobalDisabled := .UnitTypePackages.UnitGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.packages"}}</label>
+		<div class="ui checkbox{{if $isPackagesGlobalDisabled}} disabled{{end}}"{{if $isPackagesGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_packages" type="checkbox" {{if $isPackagesEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.packages_desc"}}</label>
+		</div>
+	</div>
+
+	{{if .EnableActions}}
+		{{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}}
+		{{$isActionsGlobalDisabled := .UnitTypeActions.UnitGlobalDisabled}}
+		<div class="inline field">
+			<label>{{ctx.Locale.Tr "actions.actions"}}</label>
+			<div class="ui checkbox{{if $isActionsGlobalDisabled}} disabled{{end}}"{{if $isActionsGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+				<input class="enable-system" name="enable_actions" type="checkbox" {{if $isActionsEnabled}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.actions_desc"}}</label>
+			</div>
+		</div>
+	{{end}}
+
+	<div class="divider"></div>
+
+	<div class="field">
+		<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
+	</div>
+</div>
diff --git a/templates/repo/settings/units/pulls.tmpl b/templates/repo/settings/units/pulls.tmpl
new file mode 100644
index 0000000000..e735fe974c
--- /dev/null
+++ b/templates/repo/settings/units/pulls.tmpl
@@ -0,0 +1,121 @@
+<h4 class="ui top attached header" id="pulls">
+	{{ctx.Locale.Tr "repo.pulls"}}
+</h4>
+<div class="ui attached segment">
+	{{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}}
+	{{$pullRequestGlobalDisabled := .UnitTypePullRequests.UnitGlobalDisabled}}
+	{{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.pulls"}}</label>
+		<div class="ui checkbox{{if $pullRequestGlobalDisabled}} disabled{{end}}"{{if $pullRequestGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_pulls" type="checkbox" data-target="#pull_box" {{if $pullRequestEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.pulls_desc"}}</label>
+		</div>
+	</div>
+	<div class="field{{if not $pullRequestEnabled}} disabled{{end}}" id="pull_box">
+		<div class="field">
+			<p>
+				{{ctx.Locale.Tr "repo.settings.merge_style_desc"}}
+			</p>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowMerge)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_rebase" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebase)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_rebase_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseMerge)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_squash" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowSquash)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowManualMerge)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.pulls.merge_manually"}}</label>
+			</div>
+		</div>
+
+		<div class="field">
+			<p>
+				{{ctx.Locale.Tr "repo.settings.default_merge_style_desc"}}
+			</p>
+			<div class="ui dropdown selection">
+				<select name="pulls_default_merge_style">
+					<option value="merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</option>
+					<option value="rebase" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</option>
+					<option value="rebase-merge" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</option>
+					<option value="squash" {{if or (not $pullRequestEnabled) (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}selected{{end}}>{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</option>
+				</select>{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				<div class="default text">
+					{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}}
+						{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}
+					{{end}}
+					{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase")}}
+						{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}
+					{{end}}
+					{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "rebase-merge")}}
+						{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}
+					{{end}}
+					{{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}}
+						{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}
+					{{end}}
+				</div>
+				<div class="menu">
+					<div class="item" data-value="merge">{{ctx.Locale.Tr "repo.pulls.merge_pull_request"}}</div>
+					<div class="item" data-value="rebase">{{ctx.Locale.Tr "repo.pulls.rebase_merge_pull_request"}}</div>
+					<div class="item" data-value="rebase-merge">{{ctx.Locale.Tr "repo.pulls.rebase_merge_commit_pull_request"}}</div>
+					<div class="item" data-value="squash">{{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}}</div>
+				</div>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="default_allow_maintainer_edit" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultAllowMaintainerEdit)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.pulls.default_allow_edits_from_maintainers"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_allow_rebase_update" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AllowRebaseUpdate)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.pulls.allow_rebase_update"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="default_delete_branch_after_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.DefaultDeleteBranchAfterMerge)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.pulls.default_delete_branch_after_merge"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="enable_autodetect_manual_merge" type="checkbox" {{if or (not $pullRequestEnabled) ($prUnit.PullRequestsConfig.AutodetectManualMerge)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.pulls.enable_autodetect_manual_merge"}}</label>
+			</div>
+		</div>
+		<div class="field">
+			<div class="ui checkbox">
+				<input name="pulls_ignore_whitespace" type="checkbox" {{if and $pullRequestEnabled ($prUnit.PullRequestsConfig.IgnoreWhitespaceConflicts)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.pulls.ignore_whitespace"}}</label>
+			</div>
+		</div>
+	</div>
+
+	<div class="divider"></div>
+	<div class="field">
+		<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
+	</div>
+</div>
diff --git a/templates/repo/settings/units/wiki.tmpl b/templates/repo/settings/units/wiki.tmpl
new file mode 100644
index 0000000000..c3be39f1cc
--- /dev/null
+++ b/templates/repo/settings/units/wiki.tmpl
@@ -0,0 +1,51 @@
+<h4 class="ui top attached header" id="wiki">
+	{{ctx.Locale.Tr "repo.wiki"}}
+</h4>
+<div class="ui attached segment">
+	{{$isWikiEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeWiki) (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}
+	{{$isWikiGlobalDisabled := .UnitTypeWiki.UnitGlobalDisabled}}
+	{{$isExternalWikiGlobalDisabled := .UnitTypeExternalWiki.UnitGlobalDisabled}}
+	{{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}}
+	<div class="inline field">
+		<label>{{ctx.Locale.Tr "repo.wiki"}}</label>
+		<div class="ui checkbox{{if $isBothWikiGlobalDisabled}} disabled{{end}}"{{if $isBothWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+			<input class="enable-system" name="enable_wiki" type="checkbox" data-target="#wiki_box" {{if $isWikiEnabled}}checked{{end}}>
+			<label>{{ctx.Locale.Tr "repo.settings.wiki_desc"}}</label>
+		</div>
+	</div>
+	<div class="field{{if not $isWikiEnabled}} disabled{{end}}" id="wiki_box">
+		<div class="field">
+			<div class="ui radio checkbox{{if $isWikiGlobalDisabled}} disabled{{end}}"{{if $isWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+				<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="false" data-target="#external_wiki_box" {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.use_internal_wiki"}}</label>
+			</div>
+		</div>
+		{{if (not .Repository.IsPrivate)}}
+			<div class="field {{if (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}">
+				<div class="field">
+					<div class="ui checkbox">
+						<input name="globally_writeable_wiki" type="checkbox" {{if .Permission.IsGloballyWriteable $.UnitTypeWiki}}checked{{end}}>
+						<label>{{ctx.Locale.Tr "repo.settings.wiki_globally_editable"}}</label>
+					</div>
+				</div>
+			</div>
+		{{end}}
+		<div class="field">
+			<div class="ui radio checkbox{{if $isExternalWikiGlobalDisabled}} disabled{{end}}"{{if $isExternalWikiGlobalDisabled}} data-tooltip-content="{{ctx.Locale.Tr "repo.unit_disabled"}}"{{end}}>
+				<input class="enable-system-radio" name="enable_external_wiki" type="radio" value="true" data-target="#external_wiki_box" {{if .Repository.UnitEnabled $.Context $.UnitTypeExternalWiki}}checked{{end}}>
+				<label>{{ctx.Locale.Tr "repo.settings.use_external_wiki"}}</label>
+			</div>
+		</div>
+		<div class="field gt-pl-4 {{if not (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}}disabled{{end}}" id="external_wiki_box">
+			<label for="external_wiki_url">{{ctx.Locale.Tr "repo.settings.external_wiki_url"}}</label>
+			<input id="external_wiki_url" name="external_wiki_url" type="url" value="{{(.Repository.MustGetUnit $.Context $.UnitTypeExternalWiki).ExternalWikiConfig.ExternalWikiURL}}">
+			<p class="help">{{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}</p>
+		</div>
+	</div>
+
+	<div class="divider"></div>
+
+	<div class="field">
+		<button class="ui primary button">{{ctx.Locale.Tr "repo.settings.update_settings"}}</button>
+	</div>
+</div>