From 47246da8d3f50a02d11b77b3d402618b144aa720 Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Sat, 14 Jan 2023 10:07:01 +0100
Subject: [PATCH] [BRANDING] [v1.20] define the forgejo webhook type

templates/swagger/v1_json.tmpl updated with `make generate-swagger`

(cherry picked from commit 88899c492efeedd138ba088a36b9c0bc733ead7b)
(cherry picked from commit 7171bd9617c32c4911e3bdbc23c02a19e80d2465)
(cherry picked from commit 1a742446c17aef9ca62fe75bfc0a388d40138154)
(cherry picked from commit d7c189d7b2f9fea299a31adf068db969920ae39d)

Conflicts:
	routers/web/web.go
(cherry picked from commit cbdea868e41fb38ca491f8b449c3e525ec82d6b9)
(cherry picked from commit 6cd150483b06e17aee023c0afd01a3f2460b3415)
---
 modules/setting/webhook.go                    |  2 +-
 modules/structs/hook.go                       |  2 +-
 modules/webhook/type.go                       |  1 +
 routers/web/repo/webhook.go                   | 28 +++++++++++++
 routers/web/web.go                            |  2 +
 services/webhook/webhook.go                   |  4 +-
 templates/admin/hook_new.tmpl                 |  7 +++-
 templates/org/settings/hook_new.tmpl          |  6 ++-
 .../repo/settings/webhook/base_list.tmpl      |  5 ++-
 templates/repo/settings/webhook/forgejo.tmpl  | 40 +++++++++++++++++++
 templates/repo/settings/webhook/new.tmpl      |  7 +++-
 templates/swagger/v1_json.tmpl                |  1 +
 templates/user/settings/hook_new.tmpl         |  4 +-
 tests/integration/links_test.go               | 30 ++++++++++++++
 14 files changed, 127 insertions(+), 12 deletions(-)
 create mode 100644 templates/repo/settings/webhook/forgejo.tmpl

