mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-22 12:54:53 -05:00
Added option to disable web hooks
This mod introduces DISABLE_WEB_HOOKS parameter in [security] section of app.ini (by default set to false). If set to true it disables web hooks feature. Any existing undelivered web hook tasks will be cancelled. Any existing web hook definitions will be left untouched in db but its delivery tasks will be ignored. Author-Change-Id: IB#1105130
This commit is contained in:
parent
b222dbc1d1
commit
07df6614dc
12 changed files with 62 additions and 19 deletions
|
@ -541,6 +541,8 @@ IMPORT_LOCAL_PATHS = false
|
||||||
; It also enables them to access other resources available to the user on the operating system that is running the Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
|
; It also enables them to access other resources available to the user on the operating system that is running the Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
|
||||||
; WARNING: This maybe harmful to you website or your operating system.
|
; WARNING: This maybe harmful to you website or your operating system.
|
||||||
DISABLE_GIT_HOOKS = true
|
DISABLE_GIT_HOOKS = true
|
||||||
|
; Set to false to disable web hooks feature.
|
||||||
|
DISABLE_WEB_HOOKS = false
|
||||||
; Set to false to allow pushes to gitea repositories despite having an incomplete environment - NOT RECOMMENDED
|
; Set to false to allow pushes to gitea repositories despite having an incomplete environment - NOT RECOMMENDED
|
||||||
ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = true
|
ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET = true
|
||||||
;Comma separated list of character classes required to pass minimum complexity.
|
;Comma separated list of character classes required to pass minimum complexity.
|
||||||
|
|
|
@ -400,6 +400,7 @@ relation to port exhaustion.
|
||||||
It also enables them to access other resources available to the user on the operating system that is running the
|
It also enables them to access other resources available to the user on the operating system that is running the
|
||||||
Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
|
Gitea instance and perform arbitrary actions in the name of the Gitea OS user.
|
||||||
This maybe harmful to you website or your operating system.
|
This maybe harmful to you website or your operating system.
|
||||||
|
- `DISABLE_WEB_HOOKS`: **false**: Set to `true` to disable web hooks feature.
|
||||||
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to gitea repositories you should set the environment appropriately.
|
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to gitea repositories you should set the environment appropriately.
|
||||||
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
|
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
|
||||||
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
|
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
|
||||||
|
|
|
@ -152,6 +152,7 @@ var (
|
||||||
MinPasswordLength int
|
MinPasswordLength int
|
||||||
ImportLocalPaths bool
|
ImportLocalPaths bool
|
||||||
DisableGitHooks bool
|
DisableGitHooks bool
|
||||||
|
DisableWebHooks bool
|
||||||
OnlyAllowPushIfGiteaEnvironmentSet bool
|
OnlyAllowPushIfGiteaEnvironmentSet bool
|
||||||
PasswordComplexity []string
|
PasswordComplexity []string
|
||||||
PasswordHashAlgo string
|
PasswordHashAlgo string
|
||||||
|
@ -770,6 +771,7 @@ func NewContext() {
|
||||||
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
|
MinPasswordLength = sec.Key("MIN_PASSWORD_LENGTH").MustInt(6)
|
||||||
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
|
ImportLocalPaths = sec.Key("IMPORT_LOCAL_PATHS").MustBool(false)
|
||||||
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
|
DisableGitHooks = sec.Key("DISABLE_GIT_HOOKS").MustBool(true)
|
||||||
|
DisableWebHooks = sec.Key("DISABLE_WEB_HOOKS").MustBool(false)
|
||||||
OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
|
OnlyAllowPushIfGiteaEnvironmentSet = sec.Key("ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET").MustBool(true)
|
||||||
PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("argon2")
|
PasswordHashAlgo = sec.Key("PASSWORD_HASH_ALGO").MustString("argon2")
|
||||||
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
|
CSRFCookieHTTPOnly = sec.Key("CSRF_COOKIE_HTTP_ONLY").MustBool(true)
|
||||||
|
|
|
@ -225,6 +225,9 @@ func NewFuncMap() []template.FuncMap {
|
||||||
"DisableGitHooks": func() bool {
|
"DisableGitHooks": func() bool {
|
||||||
return setting.DisableGitHooks
|
return setting.DisableGitHooks
|
||||||
},
|
},
|
||||||
|
"DisableWebHooks": func() bool {
|
||||||
|
return setting.DisableWebHooks
|
||||||
|
},
|
||||||
"DisableImportLocal": func() bool {
|
"DisableImportLocal": func() bool {
|
||||||
return !setting.ImportLocalPaths
|
return !setting.ImportLocalPaths
|
||||||
},
|
},
|
||||||
|
|
|
@ -141,6 +141,10 @@ func Deliver(t *models.HookTask) error {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
if setting.DisableWebHooks {
|
||||||
|
return fmt.Errorf("Sending webhook skipped (web hooks disabled): [%d]", t.ID)
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := webhookHTTPClient.Do(req)
|
resp, err := webhookHTTPClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err)
|
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err)
|
||||||
|
|
|
@ -68,6 +68,11 @@ func checkBranch(w *models.Webhook, branch string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
func prepareWebhook(w *models.Webhook, repo *models.Repository, event models.HookEventType, p api.Payloader) error {
|
||||||
|
// Skip sending if web hooks are disabled.
|
||||||
|
if setting.DisableWebHooks {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, e := range w.EventCheckers() {
|
for _, e := range w.EventCheckers() {
|
||||||
if event == e.Type {
|
if event == e.Type {
|
||||||
if !e.Has() {
|
if !e.Has() {
|
||||||
|
|
|
@ -374,6 +374,16 @@ func reqGitHook() macaron.Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reqWebHooksEnabled requires web hooks to be enabled by admin.
|
||||||
|
func reqWebHooksEnabled() macaron.Handler {
|
||||||
|
return func(ctx *context.APIContext) {
|
||||||
|
if setting.DisableWebHooks {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "web hooks disabled by administrator")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func orgAssignment(args ...bool) macaron.Handler {
|
func orgAssignment(args ...bool) macaron.Handler {
|
||||||
var (
|
var (
|
||||||
assignOrg bool
|
assignOrg bool
|
||||||
|
@ -646,6 +656,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Combo("/notifications").
|
m.Combo("/notifications").
|
||||||
Get(reqToken(), notify.ListRepoNotifications).
|
Get(reqToken(), notify.ListRepoNotifications).
|
||||||
Put(reqToken(), notify.ReadRepoNotifications)
|
Put(reqToken(), notify.ReadRepoNotifications)
|
||||||
|
m.Group("/hooks/git", func() {
|
||||||
|
m.Combo("").Get(repo.ListGitHooks)
|
||||||
|
m.Group("/:id", func() {
|
||||||
|
m.Combo("").Get(repo.GetGitHook).
|
||||||
|
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook).
|
||||||
|
Delete(repo.DeleteGitHook)
|
||||||
|
})
|
||||||
|
}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true))
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Combo("").Get(repo.ListHooks).
|
m.Combo("").Get(repo.ListHooks).
|
||||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||||
|
@ -655,15 +673,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Delete(repo.DeleteHook)
|
Delete(repo.DeleteHook)
|
||||||
m.Post("/tests", context.RepoRef(), repo.TestHook)
|
m.Post("/tests", context.RepoRef(), repo.TestHook)
|
||||||
})
|
})
|
||||||
m.Group("/git", func() {
|
}, reqToken(), reqAdmin(), reqWebHooksEnabled())
|
||||||
m.Combo("").Get(repo.ListGitHooks)
|
|
||||||
m.Group("/:id", func() {
|
|
||||||
m.Combo("").Get(repo.GetGitHook).
|
|
||||||
Patch(bind(api.EditGitHookOption{}), repo.EditGitHook).
|
|
||||||
Delete(repo.DeleteGitHook)
|
|
||||||
})
|
|
||||||
}, reqGitHook(), context.ReferencesGitRepo(true))
|
|
||||||
}, reqToken(), reqAdmin())
|
|
||||||
m.Group("/collaborators", func() {
|
m.Group("/collaborators", func() {
|
||||||
m.Get("", reqAnyRepoReader(), repo.ListCollaborators)
|
m.Get("", reqAnyRepoReader(), repo.ListCollaborators)
|
||||||
m.Combo("/:collaborator").Get(reqAnyRepoReader(), repo.IsCollaborator).
|
m.Combo("/:collaborator").Get(reqAnyRepoReader(), repo.IsCollaborator).
|
||||||
|
@ -914,7 +924,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Combo("/:id").Get(org.GetHook).
|
m.Combo("/:id").Get(org.GetHook).
|
||||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||||
Delete(org.DeleteHook)
|
Delete(org.DeleteHook)
|
||||||
}, reqToken(), reqOrgOwnership())
|
}, reqToken(), reqOrgOwnership(), reqWebHooksEnabled())
|
||||||
}, orgAssignment(true))
|
}, orgAssignment(true))
|
||||||
m.Group("/teams/:teamid", func() {
|
m.Group("/teams/:teamid", func() {
|
||||||
m.Combo("").Get(org.GetTeam).
|
m.Combo("").Get(org.GetTeam).
|
||||||
|
|
|
@ -331,6 +331,14 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// webHooksEnabled requires web hooks to be enabled by admin.
|
||||||
|
webHooksEnabled := func(ctx *context.Context) {
|
||||||
|
if setting.DisableWebHooks {
|
||||||
|
ctx.Error(403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m.Use(user.GetNotificationCount)
|
m.Use(user.GetNotificationCount)
|
||||||
m.Use(func(ctx *context.Context) {
|
m.Use(func(ctx *context.Context) {
|
||||||
ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
|
ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
|
||||||
|
@ -550,7 +558,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
})
|
}, webHooksEnabled)
|
||||||
|
|
||||||
m.Group("/auths", func() {
|
m.Group("/auths", func() {
|
||||||
m.Get("", admin.Authentications)
|
m.Get("", admin.Authentications)
|
||||||
|
@ -655,7 +663,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
})
|
}, webHooksEnabled)
|
||||||
|
|
||||||
m.Group("/labels", func() {
|
m.Group("/labels", func() {
|
||||||
m.Get("", org.RetrieveLabels, org.Labels)
|
m.Get("", org.RetrieveLabels, org.Labels)
|
||||||
|
@ -708,6 +716,12 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
Post(bindIgnErr(auth.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
|
Post(bindIgnErr(auth.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost)
|
||||||
}, repo.MustBeNotEmpty)
|
}, repo.MustBeNotEmpty)
|
||||||
|
|
||||||
|
m.Group("/hooks/git", func() {
|
||||||
|
m.Get("", repo.GitHooks)
|
||||||
|
m.Combo("/:name").Get(repo.GitHooksEdit).
|
||||||
|
Post(repo.GitHooksEditPost)
|
||||||
|
}, context.GitHookService())
|
||||||
|
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Get("", repo.Webhooks)
|
m.Get("", repo.Webhooks)
|
||||||
m.Post("/delete", repo.DeleteWebhook)
|
m.Post("/delete", repo.DeleteWebhook)
|
||||||
|
@ -732,13 +746,7 @@ func RegisterRoutes(m *macaron.Macaron) {
|
||||||
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
m.Post("/matrix/:id", bindIgnErr(auth.NewMatrixHookForm{}), repo.MatrixHooksEditPost)
|
||||||
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
m.Post("/msteams/:id", bindIgnErr(auth.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost)
|
||||||
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
m.Post("/feishu/:id", bindIgnErr(auth.NewFeishuHookForm{}), repo.FeishuHooksEditPost)
|
||||||
|
}, webHooksEnabled)
|
||||||
m.Group("/git", func() {
|
|
||||||
m.Get("", repo.GitHooks)
|
|
||||||
m.Combo("/:name").Get(repo.GitHooksEdit).
|
|
||||||
Post(repo.GitHooksEditPost)
|
|
||||||
}, context.GitHookService())
|
|
||||||
})
|
|
||||||
|
|
||||||
m.Group("/keys", func() {
|
m.Group("/keys", func() {
|
||||||
m.Combo("").Get(repo.DeployKeys).
|
m.Combo("").Get(repo.DeployKeys).
|
||||||
|
|
|
@ -11,12 +11,14 @@
|
||||||
<a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos">
|
<a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos">
|
||||||
{{.i18n.Tr "admin.repositories"}}
|
{{.i18n.Tr "admin.repositories"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{if not DisableWebHooks}}
|
||||||
<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
|
<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
|
||||||
{{.i18n.Tr "admin.hooks"}}
|
{{.i18n.Tr "admin.hooks"}}
|
||||||
</a>
|
</a>
|
||||||
<a class="{{if .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/system-hooks">
|
<a class="{{if .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/system-hooks">
|
||||||
{{.i18n.Tr "admin.systemhooks"}}
|
{{.i18n.Tr "admin.systemhooks"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{end}}
|
||||||
<a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths">
|
<a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths">
|
||||||
{{.i18n.Tr "admin.authentication"}}
|
{{.i18n.Tr "admin.authentication"}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -4,9 +4,11 @@
|
||||||
<a class="{{if .PageIsSettingsOptions}}active{{end}} item" href="{{.OrgLink}}/settings">
|
<a class="{{if .PageIsSettingsOptions}}active{{end}} item" href="{{.OrgLink}}/settings">
|
||||||
{{.i18n.Tr "org.settings.options"}}
|
{{.i18n.Tr "org.settings.options"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{if not DisableWebHooks}}
|
||||||
<a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.OrgLink}}/settings/hooks">
|
<a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.OrgLink}}/settings/hooks">
|
||||||
{{.i18n.Tr "repo.settings.hooks"}}
|
{{.i18n.Tr "repo.settings.hooks"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{end}}
|
||||||
<a class="{{if .PageIsOrgSettingsLabels}}active{{end}} item" href="{{.OrgLink}}/settings/labels">
|
<a class="{{if .PageIsOrgSettingsLabels}}active{{end}} item" href="{{.OrgLink}}/settings/labels">
|
||||||
{{.i18n.Tr "repo.labels"}}
|
{{.i18n.Tr "repo.labels"}}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
<li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{.RepoLink}}/settings">{{.i18n.Tr "repo.settings.options"}}</a></li>
|
<li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{.RepoLink}}/settings">{{.i18n.Tr "repo.settings.options"}}</a></li>
|
||||||
<li {{if .PageIsSettingsCollaboration}}class="current"{{end}}><a href="{{.RepoLink}}/settings/collaboration">{{.i18n.Tr "repo.settings.collaboration"}}</a></li>
|
<li {{if .PageIsSettingsCollaboration}}class="current"{{end}}><a href="{{.RepoLink}}/settings/collaboration">{{.i18n.Tr "repo.settings.collaboration"}}</a></li>
|
||||||
<li {{if .PageIsSettingsBranches}}class="current"{{end}}><a href="{{.RepoLink}}/settings/branches">{{.i18n.Tr "repo.settings.branches"}}</a></li>
|
<li {{if .PageIsSettingsBranches}}class="current"{{end}}><a href="{{.RepoLink}}/settings/branches">{{.i18n.Tr "repo.settings.branches"}}</a></li>
|
||||||
|
{{if not DisableWebHooks}}
|
||||||
<li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{.RepoLink}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li>
|
<li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{.RepoLink}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li>
|
||||||
|
{{end}}
|
||||||
{{if or .SignedUser.AllowGitHook .SignedUser.IsAdmin}}
|
{{if or .SignedUser.AllowGitHook .SignedUser.IsAdmin}}
|
||||||
<li {{if .PageIsSettingsGitHooks}}class="current"{{end}}><a href="{{.RepoLink}}/settings/hooks/git">{{.i18n.Tr "repo.settings.githooks"}}</a></li>
|
<li {{if .PageIsSettingsGitHooks}}class="current"{{end}}><a href="{{.RepoLink}}/settings/hooks/git">{{.i18n.Tr "repo.settings.githooks"}}</a></li>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
{{.i18n.Tr "repo.settings.branches"}}
|
{{.i18n.Tr "repo.settings.branches"}}
|
||||||
</a>
|
</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{if not DisableWebHooks}}
|
||||||
<a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks">
|
<a class="{{if .PageIsSettingsHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks">
|
||||||
{{.i18n.Tr "repo.settings.hooks"}}
|
{{.i18n.Tr "repo.settings.hooks"}}
|
||||||
</a>
|
</a>
|
||||||
|
{{end}}
|
||||||
{{if .SignedUser.CanEditGitHook}}
|
{{if .SignedUser.CanEditGitHook}}
|
||||||
<a class="{{if .PageIsSettingsGitHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks/git">
|
<a class="{{if .PageIsSettingsGitHooks}}active{{end}} item" href="{{.RepoLink}}/settings/hooks/git">
|
||||||
{{.i18n.Tr "repo.settings.githooks"}}
|
{{.i18n.Tr "repo.settings.githooks"}}
|
||||||
|
|
Loading…
Reference in a new issue