0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-03-05 23:23:10 +01:00

Use "Enable Gravatar" but not "Disable" (#36771)

* Fix #35685
* Fix #35627
* Fix #31112


Introduce "fipped" config value type, remove unused setting variables.
Make DisableGravatar=true by defult, remove useless config options from
the "Install" page.

The legacy config options are still kept because they are still the
fallback values for the system config options.

---------

Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
wxiaoguang 2026-02-28 00:39:26 +08:00 committed by GitHub
parent fde7f7db28
commit ae2b19849d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 43 additions and 130 deletions

View File

@ -238,9 +238,6 @@ RUN_USER = ; git
;; Indicate whether to check minimum key size with corresponding type
;MINIMUM_KEY_SIZE_CHECK = false
;;
;; Disable CDN even in "prod" mode
;OFFLINE_MODE = true
;;
;; TLS Settings: Either ACME or manual
;; (Other common TLS configuration are found before)
;ENABLE_ACME = false
@ -1983,12 +1980,12 @@ LEVEL = Info
;; or a custom avatar source, like: http://cn.gravatar.com/avatar/
;GRAVATAR_SOURCE = gravatar
;;
;; This value will always be true in offline mode.
;; Deprecated, see Web UI Admin Panel -> Config -> Settings
;DISABLE_GRAVATAR = false
;;
;; Federated avatar lookup uses DNS to discover avatar associated
;; with emails, see https://www.libravatar.org
;; This value will always be false in offline mode or when Gravatar is disabled.
;; Deprecated, see Web UI Admin Panel -> Config -> Settings
;ENABLE_FEDERATED_AVATAR = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -74,7 +74,7 @@ func (u *User) AvatarLinkWithSize(ctx context.Context, size int) string {
switch {
case u.UseCustomAvatar:
useLocalAvatar = true
case disableGravatar, setting.OfflineMode:
case disableGravatar:
useLocalAvatar = true
autoGenerateAvatar = true
}

View File

@ -71,7 +71,7 @@ func initDefaultConfig() {
config.SetCfgSecKeyGetter(&cfgSecKeyGetter{})
defaultConfig = &ConfigStruct{
Picture: &PictureStruct{
DisableGravatar: config.NewOption[bool]("picture.disable_gravatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}),
DisableGravatar: config.NewOption[bool]("picture.disable_gravatar").WithDefaultSimple(true).WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "DISABLE_GRAVATAR"}),
EnableFederatedAvatar: config.NewOption[bool]("picture.enable_federated_avatar").WithFileConfig(config.CfgSecKey{Sec: "picture", Key: "ENABLE_FEDERATED_AVATAR"}),
},
Repository: &RepositoryStruct{

View File

@ -102,7 +102,7 @@ func (opt *Option[T]) ValueRevision(ctx context.Context) (v T, rev int, has bool
var valStr *string
if dynVal, hasDbValue := dg.GetValue(ctx, opt.dynKey); hasDbValue {
valStr = &dynVal
} else if cfgVal, has := GetCfgSecKeyGetter().GetValue(opt.cfgSecKey.Sec, opt.cfgSecKey.Key); has {
} else if cfgVal, hasCfgValue := GetCfgSecKeyGetter().GetValue(opt.cfgSecKey.Sec, opt.cfgSecKey.Key); hasCfgValue {
valStr = &cfgVal
}
if valStr == nil {

View File

@ -22,9 +22,7 @@ var (
RenderedSizeFactor: 2,
}
GravatarSource string
DisableGravatar bool // Depreciated: migrated to database
EnableFederatedAvatar bool // Depreciated: migrated to database
GravatarSource string
RepoAvatar = struct {
Storage *Storage
@ -65,29 +63,12 @@ func loadAvatarsFrom(rootCfg ConfigProvider) error {
GravatarSource = source
}
DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
deprecatedSettingDB(rootCfg, "", "DISABLE_GRAVATAR")
EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
deprecatedSettingDB(rootCfg, "", "ENABLE_FEDERATED_AVATAR")
deprecatedSettingDB(rootCfg, "picture", "DISABLE_GRAVATAR")
deprecatedSettingDB(rootCfg, "picture", "ENABLE_FEDERATED_AVATAR")
return nil
}
func GetDefaultDisableGravatar() bool {
return OfflineMode
}
func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
v := !InstallLock
if OfflineMode {
v = false
}
if disableGravatar {
v = false
}
return v
}
func loadRepoAvatarFrom(rootCfg ConfigProvider) error {
sec := rootCfg.Section("picture")

View File

@ -91,7 +91,6 @@ var (
RedirectOtherPort bool
RedirectorUseProxyProtocol bool
PortToRedirect string
OfflineMode bool
CertFile string
KeyFile string
StaticRootPath string
@ -346,7 +345,6 @@ func loadServerFrom(rootCfg ConfigProvider) {
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
OfflineMode = sec.Key("OFFLINE_MODE").MustBool(true)
if len(StaticRootPath) == 0 {
StaticRootPath = AppWorkPath
}

View File

@ -285,12 +285,6 @@
"install.register_confirm": "Require Email Confirmation to Register",
"install.mail_notify": "Enable Email Notifications",
"install.server_service_title": "Server and Third-Party Service Settings",
"install.offline_mode": "Enable Local Mode",
"install.offline_mode_popup": "Disable third-party content delivery networks and serve all resources locally.",
"install.disable_gravatar": "Disable Gravatar",
"install.disable_gravatar_popup": "Disable Gravatar and third-party avatar sources. A default avatar will be used unless a user locally uploads an avatar.",
"install.federated_avatar_lookup": "Enable Federated Avatars",
"install.federated_avatar_lookup_popup": "Enable federated avatar lookup using Libravatar.",
"install.disable_registration": "Disable Self-Registration",
"install.disable_registration_popup": "Disable user self-registration. Only administrators will be able to create new user accounts.",
"install.allow_only_external_registration_popup": "Allow Registration Only Through External Services",
@ -3193,7 +3187,6 @@
"admin.config.custom_conf": "Configuration File Path",
"admin.config.custom_file_root_path": "Custom File Root Path",
"admin.config.domain": "Server Domain",
"admin.config.offline_mode": "Local Mode",
"admin.config.disable_router_log": "Disable Router Log",
"admin.config.run_user": "Run As Username",
"admin.config.run_mode": "Run Mode",
@ -3296,7 +3289,7 @@
"admin.config.cookie_life_time": "Cookie Life Time",
"admin.config.picture_config": "Picture and Avatar Configuration",
"admin.config.picture_service": "Picture Service",
"admin.config.disable_gravatar": "Disable Gravatar",
"admin.config.enable_gravatar": "Enable Gravatar",
"admin.config.enable_federated_avatar": "Enable Federated Avatars",
"admin.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.",
"admin.config.git_guide_remote_name": "Repository remote name for git commands in the guide",

View File

@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/models/db"
db_install "code.gitea.io/gitea/models/db/install"
system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/generate"
@ -114,11 +113,6 @@ func Install(ctx *context.Context) {
form.RegisterConfirm = setting.Service.RegisterEmailConfirm
form.MailNotify = setting.Service.EnableNotifyMail
// Server and other services settings
form.OfflineMode = setting.OfflineMode
form.DisableGravatar = setting.DisableGravatar // when installing, there is no database connection so that given a default value
form.EnableFederatedAvatar = setting.EnableFederatedAvatar // when installing, there is no database connection so that given a default value
form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
form.DisableRegistration = setting.Service.DisableRegistration
@ -405,15 +399,6 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("service").Key("REGISTER_EMAIL_CONFIRM").SetValue(strconv.FormatBool(form.RegisterConfirm))
cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(strconv.FormatBool(form.MailNotify))
cfg.Section("server").Key("OFFLINE_MODE").SetValue(strconv.FormatBool(form.OfflineMode))
if err := system_model.SetSettings(ctx, map[string]string{
setting.Config().Picture.DisableGravatar.DynKey(): strconv.FormatBool(form.DisableGravatar),
setting.Config().Picture.EnableFederatedAvatar.DynKey(): strconv.FormatBool(form.EnableFederatedAvatar),
}); err != nil {
ctx.RenderWithErrDeprecated(ctx.Tr("install.save_config_failed", err), tplInstall, &form)
return
}
cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(strconv.FormatBool(form.EnableOpenIDSignIn))
cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(strconv.FormatBool(form.EnableOpenIDSignUp))
cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(strconv.FormatBool(form.DisableRegistration))

View File

@ -126,7 +126,6 @@ func Config(ctx *context.Context) {
ctx.Data["AppUrl"] = setting.AppURL
ctx.Data["AppBuiltWith"] = setting.AppBuiltWith
ctx.Data["Domain"] = setting.Domain
ctx.Data["OfflineMode"] = setting.OfflineMode
ctx.Data["RunUser"] = setting.RunUser
ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
ctx.Data["GitVersion"] = git.DefaultFeatures().VersionInfo()

View File

@ -45,9 +45,6 @@ type InstallForm struct {
RegisterConfirm bool
MailNotify bool
OfflineMode bool
DisableGravatar bool
EnableFederatedAvatar bool
EnableOpenIDSignIn bool
EnableOpenIDSignUp bool
DisableRegistration bool

View File

@ -15,8 +15,6 @@
<dd>{{.AppUrl}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.domain"}}</dt>
<dd>{{.Domain}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.offline_mode"}}</dt>
<dd>{{svg (Iif .OfflineMode "octicon-check" "octicon-x")}}</dd>
<dt>{{ctx.Locale.Tr "admin.config.disable_router_log"}}</dt>
<dd>{{svg (Iif .DisableRouterLog "octicon-check" "octicon-x")}}</dd>

View File

@ -3,17 +3,21 @@
</h4>
<div class="ui attached table segment">
<dl class="admin-dl-horizontal">
<dt>{{ctx.Locale.Tr "admin.config.disable_gravatar"}}</dt>
{{$cfgOpt := .SystemConfig.Picture.DisableGravatar}}
<dt>{{ctx.Locale.Tr "admin.config.enable_gravatar"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.disable_gravatar"}}">
<input type="checkbox" data-config-dyn-key="picture.disable_gravatar" {{if .SystemConfig.Picture.DisableGravatar.Value ctx}}checked{{end}}><label></label>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.enable_gravatar"}}">
<input type="checkbox" data-config-dyn-key="{{$cfgOpt.DynKey}}" data-config-value-type="flipped" {{if not ($cfgOpt.Value ctx)}}checked{{end}}><label></label>
</div>
</dd>
<div class="divider"></div>
{{$cfgOpt = .SystemConfig.Picture.EnableFederatedAvatar}}
<dt>{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}</dt>
<dd>
<div class="ui toggle checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.config.enable_federated_avatar"}}">
<input type="checkbox" data-config-dyn-key="picture.enable_federated_avatar" {{if .SystemConfig.Picture.EnableFederatedAvatar.Value ctx}}checked{{end}}><label></label>
<input type="checkbox" data-config-dyn-key="{{$cfgOpt.DynKey}}" data-config-value-type="boolean" {{if $cfgOpt.Value ctx}}checked{{end}}><label></label>
</div>
</dd>
</dl>

View File

@ -203,24 +203,6 @@
<summary class="right-content tw-py-2{{if .Err_Services}} tw-text-red{{end}}">
{{ctx.Locale.Tr "install.server_service_title"}}
</summary>
<div class="inline field">
<div class="ui checkbox" id="offline-mode">
<label data-tooltip-content="{{ctx.Locale.Tr "install.offline_mode_popup"}}">{{ctx.Locale.Tr "install.offline_mode"}}</label>
<input name="offline_mode" type="checkbox" {{if .offline_mode}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="disable-gravatar">
<label data-tooltip-content="{{ctx.Locale.Tr "install.disable_gravatar_popup"}}">{{ctx.Locale.Tr "install.disable_gravatar"}}</label>
<input name="disable_gravatar" type="checkbox" {{if .disable_gravatar}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="federated-avatar-lookup">
<label data-tooltip-content="{{ctx.Locale.Tr "install.federated_avatar_lookup_popup"}}">{{ctx.Locale.Tr "install.federated_avatar_lookup"}}</label>
<input name="enable_federated_avatar" type="checkbox" {{if .enable_federated_avatar}}checked{{end}}>
</div>
</div>
<div class="inline field">
<div class="ui checkbox" id="enable-openid-signin">
<label data-tooltip-content="{{ctx.Locale.Tr "install.openid_signin_popup"}}">{{ctx.Locale.Tr "install.openid_signin"}}</label>

View File

@ -41,7 +41,6 @@ SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
START_SSH_SERVER = true
LFS_START_SERVER = true
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
BUILTIN_SSH_SERVER_USER = git
SSH_TRUSTED_USER_CA_KEYS = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb4DC1dMFnJ6pXWo7GMxTchtzmJHYzfN6sZ9FAPFR4ijMLfGki+olvOMO5Fql1/yGnGfbELQa1S6y4shSvj/5K+zUFScmEXYf3Gcr87RqilLkyk16RS+cHNB1u87xTHbETaa3nyCJeGQRpd4IQ4NKob745mwDZ7jQBH8AZEng50Oh8y8fi8skBBBzaYp1ilgvzG740L7uex6fHV62myq0SXeCa+oJUjq326FU8y+Vsa32H8A3e7tOgXZPdt2TVNltx2S9H2WO8RMi7LfaSwARNfy1zu+bfR50r6ef8Yx5YKCMz4wWb1SHU1GS800mjOjlInLQORYRNMlSwR1+vLlVDciOqFapDSbj+YOVOawR0R1aqlSKpZkt33DuOBPx9qe6CVnIi7Z+Px/KqM+OLCzlLY/RS+LbxQpDWcfTVRiP+S5qRTcE3M3UioN/e0BE/1+MpX90IGpvVkA63ILYbKEa4bM3ASL7ChTCr6xN5XT+GpVJveFKK1cfNx9ExHI4rzYE=
@ -62,10 +61,6 @@ DEFAULT_ALLOW_CREATE_ORGANIZATION = true
NO_REPLY_ADDRESS = noreply.example.org
ENABLE_NOTIFY_MAIL = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file

View File

@ -43,7 +43,6 @@ SSH_LISTEN_HOST = localhost
SSH_PORT = 2201
BUILTIN_SSH_SERVER_USER = git
START_SSH_SERVER = true
OFFLINE_MODE = false
LFS_START_SERVER = true
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
@ -65,10 +64,6 @@ DEFAULT_ALLOW_CREATE_ORGANIZATION = true
NO_REPLY_ADDRESS = noreply.example.org
ENABLE_NOTIFY_MAIL = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file

View File

@ -42,7 +42,6 @@ SSH_LISTEN_HOST = localhost
SSH_PORT = 2202
START_SSH_SERVER = true
LFS_START_SERVER = true
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
BUILTIN_SSH_SERVER_USER = git
SSH_TRUSTED_USER_CA_KEYS = ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb4DC1dMFnJ6pXWo7GMxTchtzmJHYzfN6sZ9FAPFR4ijMLfGki+olvOMO5Fql1/yGnGfbELQa1S6y4shSvj/5K+zUFScmEXYf3Gcr87RqilLkyk16RS+cHNB1u87xTHbETaa3nyCJeGQRpd4IQ4NKob745mwDZ7jQBH8AZEng50Oh8y8fi8skBBBzaYp1ilgvzG740L7uex6fHV62myq0SXeCa+oJUjq326FU8y+Vsa32H8A3e7tOgXZPdt2TVNltx2S9H2WO8RMi7LfaSwARNfy1zu+bfR50r6ef8Yx5YKCMz4wWb1SHU1GS800mjOjlInLQORYRNMlSwR1+vLlVDciOqFapDSbj+YOVOawR0R1aqlSKpZkt33DuOBPx9qe6CVnIi7Z+Px/KqM+OLCzlLY/RS+LbxQpDWcfTVRiP+S5qRTcE3M3UioN/e0BE/1+MpX90IGpvVkA63ILYbKEa4bM3ASL7ChTCr6xN5XT+GpVJveFKK1cfNx9ExHI4rzYE=
@ -63,10 +62,6 @@ DEFAULT_ALLOW_CREATE_ORGANIZATION = true
NO_REPLY_ADDRESS = noreply.example.org
ENABLE_NOTIFY_MAIL = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file

View File

@ -37,7 +37,6 @@ SSH_LISTEN_HOST = localhost
SSH_PORT = 2203
START_SSH_SERVER = true
LFS_START_SERVER = true
OFFLINE_MODE = false
LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w
ENABLE_GZIP = true
BUILTIN_SSH_SERVER_USER = git
@ -59,10 +58,6 @@ DEFAULT_KEEP_EMAIL_PRIVATE = false
DEFAULT_ALLOW_CREATE_ORGANIZATION = true
NO_REPLY_ADDRESS = noreply.example.org
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file

View File

@ -7,10 +7,15 @@ test('ConfigFormValueMapper', () => {
<!-- top-level key -->
<input name="k1" type="checkbox" value="v-key-only" data-config-dyn-key="k1" data-config-value-json="true" data-config-value-type="boolean">
<input type="hidden" data-config-dyn-key="k2" data-config-value-json='"k2-val"'>
<input name="k2">
<textarea name="repository.open-with.editor-apps"> a = b\n</textarea>
<input name="k-flipped-true" type="checkbox" data-config-value-type="flipped">
<input name="k-flipped-false" type="checkbox" checked data-config-value-type="flipped">
<!-- sub key -->
<input type="hidden" data-config-dyn-key="struct" data-config-value-json='{"SubBoolean": true, "SubTimestamp": 123456789, "OtherKey": "other-value"}'>
<input name="struct.SubBoolean" type="checkbox" data-config-value-type="boolean">
@ -35,6 +40,8 @@ test('ConfigFormValueMapper', () => {
expect(result).toEqual({
'k1': 'true',
'k2': '"k2-val"',
'k-flipped-false': 'false',
'k-flipped-true': 'true',
'repository.open-with.editor-apps': '[{"DisplayName":"a","OpenURL":"b"}]', // TODO: OPEN-WITH-EDITOR-APP-JSON: it must match backend
'struct': '{"SubBoolean":true,"SubTimestamp":123456780,"OtherKey":"other-value","NewKey":"new-value"}',
});

View File

@ -6,14 +6,23 @@ import {submitFormFetchAction} from '../common-fetch-action.ts';
const {appSubUrl} = window.config;
function collectCheckboxBooleanValue(el: HTMLInputElement): boolean {
const valType = el.getAttribute('data-config-value-type') as ConfigValueType;
if (valType === 'boolean') return el.checked;
if (valType === 'flipped') return !el.checked;
requireExplicitValueType(el);
}
function initSystemConfigAutoCheckbox(el: HTMLInputElement) {
el.addEventListener('change', async () => {
// if the checkbox is inside a form, we assume it's handled by the form submit and do not send an individual request
if (el.closest('form')) return;
try {
const resp = await POST(`${appSubUrl}/-/admin/config`, {
data: new URLSearchParams({key: el.getAttribute('data-config-dyn-key')!, value: String(el.checked)}),
const data = new URLSearchParams({
key: el.getAttribute('data-config-dyn-key')!,
value: String(collectCheckboxBooleanValue(el)),
});
const resp = await POST(`${appSubUrl}/-/admin/config`, {data});
const json: Record<string, any> = await resp.json();
if (json.errorMessage) throw new Error(json.errorMessage);
} catch (ex) {
@ -47,7 +56,7 @@ function extractElemConfigSubKey(el: GeneralFormFieldElement, dynKey: string): s
// Due to the different design between HTML form elements and the JSON struct of the config values, we need to explicitly define some types.
// * checkbox can be used for boolean value, it can also be used for multiple values (array)
type ConfigValueType = 'boolean' | 'string' | 'number' | 'timestamp'; // TODO: support more types like array, not used at the moment.
type ConfigValueType = 'boolean' | 'flipped' | 'string' | 'number' | 'timestamp'; // TODO: support more types like array, not used at the moment.
function toDatetimeLocalValue(unixSeconds: number) {
const d = new Date(unixSeconds * 1000);
@ -102,13 +111,14 @@ export class ConfigFormValueMapper {
return true;
}
collectConfigValueFromElement(el: GeneralFormFieldElement, _oldVal: any = null) {
collectConfigValueFromElement(el: GeneralFormFieldElement) {
let val: any;
const valType = this.presetValueTypes[el.name];
if (el.matches('[type="checkbox"]')) {
if (valType !== 'boolean') requireExplicitValueType(el);
val = el.checked;
// oldVal: for future use when we support array value with checkbox
// TODO: if it needs to support array values in the future,
// it needs to iterate the "namedElems" to find all the checkboxes with the same name and collect values accordingly,
// and set the namedElems[matchedIdx] to null to avoid duplicate processing.
val = collectCheckboxBooleanValue(el);
} else if (el.matches('[type="datetime-local"]')) {
if (valType !== 'timestamp') requireExplicitValueType(el);
val = Math.floor(new Date(el.value).getTime() / 1000) ?? 0; // NaN is fine to JSON.stringify, it becomes null.
@ -128,7 +138,7 @@ export class ConfigFormValueMapper {
if (!el) continue;
const subKey = extractElemConfigSubKey(el, dynKey);
if (!subKey) continue; // if not match, skip
cfgVal[subKey] = this.collectConfigValueFromElement(el, cfgVal[subKey]);
cfgVal[subKey] = this.collectConfigValueFromElement(el);
namedElems[idx] = null;
}
}
@ -180,6 +190,7 @@ export class ConfigFormValueMapper {
// now, the namedElems should only contain the config options without sub values,
// directly store the value in formData with key as the element name, for example:
// "foo.enabled" => "true"
for (const el of namedElems) {
if (!el) continue;
const dynKey = el.name;

View File

@ -60,25 +60,6 @@ function initPreInstall() {
}
// TODO: better handling of exclusive relations.
document.querySelector<HTMLInputElement>('#offline-mode input')!.addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#disable-gravatar input')!.checked = true;
document.querySelector<HTMLInputElement>('#federated-avatar-lookup input')!.checked = false;
}
});
document.querySelector<HTMLInputElement>('#disable-gravatar input')!.addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#federated-avatar-lookup input')!.checked = false;
} else {
document.querySelector<HTMLInputElement>('#offline-mode input')!.checked = false;
}
});
document.querySelector<HTMLInputElement>('#federated-avatar-lookup input')!.addEventListener('change', function () {
if (this.checked) {
document.querySelector<HTMLInputElement>('#disable-gravatar input')!.checked = false;
document.querySelector<HTMLInputElement>('#offline-mode input')!.checked = false;
}
});
document.querySelector<HTMLInputElement>('#enable-openid-signin input')!.addEventListener('change', function () {
if (this.checked) {
if (!document.querySelector<HTMLInputElement>('#disable-registration input')!.checked) {