aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorwxiaoguang <wxiaoguang@gmail.com>2024-02-24 21:12:17 +0800
committerGitHub <noreply@github.com>2024-02-24 13:12:17 +0000
commit29a26d9d8c573c9fb7e79a66ac3d578e8b20dcae (patch)
tree2d51372c6d14b0f1c96c35d1a2cf4ff8f6fb050b /modules
parentc42083a33950be6ee9f822c6d0de3c3a79d1f51b (diff)
downloadgitea-29a26d9d8c573c9fb7e79a66ac3d578e8b20dcae.tar.gz
gitea-29a26d9d8c573c9fb7e79a66ac3d578e8b20dcae.zip
Customizable "Open with" applications for repository clone (#29320)
Users could customize the "clone" menu with their own application URLs on the admin panel. Replace #22378 Close #21121 Close #22149
Diffstat (limited to 'modules')
-rw-r--r--modules/context/context.go1
-rw-r--r--modules/setting/config.go49
-rw-r--r--modules/setting/config/value.go35
3 files changed, 71 insertions, 14 deletions
diff --git a/modules/context/context.go b/modules/context/context.go
index 66732eaa8a..4b318f7e33 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -192,6 +192,7 @@ func Contexter() func(next http.Handler) http.Handler {
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
+ ctx.Data["SystemConfig"] = setting.Config()
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
diff --git a/modules/setting/config.go b/modules/setting/config.go
index db189f44ac..03558574c2 100644
--- a/modules/setting/config.go
+++ b/modules/setting/config.go
@@ -15,8 +15,45 @@ type PictureStruct struct {
EnableFederatedAvatar *config.Value[bool]
}
+type OpenWithEditorApp struct {
+ DisplayName string
+ OpenURL string
+}
+
+type OpenWithEditorAppsType []OpenWithEditorApp
+
+func (t OpenWithEditorAppsType) ToTextareaString() string {
+ ret := ""
+ for _, app := range t {
+ ret += app.DisplayName + " = " + app.OpenURL + "\n"
+ }
+ return ret
+}
+
+func DefaultOpenWithEditorApps() OpenWithEditorAppsType {
+ return OpenWithEditorAppsType{
+ {
+ DisplayName: "VS Code",
+ OpenURL: "vscode://vscode.git/clone?url={url}",
+ },
+ {
+ DisplayName: "VSCodium",
+ OpenURL: "vscodium://vscode.git/clone?url={url}",
+ },
+ {
+ DisplayName: "Intellij IDEA",
+ OpenURL: "jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={url}",
+ },
+ }
+}
+
+type RepositoryStruct struct {
+ OpenWithEditorApps *config.Value[OpenWithEditorAppsType]
+}
+
type ConfigStruct struct {
- Picture *PictureStruct
+ Picture *PictureStruct
+ Repository *RepositoryStruct
}
var (
@@ -28,8 +65,11 @@ func initDefaultConfig() {
config.SetCfgSecKeyGetter(&cfgSecKeyGetter{})
defaultConfig = &ConfigStruct{
Picture: &PictureStruct{
- DisableGravatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}, "picture.disable_gravatar"),
- EnableFederatedAvatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}, "picture.enable_federated_avatar"),
+ DisableGravatar: config.ValueJSON[bool]("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}),
+ EnableFederatedAvatar: config.ValueJSON[bool]("picture.enable_federated_avatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}),
+ },
+ Repository: &RepositoryStruct{
+ OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"),
},
}
}
@@ -42,6 +82,9 @@ func Config() *ConfigStruct {
type cfgSecKeyGetter struct{}
func (c cfgSecKeyGetter) GetValue(sec, key string) (v string, has bool) {
+ if key == "" {
+ return "", false
+ }
cfgSec, err := CfgProvider.GetSection(sec)
if err != nil {
log.Error("Unable to get config section: %q", sec)
diff --git a/modules/setting/config/value.go b/modules/setting/config/value.go
index 817fcdb786..f0ec120544 100644
--- a/modules/setting/config/value.go
+++ b/modules/setting/config/value.go
@@ -5,8 +5,11 @@ package config
import (
"context"
- "strconv"
"sync"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
)
type CfgSecKey struct {
@@ -23,14 +26,14 @@ type Value[T any] struct {
revision int
}
-func (value *Value[T]) parse(s string) (v T) {
- switch any(v).(type) {
- case bool:
- b, _ := strconv.ParseBool(s)
- return any(b).(T)
- default:
- panic("unsupported config type, please complete the code")
+func (value *Value[T]) parse(key, valStr string) (v T) {
+ v = value.def
+ if valStr != "" {
+ if err := json.Unmarshal(util.UnsafeStringToBytes(valStr), &v); err != nil {
+ log.Error("Unable to unmarshal json config for key %q, err: %v", key, err)
+ }
}
+ return v
}
func (value *Value[T]) Value(ctx context.Context) (v T) {
@@ -62,7 +65,7 @@ func (value *Value[T]) Value(ctx context.Context) (v T) {
if valStr == nil {
v = value.def
} else {
- v = value.parse(*valStr)
+ v = value.parse(value.dynKey, *valStr)
}
value.mu.Lock()
@@ -76,6 +79,16 @@ func (value *Value[T]) DynKey() string {
return value.dynKey
}
-func Bool(def bool, cfgSecKey CfgSecKey, dynKey string) *Value[bool] {
- return &Value[bool]{def: def, cfgSecKey: cfgSecKey, dynKey: dynKey}
+func (value *Value[T]) WithDefault(def T) *Value[T] {
+ value.def = def
+ return value
+}
+
+func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] {
+ value.cfgSecKey = cfgSecKey
+ return value
+}
+
+func ValueJSON[T any](dynKey string) *Value[T] {
+ return &Value[T]{dynKey: dynKey}
}