mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-11-09 20:00:12 +00:00
config: migrate to new yaml format
config-base.yaml is almost identical to json version, except there's no "order" field, as "sections" and "settings" fields are now lists themselves and so Go can parse the correct order. As such, removed enumerate_config.py. Also, rewrote scripts/generate_ini.py in Go as scripts/ini/. Config structure in Go form is now in common/config.go, and is used by jfa-go and the ini script. app.configBase is now untouched once read from config-base.yaml, and instead copied to and patched in app.patchedConfig. Patching occurs at program start and config modification, so GetConfig is now just a couple of lines. Discord role patching still occurs in GetConfig, as the available roles can change regularly. Also added new "Disabled" field to sections, to avoid the nightmare of deleting from an array.
This commit is contained in:
parent
711b817cff
commit
f063b970b4
31
Makefile
31
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: configuration email typescript swagger copy compile compress tailwind bundle-css inline-css variants-html install clean npm config-description config-default precompile
|
.PHONY: configuration email typescript swagger copy compile compress inline-css variants-html install clean npm config-description config-default precompile
|
||||||
|
|
||||||
all: compile
|
all: compile
|
||||||
|
|
||||||
@ -101,20 +101,23 @@ else
|
|||||||
SWAGINSTALL :=
|
SWAGINSTALL :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
CONFIG_BASE = config/config-base.json
|
CONFIG_BASE = config/config-base.yaml
|
||||||
|
|
||||||
CONFIG_DESCRIPTION = $(DATA)/config-base.json
|
# CONFIG_DESCRIPTION = $(DATA)/config-base.json
|
||||||
CONFIG_DEFAULT = $(DATA)/config-default.ini
|
CONFIG_DEFAULT = $(DATA)/config-default.ini
|
||||||
$(CONFIG_DESCRIPTION) &: $(CONFIG_BASE)
|
# $(CONFIG_DESCRIPTION) &: $(CONFIG_BASE)
|
||||||
$(info Fixing config-base)
|
# $(info Fixing config-base)
|
||||||
-mkdir -p $(DATA)
|
# -mkdir -p $(DATA)
|
||||||
python3 scripts/enumerate_config.py -i config/config-base.json -o $(DATA)/config-base.json
|
# python3 scripts/enumerate_config.py -i config/config-base.json -o $(DATA)/config-base.json
|
||||||
|
|
||||||
$(CONFIG_DEFAULT) &: $(CONFIG_BASE)
|
$(DATA):
|
||||||
|
mkdir -p $(DATA)
|
||||||
|
|
||||||
|
$(CONFIG_DEFAULT): $(DATA) $(CONFIG_BASE)
|
||||||
$(info Generating config-default.ini)
|
$(info Generating config-default.ini)
|
||||||
python3 scripts/generate_ini.py -i config/config-base.json -o $(DATA)/config-default.ini
|
go run scripts/ini/main.go -in $(CONFIG_BASE) -out $(DATA)/config-default.ini
|
||||||
|
|
||||||
configuration: $(CONFIG_DESCRIPTION) $(CONFIG_DEFAULT)
|
configuration: $(CONFIG_DEFAULT)
|
||||||
|
|
||||||
EMAIL_SRC_MJML = $(wildcard mail/*.mjml)
|
EMAIL_SRC_MJML = $(wildcard mail/*.mjml)
|
||||||
EMAIL_SRC_TXT = $(wildcard mail/*.txt)
|
EMAIL_SRC_TXT = $(wildcard mail/*.txt)
|
||||||
@ -179,8 +182,6 @@ $(CSS_FULLTARGET): $(TYPESCRIPT_TARGET) $(VARIANTS_TARGET) $(ALL_CSS_SRC) $(wild
|
|||||||
# mv $(CSS_BUNDLE) $(DATA)/web/css/$(CSSVERSION)bundle.css
|
# mv $(CSS_BUNDLE) $(DATA)/web/css/$(CSSVERSION)bundle.css
|
||||||
# npx postcss -o $(CSS_TARGET) $(CSS_TARGET)
|
# npx postcss -o $(CSS_TARGET) $(CSS_TARGET)
|
||||||
|
|
||||||
bundle-css: tailwind
|
|
||||||
|
|
||||||
INLINE_SRC = html/crash.html
|
INLINE_SRC = html/crash.html
|
||||||
INLINE_TARGET = $(DATA)/crash.html
|
INLINE_TARGET = $(DATA)/crash.html
|
||||||
$(INLINE_TARGET): $(CSS_FULLTARGET) $(INLINE_SRC)
|
$(INLINE_TARGET): $(CSS_FULLTARGET) $(INLINE_SRC)
|
||||||
@ -197,6 +198,8 @@ COPY_SRC = images/banner.svg jfa-go.service LICENSE $(LANG_SRC) $(STATIC_SRC)
|
|||||||
COPY_TARGET = $(DATA)/jfa-go.service
|
COPY_TARGET = $(DATA)/jfa-go.service
|
||||||
# $(DATA)/LICENSE $(LANG_TARGET) $(STATIC_TARGET) $(DATA)/web/css/$(CSSVERSION)bundle.css
|
# $(DATA)/LICENSE $(LANG_TARGET) $(STATIC_TARGET) $(DATA)/web/css/$(CSSVERSION)bundle.css
|
||||||
$(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
|
$(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
|
||||||
|
$(info copying $(CONFIG_BASE))
|
||||||
|
cp $(CONFIG_BASE) $(DATA)/
|
||||||
$(info copying crash page)
|
$(info copying crash page)
|
||||||
cp $(DATA)/crash.html $(DATA)/html/
|
cp $(DATA)/crash.html $(DATA)/html/
|
||||||
$(info copying static data)
|
$(info copying static data)
|
||||||
@ -209,11 +212,11 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
|
|||||||
cp -r lang $(DATA)/
|
cp -r lang $(DATA)/
|
||||||
cp LICENSE $(DATA)/
|
cp LICENSE $(DATA)/
|
||||||
|
|
||||||
precompile: $(CONFIG_DESCRIPTION) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
|
precompile: $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
|
||||||
|
|
||||||
GO_SRC = $(shell find ./ -name "*.go")
|
GO_SRC = $(shell find ./ -name "*.go")
|
||||||
GO_TARGET = build/jfa-go
|
GO_TARGET = build/jfa-go
|
||||||
$(GO_TARGET): $(CONFIG_DESCRIPTION) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
|
$(GO_TARGET): $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
|
||||||
$(info Downloading deps)
|
$(info Downloading deps)
|
||||||
$(GOBINARY) mod download
|
$(GOBINARY) mod download
|
||||||
$(info Building)
|
$(info Building)
|
||||||
|
153
api.go
153
api.go
@ -6,6 +6,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/hrfee/jfa-go/common"
|
||||||
lm "github.com/hrfee/jfa-go/logmessages"
|
lm "github.com/hrfee/jfa-go/logmessages"
|
||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
"github.com/itchyny/timefmt-go"
|
"github.com/itchyny/timefmt-go"
|
||||||
@ -229,102 +230,15 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) {
|
|||||||
|
|
||||||
// @Summary Get jfa-go configuration.
|
// @Summary Get jfa-go configuration.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} settings "Uses the same format as config-base.json"
|
// @Success 200 {object} common.Config "Uses the same format as config-base.json"
|
||||||
// @Router /config [get]
|
// @Router /config [get]
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Configuration
|
// @tags Configuration
|
||||||
func (app *appContext) GetConfig(gc *gin.Context) {
|
func (app *appContext) GetConfig(gc *gin.Context) {
|
||||||
resp := app.configBase
|
|
||||||
// Load language options
|
|
||||||
formOptions := app.storage.lang.User.getOptions()
|
|
||||||
fl := resp.Sections["ui"].Settings["language-form"]
|
|
||||||
fl.Options = formOptions
|
|
||||||
fl.Value = app.config.Section("ui").Key("language-form").MustString("en-us")
|
|
||||||
pwrOptions := app.storage.lang.PasswordReset.getOptions()
|
|
||||||
pl := resp.Sections["password_resets"].Settings["language"]
|
|
||||||
pl.Options = pwrOptions
|
|
||||||
pl.Value = app.config.Section("password_resets").Key("language").MustString("en-us")
|
|
||||||
adminOptions := app.storage.lang.Admin.getOptions()
|
|
||||||
al := resp.Sections["ui"].Settings["language-admin"]
|
|
||||||
al.Options = adminOptions
|
|
||||||
al.Value = app.config.Section("ui").Key("language-admin").MustString("en-us")
|
|
||||||
emailOptions := app.storage.lang.Email.getOptions()
|
|
||||||
el := resp.Sections["email"].Settings["language"]
|
|
||||||
el.Options = emailOptions
|
|
||||||
el.Value = app.config.Section("email").Key("language").MustString("en-us")
|
|
||||||
telegramOptions := app.storage.lang.Email.getOptions()
|
|
||||||
tl := resp.Sections["telegram"].Settings["language"]
|
|
||||||
tl.Options = telegramOptions
|
|
||||||
tl.Value = app.config.Section("telegram").Key("language").MustString("en-us")
|
|
||||||
if updater == "" {
|
|
||||||
delete(resp.Sections, "updates")
|
|
||||||
for i, v := range resp.Order {
|
|
||||||
if v == "updates" {
|
|
||||||
resp.Order = append(resp.Order[:i], resp.Order[i+1:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if PLATFORM == "windows" {
|
|
||||||
delete(resp.Sections["smtp"].Settings, "ssl_cert")
|
|
||||||
for i, v := range resp.Sections["smtp"].Order {
|
|
||||||
if v == "ssl_cert" {
|
|
||||||
sect := resp.Sections["smtp"]
|
|
||||||
sect.Order = append(sect.Order[:i], sect.Order[i+1:]...)
|
|
||||||
resp.Sections["smtp"] = sect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !MatrixE2EE() {
|
|
||||||
delete(resp.Sections["matrix"].Settings, "encryption")
|
|
||||||
for i, v := range resp.Sections["matrix"].Order {
|
|
||||||
if v == "encryption" {
|
|
||||||
sect := resp.Sections["matrix"]
|
|
||||||
sect.Order = append(sect.Order[:i], sect.Order[i+1:]...)
|
|
||||||
resp.Sections["matrix"] = sect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for sectName, section := range resp.Sections {
|
|
||||||
for settingName, setting := range section.Settings {
|
|
||||||
val := app.config.Section(sectName).Key(settingName)
|
|
||||||
s := resp.Sections[sectName].Settings[settingName]
|
|
||||||
switch setting.Type {
|
|
||||||
case "list":
|
|
||||||
s.Value = val.StringsWithShadows("|")
|
|
||||||
case "text", "email", "select", "password", "note":
|
|
||||||
s.Value = val.MustString("")
|
|
||||||
case "number":
|
|
||||||
s.Value = val.MustInt(0)
|
|
||||||
case "bool":
|
|
||||||
s.Value = val.MustBool(false)
|
|
||||||
}
|
|
||||||
resp.Sections[sectName].Settings[settingName] = s
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if discordEnabled {
|
if discordEnabled {
|
||||||
r, err := app.discord.ListRoles()
|
app.PatchConfigDiscordRoles()
|
||||||
if err == nil {
|
|
||||||
roles := make([][2]string, len(r)+1)
|
|
||||||
roles[0] = [2]string{"", "None"}
|
|
||||||
for i, role := range r {
|
|
||||||
roles[i+1] = role
|
|
||||||
}
|
|
||||||
s := resp.Sections["discord"].Settings["apply_role"]
|
|
||||||
s.Options = roles
|
|
||||||
resp.Sections["discord"].Settings["apply_role"] = s
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
gc.JSON(200, app.patchedConfig)
|
||||||
resp.Sections["ui"].Settings["language-form"] = fl
|
|
||||||
resp.Sections["ui"].Settings["language-admin"] = al
|
|
||||||
resp.Sections["email"].Settings["language"] = el
|
|
||||||
resp.Sections["password_resets"].Settings["language"] = pl
|
|
||||||
resp.Sections["telegram"].Settings["language"] = tl
|
|
||||||
resp.Sections["discord"].Settings["language"] = tl
|
|
||||||
resp.Sections["matrix"].Settings["language"] = tl
|
|
||||||
|
|
||||||
gc.JSON(200, resp)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Summary Modify app config.
|
// @Summary Modify app config.
|
||||||
@ -340,35 +254,46 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
gc.BindJSON(&req)
|
gc.BindJSON(&req)
|
||||||
// Load a new config, as we set various default values in app.config that shouldn't be stored.
|
// Load a new config, as we set various default values in app.config that shouldn't be stored.
|
||||||
tempConfig, _ := ini.ShadowLoad(app.configPath)
|
tempConfig, _ := ini.ShadowLoad(app.configPath)
|
||||||
for section, settings := range req {
|
for _, section := range app.configBase.Sections {
|
||||||
if section != "restart-program" {
|
ns, ok := req[section.Section]
|
||||||
_, err := tempConfig.GetSection(section)
|
if !ok {
|
||||||
if err != nil {
|
continue
|
||||||
tempConfig.NewSection(section)
|
}
|
||||||
|
newSection := ns.(map[string]any)
|
||||||
|
iniSection, err := tempConfig.GetSection(section.Section)
|
||||||
|
if err != nil {
|
||||||
|
iniSection, _ = tempConfig.NewSection(section.Section)
|
||||||
|
}
|
||||||
|
for _, setting := range section.Settings {
|
||||||
|
newValue, ok := newSection[setting.Setting]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
for setting, value := range settings.(map[string]interface{}) {
|
// Patch disabled to actually be an empty string
|
||||||
if section == "email" && setting == "method" && value == "disabled" {
|
if section.Section == "email" && setting.Setting == "method" && newValue == "disabled" {
|
||||||
value = ""
|
newValue = ""
|
||||||
}
|
}
|
||||||
if (section == "discord" || section == "matrix") && setting == "language" {
|
// Copy language preference for chatbots to root one in "telegram"
|
||||||
tempConfig.Section("telegram").Key("language").SetValue(value.(string))
|
if (section.Section == "discord" || section.Section == "matrix") && setting.Setting == "language" {
|
||||||
} else if app.configBase.Sections[section].Settings[setting].Type == "list" {
|
iniSection.Key("language").SetValue(newValue.(string))
|
||||||
splitValues := strings.Split(value.(string), "|")
|
} else if setting.Type == common.ListType {
|
||||||
// Delete the key first to get rid of any shadow values
|
splitValues := strings.Split(newValue.(string), "|")
|
||||||
tempConfig.Section(section).DeleteKey(setting)
|
// Delete the key first to get rid of any shadow values
|
||||||
for i, v := range splitValues {
|
iniSection.DeleteKey(setting.Setting)
|
||||||
if i == 0 {
|
for i, v := range splitValues {
|
||||||
tempConfig.Section(section).Key(setting).SetValue(v)
|
if i == 0 {
|
||||||
} else {
|
iniSection.Key(setting.Setting).SetValue(v)
|
||||||
tempConfig.Section(section).Key(setting).AddShadow(v)
|
} else {
|
||||||
}
|
iniSection.Key(setting.Setting).AddShadow(v)
|
||||||
}
|
}
|
||||||
} else if value.(string) != app.config.Section(section).Key(setting).MustString("") {
|
|
||||||
tempConfig.Section(section).Key(setting).SetValue(value.(string))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if newValue.(string) != iniSection.Key(setting.Setting).MustString("") {
|
||||||
|
iniSection.Key(setting.Setting).SetValue(newValue.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempConfig.Section("").Key("first_run").SetValue("false")
|
tempConfig.Section("").Key("first_run").SetValue("false")
|
||||||
if err := tempConfig.SaveTo(app.configPath); err != nil {
|
if err := tempConfig.SaveTo(app.configPath); err != nil {
|
||||||
app.err.Printf(lm.FailedWriting, app.configPath, err)
|
app.err.Printf(lm.FailedWriting, app.configPath, err)
|
||||||
@ -381,6 +306,8 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
app.Restart()
|
app.Restart()
|
||||||
}
|
}
|
||||||
app.loadConfig()
|
app.loadConfig()
|
||||||
|
// Patch new settings for next GetConfig
|
||||||
|
app.PatchConfigBase()
|
||||||
// Reinitialize password validator on config change, as opposed to every applicable request like in python.
|
// Reinitialize password validator on config change, as opposed to every applicable request like in python.
|
||||||
if _, ok := req["password_validation"]; ok {
|
if _, ok := req["password_validation"]; ok {
|
||||||
validatorConf := ValidatorConf{
|
validatorConf := ValidatorConf{
|
||||||
|
62
common/config.go
Normal file
62
common/config.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
type SectionMeta struct {
|
||||||
|
Name string `json:"name" yaml:"name" example:"My Section"` // friendly name of the section
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Advanced bool `json:"advanced,omitempty" yaml:"advanced,omitempty"`
|
||||||
|
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
||||||
|
DependsTrue string `json:"depends_true,omitempty" yaml:"depends_true,omitempty"`
|
||||||
|
DependsFalse string `json:"depends_false,omitempty" yaml:"depends_false,omitempty"`
|
||||||
|
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option [2]string
|
||||||
|
|
||||||
|
type SettingType string
|
||||||
|
|
||||||
|
var (
|
||||||
|
BoolType SettingType = "bool"
|
||||||
|
SelectType SettingType = "select"
|
||||||
|
TextType SettingType = "text"
|
||||||
|
PasswordType SettingType = "password"
|
||||||
|
NumberType SettingType = "number"
|
||||||
|
NoteType SettingType = "note"
|
||||||
|
EmailType SettingType = "email"
|
||||||
|
ListType SettingType = "list"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Setting struct {
|
||||||
|
Setting string `json:"setting" yaml:"setting" example:"my_setting"`
|
||||||
|
Name string `json:"name" yaml:"name" example:"My Setting"`
|
||||||
|
Description string `json:"description" yaml:"description"`
|
||||||
|
Required bool `json:"required" yaml:"required"`
|
||||||
|
RequiresRestart bool `json:"requires_restart" yaml:"requires_restart"`
|
||||||
|
Advanced bool `json:"advanced,omitempty" yaml:"advanced,omitempty"`
|
||||||
|
Type SettingType `json:"type" yaml:"type"` // Type (string, number, bool, etc.)
|
||||||
|
Value any `json:"value" yaml:"value"`
|
||||||
|
Options []Option `json:"options,omitempty" yaml:"options,omitempty"`
|
||||||
|
DependsTrue string `json:"depends_true,omitempty" yaml:"depends_true,omitempty"` // If specified, this field is enabled when the specified bool setting is enabled.
|
||||||
|
DependsFalse string `json:"depends_false,omitempty" yaml:"depends_false,omitempty"` // If specified, opposite behaviour of DependsTrue.
|
||||||
|
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||||
|
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||||
|
WikiLink string `json:"wiki_link,omitempty" yaml:"wiki_link,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Section struct {
|
||||||
|
Section string `json:"section" yaml:"section" example:"my_section"`
|
||||||
|
Meta SectionMeta `json:"meta" yaml:"meta"`
|
||||||
|
Settings []Setting `json:"settings" yaml:"settings"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Sections []Section `json:"sections" yaml:"sections"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) removeSection(section string) {
|
||||||
|
for i, v := range c.Sections {
|
||||||
|
if v.Section == section {
|
||||||
|
c.Sections = append(c.Sections[:i], c.Sections[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
96
config.go
96
config.go
@ -10,6 +10,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hrfee/jfa-go/common"
|
||||||
"github.com/hrfee/jfa-go/easyproxy"
|
"github.com/hrfee/jfa-go/easyproxy"
|
||||||
lm "github.com/hrfee/jfa-go/logmessages"
|
lm "github.com/hrfee/jfa-go/logmessages"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
@ -250,3 +251,98 @@ func (app *appContext) loadConfig() error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *appContext) PatchConfigBase() {
|
||||||
|
conf := app.configBase
|
||||||
|
// Load language options
|
||||||
|
formOptions := app.storage.lang.User.getOptions()
|
||||||
|
pwrOptions := app.storage.lang.PasswordReset.getOptions()
|
||||||
|
adminOptions := app.storage.lang.Admin.getOptions()
|
||||||
|
emailOptions := app.storage.lang.Email.getOptions()
|
||||||
|
telegramOptions := app.storage.lang.Email.getOptions()
|
||||||
|
|
||||||
|
for i, section := range app.configBase.Sections {
|
||||||
|
if section.Section == "updates" && updater == "" {
|
||||||
|
section.Meta.Disabled = true
|
||||||
|
}
|
||||||
|
for j, setting := range section.Settings {
|
||||||
|
if section.Section == "ui" {
|
||||||
|
if setting.Setting == "language-form" {
|
||||||
|
setting.Options = formOptions
|
||||||
|
setting.Value = "en-us"
|
||||||
|
} else if setting.Setting == "language-admin" {
|
||||||
|
setting.Options = adminOptions
|
||||||
|
setting.Value = "en-us"
|
||||||
|
}
|
||||||
|
} else if section.Section == "password_resets" {
|
||||||
|
if setting.Setting == "language" {
|
||||||
|
setting.Options = pwrOptions
|
||||||
|
setting.Value = "en-us"
|
||||||
|
}
|
||||||
|
} else if section.Section == "email" {
|
||||||
|
if setting.Setting == "language" {
|
||||||
|
setting.Options = emailOptions
|
||||||
|
setting.Value = "en-us"
|
||||||
|
}
|
||||||
|
} else if section.Section == "telegram" {
|
||||||
|
if setting.Setting == "language" {
|
||||||
|
setting.Options = telegramOptions
|
||||||
|
setting.Value = "en-us"
|
||||||
|
}
|
||||||
|
} else if section.Section == "smtp" {
|
||||||
|
if setting.Setting == "ssl_cert" && PLATFORM == "windows" {
|
||||||
|
// Not accurate but the effect is hiding the option, which we want.
|
||||||
|
setting.Deprecated = true
|
||||||
|
}
|
||||||
|
} else if section.Section == "matrix" {
|
||||||
|
if setting.Setting == "encryption" && !MatrixE2EE() {
|
||||||
|
// Not accurate but the effect is hiding the option, which we want.
|
||||||
|
setting.Deprecated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val := app.config.Section(section.Section).Key(setting.Setting)
|
||||||
|
switch setting.Type {
|
||||||
|
case "list":
|
||||||
|
setting.Value = val.StringsWithShadows("|")
|
||||||
|
case "text", "email", "select", "password", "note":
|
||||||
|
setting.Value = val.MustString("")
|
||||||
|
case "number":
|
||||||
|
setting.Value = val.MustInt(0)
|
||||||
|
case "bool":
|
||||||
|
setting.Value = val.MustBool(false)
|
||||||
|
}
|
||||||
|
section.Settings[j] = setting
|
||||||
|
}
|
||||||
|
conf.Sections[i] = section
|
||||||
|
}
|
||||||
|
app.patchedConfig = conf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *appContext) PatchConfigDiscordRoles() {
|
||||||
|
if !discordEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
r, err := app.discord.ListRoles()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
roles := make([]common.Option, len(r)+1)
|
||||||
|
roles[0] = common.Option{"", "None"}
|
||||||
|
for i, role := range r {
|
||||||
|
roles[i+1] = role
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, section := range app.patchedConfig.Sections {
|
||||||
|
if section.Section != "discord" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j, setting := range section.Settings {
|
||||||
|
if setting.Setting != "apply_role" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
setting.Options = roles
|
||||||
|
section.Settings[j] = setting
|
||||||
|
}
|
||||||
|
app.patchedConfig.Sections[i] = section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
1577
config/config-base.yaml
Normal file
1577
config/config-base.yaml
Normal file
File diff suppressed because it is too large
Load Diff
35
config/config-json-to-new-yaml.py
Normal file
35
config/config-json-to-new-yaml.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from ruamel.yaml import YAML
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import sys
|
||||||
|
yaml = YAML()
|
||||||
|
|
||||||
|
# c = yaml.load(Path(sys.argv[len(sys.argv)-1]))
|
||||||
|
with open(sys.argv[len(sys.argv)-1], 'r') as f:
|
||||||
|
c = json.load(f)
|
||||||
|
|
||||||
|
c.pop("order")
|
||||||
|
|
||||||
|
c1 = c.copy()
|
||||||
|
c1["sections"] = []
|
||||||
|
for section in c["sections"]:
|
||||||
|
codeSection = { "section": section }
|
||||||
|
s = codeSection | c["sections"][section]
|
||||||
|
s.pop("order")
|
||||||
|
c1["sections"].append(s)
|
||||||
|
|
||||||
|
c2 = c.copy()
|
||||||
|
c2["sections"] = []
|
||||||
|
|
||||||
|
for section in c1["sections"]:
|
||||||
|
sArray = []
|
||||||
|
for setting in section["settings"]:
|
||||||
|
codeSetting = { "setting": setting }
|
||||||
|
s = codeSetting | section["settings"][setting]
|
||||||
|
sArray.append(s)
|
||||||
|
|
||||||
|
section["settings"] = sArray
|
||||||
|
c2["sections"].append(section)
|
||||||
|
|
||||||
|
|
||||||
|
yaml.dump(c2, sys.stdout)
|
40
config/gen-rough-schema.py
Normal file
40
config/gen-rough-schema.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sectionSchema = {}
|
||||||
|
metaSchema = {}
|
||||||
|
settingSchema = {}
|
||||||
|
typeValues = {}
|
||||||
|
|
||||||
|
# c = yaml.load(Path(sys.argv[len(sys.argv)-1]))
|
||||||
|
with open(sys.argv[len(sys.argv)-1], 'r') as f:
|
||||||
|
c = json.load(f)
|
||||||
|
|
||||||
|
for section in c["sections"]:
|
||||||
|
for key in c["sections"][section]:
|
||||||
|
sectionSchema[key] = True
|
||||||
|
|
||||||
|
for key in c["sections"][section]["meta"]:
|
||||||
|
metaSchema[key] = c["sections"][section]["meta"][key]
|
||||||
|
|
||||||
|
for setting in c["sections"][section]["settings"]:
|
||||||
|
for field in c["sections"][section]["settings"][setting]:
|
||||||
|
settingSchema[field] = c["sections"][section]["settings"][setting][field]
|
||||||
|
typeValues[c["sections"][section]["settings"][setting]["type"]] = True
|
||||||
|
|
||||||
|
print("Section Content:")
|
||||||
|
for v in sectionSchema:
|
||||||
|
print(v)
|
||||||
|
print("---")
|
||||||
|
print("Meta Schema")
|
||||||
|
for v in metaSchema:
|
||||||
|
print(v, "=", type(metaSchema[v]))
|
||||||
|
print("---")
|
||||||
|
print("Setting Schema")
|
||||||
|
for v in settingSchema:
|
||||||
|
print(v, "=", type(settingSchema[v]))
|
||||||
|
print("---")
|
||||||
|
print("Possible Types")
|
||||||
|
for v in typeValues:
|
||||||
|
print(v)
|
||||||
|
|
38
lang.go
38
lang.go
@ -1,5 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "github.com/hrfee/jfa-go/common"
|
||||||
|
|
||||||
type langMeta struct {
|
type langMeta struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
// Language to fall back on if strings are missing. Defaults to en-us.
|
// Language to fall back on if strings are missing. Defaults to en-us.
|
||||||
@ -13,11 +15,11 @@ type quantityString struct {
|
|||||||
|
|
||||||
type adminLangs map[string]adminLang
|
type adminLangs map[string]adminLang
|
||||||
|
|
||||||
func (ls *adminLangs) getOptions() [][2]string {
|
func (ls *adminLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ls))
|
opts := make([]common.Option, len(*ls))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
@ -42,11 +44,11 @@ type adminLang struct {
|
|||||||
|
|
||||||
type userLangs map[string]userLang
|
type userLangs map[string]userLang
|
||||||
|
|
||||||
func (ls *userLangs) getOptions() [][2]string {
|
func (ls *userLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ls))
|
opts := make([]common.Option, len(*ls))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
@ -65,11 +67,11 @@ type userLang struct {
|
|||||||
|
|
||||||
type pwrLangs map[string]pwrLang
|
type pwrLangs map[string]pwrLang
|
||||||
|
|
||||||
func (ls *pwrLangs) getOptions() [][2]string {
|
func (ls *pwrLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ls))
|
opts := make([]common.Option, len(*ls))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
@ -82,11 +84,11 @@ type pwrLang struct {
|
|||||||
|
|
||||||
type emailLangs map[string]emailLang
|
type emailLangs map[string]emailLang
|
||||||
|
|
||||||
func (ls *emailLangs) getOptions() [][2]string {
|
func (ls *emailLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ls))
|
opts := make([]common.Option, len(*ls))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
@ -135,11 +137,11 @@ type setupLang struct {
|
|||||||
JSON string
|
JSON string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *setupLangs) getOptions() [][2]string {
|
func (ls *setupLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ls))
|
opts := make([]common.Option, len(*ls))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
@ -152,11 +154,11 @@ type telegramLang struct {
|
|||||||
Strings langSection `json:"strings"`
|
Strings langSection `json:"strings"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ts *telegramLangs) getOptions() [][2]string {
|
func (ts *telegramLangs) getOptions() []common.Option {
|
||||||
opts := make([][2]string, len(*ts))
|
opts := make([]common.Option, len(*ts))
|
||||||
i := 0
|
i := 0
|
||||||
for key, lang := range *ts {
|
for key, lang := range *ts {
|
||||||
opts[i] = [2]string{key, lang.Meta.Name}
|
opts[i] = common.Option{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
|
10
main.go
10
main.go
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/hrfee/mediabrowser"
|
"github.com/hrfee/mediabrowser"
|
||||||
"github.com/lithammer/shortuuid/v3"
|
"github.com/lithammer/shortuuid/v3"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -93,7 +94,8 @@ type appContext struct {
|
|||||||
config *ini.File
|
config *ini.File
|
||||||
configPath string
|
configPath string
|
||||||
configBasePath string
|
configBasePath string
|
||||||
configBase settings
|
configBase common.Config
|
||||||
|
patchedConfig common.Config
|
||||||
dataPath string
|
dataPath string
|
||||||
webFS httpFS
|
webFS httpFS
|
||||||
cssClass string // Default theme, "light"|"dark".
|
cssClass string // Default theme, "light"|"dark".
|
||||||
@ -388,9 +390,11 @@ func start(asDaemon, firstCall bool) {
|
|||||||
defer app.storage.db.Close()
|
defer app.storage.db.Close()
|
||||||
|
|
||||||
// Read config-base for settings on web.
|
// Read config-base for settings on web.
|
||||||
app.configBasePath = "config-base.json"
|
app.configBasePath = "config-base.yaml"
|
||||||
configBase, _ := fs.ReadFile(localFS, app.configBasePath)
|
configBase, _ := fs.ReadFile(localFS, app.configBasePath)
|
||||||
json.Unmarshal(configBase, &app.configBase)
|
yaml.Unmarshal(configBase, &app.configBase)
|
||||||
|
// copy it to app.patchedConfig, and patch in settings from app.config, and language stuff.
|
||||||
|
app.PatchConfigBase()
|
||||||
|
|
||||||
secret, err := generateSecret(16)
|
secret, err := generateSecret(16)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
37
models.go
37
models.go
@ -212,43 +212,6 @@ type errorListDTO map[string]map[string]string
|
|||||||
|
|
||||||
type configDTO map[string]interface{}
|
type configDTO map[string]interface{}
|
||||||
|
|
||||||
// Below are for sending config
|
|
||||||
|
|
||||||
type meta struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Advanced bool `json:"advanced,omitempty"`
|
|
||||||
DependsTrue string `json:"depends_true,omitempty"`
|
|
||||||
DependsFalse string `json:"depends_false,omitempty"`
|
|
||||||
WikiLink string `json:"wiki_link,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type setting struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Required bool `json:"required"`
|
|
||||||
Advanced bool `json:"advanced,omitempty"`
|
|
||||||
RequiresRestart bool `json:"requires_restart"`
|
|
||||||
Type string `json:"type"` // Type (string, number, bool, etc.)
|
|
||||||
Value interface{} `json:"value"`
|
|
||||||
Options [][2]string `json:"options,omitempty"`
|
|
||||||
DependsTrue string `json:"depends_true,omitempty"` // If specified, this field is enabled when the specified bool setting is enabled.
|
|
||||||
DependsFalse string `json:"depends_false,omitempty"` // If specified, opposite behaviour of DependsTrue.
|
|
||||||
Style string `json:"style,omitempty"`
|
|
||||||
Deprecated bool `json:"deprecated,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type section struct {
|
|
||||||
Meta meta `json:"meta"`
|
|
||||||
Order []string `json:"order"`
|
|
||||||
Settings map[string]setting `json:"settings"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type settings struct {
|
|
||||||
Order []string `json:"order"`
|
|
||||||
Sections map[string]section `json:"sections"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type langDTO map[string]string
|
type langDTO map[string]string
|
||||||
|
|
||||||
type emailListDTO map[string]emailListEl
|
type emailListDTO map[string]emailListEl
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
# Since go doesn't order its json, this script adds ordered lists
|
|
||||||
# of section/setting names for the settings tab to use.
|
|
||||||
import json, argparse
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-i", "--input", help="input config base from jf-accounts")
|
|
||||||
parser.add_argument("-o", "--output", help="output config base for jfa-go")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
with open(args.input, 'r') as f:
|
|
||||||
config = json.load(f)
|
|
||||||
|
|
||||||
newconfig = {"sections": {}, "order": []}
|
|
||||||
|
|
||||||
for sect in config["sections"]:
|
|
||||||
newconfig["order"].append(sect)
|
|
||||||
newconfig["sections"][sect] = {}
|
|
||||||
newconfig["sections"][sect]["order"] = []
|
|
||||||
newconfig["sections"][sect]["meta"] = config["sections"][sect]["meta"]
|
|
||||||
newconfig["sections"][sect]["settings"] = {}
|
|
||||||
for setting in config["sections"][sect]["settings"]:
|
|
||||||
newconfig["sections"][sect]["order"].append(setting)
|
|
||||||
newconfig["sections"][sect]["settings"][setting] = config["sections"][sect]["settings"][setting]
|
|
||||||
|
|
||||||
with open(args.output, 'w') as f:
|
|
||||||
f.write(json.dumps(newconfig, indent=4))
|
|
||||||
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
# Generates config file
|
|
||||||
import configparser
|
|
||||||
import json
|
|
||||||
import argparse
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
def fix_description(desc):
|
|
||||||
return "; " + desc.replace("\n", "\n; ")
|
|
||||||
|
|
||||||
def generate_ini(base_file, ini_file):
|
|
||||||
"""
|
|
||||||
Generates .ini file from config-base file.
|
|
||||||
"""
|
|
||||||
with open(Path(base_file), "r") as f:
|
|
||||||
config_base = json.load(f)
|
|
||||||
|
|
||||||
ini = configparser.RawConfigParser(allow_no_value=True)
|
|
||||||
|
|
||||||
for section in config_base["sections"]:
|
|
||||||
ini.add_section(section)
|
|
||||||
if "meta" in config_base["sections"][section]:
|
|
||||||
ini.set(section, fix_description(config_base["sections"][section]["meta"]["description"]))
|
|
||||||
for entry in config_base["sections"][section]["settings"]:
|
|
||||||
if config_base["sections"][section]["settings"][entry]["type"] == "note":
|
|
||||||
continue
|
|
||||||
if "description" in config_base["sections"][section]["settings"][entry]:
|
|
||||||
ini.set(section, fix_description(config_base["sections"][section]["settings"][entry]["description"]))
|
|
||||||
value = config_base["sections"][section]["settings"][entry]["value"]
|
|
||||||
if isinstance(value, bool):
|
|
||||||
value = str(value).lower()
|
|
||||||
else:
|
|
||||||
value = str(value)
|
|
||||||
ini.set(section, entry, value)
|
|
||||||
|
|
||||||
with open(Path(ini_file), "w") as config_file:
|
|
||||||
ini.write(config_file)
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = argparse.ArgumentParser()
|
|
||||||
parser.add_argument("-i", "--input", help="input config base from jf-accounts")
|
|
||||||
parser.add_argument("-o", "--output", help="output ini")
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
print(generate_ini(base_file=args.input, ini_file=args.output))
|
|
12
scripts/ini/go.mod
Normal file
12
scripts/ini/go.mod
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
module github.com/hrfee/jfa-go/scripts/ini
|
||||||
|
|
||||||
|
replace github.com/hrfee/jfa-go/common => ../../common
|
||||||
|
|
||||||
|
go 1.22.4
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/hrfee/jfa-go/common v0.0.0-20240824141650-fcdd4e451882 // indirect
|
||||||
|
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a // indirect
|
||||||
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
7
scripts/ini/go.sum
Normal file
7
scripts/ini/go.sum
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a h1:qbXZgCqb9eaPSJfLEXczQD2lxTv6jb6silMPIWW9j6o=
|
||||||
|
github.com/hrfee/jfa-go/logmessages v0.0.0-20240806200606-6308db495a0a/go.mod h1:c5HKkLayo0GrEUDlJwT12b67BL9cdPjP271Xlv/KDRQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
130
scripts/ini/main.go
Normal file
130
scripts/ini/main.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hrfee/jfa-go/common"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func fixDescription(desc string) string {
|
||||||
|
return "; " + strings.ReplaceAll(desc, "\n", "\n; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateIni(yamlPath string, iniPath string) {
|
||||||
|
yamlFile, err := os.ReadFile(yamlPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
configBase := common.Config{}
|
||||||
|
err = yaml.Unmarshal(yamlFile, &configBase)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
conf := ini.Empty()
|
||||||
|
|
||||||
|
for _, section := range configBase.Sections {
|
||||||
|
cSection, err := conf.NewSection(section.Section)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if section.Meta.Description != "" {
|
||||||
|
cSection.Comment = fixDescription(section.Meta.Description)
|
||||||
|
}
|
||||||
|
for _, setting := range section.Settings {
|
||||||
|
if setting.Type == common.NoteType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val := ""
|
||||||
|
if setting.Value != nil {
|
||||||
|
// Easy way to convert bools and numbers to strings,
|
||||||
|
// Instead of checking setting.Type
|
||||||
|
val = fmt.Sprintf("%v", setting.Value)
|
||||||
|
}
|
||||||
|
cKey, err := cSection.NewKey(setting.Setting, val)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if setting.Description != "" {
|
||||||
|
cKey.Comment = fixDescription(setting.Description)
|
||||||
|
}
|
||||||
|
// Explain how to use list type
|
||||||
|
if setting.Type == common.ListType {
|
||||||
|
if cKey.Comment != "" {
|
||||||
|
cKey.Comment += "\n"
|
||||||
|
}
|
||||||
|
cKey.Comment += `List type: duplicate and edit the line to add more entries.`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = conf.SaveTo(iniPath)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compares two inis, used to check this script does the equivalent of the old generate_ini.py.
|
||||||
|
func compareInis(p1, p2 string) {
|
||||||
|
cA, err := ini.ShadowLoad(p1)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cB, err := ini.ShadowLoad(p2)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pair := range [][2]*ini.File{{cA, cB}, {cB, cA}} {
|
||||||
|
s1 := pair[0].Sections()
|
||||||
|
s2 := pair[1].Sections()
|
||||||
|
for i := range s1 {
|
||||||
|
if s1[i].Name() != s2[i].Name() {
|
||||||
|
panic(fmt.Errorf("mismatching section order: s0[i]=%s, s1[i]=%s", s1[i].Name(), s2[i].Name()))
|
||||||
|
}
|
||||||
|
// fmt.Println("Section order matches")
|
||||||
|
st1 := s1[i].Keys()
|
||||||
|
st2 := s2[i].Keys()
|
||||||
|
for i := range st1 {
|
||||||
|
if st1[i].Name() != st2[i].Name() {
|
||||||
|
panic(fmt.Errorf("mismatching setting order: st1[i]=%s, st2[i]=%s", st1[i].Name(), st2[i].Name()))
|
||||||
|
}
|
||||||
|
if st1[i].Value() != st2[i].Value() {
|
||||||
|
panic(fmt.Errorf("mismatching setting values: st1[i]=%s, st2[i]=%s", st1[i].Value(), st2[i].Value()))
|
||||||
|
}
|
||||||
|
// fmt.Println("Setting matches")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var yamlPath string
|
||||||
|
var iniPath string
|
||||||
|
var comparePath string
|
||||||
|
flag.StringVar(&yamlPath, "in", "", "Input of the config base in yaml.")
|
||||||
|
flag.StringVar(&iniPath, "out", "", "Output path of an ini file.")
|
||||||
|
flag.StringVar(&comparePath, "comp", "", "Path to ini file to compare against.")
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if yamlPath == "" {
|
||||||
|
panic(errors.New("invalid yaml path"))
|
||||||
|
}
|
||||||
|
if iniPath == "" {
|
||||||
|
panic(errors.New("invalid ini path"))
|
||||||
|
}
|
||||||
|
|
||||||
|
generateIni(yamlPath, iniPath)
|
||||||
|
|
||||||
|
if comparePath != "" {
|
||||||
|
compareInis(iniPath, comparePath)
|
||||||
|
fmt.Println("Passed.")
|
||||||
|
}
|
||||||
|
}
|
@ -19,20 +19,33 @@ interface settingsChangedEvent extends Event {
|
|||||||
detail: string;
|
detail: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SettingType = string;
|
||||||
|
|
||||||
|
const BoolType: SettingType = "bool";
|
||||||
|
const SelectType: SettingType = "select";
|
||||||
|
const TextType: SettingType = "text";
|
||||||
|
const PasswordType: SettingType = "password";
|
||||||
|
const NumberType: SettingType = "number";
|
||||||
|
const NoteType: SettingType = "note";
|
||||||
|
const EmailType: SettingType = "email";
|
||||||
|
const ListType: SettingType = "list";
|
||||||
|
|
||||||
interface Meta {
|
interface Meta {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
depends_true?: string;
|
depends_true?: string;
|
||||||
depends_false?: string;
|
depends_false?: string;
|
||||||
wiki_link?: string;
|
wiki_link?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Setting {
|
interface Setting {
|
||||||
|
setting: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
required: boolean;
|
required?: boolean;
|
||||||
requires_restart: boolean;
|
requires_restart?: boolean;
|
||||||
advanced?: boolean;
|
advanced?: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
value: string | boolean | number | string[];
|
value: string | boolean | number | string[];
|
||||||
@ -67,17 +80,17 @@ class DOMSetting {
|
|||||||
protected _restart: HTMLSpanElement;
|
protected _restart: HTMLSpanElement;
|
||||||
protected _advanced: boolean;
|
protected _advanced: boolean;
|
||||||
protected _section: string;
|
protected _section: string;
|
||||||
protected _name: string;
|
setting: string;
|
||||||
|
|
||||||
hide = () => {
|
hide = () => {
|
||||||
this._hideEl.classList.add("unfocused");
|
this._hideEl.classList.add("unfocused");
|
||||||
const event = new CustomEvent(`settings-${this._section}-${this._name}`, { "detail": false })
|
const event = new CustomEvent(`settings-${this._section}-${this.setting}`, { "detail": false })
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
|
|
||||||
};
|
};
|
||||||
show = () => {
|
show = () => {
|
||||||
this._hideEl.classList.remove("unfocused");
|
this._hideEl.classList.remove("unfocused");
|
||||||
const event = new CustomEvent(`settings-${this._section}-${this._name}`, { "detail": this.valueAsString() })
|
const event = new CustomEvent(`settings-${this._section}-${this.setting}`, { "detail": this.valueAsString() })
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -142,8 +155,8 @@ class DOMSetting {
|
|||||||
valueAsString = (): string => { return ""+this.value; };
|
valueAsString = (): string => { return ""+this.value; };
|
||||||
|
|
||||||
onValueChange = () => {
|
onValueChange = () => {
|
||||||
const event = new CustomEvent(`settings-${this._section}-${this._name}`, { "detail": this.valueAsString() })
|
const event = new CustomEvent(`settings-${this._section}-${this.setting}`, { "detail": this.valueAsString() })
|
||||||
const setEvent = new CustomEvent(`settings-set-${this._section}-${this._name}`, { "detail": this.valueAsString() })
|
const setEvent = new CustomEvent(`settings-set-${this._section}-${this.setting}`, { "detail": this.valueAsString() })
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
document.dispatchEvent(setEvent);
|
document.dispatchEvent(setEvent);
|
||||||
if (this.requires_restart) { document.dispatchEvent(new CustomEvent("settings-requires-restart")); }
|
if (this.requires_restart) { document.dispatchEvent(new CustomEvent("settings-requires-restart")); }
|
||||||
@ -151,7 +164,7 @@ class DOMSetting {
|
|||||||
|
|
||||||
constructor(input: string, setting: Setting, section: string, name: string, inputOnTop: boolean = false) {
|
constructor(input: string, setting: Setting, section: string, name: string, inputOnTop: boolean = false) {
|
||||||
this._section = section;
|
this._section = section;
|
||||||
this._name = name;
|
this.setting = name;
|
||||||
this._container = document.createElement("div");
|
this._container = document.createElement("div");
|
||||||
this._container.classList.add("setting");
|
this._container.classList.add("setting");
|
||||||
this._container.setAttribute("data-name", name);
|
this._container.setAttribute("data-name", name);
|
||||||
@ -223,7 +236,7 @@ interface SText extends Setting {
|
|||||||
}
|
}
|
||||||
class DOMText extends DOMInput implements SText {
|
class DOMText extends DOMInput implements SText {
|
||||||
constructor(setting: Setting, section: string, name: string) { super("text", setting, section, name); }
|
constructor(setting: Setting, section: string, name: string) { super("text", setting, section, name); }
|
||||||
type: string = "text";
|
type: SettingType = TextType;
|
||||||
get value(): string { return this._input.value }
|
get value(): string { return this._input.value }
|
||||||
set value(v: string) { this._input.value = v; }
|
set value(v: string) { this._input.value = v; }
|
||||||
}
|
}
|
||||||
@ -233,7 +246,7 @@ interface SPassword extends Setting {
|
|||||||
}
|
}
|
||||||
class DOMPassword extends DOMInput implements SPassword {
|
class DOMPassword extends DOMInput implements SPassword {
|
||||||
constructor(setting: Setting, section: string, name: string) { super("password", setting, section, name); }
|
constructor(setting: Setting, section: string, name: string) { super("password", setting, section, name); }
|
||||||
type: string = "password";
|
type: SettingType = PasswordType;
|
||||||
get value(): string { return this._input.value }
|
get value(): string { return this._input.value }
|
||||||
set value(v: string) { this._input.value = v; }
|
set value(v: string) { this._input.value = v; }
|
||||||
}
|
}
|
||||||
@ -243,7 +256,7 @@ interface SEmail extends Setting {
|
|||||||
}
|
}
|
||||||
class DOMEmail extends DOMInput implements SEmail {
|
class DOMEmail extends DOMInput implements SEmail {
|
||||||
constructor(setting: Setting, section: string, name: string) { super("email", setting, section, name); }
|
constructor(setting: Setting, section: string, name: string) { super("email", setting, section, name); }
|
||||||
type: string = "email";
|
type: SettingType = EmailType;
|
||||||
get value(): string { return this._input.value }
|
get value(): string { return this._input.value }
|
||||||
set value(v: string) { this._input.value = v; }
|
set value(v: string) { this._input.value = v; }
|
||||||
}
|
}
|
||||||
@ -253,7 +266,7 @@ interface SNumber extends Setting {
|
|||||||
}
|
}
|
||||||
class DOMNumber extends DOMInput implements SNumber {
|
class DOMNumber extends DOMInput implements SNumber {
|
||||||
constructor(setting: Setting, section: string, name: string) { super("number", setting, section, name); }
|
constructor(setting: Setting, section: string, name: string) { super("number", setting, section, name); }
|
||||||
type: string = "number";
|
type: SettingType = NumberType;
|
||||||
get value(): number { return +this._input.value; }
|
get value(): number { return +this._input.value; }
|
||||||
set value(v: number) { this._input.value = ""+v; }
|
set value(v: number) { this._input.value = ""+v; }
|
||||||
}
|
}
|
||||||
@ -263,7 +276,7 @@ interface SList extends Setting {
|
|||||||
}
|
}
|
||||||
class DOMList extends DOMSetting implements SList {
|
class DOMList extends DOMSetting implements SList {
|
||||||
protected _inputs: HTMLDivElement;
|
protected _inputs: HTMLDivElement;
|
||||||
type: string = "list";
|
type: SettingType = ListType;
|
||||||
|
|
||||||
valueAsString = (): string => { return this.value.join("|"); };
|
valueAsString = (): string => { return this.value.join("|"); };
|
||||||
|
|
||||||
@ -334,7 +347,7 @@ interface SBool extends Setting {
|
|||||||
value: boolean;
|
value: boolean;
|
||||||
}
|
}
|
||||||
class DOMBool extends DOMSetting implements SBool {
|
class DOMBool extends DOMSetting implements SBool {
|
||||||
type: string = "bool";
|
type: SettingType = BoolType;
|
||||||
|
|
||||||
get value(): boolean { return this._input.checked; }
|
get value(): boolean { return this._input.checked; }
|
||||||
set value(state: boolean) { this._input.checked = state; }
|
set value(state: boolean) { this._input.checked = state; }
|
||||||
@ -357,7 +370,7 @@ interface SSelect extends Setting {
|
|||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
class DOMSelect extends DOMSetting implements SSelect {
|
class DOMSelect extends DOMSetting implements SSelect {
|
||||||
type: string = "bool";
|
type: SettingType = SelectType;
|
||||||
private _options: string[][];
|
private _options: string[][];
|
||||||
|
|
||||||
get options(): string[][] { return this._options; }
|
get options(): string[][] { return this._options; }
|
||||||
@ -395,7 +408,7 @@ interface SNote extends Setting {
|
|||||||
class DOMNote extends DOMSetting implements SNote {
|
class DOMNote extends DOMSetting implements SNote {
|
||||||
private _nameEl: HTMLElement;
|
private _nameEl: HTMLElement;
|
||||||
private _description: HTMLElement;
|
private _description: HTMLElement;
|
||||||
type: string = "note";
|
type: SettingType = NoteType;
|
||||||
private _style: string;
|
private _style: string;
|
||||||
|
|
||||||
// We're a note, no one depends on us so we don't need to broadcast a state change.
|
// We're a note, no one depends on us so we don't need to broadcast a state change.
|
||||||
@ -457,9 +470,9 @@ class DOMNote extends DOMSetting implements SNote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Section {
|
interface Section {
|
||||||
|
section: string;
|
||||||
meta: Meta;
|
meta: Meta;
|
||||||
order: string[];
|
settings: Setting[];
|
||||||
settings: { [settingName: string]: Setting };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class sectionPanel {
|
class sectionPanel {
|
||||||
@ -491,50 +504,49 @@ class sectionPanel {
|
|||||||
this.update(s);
|
this.update(s);
|
||||||
}
|
}
|
||||||
update = (s: Section) => {
|
update = (s: Section) => {
|
||||||
for (let name of s.order) {
|
for (let setting of s.settings) {
|
||||||
let setting: Setting = s.settings[name];
|
if (setting.setting in this._settings) {
|
||||||
if (name in this._settings) {
|
this._settings[setting.setting].update(setting);
|
||||||
this._settings[name].update(setting);
|
|
||||||
} else {
|
} else {
|
||||||
if (setting.deprecated) continue;
|
if (setting.deprecated) continue;
|
||||||
switch (setting.type) {
|
switch (setting.type) {
|
||||||
case "text":
|
case TextType:
|
||||||
setting = new DOMText(setting, this._sectionName, name);
|
setting = new DOMText(setting, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "password":
|
case PasswordType:
|
||||||
setting = new DOMPassword(setting, this._sectionName, name);
|
setting = new DOMPassword(setting, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "email":
|
case EmailType:
|
||||||
setting = new DOMEmail(setting, this._sectionName, name);
|
setting = new DOMEmail(setting, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "number":
|
case NumberType:
|
||||||
setting = new DOMNumber(setting, this._sectionName, name);
|
setting = new DOMNumber(setting, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "bool":
|
case BoolType:
|
||||||
setting = new DOMBool(setting as SBool, this._sectionName, name);
|
setting = new DOMBool(setting as SBool, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "select":
|
case SelectType:
|
||||||
setting = new DOMSelect(setting as SSelect, this._sectionName, name);
|
setting = new DOMSelect(setting as SSelect, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
case "note":
|
case NoteType:
|
||||||
setting = new DOMNote(setting as SNote, this._sectionName);
|
setting = new DOMNote(setting as SNote, this._sectionName);
|
||||||
break;
|
break;
|
||||||
case "list":
|
case ListType:
|
||||||
setting = new DOMList(setting as SList, this._sectionName, name);
|
setting = new DOMList(setting as SList, this._sectionName, setting.setting);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (setting.type != "note") {
|
if (setting.type != "note") {
|
||||||
this.values[name] = ""+setting.value;
|
this.values[setting.setting] = ""+setting.value;
|
||||||
// settings-section-name: Implies the setting changed or was shown/hidden.
|
// settings-section-name: Implies the setting changed or was shown/hidden.
|
||||||
// settings-set-section-name: Implies the setting changed.
|
// settings-set-section-name: Implies the setting changed.
|
||||||
document.addEventListener(`settings-set-${this._sectionName}-${name}`, (event: CustomEvent) => {
|
document.addEventListener(`settings-set-${this._sectionName}-${setting.setting}`, (event: CustomEvent) => {
|
||||||
// const oldValue = this.values[name];
|
// const oldValue = this.values[name];
|
||||||
this.values[name] = event.detail;
|
this.values[setting.setting] = event.detail;
|
||||||
document.dispatchEvent(new CustomEvent("settings-section-changed"));
|
document.dispatchEvent(new CustomEvent("settings-section-changed"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this._section.appendChild(setting.asElement());
|
this._section.appendChild(setting.asElement());
|
||||||
this._settings[name] = setting;
|
this._settings[setting.setting] = setting;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -552,8 +564,7 @@ class sectionPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
order: string[];
|
sections: Section[];
|
||||||
sections: { [sectionName: string]: Section };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class settingsList {
|
export class settingsList {
|
||||||
@ -854,65 +865,65 @@ export class settingsList {
|
|||||||
}
|
}
|
||||||
addLoader(this._loader, false, true);
|
addLoader(this._loader, false, true);
|
||||||
_get("/config", null, (req: XMLHttpRequest) => {
|
_get("/config", null, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4) {
|
if (req.readyState != 4) return;
|
||||||
if (req.status != 200) {
|
if (req.status != 200) {
|
||||||
window.notifications.customError("settingsLoadError", window.lang.notif("errorLoadSettings"));
|
window.notifications.customError("settingsLoadError", window.lang.notif("errorLoadSettings"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._settings = req.response as Settings;
|
this._settings = req.response as Settings;
|
||||||
for (let name of this._settings.order) {
|
for (let section of this._settings.sections) {
|
||||||
if (name in this._sections) {
|
if (section.meta.disabled) continue;
|
||||||
this._sections[name].update(this._settings.sections[name]);
|
if (section.section in this._sections) {
|
||||||
} else {
|
this._sections[section.section].update(section);
|
||||||
if (name == "messages" || name == "user_page") {
|
} else {
|
||||||
const editButton = document.createElement("div");
|
if (section.section == "messages" || section.section == "user_page") {
|
||||||
editButton.classList.add("tooltip", "left");
|
const editButton = document.createElement("div");
|
||||||
editButton.innerHTML = `
|
editButton.classList.add("tooltip", "left");
|
||||||
<span class="button ~neutral @low">
|
editButton.innerHTML = `
|
||||||
<i class="icon ri-edit-line"></i>
|
<span class="button ~neutral @low">
|
||||||
</span>
|
<i class="icon ri-edit-line"></i>
|
||||||
<span class="content sm">
|
</span>
|
||||||
${window.lang.get("strings", "customizeMessages")}
|
<span class="content sm">
|
||||||
</span>
|
${window.lang.get("strings", "customizeMessages")}
|
||||||
`;
|
</span>
|
||||||
(editButton.querySelector("span.button") as HTMLSpanElement).onclick = () => {
|
`;
|
||||||
this._messageEditor.showList(name == "messages" ? "email" : "user");
|
(editButton.querySelector("span.button") as HTMLSpanElement).onclick = () => {
|
||||||
};
|
this._messageEditor.showList(section.section == "messages" ? "email" : "user");
|
||||||
this.addSection(name, this._settings.sections[name], editButton);
|
};
|
||||||
} else if (name == "updates") {
|
this.addSection(section.section, section, editButton);
|
||||||
const icon = document.createElement("span") as HTMLSpanElement;
|
} else if (section.section == "updates") {
|
||||||
if (window.updater.updateAvailable) {
|
const icon = document.createElement("span") as HTMLSpanElement;
|
||||||
icon.classList.add("button", "~urge");
|
if (window.updater.updateAvailable) {
|
||||||
icon.innerHTML = `<i class="ri-download-line" title="${window.lang.strings("update")}"></i>`;
|
icon.classList.add("button", "~urge");
|
||||||
icon.onclick = () => window.updater.checkForUpdates(window.modals.updateInfo.show);
|
icon.innerHTML = `<i class="ri-download-line" title="${window.lang.strings("update")}"></i>`;
|
||||||
}
|
icon.onclick = () => window.updater.checkForUpdates(window.modals.updateInfo.show);
|
||||||
this.addSection(name, this._settings.sections[name], icon);
|
|
||||||
} else if (name == "matrix" && !window.matrixEnabled) {
|
|
||||||
const addButton = document.createElement("div");
|
|
||||||
addButton.classList.add("tooltip", "left");
|
|
||||||
addButton.innerHTML = `
|
|
||||||
<span class="button ~neutral @low">+</span>
|
|
||||||
<span class="content sm">
|
|
||||||
${window.lang.strings("linkMatrix")}
|
|
||||||
</span>
|
|
||||||
`;
|
|
||||||
(addButton.querySelector("span.button") as HTMLSpanElement).onclick = this._addMatrix;
|
|
||||||
this.addSection(name, this._settings.sections[name], addButton);
|
|
||||||
} else {
|
|
||||||
this.addSection(name, this._settings.sections[name]);
|
|
||||||
}
|
}
|
||||||
|
this.addSection(section.section, section, icon);
|
||||||
|
} else if (section.section == "matrix" && !window.matrixEnabled) {
|
||||||
|
const addButton = document.createElement("div");
|
||||||
|
addButton.classList.add("tooltip", "left");
|
||||||
|
addButton.innerHTML = `
|
||||||
|
<span class="button ~neutral @low">+</span>
|
||||||
|
<span class="content sm">
|
||||||
|
${window.lang.strings("linkMatrix")}
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
(addButton.querySelector("span.button") as HTMLSpanElement).onclick = this._addMatrix;
|
||||||
|
this.addSection(section.section, section, addButton);
|
||||||
|
} else {
|
||||||
|
this.addSection(section.section, section);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removeLoader(this._loader);
|
|
||||||
for (let i = 0; i < this._loader.children.length; i++) {
|
|
||||||
this._loader.children[i].classList.remove("invisible");
|
|
||||||
}
|
|
||||||
this._showPanel(this._settings.order[0]);
|
|
||||||
document.dispatchEvent(new CustomEvent("settings-loaded"));
|
|
||||||
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: false }));
|
|
||||||
this._saveButton.classList.add("unfocused");
|
|
||||||
this._needsRestart = false;
|
|
||||||
}
|
}
|
||||||
|
removeLoader(this._loader);
|
||||||
|
for (let i = 0; i < this._loader.children.length; i++) {
|
||||||
|
this._loader.children[i].classList.remove("invisible");
|
||||||
|
}
|
||||||
|
this._showPanel(this._settings.sections[0].section);
|
||||||
|
document.dispatchEvent(new CustomEvent("settings-loaded"));
|
||||||
|
document.dispatchEvent(new CustomEvent("settings-advancedState", { detail: false }));
|
||||||
|
this._saveButton.classList.add("unfocused");
|
||||||
|
this._needsRestart = false;
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -923,31 +934,31 @@ export class settingsList {
|
|||||||
if (query.replace(/\s+/g, "") == "") query = "";
|
if (query.replace(/\s+/g, "") == "") query = "";
|
||||||
|
|
||||||
let firstVisibleSection = "";
|
let firstVisibleSection = "";
|
||||||
for (let section of this._settings.order) {
|
for (let section of this._settings.sections) {
|
||||||
|
|
||||||
let dependencyCard = this._sections[section].asElement().querySelector(".settings-dependency-message");
|
let dependencyCard = this._sections[section.section].asElement().querySelector(".settings-dependency-message");
|
||||||
if (dependencyCard) dependencyCard.remove();
|
if (dependencyCard) dependencyCard.remove();
|
||||||
dependencyCard = null;
|
dependencyCard = null;
|
||||||
let dependencyList = null;
|
let dependencyList = null;
|
||||||
|
|
||||||
// hide button, unhide if matched
|
// hide button, unhide if matched
|
||||||
this._buttons[section].classList.add("unfocused");
|
this._buttons[section.section].classList.add("unfocused");
|
||||||
|
|
||||||
let matchedSection = false;
|
let matchedSection = false;
|
||||||
|
|
||||||
if (section.toLowerCase().includes(query) ||
|
if (section.section.toLowerCase().includes(query) ||
|
||||||
this._settings.sections[section].meta.name.toLowerCase().includes(query) ||
|
section.meta.name.toLowerCase().includes(query) ||
|
||||||
this._settings.sections[section].meta.description.toLowerCase().includes(query)) {
|
section.meta.description.toLowerCase().includes(query)) {
|
||||||
if ((this._settings.sections[section].meta.advanced && this._advanced) || !(this._settings.sections[section].meta.advanced)) {
|
if ((section.meta.advanced && this._advanced) || !(section.meta.advanced)) {
|
||||||
this._buttons[section].classList.remove("unfocused");
|
this._buttons[section.section].classList.remove("unfocused");
|
||||||
firstVisibleSection = firstVisibleSection || section;
|
firstVisibleSection = firstVisibleSection || section.section;
|
||||||
matchedSection = true;
|
matchedSection = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sectionElement = this._sections[section].asElement();
|
const sectionElement = this._sections[section.section].asElement();
|
||||||
for (let setting of this._settings.sections[section].order) {
|
for (let setting of section.settings) {
|
||||||
if (this._settings.sections[section].settings[setting].type == "note") continue;
|
if (setting.type == "note") continue;
|
||||||
const element = sectionElement.querySelector(`div[data-name="${setting}"]`) as HTMLElement;
|
const element = sectionElement.querySelector(`div[data-name="${setting.setting}"]`) as HTMLElement;
|
||||||
|
|
||||||
// If we match the whole section, don't bother searching settings.
|
// If we match the whole section, don't bother searching settings.
|
||||||
if (matchedSection) {
|
if (matchedSection) {
|
||||||
@ -959,17 +970,17 @@ export class settingsList {
|
|||||||
// element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
// element.classList.remove("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
||||||
element.classList.add("opacity-50", "pointer-events-none");
|
element.classList.add("opacity-50", "pointer-events-none");
|
||||||
element.setAttribute("aria-disabled", "true");
|
element.setAttribute("aria-disabled", "true");
|
||||||
if (setting.toLowerCase().includes(query) ||
|
if (setting.setting.toLowerCase().includes(query) ||
|
||||||
this._settings.sections[section].settings[setting].name.toLowerCase().includes(query) ||
|
setting.name.toLowerCase().includes(query) ||
|
||||||
this._settings.sections[section].settings[setting].description.toLowerCase().includes(query) ||
|
setting.description.toLowerCase().includes(query) ||
|
||||||
String(this._settings.sections[section].settings[setting].value).toLowerCase().includes(query)) {
|
String(setting.value).toLowerCase().includes(query)) {
|
||||||
if ((this._settings.sections[section].meta.advanced && this._advanced) || !(this._settings.sections[section].meta.advanced)) {
|
if ((section.meta.advanced && this._advanced) || !(section.meta.advanced)) {
|
||||||
this._buttons[section].classList.remove("unfocused");
|
this._buttons[section.section].classList.remove("unfocused");
|
||||||
firstVisibleSection = firstVisibleSection || section;
|
firstVisibleSection = firstVisibleSection || section.section;
|
||||||
}
|
}
|
||||||
const shouldShow = (query != "" &&
|
const shouldShow = (query != "" &&
|
||||||
((this._settings.sections[section].settings[setting].advanced && this._advanced) ||
|
((setting.advanced && this._advanced) ||
|
||||||
!(this._settings.sections[section].settings[setting].advanced)));
|
!(setting.advanced)));
|
||||||
if (shouldShow || query == "") {
|
if (shouldShow || query == "") {
|
||||||
// element.classList.add("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
// element.classList.add("-mx-2", "my-2", "p-2", "aside", "~neutral", "@low");
|
||||||
element.classList.remove("opacity-50", "pointer-events-none");
|
element.classList.remove("opacity-50", "pointer-events-none");
|
||||||
@ -989,21 +1000,21 @@ export class settingsList {
|
|||||||
`;
|
`;
|
||||||
dependencyList = dependencyCard.querySelector(".settings-dependency-list") as HTMLUListElement;
|
dependencyList = dependencyCard.querySelector(".settings-dependency-list") as HTMLUListElement;
|
||||||
// Insert it right after the description
|
// Insert it right after the description
|
||||||
this._sections[section].asElement().insertBefore(dependencyCard, this._sections[section].asElement().querySelector(".settings-section-description").nextElementSibling);
|
this._sections[section.section].asElement().insertBefore(dependencyCard, this._sections[section.section].asElement().querySelector(".settings-section-description").nextElementSibling);
|
||||||
}
|
}
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
if (shouldShow) {
|
if (shouldShow) {
|
||||||
const depCode = this._settings.sections[section].settings[setting].depends_true || this._settings.sections[section].settings[setting].depends_false;
|
const depCode = setting.depends_true || setting.depends_false;
|
||||||
const dep = splitDependant(section, depCode);
|
const dep = splitDependant(section.section, depCode);
|
||||||
|
|
||||||
let depName = this._settings.sections[dep[0]].settings[dep[1]].name;
|
let depName = this._settings.sections[dep[0]].settings[dep[1]].name;
|
||||||
if (dep[0] != section) {
|
if (dep[0] != section.section) {
|
||||||
depName = this._settings.sections[dep[0]].meta.name + " > " + depName;
|
depName = this._settings.sections[dep[0]].meta.name + " > " + depName;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.textContent = window.lang.strings("settingsDependsOn").replace("{setting}", `"`+this._settings.sections[section].settings[setting].name+`"`).replace("{dependency}", `"`+depName+`"`);
|
li.textContent = window.lang.strings("settingsDependsOn").replace("{setting}", `"`+setting.name+`"`).replace("{dependency}", `"`+depName+`"`);
|
||||||
} else {
|
} else {
|
||||||
li.textContent = window.lang.strings("settingsAdvancedMode").replace("{setting}", `"`+this._settings.sections[section].settings[setting].name+`"`);
|
li.textContent = window.lang.strings("settingsAdvancedMode").replace("{setting}", `"`+setting.name+`"`);
|
||||||
}
|
}
|
||||||
dependencyList.appendChild(li);
|
dependencyList.appendChild(li);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user