Skip to content

Commit 7ada444

Browse files
committed
fix
1 parent a70c00b commit 7ada444

19 files changed

+283
-52
lines changed

Diff for: modules/context/context.go

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func Contexter() func(next http.Handler) http.Handler {
192192
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
193193
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
194194

195+
ctx.Data["SystemConfig"] = setting.Config()
195196
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
196197
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
197198

Diff for: modules/setting/config.go

+46-3
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,45 @@ type PictureStruct struct {
1515
EnableFederatedAvatar *config.Value[bool]
1616
}
1717

18+
type OpenWithEditorApp struct {
19+
DisplayName string
20+
OpenURL string
21+
}
22+
23+
type OpenWithEditorAppsType []OpenWithEditorApp
24+
25+
func (t OpenWithEditorAppsType) ToTextareaString() string {
26+
ret := ""
27+
for _, app := range t {
28+
ret += app.DisplayName + " = " + app.OpenURL + "\n"
29+
}
30+
return ret
31+
}
32+
33+
func DefaultOpenWithEditorApps() OpenWithEditorAppsType {
34+
return OpenWithEditorAppsType{
35+
{
36+
DisplayName: "VS Code",
37+
OpenURL: "vscode://vscode.git/clone?url={url}",
38+
},
39+
{
40+
DisplayName: "VSCodium",
41+
OpenURL: "vscodium://vscode.git/clone?url={url}",
42+
},
43+
{
44+
DisplayName: "Intellij IDEA",
45+
OpenURL: "jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={url}",
46+
},
47+
}
48+
}
49+
50+
type RepositoryStruct struct {
51+
OpenWithEditorApps *config.Value[OpenWithEditorAppsType]
52+
}
53+
1854
type ConfigStruct struct {
19-
Picture *PictureStruct
55+
Picture *PictureStruct
56+
Repository *RepositoryStruct
2057
}
2158

2259
var (
@@ -28,8 +65,11 @@ func initDefaultConfig() {
2865
config.SetCfgSecKeyGetter(&cfgSecKeyGetter{})
2966
defaultConfig = &ConfigStruct{
3067
Picture: &PictureStruct{
31-
DisableGravatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}, "picture.disable_gravatar"),
32-
EnableFederatedAvatar: config.Bool(false, config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}, "picture.enable_federated_avatar"),
68+
DisableGravatar: config.ValueJSON[bool]("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}),
69+
EnableFederatedAvatar: config.ValueJSON[bool]("picture.enable_federated_avatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}),
70+
},
71+
Repository: &RepositoryStruct{
72+
OpenWithEditorApps: config.ValueJSON[OpenWithEditorAppsType]("repository.open-with.editor-apps"),
3373
},
3474
}
3575
}
@@ -42,6 +82,9 @@ func Config() *ConfigStruct {
4282
type cfgSecKeyGetter struct{}
4383

4484
func (c cfgSecKeyGetter) GetValue(sec, key string) (v string, has bool) {
85+
if key == "" {
86+
return "", false
87+
}
4588
cfgSec, err := CfgProvider.GetSection(sec)
4689
if err != nil {
4790
log.Error("Unable to get config section: %q", sec)

Diff for: modules/setting/config/value.go

+24-11
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ package config
55

66
import (
77
"context"
8-
"strconv"
98
"sync"
9+
10+
"code.gitea.io/gitea/modules/json"
11+
"code.gitea.io/gitea/modules/log"
12+
"code.gitea.io/gitea/modules/util"
1013
)
1114

1215
type CfgSecKey struct {
@@ -23,14 +26,14 @@ type Value[T any] struct {
2326
revision int
2427
}
2528

26-
func (value *Value[T]) parse(s string) (v T) {
27-
switch any(v).(type) {
28-
case bool:
29-
b, _ := strconv.ParseBool(s)
30-
return any(b).(T)
31-
default:
32-
panic("unsupported config type, please complete the code")
29+
func (value *Value[T]) parse(key, valStr string) (v T) {
30+
v = value.def
31+
if valStr != "" {
32+
if err := json.Unmarshal(util.UnsafeStringToBytes(valStr), &v); err != nil {
33+
log.Error("Unable to unmarshal json config for key %q, err: %v", key, err)
34+
}
3335
}
36+
return v
3437
}
3538

3639
func (value *Value[T]) Value(ctx context.Context) (v T) {
@@ -62,7 +65,7 @@ func (value *Value[T]) Value(ctx context.Context) (v T) {
6265
if valStr == nil {
6366
v = value.def
6467
} else {
65-
v = value.parse(*valStr)
68+
v = value.parse(value.dynKey, *valStr)
6669
}
6770

6871
value.mu.Lock()
@@ -76,6 +79,16 @@ func (value *Value[T]) DynKey() string {
7679
return value.dynKey
7780
}
7881

79-
func Bool(def bool, cfgSecKey CfgSecKey, dynKey string) *Value[bool] {
80-
return &Value[bool]{def: def, cfgSecKey: cfgSecKey, dynKey: dynKey}
82+
func (value *Value[T]) WithDefault(def T) *Value[T] {
83+
value.def = def
84+
return value
85+
}
86+
87+
func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] {
88+
value.cfgSecKey = cfgSecKey
89+
return value
90+
}
91+
92+
func ValueJSON[T any](dynKey string) *Value[T] {
93+
return &Value[T]{dynKey: dynKey}
8194
}

Diff for: options/locale/locale_en-US.ini

+4-1
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ fork_branch = Branch to be cloned to the fork
955955
all_branches = All branches
956956
fork_no_valid_owners = This repository can not be forked because there are no valid owners.
957957
use_template = Use this template
958-
clone_in_vsc = Clone in VS Code
958+
open_with_editor = Open with %s
959959
download_zip = Download ZIP
960960
download_tar = Download TAR.GZ
961961
download_bundle = Download BUNDLE
@@ -2732,6 +2732,8 @@ integrations = Integrations
27322732
authentication = Authentication Sources
27332733
emails = User Emails
27342734
config = Configuration
2735+
config_summary = Summary
2736+
config_settings = Settings
27352737
notices = System Notices
27362738
monitor = Monitoring
27372739
first_page = First
@@ -3171,6 +3173,7 @@ config.picture_config = Picture and Avatar Configuration
31713173
config.picture_service = Picture Service
31723174
config.disable_gravatar = Disable Gravatar
31733175
config.enable_federated_avatar = Enable Federated Avatars
3176+
config.open_with_editor_app_help = The "Open with" editors for the clone menu. If left empty, the default will be used. Expand to see the default.
31743177

31753178
config.git_config = Git Configuration
31763179
config.git_disable_diff_highlight = Disable Diff Syntax Highlight

Diff for: public/assets/img/svg/gitea-open-with-jetbrains.svg

+1
Loading

Diff for: public/assets/img/svg/gitea-open-with-vscode.svg

+1
Loading

Diff for: public/assets/img/svg/gitea-open-with-vscodium.svg

+1
Loading

Diff for: public/assets/img/svg/gitea-vscode.svg

-1
This file was deleted.

Diff for: routers/web/admin/config.go

+56-7
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ package admin
77
import (
88
"net/http"
99
"net/url"
10+
"strconv"
1011
"strings"
1112

1213
system_model "code.gitea.io/gitea/models/system"
1314
"code.gitea.io/gitea/modules/base"
14-
"code.gitea.io/gitea/modules/container"
1515
"code.gitea.io/gitea/modules/context"
1616
"code.gitea.io/gitea/modules/git"
1717
"code.gitea.io/gitea/modules/json"
@@ -25,6 +25,7 @@ import (
2525
)
2626

2727
const tplConfig base.TplName = "admin/config"
28+
const tplConfigSettings base.TplName = "admin/config_settings"
2829

2930
// SendTestMail send test mail to confirm mail service is OK
3031
func SendTestMail(ctx *context.Context) {
@@ -98,8 +99,9 @@ func shadowPassword(provider, cfgItem string) string {
9899

99100
// Config show admin config page
100101
func Config(ctx *context.Context) {
101-
ctx.Data["Title"] = ctx.Tr("admin.config")
102+
ctx.Data["Title"] = ctx.Tr("admin.config_summary")
102103
ctx.Data["PageIsAdminConfig"] = true
104+
ctx.Data["PageIsAdminConfigSummary"] = true
103105

104106
ctx.Data["CustomConf"] = setting.CustomConf
105107
ctx.Data["AppUrl"] = setting.AppURL
@@ -161,23 +163,70 @@ func Config(ctx *context.Context) {
161163

162164
ctx.Data["Loggers"] = log.GetManager().DumpLoggers()
163165
config.GetDynGetter().InvalidateCache()
164-
ctx.Data["SystemConfig"] = setting.Config()
165166
prepareDeprecatedWarningsAlert(ctx)
166167

167168
ctx.HTML(http.StatusOK, tplConfig)
168169
}
169170

171+
func ConfigSettings(ctx *context.Context) {
172+
ctx.Data["Title"] = ctx.Tr("admin.config_settings")
173+
ctx.Data["PageIsAdminConfig"] = true
174+
ctx.Data["PageIsAdminConfigSettings"] = true
175+
ctx.Data["DefaultOpenWithEditorAppsString"] = setting.DefaultOpenWithEditorApps().ToTextareaString()
176+
ctx.HTML(http.StatusOK, tplConfigSettings)
177+
}
178+
170179
func ChangeConfig(ctx *context.Context) {
171180
key := strings.TrimSpace(ctx.FormString("key"))
172181
value := ctx.FormString("value")
173182
cfg := setting.Config()
174-
allowedKeys := container.SetOf(cfg.Picture.DisableGravatar.DynKey(), cfg.Picture.EnableFederatedAvatar.DynKey())
175-
if !allowedKeys.Contains(key) {
183+
184+
marshalBool := func(v string) (string, error) {
185+
if b, _ := strconv.ParseBool(v); b {
186+
return "true", nil
187+
}
188+
return "false", nil
189+
}
190+
marshalOpenWithApps := func(value string) (string, error) {
191+
lines := strings.Split(value, "\n")
192+
var openWithEditorApps setting.OpenWithEditorAppsType
193+
for _, line := range lines {
194+
line = strings.TrimSpace(line)
195+
if line == "" {
196+
continue
197+
}
198+
displayName, openURL, ok := strings.Cut(line, "=")
199+
displayName, openURL = strings.TrimSpace(displayName), strings.TrimSpace(openURL)
200+
if !ok || displayName == "" || openURL == "" {
201+
continue
202+
}
203+
openWithEditorApps = append(openWithEditorApps, setting.OpenWithEditorApp{
204+
DisplayName: strings.TrimSpace(displayName),
205+
OpenURL: strings.TrimSpace(openURL),
206+
})
207+
}
208+
b, err := json.Marshal(openWithEditorApps)
209+
if err != nil {
210+
return "", err
211+
}
212+
return string(b), nil
213+
}
214+
marshallers := map[string]func(string) (string, error){
215+
cfg.Picture.DisableGravatar.DynKey(): marshalBool,
216+
cfg.Picture.EnableFederatedAvatar.DynKey(): marshalBool,
217+
cfg.Repository.OpenWithEditorApps.DynKey(): marshalOpenWithApps,
218+
}
219+
marshaller, hasMarshaller := marshallers[key]
220+
if !hasMarshaller {
221+
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
222+
return
223+
}
224+
marshaledValue, err := marshaller(value)
225+
if err != nil {
176226
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
177227
return
178228
}
179-
if err := system_model.SetSettings(ctx, map[string]string{key: value}); err != nil {
180-
log.Error("set setting failed: %v", err)
229+
if err = system_model.SetSettings(ctx, map[string]string{key: marshaledValue}); err != nil {
181230
ctx.JSONError(ctx.Tr("admin.config.set_setting_failed", key))
182231
return
183232
}

Diff for: routers/web/repo/view.go

+27-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package repo
66

77
import (
88
"bytes"
9+
"code.gitea.io/gitea/modules/svg"
910
gocontext "context"
1011
"encoding/base64"
1112
"fmt"
@@ -792,7 +793,7 @@ func Home(ctx *context.Context) {
792793
return
793794
}
794795

795-
renderCode(ctx)
796+
renderHomeCode(ctx)
796797
}
797798

798799
// LastCommit returns lastCommit data for the provided branch/tag/commit and directory (in url) and filenames in body
@@ -919,9 +920,33 @@ func renderRepoTopics(ctx *context.Context) {
919920
ctx.Data["Topics"] = topics
920921
}
921922

922-
func renderCode(ctx *context.Context) {
923+
func prepareOpenWithEditorApps(ctx *context.Context) {
924+
var tmplApps []map[string]any
925+
apps := setting.Config().Repository.OpenWithEditorApps.Value(ctx)
926+
if len(apps) == 0 {
927+
apps = setting.DefaultOpenWithEditorApps()
928+
}
929+
for _, app := range apps {
930+
schema, _, _ := strings.Cut(app.OpenURL, ":")
931+
var iconHTML template.HTML
932+
if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" {
933+
iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "gt-mr-3")
934+
} else {
935+
iconHTML = svg.RenderHTML("gitea-git", 16, "gt-mr-3") // TODO: it could support user's customized icon in the future
936+
}
937+
tmplApps = append(tmplApps, map[string]any{
938+
"DisplayName": app.DisplayName,
939+
"OpenURL": app.OpenURL,
940+
"IconHTML": iconHTML,
941+
})
942+
}
943+
ctx.Data["OpenWithEditorApps"] = tmplApps
944+
}
945+
946+
func renderHomeCode(ctx *context.Context) {
923947
ctx.Data["PageIsViewCode"] = true
924948
ctx.Data["RepositoryUploadEnabled"] = setting.Repository.Upload.Enabled
949+
prepareOpenWithEditorApps(ctx)
925950

926951
if ctx.Repo.Commit == nil || ctx.Repo.Repository.IsEmpty || ctx.Repo.Repository.IsBroken() {
927952
showEmpty := true

Diff for: routers/web/web.go

+1
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ func registerRoutes(m *web.Route) {
686686
m.Get("", admin.Config)
687687
m.Post("", admin.ChangeConfig)
688688
m.Post("/test_mail", admin.SendTestMail)
689+
m.Get("/settings", admin.ConfigSettings)
689690
})
690691

691692
m.Group("/monitor", func() {

Diff for: templates/admin/config.tmpl

-21
Original file line numberDiff line numberDiff line change
@@ -283,27 +283,6 @@
283283
</dl>
284284
</div>
285285

286-
<h4 class="ui top attached header">
287-
{{ctx.Locale.Tr "admin.config.picture_config"}}
288-
</h4>
289-
<div class="ui attached table segment">
290-
<dl class="admin-dl-horizontal">
291-
<dt>{{ctx.Locale.Tr "admin.config.disable_gravatar"}}</dt>
292-
<dd>
293-
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.disable_gravatar"}}">
294-
<input type="checkbox" data-config-dyn-key="picture.disable_gravatar" {{if .SystemConfig.Picture.DisableGravatar.Value ctx}}checked{{end}}>
295-
</div>
296-
</dd>
297-
<div class="divider"></div>
298-
<dt>{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}</dt>
299-
<dd>
300-
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}">
301-
<input type="checkbox" data-config-dyn-key="picture.enable_federated_avatar" {{if .SystemConfig.Picture.EnableFederatedAvatar.Value ctx}}checked{{end}}>
302-
</div>
303-
</dd>
304-
</dl>
305-
</div>
306-
307286
<h4 class="ui top attached header">
308287
{{ctx.Locale.Tr "admin.config.git_config"}}
309288
</h4>

0 commit comments

Comments
 (0)