mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
add inter-section dependency for settings
Currently used to hide all email sections when [email]/method is blank (disabled).
This commit is contained in:
parent
ce98b2eb5a
commit
456ef556b1
69
api.go
69
api.go
@ -116,7 +116,7 @@ func (app *appContext) checkInvites() {
|
|||||||
}
|
}
|
||||||
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
|
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
|
||||||
notify := data.Notify
|
notify := data.Notify
|
||||||
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
||||||
app.debug.Printf("%s: Expiry notification", code)
|
app.debug.Printf("%s: Expiry notification", code)
|
||||||
var wait sync.WaitGroup
|
var wait sync.WaitGroup
|
||||||
for address, settings := range notify {
|
for address, settings := range notify {
|
||||||
@ -160,7 +160,7 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
|
|||||||
if currentTime.After(expiry) {
|
if currentTime.After(expiry) {
|
||||||
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
|
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
|
||||||
notify := inv.Notify
|
notify := inv.Notify
|
||||||
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
|
||||||
app.debug.Printf("%s: Expiry notification", code)
|
app.debug.Printf("%s: Expiry notification", code)
|
||||||
for address, settings := range notify {
|
for address, settings := range notify {
|
||||||
if settings["notify-expiry"] {
|
if settings["notify-expiry"] {
|
||||||
@ -301,7 +301,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||||
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
||||||
msg, err := app.email.constructWelcome(req.Username, app)
|
msg, err := app.email.constructWelcome(req.Username, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -332,7 +332,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
success = false
|
success = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if app.config.Section("email_confirmation").Key("enabled").MustBool(false) && !confirmed {
|
if emailEnabled && app.config.Section("email_confirmation").Key("enabled").MustBool(false) && !confirmed {
|
||||||
claims := jwt.MapClaims{
|
claims := jwt.MapClaims{
|
||||||
"valid": true,
|
"valid": true,
|
||||||
"invite": req.Code,
|
"invite": req.Code,
|
||||||
@ -385,7 +385,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
app.storage.loadProfiles()
|
app.storage.loadProfiles()
|
||||||
invite := app.storage.invites[req.Code]
|
invite := app.storage.invites[req.Code]
|
||||||
app.checkInvite(req.Code, true, req.Username)
|
app.checkInvite(req.Code, true, req.Username)
|
||||||
if app.config.Section("notifications").Key("enabled").MustBool(false) {
|
if emailEnabled && app.config.Section("notifications").Key("enabled").MustBool(false) {
|
||||||
for address, settings := range invite.Notify {
|
for address, settings := range invite.Notify {
|
||||||
if settings["notify-creation"] {
|
if settings["notify-creation"] {
|
||||||
go func() {
|
go func() {
|
||||||
@ -450,7 +450,7 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
if emailEnabled && app.config.Section("welcome_email").Key("enabled").MustBool(false) && req.Email != "" {
|
||||||
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
app.debug.Printf("%s: Sending welcome email to %s", req.Username, req.Email)
|
||||||
msg, err := app.email.constructWelcome(req.Username, app)
|
msg, err := app.email.constructWelcome(req.Username, app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -544,7 +544,7 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
|
|||||||
errors[userID] += msg
|
errors[userID] += msg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if req.Notify {
|
if emailEnabled && req.Notify {
|
||||||
addr, ok := app.storage.emails[userID]
|
addr, ok := app.storage.emails[userID]
|
||||||
if addr != nil && ok {
|
if addr != nil && ok {
|
||||||
go func(userID, reason, address string) {
|
go func(userID, reason, address string) {
|
||||||
@ -611,7 +611,7 @@ func (app *appContext) GenerateInvite(gc *gin.Context) {
|
|||||||
invite.RemainingUses = 1
|
invite.RemainingUses = 1
|
||||||
}
|
}
|
||||||
invite.ValidTill = validTill
|
invite.ValidTill = validTill
|
||||||
if req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
if emailEnabled && req.Email != "" && app.config.Section("invite_emails").Key("enabled").MustBool(false) {
|
||||||
app.debug.Printf("%s: Sending invite email", inviteCode)
|
app.debug.Printf("%s: Sending invite email", inviteCode)
|
||||||
invite.Email = req.Email
|
invite.Email = req.Email
|
||||||
msg, err := app.email.constructInvite(inviteCode, invite, app)
|
msg, err := app.email.constructInvite(inviteCode, invite, app)
|
||||||
@ -1190,18 +1190,18 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
|||||||
app.info.Println("Config requested")
|
app.info.Println("Config requested")
|
||||||
resp := app.configBase
|
resp := app.configBase
|
||||||
// Load language options
|
// Load language options
|
||||||
formChosen, formOptions := app.storage.lang.Form.getOptions(app.config.Section("ui").Key("language-form").MustString("en-us"))
|
formOptions := app.storage.lang.Form.getOptions()
|
||||||
fl := resp.Sections["ui"].Settings["language-form"]
|
fl := resp.Sections["ui"].Settings["language-form"]
|
||||||
fl.Options = formOptions
|
fl.Options = formOptions
|
||||||
fl.Value = formChosen
|
fl.Value = app.config.Section("ui").Key("language-form").MustString("en-us")
|
||||||
adminChosen, adminOptions := app.storage.lang.Admin.getOptions(app.config.Section("ui").Key("language-admin").MustString("en-us"))
|
adminOptions := app.storage.lang.Admin.getOptions()
|
||||||
al := resp.Sections["ui"].Settings["language-admin"]
|
al := resp.Sections["ui"].Settings["language-admin"]
|
||||||
al.Options = adminOptions
|
al.Options = adminOptions
|
||||||
al.Value = adminChosen
|
al.Value = app.config.Section("ui").Key("language-admin").MustString("en-us")
|
||||||
emailChosen, emailOptions := app.storage.lang.Email.getOptions(app.config.Section("email").Key("language").MustString("en-us"))
|
emailOptions := app.storage.lang.Email.getOptions()
|
||||||
el := resp.Sections["email"].Settings["language"]
|
el := resp.Sections["email"].Settings["language"]
|
||||||
el.Options = emailOptions
|
el.Options = emailOptions
|
||||||
el.Value = emailChosen
|
el.Value = app.config.Section("email").Key("language").MustString("en-us")
|
||||||
for sectName, section := range resp.Sections {
|
for sectName, section := range resp.Sections {
|
||||||
for settingName, setting := range section.Settings {
|
for settingName, setting := range section.Settings {
|
||||||
val := app.config.Section(sectName).Key(settingName)
|
val := app.config.Section(sectName).Key(settingName)
|
||||||
@ -1221,17 +1221,6 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
|||||||
resp.Sections["ui"].Settings["language-admin"] = al
|
resp.Sections["ui"].Settings["language-admin"] = al
|
||||||
resp.Sections["email"].Settings["language"] = el
|
resp.Sections["email"].Settings["language"] = el
|
||||||
|
|
||||||
t := resp.Sections["jellyfin"].Settings["type"]
|
|
||||||
opts := make([]string, len(serverTypes))
|
|
||||||
i := 0
|
|
||||||
for _, v := range serverTypes {
|
|
||||||
opts[i] = v
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
t.Options = opts
|
|
||||||
t.Value = serverTypes[app.config.Section("jellyfin").Key("type").MustString("jellyfin")]
|
|
||||||
resp.Sections["jellyfin"].Settings["type"] = t
|
|
||||||
|
|
||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1254,35 +1243,7 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
tempConfig.NewSection(section)
|
tempConfig.NewSection(section)
|
||||||
}
|
}
|
||||||
for setting, value := range settings.(map[string]interface{}) {
|
for setting, value := range settings.(map[string]interface{}) {
|
||||||
if section == "ui" && setting == "language-form" {
|
if value.(string) != app.config.Section(section).Key(setting).MustString("") {
|
||||||
for key, lang := range app.storage.lang.Form {
|
|
||||||
if lang.Meta.Name == value.(string) || value.(string) == key {
|
|
||||||
tempConfig.Section("ui").Key("language-form").SetValue(key)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if section == "ui" && setting == "language-admin" {
|
|
||||||
for key, lang := range app.storage.lang.Admin {
|
|
||||||
if lang.Meta.Name == value.(string) || value.(string) == key {
|
|
||||||
tempConfig.Section("ui").Key("language-admin").SetValue(key)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if section == "email" && setting == "language" {
|
|
||||||
for key, lang := range app.storage.lang.Email {
|
|
||||||
if lang.Meta.Name == value.(string) || value.(string) == key {
|
|
||||||
tempConfig.Section("email").Key("language").SetValue(key)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if section == "jellyfin" && setting == "type" {
|
|
||||||
for k, v := range serverTypes {
|
|
||||||
if v == value.(string) {
|
|
||||||
tempConfig.Section("jellyfin").Key("type").SetValue(k)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if value.(string) != app.config.Section(section).Key(setting).MustString("") {
|
|
||||||
tempConfig.Section(section).Key(setting).SetValue(value.(string))
|
tempConfig.Section(section).Key(setting).SetValue(value.(string))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,8 @@ import (
|
|||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var emailEnabled = false
|
||||||
|
|
||||||
func (app *appContext) loadConfig() error {
|
func (app *appContext) loadConfig() error {
|
||||||
var err error
|
var err error
|
||||||
app.config, err = ini.Load(app.configPath)
|
app.config, err = ini.Load(app.configPath)
|
||||||
@ -55,6 +57,12 @@ func (app *appContext) loadConfig() error {
|
|||||||
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")
|
||||||
app.config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", VERSION, COMMIT))
|
app.config.Section("jellyfin").Key("device_id").SetValue(fmt.Sprintf("jfa-go-%s-%s", VERSION, COMMIT))
|
||||||
|
|
||||||
|
if app.config.Section("email").Key("method").MustString("") == "" {
|
||||||
|
emailEnabled = false
|
||||||
|
} else {
|
||||||
|
emailEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("")
|
||||||
|
|
||||||
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
oldFormLang := app.config.Section("ui").Key("language").MustString("")
|
||||||
|
@ -61,8 +61,8 @@
|
|||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"jellyfin",
|
["jellyfin", "Jellyfin"],
|
||||||
"emby"
|
["emby", "Emby"]
|
||||||
],
|
],
|
||||||
"value": "jellyfin",
|
"value": "jellyfin",
|
||||||
"description": "Note: Emby integration works is missing some features, such as Password Resets."
|
"description": "Note: Emby integration works is missing some features, such as Password Resets."
|
||||||
@ -90,7 +90,7 @@
|
|||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"en-us"
|
["en-us", "English (US)"]
|
||||||
],
|
],
|
||||||
"value": "en-us",
|
"value": "en-us",
|
||||||
"description": "Default Account Form Language. Submit a PR on github if you'd like to translate."
|
"description": "Default Account Form Language. Submit a PR on github if you'd like to translate."
|
||||||
@ -101,7 +101,7 @@
|
|||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"en-us"
|
["en-us", "English (US)"]
|
||||||
],
|
],
|
||||||
"value": "en-us",
|
"value": "en-us",
|
||||||
"description": "Default Admin page Language. Settings has not been translated. Submit a PR on github if you'd like to translate."
|
"description": "Default Admin page Language. Settings has not been translated. Submit a PR on github if you'd like to translate."
|
||||||
@ -112,8 +112,8 @@
|
|||||||
"requires_restart": true,
|
"requires_restart": true,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"Jellyfin (Dark)",
|
["Jellyfin (Dark)", "Jellyfin (Dark)"],
|
||||||
"Default (Light)"
|
["Default (Light)", "Default (Light)"]
|
||||||
],
|
],
|
||||||
"value": "Jellyfin (Dark)",
|
"value": "Jellyfin (Dark)",
|
||||||
"description": "Default appearance for all users."
|
"description": "Default appearance for all users."
|
||||||
@ -318,7 +318,7 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Email",
|
"name": "Email",
|
||||||
"description": "General email settings. Ignore if not using email features."
|
"description": "General email settings."
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"language": {
|
"language": {
|
||||||
@ -328,7 +328,7 @@
|
|||||||
"depends_true": "method",
|
"depends_true": "method",
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"en-us"
|
["en-us", "English (US)"]
|
||||||
],
|
],
|
||||||
"value": "en-us",
|
"value": "en-us",
|
||||||
"description": "Default email language. Submit a PR on github if you'd like to translate."
|
"description": "Default email language. Submit a PR on github if you'd like to translate."
|
||||||
@ -374,8 +374,9 @@
|
|||||||
"requires_restart": false,
|
"requires_restart": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"smtp",
|
["", "Disabled"],
|
||||||
"mailgun"
|
["smtp", "SMTP"],
|
||||||
|
["mailgun", "Mailgun"]
|
||||||
],
|
],
|
||||||
"value": "smtp",
|
"value": "smtp",
|
||||||
"description": "Method of sending email to use."
|
"description": "Method of sending email to use."
|
||||||
@ -404,7 +405,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Password Resets",
|
"name": "Password Resets",
|
||||||
"description": "Settings for the password reset handler."
|
"description": "Settings for the password reset handler.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@ -457,7 +459,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Invite emails",
|
"name": "Invite emails",
|
||||||
"description": "Settings for sending invites directly to users."
|
"description": "Settings for sending invites directly to users.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@ -509,7 +512,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Notifications",
|
"name": "Notifications",
|
||||||
"description": "Notification related settings."
|
"description": "Notification related settings.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@ -562,7 +566,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Mailgun (Email)",
|
"name": "Mailgun (Email)",
|
||||||
"description": "Mailgun API connection settings"
|
"description": "Mailgun API connection settings",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"api_url": {
|
"api_url": {
|
||||||
@ -585,7 +590,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "SMTP (Email)",
|
"name": "SMTP (Email)",
|
||||||
"description": "SMTP Server connection settings."
|
"description": "SMTP Server connection settings.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"username": {
|
"username": {
|
||||||
@ -602,8 +608,8 @@
|
|||||||
"requires_restart": false,
|
"requires_restart": false,
|
||||||
"type": "select",
|
"type": "select",
|
||||||
"options": [
|
"options": [
|
||||||
"ssl_tls",
|
["ssl_tls", "SSL/TLS"],
|
||||||
"starttls"
|
["starttls", "STARTTLS"]
|
||||||
],
|
],
|
||||||
"value": "starttls",
|
"value": "starttls",
|
||||||
"description": "Your email provider should provide different ports for each encryption method. Generally 465 for ssl_tls, 587 for starttls."
|
"description": "Your email provider should provide different ports for each encryption method. Generally 465 for ssl_tls, 587 for starttls."
|
||||||
@ -671,7 +677,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Welcome Emails",
|
"name": "Welcome Emails",
|
||||||
"description": "Optionally send a welcome email to new users with the Jellyfin URL and their username."
|
"description": "Optionally send a welcome email to new users with the Jellyfin URL and their username.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@ -712,7 +719,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Email confirmation",
|
"name": "Email confirmation",
|
||||||
"description": "If enabled, a user will be sent an email confirmation link to ensure their password is right before they can make an account."
|
"description": "If enabled, a user will be sent an email confirmation link to ensure their password is right before they can make an account.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"enabled": {
|
"enabled": {
|
||||||
@ -752,7 +760,8 @@
|
|||||||
"order": [],
|
"order": [],
|
||||||
"meta": {
|
"meta": {
|
||||||
"name": "Account Deletion",
|
"name": "Account Deletion",
|
||||||
"description": "Subject/email files for account deletion emails."
|
"description": "Subject/email files for account deletion emails.",
|
||||||
|
"depends_true": "email|method"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"subject": {
|
"subject": {
|
||||||
|
44
lang.go
44
lang.go
@ -15,15 +15,14 @@ type quantityString struct {
|
|||||||
|
|
||||||
type adminLangs map[string]adminLang
|
type adminLangs map[string]adminLang
|
||||||
|
|
||||||
func (ls *adminLangs) getOptions(chosen string) (string, []string) {
|
func (ls *adminLangs) getOptions() [][2]string {
|
||||||
opts := make([]string, len(*ls))
|
opts := make([][2]string, len(*ls))
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = lang.Meta.Name
|
opts[i] = [2]string{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return chosenLang, opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type commonLangs map[string]commonLang
|
type commonLangs map[string]commonLang
|
||||||
@ -43,15 +42,14 @@ type adminLang struct {
|
|||||||
|
|
||||||
type formLangs map[string]formLang
|
type formLangs map[string]formLang
|
||||||
|
|
||||||
func (ls *formLangs) getOptions(chosen string) (string, []string) {
|
func (ls *formLangs) getOptions() [][2]string {
|
||||||
opts := make([]string, len(*ls))
|
opts := make([][2]string, len(*ls))
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = lang.Meta.Name
|
opts[i] = [2]string{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return chosenLang, opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type formLang struct {
|
type formLang struct {
|
||||||
@ -65,15 +63,14 @@ type formLang struct {
|
|||||||
|
|
||||||
type emailLangs map[string]emailLang
|
type emailLangs map[string]emailLang
|
||||||
|
|
||||||
func (ls *emailLangs) getOptions(chosen string) (string, []string) {
|
func (ls *emailLangs) getOptions() [][2]string {
|
||||||
opts := make([]string, len(*ls))
|
opts := make([][2]string, len(*ls))
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = lang.Meta.Name
|
opts[i] = [2]string{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return chosenLang, opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type emailLang struct {
|
type emailLang struct {
|
||||||
@ -110,15 +107,14 @@ type setupLang struct {
|
|||||||
JSON string
|
JSON string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *setupLangs) getOptions(chosen string) (string, []string) {
|
func (ls *setupLangs) getOptions() [][2]string {
|
||||||
opts := make([]string, len(*ls))
|
opts := make([][2]string, len(*ls))
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
i := 0
|
||||||
for _, lang := range *ls {
|
for key, lang := range *ls {
|
||||||
opts[i] = lang.Meta.Name
|
opts[i] = [2]string{key, lang.Meta.Name}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return chosenLang, opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
type langSection map[string]string
|
type langSection map[string]string
|
||||||
|
@ -138,8 +138,10 @@ type configDTO map[string]interface{}
|
|||||||
// Below are for sending config
|
// Below are for sending config
|
||||||
|
|
||||||
type meta struct {
|
type meta struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
|
DependsTrue string `json:"depends_true,omitempty"`
|
||||||
|
DependsFalse string `json:"depends_false,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setting struct {
|
type setting struct {
|
||||||
@ -149,7 +151,7 @@ type setting struct {
|
|||||||
RequiresRestart bool `json:"requires_restart"`
|
RequiresRestart bool `json:"requires_restart"`
|
||||||
Type string `json:"type"` // Type (string, number, bool, etc.)
|
Type string `json:"type"` // Type (string, number, bool, etc.)
|
||||||
Value interface{} `json:"value"`
|
Value interface{} `json:"value"`
|
||||||
Options []string `json:"options,omitempty"`
|
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.
|
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.
|
DependsFalse string `json:"depends_false,omitempty"` // If specified, opposite behaviour of DependsTrue.
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,9 @@ type PasswordReset struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
||||||
|
if !emailEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event, ok := <-watcher.Events:
|
case event, ok := <-watcher.Events:
|
||||||
|
@ -7,6 +7,8 @@ interface settingsBoolEvent extends Event {
|
|||||||
interface Meta {
|
interface Meta {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
depends_true?: string;
|
||||||
|
depends_false?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Setting {
|
interface Setting {
|
||||||
@ -16,13 +18,21 @@ interface Setting {
|
|||||||
requires_restart: boolean;
|
requires_restart: boolean;
|
||||||
type: string;
|
type: string;
|
||||||
value: string | boolean | number;
|
value: string | boolean | number;
|
||||||
depends_true?: Setting;
|
depends_true?: string;
|
||||||
depends_false?: Setting;
|
depends_false?: string;
|
||||||
|
|
||||||
asElement: () => HTMLElement;
|
asElement: () => HTMLElement;
|
||||||
update: (s: Setting) => void;
|
update: (s: Setting) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const splitDependant = (section: string, dep: string): string[] => {
|
||||||
|
let parts = dep.split("|");
|
||||||
|
if (parts.length == 1) {
|
||||||
|
parts = [section, parts[0]];
|
||||||
|
}
|
||||||
|
return parts
|
||||||
|
};
|
||||||
|
|
||||||
class DOMInput {
|
class DOMInput {
|
||||||
protected _input: HTMLInputElement;
|
protected _input: HTMLInputElement;
|
||||||
private _container: HTMLDivElement;
|
private _container: HTMLDivElement;
|
||||||
@ -76,7 +86,7 @@ class DOMInput {
|
|||||||
<i class="icon ri-information-line"></i>
|
<i class="icon ri-information-line"></i>
|
||||||
<span class="content sm"></span>
|
<span class="content sm"></span>
|
||||||
</div>
|
</div>
|
||||||
<input type="${inputType}" class="input ~neutral !normal mt-half">
|
<input type="${inputType}" class="input ~neutral !normal mt-half mb-half">
|
||||||
</label>
|
</label>
|
||||||
`;
|
`;
|
||||||
this._tooltip = this._container.querySelector("div.setting-tooltip") as HTMLDivElement;
|
this._tooltip = this._container.querySelector("div.setting-tooltip") as HTMLDivElement;
|
||||||
@ -84,11 +94,15 @@ class DOMInput {
|
|||||||
this._restart = this._container.querySelector("span.setting-restart") as HTMLSpanElement;
|
this._restart = this._container.querySelector("span.setting-restart") as HTMLSpanElement;
|
||||||
this._input = this._container.querySelector("input[type=" + inputType + "]") as HTMLInputElement;
|
this._input = this._container.querySelector("input[type=" + inputType + "]") as HTMLInputElement;
|
||||||
if (setting.depends_false || setting.depends_true) {
|
if (setting.depends_false || setting.depends_true) {
|
||||||
let dependant = setting.depends_true || setting.depends_false;
|
let dependant = splitDependant(section, setting.depends_true || setting.depends_false);
|
||||||
let state = true;
|
let state = true;
|
||||||
if (setting.depends_false) { state = false; }
|
if (setting.depends_false) { state = false; }
|
||||||
document.addEventListener(`settings-${section}-${dependant}`, (event: settingsBoolEvent) => {
|
document.addEventListener(`settings-${dependant[0]}-${dependant[1]}`, (event: settingsBoolEvent) => {
|
||||||
this._input.disabled = (event.detail !== state);
|
if (Boolean(event.detail) !== state) {
|
||||||
|
this._input.parentElement.classList.add("unfocused");
|
||||||
|
} else {
|
||||||
|
this._input.parentElement.classList.remove("unfocused");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const onValueChange = () => {
|
const onValueChange = () => {
|
||||||
@ -206,7 +220,7 @@ class DOMBool implements SBool {
|
|||||||
this._container = document.createElement("div");
|
this._container = document.createElement("div");
|
||||||
this._container.classList.add("setting");
|
this._container.classList.add("setting");
|
||||||
this._container.innerHTML = `
|
this._container.innerHTML = `
|
||||||
<label class="switch">
|
<label class="switch mb-half">
|
||||||
<input type="checkbox">
|
<input type="checkbox">
|
||||||
<span class="setting-label"></span> <span class="setting-required"></span> <span class="setting-restart"></span>
|
<span class="setting-label"></span> <span class="setting-required"></span> <span class="setting-restart"></span>
|
||||||
<div class="setting-tooltip tooltip right unfocused">
|
<div class="setting-tooltip tooltip right unfocused">
|
||||||
@ -230,11 +244,15 @@ class DOMBool implements SBool {
|
|||||||
document.addEventListener(`settings-loaded`, onValueChange);
|
document.addEventListener(`settings-loaded`, onValueChange);
|
||||||
|
|
||||||
if (setting.depends_false || setting.depends_true) {
|
if (setting.depends_false || setting.depends_true) {
|
||||||
let dependant = setting.depends_true || setting.depends_false;
|
let dependant = splitDependant(section, setting.depends_true || setting.depends_false);
|
||||||
let state = true;
|
let state = true;
|
||||||
if (setting.depends_false) { state = false; }
|
if (setting.depends_false) { state = false; }
|
||||||
document.addEventListener(`settings-${section}-${dependant}`, (event: settingsBoolEvent) => {
|
document.addEventListener(`settings-${dependant[0]}-${dependant[1]}`, (event: settingsBoolEvent) => {
|
||||||
this._input.disabled = (event.detail !== state);
|
if (Boolean(event.detail) !== state) {
|
||||||
|
this._input.parentElement.classList.add("unfocused");
|
||||||
|
} else {
|
||||||
|
this._input.parentElement.classList.remove("unfocused");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.update(setting);
|
this.update(setting);
|
||||||
@ -251,7 +269,7 @@ class DOMBool implements SBool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface SSelect extends Setting {
|
interface SSelect extends Setting {
|
||||||
options: string[];
|
options: string[][];
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
class DOMSelect implements SSelect {
|
class DOMSelect implements SSelect {
|
||||||
@ -260,7 +278,7 @@ class DOMSelect implements SSelect {
|
|||||||
private _tooltip: HTMLDivElement;
|
private _tooltip: HTMLDivElement;
|
||||||
private _required: HTMLSpanElement;
|
private _required: HTMLSpanElement;
|
||||||
private _restart: HTMLSpanElement;
|
private _restart: HTMLSpanElement;
|
||||||
private _options: string[];
|
private _options: string[][];
|
||||||
type: string = "bool";
|
type: string = "bool";
|
||||||
|
|
||||||
get name(): string { return this._container.querySelector("span.setting-label").textContent; }
|
get name(): string { return this._container.querySelector("span.setting-label").textContent; }
|
||||||
@ -301,12 +319,12 @@ class DOMSelect implements SSelect {
|
|||||||
get value(): string { return this._select.value; }
|
get value(): string { return this._select.value; }
|
||||||
set value(v: string) { this._select.value = v; }
|
set value(v: string) { this._select.value = v; }
|
||||||
|
|
||||||
get options(): string[] { return this._options; }
|
get options(): string[][] { return this._options; }
|
||||||
set options(opt: string[]) {
|
set options(opt: string[][]) {
|
||||||
this._options = opt;
|
this._options = opt;
|
||||||
let innerHTML = "";
|
let innerHTML = "";
|
||||||
for (let option of this._options) {
|
for (let option of this._options) {
|
||||||
innerHTML += `<option value="${option}">${option}</option>`;
|
innerHTML += `<option value="${option[0]}">${option[1]}</option>`;
|
||||||
}
|
}
|
||||||
this._select.innerHTML = innerHTML;
|
this._select.innerHTML = innerHTML;
|
||||||
}
|
}
|
||||||
@ -322,7 +340,7 @@ class DOMSelect implements SSelect {
|
|||||||
<i class="icon ri-information-line"></i>
|
<i class="icon ri-information-line"></i>
|
||||||
<span class="content sm"></span>
|
<span class="content sm"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="select ~neutral !normal mt-half">
|
<div class="select ~neutral !normal mt-half mb-half">
|
||||||
<select class="settings-select"></select>
|
<select class="settings-select"></select>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
@ -332,11 +350,15 @@ class DOMSelect implements SSelect {
|
|||||||
this._restart = this._container.querySelector("span.setting-restart") as HTMLSpanElement;
|
this._restart = this._container.querySelector("span.setting-restart") as HTMLSpanElement;
|
||||||
this._select = this._container.querySelector("select.settings-select") as HTMLSelectElement;
|
this._select = this._container.querySelector("select.settings-select") as HTMLSelectElement;
|
||||||
if (setting.depends_false || setting.depends_true) {
|
if (setting.depends_false || setting.depends_true) {
|
||||||
let dependant = setting.depends_true || setting.depends_false;
|
let dependant = splitDependant(section, setting.depends_true || setting.depends_false);
|
||||||
let state = true;
|
let state = true;
|
||||||
if (setting.depends_false) { state = false; }
|
if (setting.depends_false) { state = false; }
|
||||||
document.addEventListener(`settings-${section}-${dependant}`, (event: settingsBoolEvent) => {
|
document.addEventListener(`settings-${dependant[0]}-${dependant[1]}`, (event: settingsBoolEvent) => {
|
||||||
this._input.disabled = (event.detail !== state);
|
if (Boolean(event.detail) !== state) {
|
||||||
|
this._container.classList.add("unfocused");
|
||||||
|
} else {
|
||||||
|
this._container.classList.remove("unfocused");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const onValueChange = () => {
|
const onValueChange = () => {
|
||||||
@ -345,6 +367,7 @@ class DOMSelect implements SSelect {
|
|||||||
if (this.requires_restart) { document.dispatchEvent(new CustomEvent("settings-requires-restart")); }
|
if (this.requires_restart) { document.dispatchEvent(new CustomEvent("settings-requires-restart")); }
|
||||||
};
|
};
|
||||||
this._select.onchange = onValueChange;
|
this._select.onchange = onValueChange;
|
||||||
|
document.addEventListener(`settings-loaded`, onValueChange);
|
||||||
|
|
||||||
const message = document.getElementById("settings-message") as HTMLElement;
|
const message = document.getElementById("settings-message") as HTMLElement;
|
||||||
message.innerHTML = window.lang.var("strings",
|
message.innerHTML = window.lang.var("strings",
|
||||||
@ -353,9 +376,6 @@ class DOMSelect implements SSelect {
|
|||||||
`<span class="badge ~info">R</span>`
|
`<span class="badge ~info">R</span>`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.update(setting);
|
this.update(setting);
|
||||||
}
|
}
|
||||||
update = (s: SSelect) => {
|
update = (s: SSelect) => {
|
||||||
@ -391,6 +411,7 @@ class sectionPanel {
|
|||||||
<span class="heading">${s.meta.name}</span>
|
<span class="heading">${s.meta.name}</span>
|
||||||
<p class="support lg">${s.meta.description}</p>
|
<p class="support lg">${s.meta.description}</p>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
this.update(s);
|
this.update(s);
|
||||||
}
|
}
|
||||||
update = (s: Section) => {
|
update = (s: Section) => {
|
||||||
@ -443,8 +464,6 @@ class sectionPanel {
|
|||||||
asElement = (): HTMLDivElement => { return this._section; }
|
asElement = (): HTMLDivElement => { return this._section; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
order: string[];
|
order: string[];
|
||||||
sections: { [sectionName: string]: Section };
|
sections: { [sectionName: string]: Section };
|
||||||
@ -469,6 +488,18 @@ export class settingsList {
|
|||||||
button.classList.add("button", "~neutral", "!low", "settings-section-button", "mb-half");
|
button.classList.add("button", "~neutral", "!low", "settings-section-button", "mb-half");
|
||||||
button.textContent = s.meta.name;
|
button.textContent = s.meta.name;
|
||||||
button.onclick = () => { this._showPanel(name); };
|
button.onclick = () => { this._showPanel(name); };
|
||||||
|
if (s.meta.depends_true || s.meta.depends_false) {
|
||||||
|
let dependant = splitDependant(name, s.meta.depends_true || s.meta.depends_false);
|
||||||
|
let state = true;
|
||||||
|
if (s.meta.depends_false) { state = false; }
|
||||||
|
document.addEventListener(`settings-${dependant[0]}-${dependant[1]}`, (event: settingsBoolEvent) => {
|
||||||
|
if (Boolean(event.detail) !== state) {
|
||||||
|
button.classList.add("unfocused");
|
||||||
|
} else {
|
||||||
|
button.classList.remove("unfocused");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
this._buttons[name] = button;
|
this._buttons[name] = button;
|
||||||
this._sidebar.appendChild(this._buttons[name]);
|
this._sidebar.appendChild(this._buttons[name]);
|
||||||
}
|
}
|
||||||
@ -549,9 +580,9 @@ export class settingsList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._showPanel(settings.order[0]);
|
this._showPanel(settings.order[0]);
|
||||||
this._needsRestart = false;
|
|
||||||
document.dispatchEvent(new CustomEvent("settings-loaded"));
|
document.dispatchEvent(new CustomEvent("settings-loaded"));
|
||||||
this._saveButton.classList.add("unfocused");
|
this._saveButton.classList.add("unfocused");
|
||||||
|
this._needsRestart = false;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user