diff --git a/modules/setting/webhook.go b/modules/setting/webhook.go
index c01261dbbd..b56c55c439 100644
--- a/modules/setting/webhook.go
+++ b/modules/setting/webhook.go
@@ -35,7 +35,7 @@ func loadWebhookFrom(rootCfg ConfigProvider) {
 	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
 	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
 	Webhook.AllowedHostList = sec.Key("ALLOWED_HOST_LIST").MustString("")
-	Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
+	Webhook.Types = []string{"forgejo", "gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix", "wechatwork", "packagist"}
 	Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10)
 	Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("")
 	if Webhook.ProxyURL != "" {
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index df5da6790f..5055561a4b 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -40,7 +40,7 @@ type CreateHookOptionConfig map[string]string
 // CreateHookOption options when create a hook
 type CreateHookOption struct {
 	// required: true
-	// enum: dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork,packagist
+	// enum: forgejo,dingtalk,discord,gitea,gogs,msteams,slack,telegram,feishu,wechatwork,packagist
 	Type string `json:"type" binding:"Required"`
 	// required: true
 	Config              CreateHookOptionConfig `json:"config" binding:"Required"`
diff --git a/modules/webhook/type.go b/modules/webhook/type.go
index db4ab17931..fac5590e21 100644
--- a/modules/webhook/type.go
+++ b/modules/webhook/type.go
@@ -71,6 +71,7 @@ type HookType = string
 
 // Types of webhooks
 const (
+	FORGEJO    HookType = "forgejo"
 	GITEA      HookType = "gitea"
 	GOGS       HookType = "gogs"
 	SLACK      HookType = "slack"
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index f30588967e..aedbe27596 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -307,6 +307,34 @@ func editWebhook(ctx *context.Context, params webhookParams) {
 	ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
 }
 
+// ForgejoHooksNewPost response for creating Forgejo webhook
+func ForgejoHooksNewPost(ctx *context.Context) {
+	createWebhook(ctx, forgejoHookParams(ctx))
+}
+
+// ForgejoHooksEditPost response for editing Forgejo webhook
+func ForgejoHooksEditPost(ctx *context.Context) {
+	editWebhook(ctx, forgejoHookParams(ctx))
+}
+
+func forgejoHookParams(ctx *context.Context) webhookParams {
+	form := web.GetForm(ctx).(*forms.NewWebhookForm)
+
+	contentType := webhook.ContentTypeJSON
+	if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
+		contentType = webhook.ContentTypeForm
+	}
+
+	return webhookParams{
+		Type:        webhook_module.FORGEJO,
+		URL:         form.PayloadURL,
+		ContentType: contentType,
+		Secret:      form.Secret,
+		HTTPMethod:  form.HTTPMethod,
+		WebhookForm: form.WebhookForm,
+	}
+}
+
 // GiteaHooksNewPost response for creating Gitea webhook
 func GiteaHooksNewPost(ctx *context.Context) {
 	createWebhook(ctx, giteaHookParams(ctx))
diff --git a/routers/web/web.go b/routers/web/web.go
index 00ccff85f3..78a687055d 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -318,6 +318,7 @@ func RegisterRoutes(m *web.Route) {
 
 	addWebhookAddRoutes := func() {
 		m.Get("/{type}/new", repo.WebhooksNew)
+		m.Post("/forgejo/new", web.Bind(forms.NewWebhookForm{}), repo.ForgejoHooksNewPost)
 		m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost)
 		m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost)
 		m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost)
@@ -332,6 +333,7 @@ func RegisterRoutes(m *web.Route) {
 	}
 
 	addWebhookEditRoutes := func() {
+		m.Post("/forgejo/{id}", web.Bind(forms.NewWebhookForm{}), repo.ForgejoHooksEditPost)
 		m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
 		m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
 		m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go
index b862d5bff1..bdbacd7512 100644
--- a/services/webhook/webhook.go
+++ b/services/webhook/webhook.go
@@ -69,7 +69,7 @@ var webhooks = map[webhook_module.HookType]*webhook{
 
 // IsValidHookTaskType returns true if a webhook registered
 func IsValidHookTaskType(name string) bool {
-	if name == webhook_module.GITEA || name == webhook_module.GOGS {
+	if name == webhook_module.FORGEJO || name == webhook_module.GITEA || name == webhook_module.GOGS {
 		return true
 	}
 	_, ok := webhooks[name]
@@ -172,7 +172,7 @@ func PrepareWebhook(ctx context.Context, w *webhook_model.Webhook, event webhook
 	// Avoid sending "0 new commits" to non-integration relevant webhooks (e.g. slack, discord, etc.).
 	// Integration webhooks (e.g. drone) still receive the required data.
 	if pushEvent, ok := p.(*api.PushPayload); ok &&
-		w.Type != webhook_module.GITEA && w.Type != webhook_module.GOGS &&
+		w.Type != webhook_module.FORGEJO && w.Type != webhook_module.GITEA && w.Type != webhook_module.GOGS &&
 		len(pushEvent.Commits) == 0 {
 		return nil
 	}
diff --git a/templates/admin/hook_new.tmpl b/templates/admin/hook_new.tmpl
index 0c018ff293..f15a1c20f4 100644
--- a/templates/admin/hook_new.tmpl
+++ b/templates/admin/hook_new.tmpl
@@ -14,8 +14,10 @@
 				{{.locale.Tr "admin.defaulthooks.update_webhook"}}
 			{{end}}
 			<div class="ui right">
-				{{if eq .HookType "gitea"}}
-					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea.svg">
+				{{if eq .HookType "forgejo"}}
+					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/forgejo.svg">
+				{{else if eq .HookType "gitea"}}
+					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea-original.svg">
 				{{else if eq .HookType "gogs"}}
 					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gogs.ico">
 				{{else if eq .HookType "slack"}}
@@ -40,6 +42,7 @@
 			</div>
 		</h4>
 		<div class="ui attached segment">
+			{{template "repo/settings/webhook/forgejo" .}}
 			{{template "repo/settings/webhook/gitea" .}}
 			{{template "repo/settings/webhook/gogs" .}}
 			{{template "repo/settings/webhook/slack" .}}
diff --git a/templates/org/settings/hook_new.tmpl b/templates/org/settings/hook_new.tmpl
index 4685225f4c..35c26c4465 100644
--- a/templates/org/settings/hook_new.tmpl
+++ b/templates/org/settings/hook_new.tmpl
@@ -9,8 +9,10 @@
 				<h4 class="ui top attached header">
 					{{if .PageIsSettingsHooksNew}}{{.locale.Tr "repo.settings.add_webhook"}}{{else}}{{.locale.Tr "repo.settings.update_webhook"}}{{end}}
 					<div class="ui right">
-						{{if eq .HookType "gitea"}}
-							<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea.svg">
+						{{if eq .HookType "forgejo"}}
+							<img width="26" height="26" src="{{AssetUrlPrefix}}/img/forgejo.svg">
+						{{else if eq .HookType "gitea"}}
+							<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea-original.svg">
 						{{else if eq .HookType "gogs"}}
 							<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gogs.ico">
 						{{else if eq .HookType "slack"}}
diff --git a/templates/repo/settings/webhook/base_list.tmpl b/templates/repo/settings/webhook/base_list.tmpl
index 61c5799513..6c69e3a2a0 100644
--- a/templates/repo/settings/webhook/base_list.tmpl
+++ b/templates/repo/settings/webhook/base_list.tmpl
@@ -4,8 +4,11 @@
 		<div class="ui floating1 jump dropdown">
 			<div class="ui primary tiny button">{{.locale.Tr "repo.settings.add_webhook"}}</div>
 			<div class="menu">
+				<a class="item" href="{{.BaseLinkNew}}/forgejo/new">
+					<img width="20" height="20" src="{{AssetUrlPrefix}}/img/forgejo.svg">{{.locale.Tr "repo.settings.web_hook_name_forgejo"}}
+				</a>
 				<a class="item" href="{{.BaseLinkNew}}/gitea/new">
-					<img width="20" height="20" src="{{AssetUrlPrefix}}/img/gitea.svg">{{.locale.Tr "repo.settings.web_hook_name_gitea"}}
+					<img width="20" height="20" src="{{AssetUrlPrefix}}/img/gitea-original.svg">{{.locale.Tr "repo.settings.web_hook_name_gitea"}}
 				</a>
 				<a class="item" href="{{.BaseLinkNew}}/gogs/new">
 					<img width="20" height="20" src="{{AssetUrlPrefix}}/img/gogs.ico">{{.locale.Tr "repo.settings.web_hook_name_gogs"}}
diff --git a/templates/repo/settings/webhook/forgejo.tmpl b/templates/repo/settings/webhook/forgejo.tmpl
new file mode 100644
index 0000000000..0d733f3362
--- /dev/null
+++ b/templates/repo/settings/webhook/forgejo.tmpl
@@ -0,0 +1,40 @@
+{{if eq .HookType "forgejo"}}
+	<p>{{.locale.Tr "repo.settings.add_web_hook_desc" "https://docs.gitea.io/en-us/webhooks/" (.locale.Tr "repo.settings.web_hook_name_forgejo") | Str2html}}</p>
+	<form class="ui form" action="{{.BaseLink}}/forgejo/{{or .Webhook.ID "new"}}" method="post">
+		{{template "base/disable_form_autofill"}}
+		{{.CsrfTokenHtml}}
+		<div class="required field {{if .Err_PayloadURL}}error{{end}}">
+			<label for="payload_url">{{.locale.Tr "repo.settings.payload_url"}}</label>
+			<input id="payload_url" name="payload_url" type="url" value="{{.Webhook.URL}}" autofocus required>
+		</div>
+		<div class="field">
+			<label>{{.locale.Tr "repo.settings.http_method"}}</label>
+			<div class="ui selection dropdown">
+				<input type="hidden" id="http_method" name="http_method" value="{{if .Webhook.HTTPMethod}}{{.Webhook.HTTPMethod}}{{else}}POST{{end}}">
+				<div class="default text"></div>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				<div class="menu">
+					<div class="item" data-value="POST">POST</div>
+					<div class="item" data-value="GET">GET</div>
+				</div>
+			</div>
+		</div>
+		<div class="field">
+			<label>{{.locale.Tr "repo.settings.content_type"}}</label>
+			<div class="ui selection dropdown">
+				<input type="hidden" id="content_type" name="content_type" value="{{if .Webhook.ContentType}}{{.Webhook.ContentType}}{{else}}1{{end}}">
+				<div class="default text"></div>
+				{{svg "octicon-triangle-down" 14 "dropdown icon"}}
+				<div class="menu">
+					<div class="item" data-value="1">application/json</div>
+					<div class="item" data-value="2">application/x-www-form-urlencoded</div>
+				</div>
+			</div>
+		</div>
+		<div class="field {{if .Err_Secret}}error{{end}}">
+			<label for="secret">{{.locale.Tr "repo.settings.secret"}}</label>
+			<input id="secret" name="secret" type="password" value="{{.Webhook.Secret}}" autocomplete="off">
+		</div>
+		{{template "repo/settings/webhook/settings" .}}
+	</form>
+{{end}}
diff --git a/templates/repo/settings/webhook/new.tmpl b/templates/repo/settings/webhook/new.tmpl
index 5102390db9..c0ee082e25 100644
--- a/templates/repo/settings/webhook/new.tmpl
+++ b/templates/repo/settings/webhook/new.tmpl
@@ -7,8 +7,10 @@
 		<h4 class="ui top attached header">
 			{{if .PageIsSettingsHooksNew}}{{.locale.Tr "repo.settings.add_webhook"}}{{else}}{{.locale.Tr "repo.settings.update_webhook"}}{{end}}
 			<div class="ui right">
-				{{if eq .HookType "gitea"}}
-					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea.svg">
+				{{if eq .HookType "forgejo"}}
+					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/forgejo.svg">
+				{{else if eq .HookType "gitea"}}
+					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea-original.svg">
 				{{else if eq .HookType "gogs"}}
 					<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gogs.ico">
 				{{else if eq .HookType "slack"}}
@@ -33,6 +35,7 @@
 			</div>
 		</h4>
 		<div class="ui attached segment">
+			{{template "repo/settings/webhook/forgejo" .}}
 			{{template "repo/settings/webhook/gitea" .}}
 			{{template "repo/settings/webhook/gogs" .}}
 			{{template "repo/settings/webhook/slack" .}}
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 440cd66399..ee694d5678 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -15550,6 +15550,7 @@
         "type": {
           "type": "string",
           "enum": [
+            "forgejo",
             "dingtalk",
             "discord",
             "gitea",
diff --git a/templates/user/settings/hook_new.tmpl b/templates/user/settings/hook_new.tmpl
index 20aaf65f62..95a9ff65b5 100644
--- a/templates/user/settings/hook_new.tmpl
+++ b/templates/user/settings/hook_new.tmpl
@@ -7,7 +7,9 @@
 			<h4 class="ui top attached header">
 				{{if .PageIsSettingsHooksNew}}{{.locale.Tr "repo.settings.add_webhook"}}{{else}}{{.locale.Tr "repo.settings.update_webhook"}}{{end}}
 				<div class="ui right">
-					{{if eq .HookType "gitea"}}
+					{{if eq .HookType "forgejo"}}
+						<img width="26" height="26" src="{{AssetUrlPrefix}}/img/forgejo.svg">
+					{{else if eq .HookType "gitea"}}
 						<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gitea.svg">
 					{{else if eq .HookType "gogs"}}
 						<img width="26" height="26" src="{{AssetUrlPrefix}}/img/gogs.ico">
diff --git a/tests/integration/links_test.go b/tests/integration/links_test.go
index 9136f8f915..74d9fe20cb 100644
--- a/tests/integration/links_test.go
+++ b/tests/integration/links_test.go
@@ -173,3 +173,33 @@ func TestLinksLogin(t *testing.T) {
 
 	testLinksAsUser("user2", t)
 }
+
+func TestRedirectsWebhooks(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	//
+	// A redirect means the route exists but not if it performs as intended.
+	//
+	for _, kind := range []string{"forgejo", "gitea"} {
+		redirects := []struct {
+			from string
+			to   string
+			verb string
+		}{
+			{from: "/user2/repo1/settings/hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
+			{from: "/user/settings/hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
+			{from: "/admin/system-hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
+			{from: "/admin/default-hooks/" + kind + "/new", to: "/user/login", verb: "GET"},
+			{from: "/user2/repo1/settings/hooks/" + kind + "/new", to: "/", verb: "POST"},
+			{from: "/admin/system-hooks/" + kind + "/new", to: "/", verb: "POST"},
+			{from: "/admin/default-hooks/" + kind + "/new", to: "/", verb: "POST"},
+			{from: "/user2/repo1/settings/hooks/" + kind + "/1", to: "/", verb: "POST"},
+			{from: "/admin/hooks/" + kind + "/1", to: "/", verb: "POST"},
+		}
+		for _, info := range redirects {
+			req := NewRequest(t, info.verb, info.from)
+			resp := MakeRequest(t, req, http.StatusSeeOther)
+			assert.EqualValues(t, path.Join(setting.AppSubURL, info.to), test.RedirectURL(resp), info.from)
+		}
+	}
+}