diff options
author | wxiaoguang <wxiaoguang@gmail.com> | 2024-02-24 21:12:17 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-24 13:12:17 +0000 |
commit | 29a26d9d8c573c9fb7e79a66ac3d578e8b20dcae (patch) | |
tree | 2d51372c6d14b0f1c96c35d1a2cf4ff8f6fb050b /modules | |
parent | c42083a33950be6ee9f822c6d0de3c3a79d1f51b (diff) | |
download | gitea-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.go | 1 | ||||
-rw-r--r-- | modules/setting/config.go | 49 | ||||
-rw-r--r-- | modules/setting/config/value.go | 35 |
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} } |