mirror of
https://github.com/hrfee/jfa-go.git
synced 2025-04-20 01:52:53 +00:00
Compare commits
No commits in common. "e834445b0bf01eebcb2095b248bea44d32e0908d" and "e0a17c6a749c46bd47235683b1ecb04204e90d5d" have entirely different histories.
e834445b0b
...
e0a17c6a74
51
api.go
51
api.go
@ -1085,15 +1085,33 @@ 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"))
|
loadLangs := func(langs *map[string]map[string]interface{}, settingsKey string) (string, []string) {
|
||||||
|
langOptions := make([]string, len(*langs))
|
||||||
|
chosenLang := app.config.Section("ui").Key("language" + settingsKey).MustString("en-us")
|
||||||
|
chosenLangName := (*langs)[chosenLang]["meta"].(map[string]interface{})["name"].(string)
|
||||||
|
i := 0
|
||||||
|
for _, lang := range *langs {
|
||||||
|
langOptions[i] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return chosenLangName, langOptions
|
||||||
|
}
|
||||||
|
formChosen, formOptions := loadLangs(&app.storage.lang.Form, "-form")
|
||||||
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 = formChosen
|
||||||
adminChosen, adminOptions := app.storage.lang.Admin.getOptions(app.config.Section("ui").Key("language-admin").MustString("en-us"))
|
adminChosen, adminOptions := loadLangs(&app.storage.lang.Admin, "-admin")
|
||||||
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 = adminChosen
|
||||||
emailChosen, emailOptions := app.storage.lang.Email.getOptions(app.config.Section("email").Key("language").MustString("en-us"))
|
emailOptions := make([]string, len(app.storage.lang.Email))
|
||||||
|
chosenLang := app.config.Section("email").Key("language").MustString("en-us")
|
||||||
|
emailChosen := app.storage.lang.Email.get(chosenLang, "meta", "name")
|
||||||
|
i := 0
|
||||||
|
for langName := range app.storage.lang.Email {
|
||||||
|
emailOptions[i] = app.storage.lang.Email.get(langName, "meta", "name")
|
||||||
|
i++
|
||||||
|
}
|
||||||
el := resp.Sections["email"].Settings["language"]
|
el := resp.Sections["email"].Settings["language"]
|
||||||
el.Options = emailOptions
|
el.Options = emailOptions
|
||||||
el.Value = emailChosen
|
el.Value = emailChosen
|
||||||
@ -1118,7 +1136,7 @@ func (app *appContext) GetConfig(gc *gin.Context) {
|
|||||||
|
|
||||||
t := resp.Sections["jellyfin"].Settings["type"]
|
t := resp.Sections["jellyfin"].Settings["type"]
|
||||||
opts := make([]string, len(serverTypes))
|
opts := make([]string, len(serverTypes))
|
||||||
i := 0
|
i = 0
|
||||||
for _, v := range serverTypes {
|
for _, v := range serverTypes {
|
||||||
opts[i] = v
|
opts[i] = v
|
||||||
i++
|
i++
|
||||||
@ -1151,21 +1169,21 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
|
|||||||
for setting, value := range settings.(map[string]interface{}) {
|
for setting, value := range settings.(map[string]interface{}) {
|
||||||
if section == "ui" && setting == "language-form" {
|
if section == "ui" && setting == "language-form" {
|
||||||
for key, lang := range app.storage.lang.Form {
|
for key, lang := range app.storage.lang.Form {
|
||||||
if lang.Meta.Name == value.(string) {
|
if lang["meta"].(map[string]interface{})["name"].(string) == value.(string) {
|
||||||
tempConfig.Section("ui").Key("language-form").SetValue(key)
|
tempConfig.Section("ui").Key("language-form").SetValue(key)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if section == "ui" && setting == "language-admin" {
|
} else if section == "ui" && setting == "language-admin" {
|
||||||
for key, lang := range app.storage.lang.Admin {
|
for key, lang := range app.storage.lang.Admin {
|
||||||
if lang.Meta.Name == value.(string) {
|
if lang["meta"].(map[string]interface{})["name"].(string) == value.(string) {
|
||||||
tempConfig.Section("ui").Key("language-admin").SetValue(key)
|
tempConfig.Section("ui").Key("language-admin").SetValue(key)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if section == "email" && setting == "language" {
|
} else if section == "email" && setting == "language" {
|
||||||
for key, lang := range app.storage.lang.Email {
|
for key := range app.storage.lang.Email {
|
||||||
if lang.Meta.Name == value.(string) {
|
if app.storage.lang.Email.get(key, "meta", "name") == value.(string) {
|
||||||
tempConfig.Section("email").Key("language").SetValue(key)
|
tempConfig.Section("email").Key("language").SetValue(key)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -1242,11 +1260,11 @@ func (app *appContext) GetLanguages(gc *gin.Context) {
|
|||||||
resp := langDTO{}
|
resp := langDTO{}
|
||||||
if page == "form" {
|
if page == "form" {
|
||||||
for key, lang := range app.storage.lang.Form {
|
for key, lang := range app.storage.lang.Form {
|
||||||
resp[key] = lang.Meta.Name
|
resp[key] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||||
}
|
}
|
||||||
} else if page == "admin" {
|
} else if page == "admin" {
|
||||||
for key, lang := range app.storage.lang.Admin {
|
for key, lang := range app.storage.lang.Admin {
|
||||||
resp[key] = lang.Meta.Name
|
resp[key] = lang["meta"].(map[string]interface{})["name"].(string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(resp) == 0 {
|
if len(resp) == 0 {
|
||||||
@ -1256,19 +1274,6 @@ func (app *appContext) GetLanguages(gc *gin.Context) {
|
|||||||
gc.JSON(200, resp)
|
gc.JSON(200, resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *appContext) ServeLang(gc *gin.Context) {
|
|
||||||
page := gc.Param("page")
|
|
||||||
lang := strings.Replace(gc.Param("file"), ".json", "", 1)
|
|
||||||
if page == "admin" {
|
|
||||||
gc.JSON(200, app.storage.lang.Admin[lang])
|
|
||||||
return
|
|
||||||
} else if page == "form" {
|
|
||||||
gc.JSON(200, app.storage.lang.Form[lang])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
respondBool(400, false, gc)
|
|
||||||
}
|
|
||||||
|
|
||||||
// func Restart() error {
|
// func Restart() error {
|
||||||
// defer func() {
|
// defer func() {
|
||||||
// if r := recover(); r != nil {
|
// if r := recover(); r != nil {
|
||||||
|
@ -92,7 +92,7 @@
|
|||||||
"options": [
|
"options": [
|
||||||
"en-us"
|
"en-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."
|
||||||
},
|
},
|
||||||
"language-admin": {
|
"language-admin": {
|
||||||
@ -103,7 +103,7 @@
|
|||||||
"options": [
|
"options": [
|
||||||
"en-us"
|
"en-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."
|
||||||
},
|
},
|
||||||
"theme": {
|
"theme": {
|
||||||
|
65
email.go
65
email.go
@ -73,7 +73,8 @@ func (sm *SMTP) send(address, fromName, fromAddr string, email *Email) error {
|
|||||||
// Emailer contains the email sender, email content, and methods to construct message content.
|
// Emailer contains the email sender, email content, and methods to construct message content.
|
||||||
type Emailer struct {
|
type Emailer struct {
|
||||||
fromAddr, fromName string
|
fromAddr, fromName string
|
||||||
lang emailLang
|
lang *EmailLang
|
||||||
|
cLang string
|
||||||
sender emailClient
|
sender emailClient
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +110,8 @@ func NewEmailer(app *appContext) *Emailer {
|
|||||||
emailer := &Emailer{
|
emailer := &Emailer{
|
||||||
fromAddr: app.config.Section("email").Key("address").String(),
|
fromAddr: app.config.Section("email").Key("address").String(),
|
||||||
fromName: app.config.Section("email").Key("from").String(),
|
fromName: app.config.Section("email").Key("from").String(),
|
||||||
lang: app.storage.lang.Email[app.storage.lang.chosenEmailLang],
|
lang: &(app.storage.lang.Email),
|
||||||
|
cLang: app.storage.lang.chosenEmailLang,
|
||||||
}
|
}
|
||||||
method := app.config.Section("email").Key("method").String()
|
method := app.config.Section("email").Key("method").String()
|
||||||
if method == "smtp" {
|
if method == "smtp" {
|
||||||
@ -135,7 +137,7 @@ func (emailer *Emailer) NewMailgun(url, key string) {
|
|||||||
sender := &Mailgun{
|
sender := &Mailgun{
|
||||||
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
||||||
}
|
}
|
||||||
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages')
|
// Mailgun client takes the base url, so we need to trim off the end (e.g 'v3/messages'
|
||||||
if strings.Contains(url, "messages") {
|
if strings.Contains(url, "messages") {
|
||||||
url = url[0:strings.LastIndex(url, "/")]
|
url = url[0:strings.LastIndex(url, "/")]
|
||||||
url = url[0:strings.LastIndex(url, "/")]
|
url = url[0:strings.LastIndex(url, "/")]
|
||||||
@ -155,8 +157,9 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructInvite(code string, invite Invite, app *appContext) (*Email, error) {
|
||||||
|
lang := emailer.cLang
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.InviteEmail.get("title")),
|
subject: app.config.Section("invite_emails").Key("subject").MustString(emailer.lang.get(lang, "inviteEmail", "title")),
|
||||||
}
|
}
|
||||||
expiry := invite.ValidTill
|
expiry := invite.ValidTill
|
||||||
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
||||||
@ -172,11 +175,11 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
|||||||
}
|
}
|
||||||
var tplData bytes.Buffer
|
var tplData bytes.Buffer
|
||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"hello": emailer.lang.InviteEmail.get("hello"),
|
"hello": emailer.lang.get(lang, "inviteEmail", "hello"),
|
||||||
"youHaveBeenInvited": emailer.lang.InviteEmail.get("youHaveBeenInvited"),
|
"youHaveBeenInvited": emailer.lang.get(lang, "inviteEmail", "youHaveBeenInvited"),
|
||||||
"toJoin": emailer.lang.InviteEmail.get("toJoin"),
|
"toJoin": emailer.lang.get(lang, "inviteEmail", "toJoin"),
|
||||||
"inviteExpiry": emailer.lang.InviteEmail.format("inviteExpiry", d, t, expiresIn),
|
"inviteExpiry": emailer.lang.format(lang, "inviteEmail", "inviteExpiry", d, t, expiresIn),
|
||||||
"linkButton": emailer.lang.InviteEmail.get("linkButton"),
|
"linkButton": emailer.lang.get(lang, "inviteEmail", "linkButton"),
|
||||||
"invite_link": inviteLink,
|
"invite_link": inviteLink,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
@ -193,8 +196,9 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appContext) (*Email, error) {
|
||||||
|
lang := emailer.cLang
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.InviteExpiry.get("title"),
|
subject: emailer.lang.get(lang, "inviteExpiry", "title"),
|
||||||
}
|
}
|
||||||
expiry := app.formatDatetime(invite.ValidTill)
|
expiry := app.formatDatetime(invite.ValidTill)
|
||||||
for _, key := range []string{"html", "text"} {
|
for _, key := range []string{"html", "text"} {
|
||||||
@ -205,9 +209,9 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
|||||||
}
|
}
|
||||||
var tplData bytes.Buffer
|
var tplData bytes.Buffer
|
||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"inviteExpired": emailer.lang.InviteExpiry.get("inviteExpired"),
|
"inviteExpired": emailer.lang.get(lang, "inviteExpiry", "inviteExpired"),
|
||||||
"expiredAt": emailer.lang.InviteExpiry.format("expiredAt", "\""+code+"\"", expiry),
|
"expiredAt": emailer.lang.format(lang, "inviteExpiry", "expiredAt", "\""+code+"\"", expiry),
|
||||||
"notificationNotice": emailer.lang.InviteExpiry.get("notificationNotice"),
|
"notificationNotice": emailer.lang.get(lang, "inviteExpiry", "notificationNotice"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -222,8 +226,9 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructCreated(code, username, address string, invite Invite, app *appContext) (*Email, error) {
|
||||||
|
lang := emailer.cLang
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.UserCreated.get("title"),
|
subject: emailer.lang.get(lang, "userCreated", "title"),
|
||||||
}
|
}
|
||||||
created := app.formatDatetime(invite.Created)
|
created := app.formatDatetime(invite.Created)
|
||||||
var tplAddress string
|
var tplAddress string
|
||||||
@ -240,14 +245,14 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
|||||||
}
|
}
|
||||||
var tplData bytes.Buffer
|
var tplData bytes.Buffer
|
||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"aUserWasCreated": emailer.lang.UserCreated.format("aUserWasCreated", "\""+code+"\""),
|
"aUserWasCreated": emailer.lang.format(lang, "userCreated", "aUserWasCreated", "\""+code+"\""),
|
||||||
"name": emailer.lang.UserCreated.get("name"),
|
"name": emailer.lang.get(lang, "userCreated", "name"),
|
||||||
"address": emailer.lang.UserCreated.get("emailAddress"),
|
"address": emailer.lang.get(lang, "userCreated", "emailAddress"),
|
||||||
"time": emailer.lang.UserCreated.get("time"),
|
"time": emailer.lang.get(lang, "userCreated", "time"),
|
||||||
"nameVal": username,
|
"nameVal": username,
|
||||||
"addressVal": tplAddress,
|
"addressVal": tplAddress,
|
||||||
"timeVal": created,
|
"timeVal": created,
|
||||||
"notificationNotice": emailer.lang.UserCreated.get("notificationNotice"),
|
"notificationNotice": emailer.lang.get(lang, "userCreated", "notificationNotice"),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -262,8 +267,9 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Email, error) {
|
||||||
|
lang := emailer.cLang
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.PasswordReset.get("title"),
|
subject: emailer.lang.get(lang, "passwordReset", "title"),
|
||||||
}
|
}
|
||||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
@ -275,12 +281,12 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
|
|||||||
}
|
}
|
||||||
var tplData bytes.Buffer
|
var tplData bytes.Buffer
|
||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"helloUser": emailer.lang.PasswordReset.format("helloUser", pwr.Username),
|
"helloUser": emailer.lang.format(lang, "passwordReset", "helloUser", pwr.Username),
|
||||||
"someoneHasRequestedReset": emailer.lang.PasswordReset.get("someoneHasRequestedReset"),
|
"someoneHasRequestedReset": emailer.lang.get(lang, "passwordReset", "someoneHasRequestedReset"),
|
||||||
"ifItWasYou": emailer.lang.PasswordReset.get("ifItWasYou"),
|
"ifItWasYou": emailer.lang.get(lang, "passwordReset", "ifItWasYou"),
|
||||||
"codeExpiry": emailer.lang.PasswordReset.format("codeExpiry", d, t, expiresIn),
|
"codeExpiry": emailer.lang.format(lang, "passwordReset", "codeExpiry", d, t, expiresIn),
|
||||||
"ifItWasNotYou": emailer.lang.PasswordReset.get("ifItWasNotYou"),
|
"ifItWasNotYou": emailer.lang.get(lang, "passwordReset", "ifItWasNotYou"),
|
||||||
"pin": emailer.lang.PasswordReset.get("pin"),
|
"pin": emailer.lang.get(lang, "passwordReset", "pin"),
|
||||||
"pinVal": pwr.Pin,
|
"pinVal": pwr.Pin,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
@ -297,8 +303,9 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email, error) {
|
func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email, error) {
|
||||||
|
lang := emailer.cLang
|
||||||
email := &Email{
|
email := &Email{
|
||||||
subject: emailer.lang.UserDeleted.get("title"),
|
subject: emailer.lang.get(lang, "userDeleted", "title"),
|
||||||
}
|
}
|
||||||
for _, key := range []string{"html", "text"} {
|
for _, key := range []string{"html", "text"} {
|
||||||
fpath := app.config.Section("deletion").Key("email_" + key).String()
|
fpath := app.config.Section("deletion").Key("email_" + key).String()
|
||||||
@ -308,8 +315,8 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email
|
|||||||
}
|
}
|
||||||
var tplData bytes.Buffer
|
var tplData bytes.Buffer
|
||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"yourAccountWasDeleted": emailer.lang.UserDeleted.get("yourAccountWasDeleted"),
|
"yourAccountWasDeleted": emailer.lang.get(lang, "userDeleted", "yourAccountWasDeleted"),
|
||||||
"reason": emailer.lang.UserDeleted.get("reason"),
|
"reason": emailer.lang.get(lang, "userDeleted", "reason"),
|
||||||
"reasonVal": reason,
|
"reasonVal": reason,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -253,9 +253,9 @@
|
|||||||
<div class="card ~neutral !low accounts mb-1">
|
<div class="card ~neutral !low accounts mb-1">
|
||||||
<span class="heading">{{ .strings.accounts }}</span>
|
<span class="heading">{{ .strings.accounts }}</span>
|
||||||
<div class="fr">
|
<div class="fr">
|
||||||
<span class="button ~neutral !normal" id="accounts-add-user">{{ .quantityStrings.addUser.Singular }}</span>
|
<span class="button ~neutral !normal" id="accounts-add-user">{{ .quantityStrings.addUser.singular }}</span>
|
||||||
<span class="button ~urge !normal" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
<span class="button ~urge !normal" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
|
||||||
<span class="button ~critical !normal" id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
|
<span class="button ~critical !normal" id="accounts-delete-user">{{ .quantityStrings.deleteUser.singular }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
|
<div class="card ~neutral !normal accounts-header table-responsive mt-half">
|
||||||
<table class="table">
|
<table class="table">
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
{{ define "form-base" }}
|
{{ define "form-base" }}
|
||||||
<script>
|
<script>
|
||||||
window.usernameEnabled = {{ .username }};
|
window.usernameEnabled = {{ .username }};
|
||||||
window.validationStrings = JSON.parse({{ .validationStrings }});
|
window.validationStrings = JSON.parse({{ .lang.validationStrings }});
|
||||||
window.invalidPassword = "{{ .strings.reEnterPasswordInvalid }}";
|
window.invalidPassword = "{{ .lang.reEnterPasswordInvalid }}";
|
||||||
window.URLBase = "{{ .urlBase }}";
|
window.URLBase = "{{ .urlBase }}";
|
||||||
window.code = "{{ .code }}";
|
window.code = "{{ .code }}";
|
||||||
</script>
|
</script>
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" type="text/css" href="css/base.css">
|
<link rel="stylesheet" type="text/css" href="css/base.css">
|
||||||
{{ template "header.html" . }}
|
{{ template "header.html" . }}
|
||||||
<title>{{ .strings.pageTitle }}</title>
|
<title>{{ .lang.pageTitle }}</title>
|
||||||
</head>
|
</head>
|
||||||
<body class="max-w-full overflow-x-hidden section">
|
<body class="max-w-full overflow-x-hidden section">
|
||||||
<div id="modal-success" class="modal">
|
<div id="modal-success" class="modal">
|
||||||
<div class="modal-content card">
|
<div class="modal-content card">
|
||||||
<span class="heading mb-1">{{ .strings.successHeader }}</span>
|
<span class="heading mb-1">{{ .lang.successHeader }}</span>
|
||||||
<p class="content mb-1">{{ .successMessage }}</p>
|
<p class="content mb-1">{{ .successMessage }}</p>
|
||||||
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .strings.successContinueButton }}</a>
|
<a class="button ~urge !normal full-width center supra submit" href="{{ .jfLink }}" id="create-success-button">{{ .lang.successContinueButton }}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="notification-box"></div>
|
<div id="notification-box"></div>
|
||||||
@ -27,34 +27,34 @@
|
|||||||
<div class="page-container">
|
<div class="page-container">
|
||||||
<div class="card ~neutral !low">
|
<div class="card ~neutral !low">
|
||||||
<div class="row baseline">
|
<div class="row baseline">
|
||||||
<span class="col heading">{{ .strings.createAccountHeader }}</span>
|
<span class="col heading">{{ .lang.createAccountHeader }}</span>
|
||||||
<span class="col subheading"> {{ .helpMessage }}</span>
|
<span class="col subheading"> {{ .helpMessage }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<form class="card ~neutral !normal" id="form-create" href="">
|
<form class="card ~neutral !normal" id="form-create" href="">
|
||||||
<label class="label supra">
|
<label class="label supra">
|
||||||
{{ .strings.username }}
|
{{ .lang.username }}
|
||||||
<input type="text" class="input ~neutral !high mt-half mb-1" placeholder="{{ .strings.username }}" id="create-username" aria-label="{{ .strings.username }}">
|
<input type="text" class="input ~neutral !high mt-half mb-1" placeholder="{{ .lang.username }}" id="create-username" aria-label="{{ .lang.username }}">
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="label supra" for="create-email">{{ .strings.emailAddress }}</label>
|
<label class="label supra" for="create-email">{{ .lang.emailAddress }}</label>
|
||||||
<input type="email" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.emailAddress }}" id="create-email" aria-label="{{ .strings.emailAddress }}" value="{{ .email }}">
|
<input type="email" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .lang.emailAddress }}" id="create-email" aria-label="{{ .lang.emailAddress }}" value="{{ .email }}">
|
||||||
|
|
||||||
<label class="label supra" for="create-password">{{ .strings.password }}</label>
|
<label class="label supra" for="create-password">{{ .lang.password }}</label>
|
||||||
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-password" aria-label="{{ .strings.password }}">
|
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .lang.password }}" id="create-password" aria-label="{{ .lang.password }}">
|
||||||
|
|
||||||
<label class="label supra" for="create-reenter-password">{{ .strings.reEnterPassword }}</label>
|
<label class="label supra" for="create-reenter-password">{{ .lang.reEnterPassword }}</label>
|
||||||
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .strings.password }}" id="create-reenter-password" aria-label="{{ .strings.reEnterPassword }}">
|
<input type="password" class="input ~neutral 1high mt-half mb-1" placeholder="{{ .lang.password }}" id="create-reenter-password" aria-label="{{ .lang.reEnterPassword }}">
|
||||||
<label>
|
<label>
|
||||||
<input type="submit" class="unfocused">
|
<input type="submit" class="unfocused">
|
||||||
<span class="button ~urge !normal full-width center supra submit">{{ .strings.createAccountButton }}</span>
|
<span class="button ~urge !normal full-width center supra submit">{{ .lang.createAccountButton }}</span>
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card ~neutral !normal">
|
<div class="card ~neutral !normal">
|
||||||
<span class="label supra" for="inv-uses">{{ .strings.passwordRequirementsHeader }}</span>
|
<span class="label supra" for="inv-uses">{{ .lang.passwordRequirementsHeader }}</span>
|
||||||
<ul>
|
<ul>
|
||||||
{{ range $key, $value := .requirements }}
|
{{ range $key, $value := .requirements }}
|
||||||
<li class="" id="requirement-{{ $key }}" min="{{ $value }}">
|
<li class="" id="requirement-{{ $key }}" min="{{ $value }}">
|
||||||
@ -70,6 +70,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
window.validationStrings = {{ .lang.validationStrings }};
|
||||||
|
</script>
|
||||||
{{ template "form-base" . }}
|
{{ template "form-base" . }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
90
lang.go
90
lang.go
@ -1,90 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
type langMeta struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type quantityString struct {
|
|
||||||
Singular string `json:"singular"`
|
|
||||||
Plural string `json:"plural"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type adminLangs map[string]adminLang
|
|
||||||
|
|
||||||
func (ls *adminLangs) getOptions(chosen string) (string, []string) {
|
|
||||||
opts := make([]string, len(*ls))
|
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
|
||||||
for _, lang := range *ls {
|
|
||||||
opts[i] = lang.Meta.Name
|
|
||||||
}
|
|
||||||
return chosenLang, opts
|
|
||||||
}
|
|
||||||
|
|
||||||
type adminLang struct {
|
|
||||||
Meta langMeta `json:"meta"`
|
|
||||||
Strings langSection `json:"strings"`
|
|
||||||
Notifications langSection `json:"notifications"`
|
|
||||||
QuantityStrings map[string]quantityString `json:"quantityStrings"`
|
|
||||||
JSON string
|
|
||||||
}
|
|
||||||
|
|
||||||
type formLangs map[string]formLang
|
|
||||||
|
|
||||||
func (ls *formLangs) getOptions(chosen string) (string, []string) {
|
|
||||||
opts := make([]string, len(*ls))
|
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
|
||||||
for _, lang := range *ls {
|
|
||||||
opts[i] = lang.Meta.Name
|
|
||||||
}
|
|
||||||
return chosenLang, opts
|
|
||||||
}
|
|
||||||
|
|
||||||
type formLang struct {
|
|
||||||
Meta langMeta `json:"meta"`
|
|
||||||
Strings langSection `json:"strings"`
|
|
||||||
ValidationStrings map[string]quantityString `json:"validationStrings"`
|
|
||||||
validationStringsJSON string
|
|
||||||
}
|
|
||||||
|
|
||||||
type emailLangs map[string]emailLang
|
|
||||||
|
|
||||||
func (ls *emailLangs) getOptions(chosen string) (string, []string) {
|
|
||||||
opts := make([]string, len(*ls))
|
|
||||||
chosenLang := (*ls)[chosen].Meta.Name
|
|
||||||
i := 0
|
|
||||||
for _, lang := range *ls {
|
|
||||||
opts[i] = lang.Meta.Name
|
|
||||||
}
|
|
||||||
return chosenLang, opts
|
|
||||||
}
|
|
||||||
|
|
||||||
type emailLang struct {
|
|
||||||
Meta langMeta `json:"meta"`
|
|
||||||
UserCreated langSection `json:"userCreated"`
|
|
||||||
InviteExpiry langSection `json:"inviteExpiry"`
|
|
||||||
PasswordReset langSection `json:"passwordReset"`
|
|
||||||
UserDeleted langSection `json:"userDeleted"`
|
|
||||||
InviteEmail langSection `json:"inviteEmail"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type langSection map[string]string
|
|
||||||
|
|
||||||
func (el *langSection) format(field string, vals ...string) string {
|
|
||||||
text := el.get(field)
|
|
||||||
for _, val := range vals {
|
|
||||||
text = strings.Replace(text, "{n}", val, 1)
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
func (el *langSection) get(field string) string {
|
|
||||||
t, ok := (*el)[field]
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
@ -14,28 +14,28 @@
|
|||||||
"createAccountButton": "Create Account",
|
"createAccountButton": "Create Account",
|
||||||
"passwordRequirementsHeader": "Password Requirements",
|
"passwordRequirementsHeader": "Password Requirements",
|
||||||
"successHeader": "Success!",
|
"successHeader": "Success!",
|
||||||
"successContinueButton": "Continue"
|
"successContinueButton": "Continue",
|
||||||
},
|
"validationStrings": {
|
||||||
"validationStrings": {
|
"length": {
|
||||||
"length": {
|
"singular": "Must have at least {n} character",
|
||||||
"singular": "Must have at least {n} character",
|
"plural": "Must have at least {n} characters"
|
||||||
"plural": "Must have at least {n} characters"
|
},
|
||||||
},
|
"uppercase": {
|
||||||
"uppercase": {
|
"singular": "Must have at least {n} uppercase character",
|
||||||
"singular": "Must have at least {n} uppercase character",
|
"plural": "Must have at least {n} uppercase characters"
|
||||||
"plural": "Must have at least {n} uppercase characters"
|
},
|
||||||
},
|
"lowercase": {
|
||||||
"lowercase": {
|
"singular": "Must have at least {n} lowercase character",
|
||||||
"singular": "Must have at least {n} lowercase character",
|
"plural": "Must have at least {n} lowercase characters"
|
||||||
"plural": "Must have at least {n} lowercase characters"
|
},
|
||||||
},
|
"number": {
|
||||||
"number": {
|
"singular": "Must have at least {n} number",
|
||||||
"singular": "Must have at least {n} number",
|
"plural": "Must have at least {n} numbers"
|
||||||
"plural": "Must have at least {n} numbers"
|
},
|
||||||
},
|
"special": {
|
||||||
"special": {
|
"singular": "Must have at least {n} special character",
|
||||||
"singular": "Must have at least {n} special character",
|
"plural": "Must have at least {n} special characters"
|
||||||
"plural": "Must have at least {n} special characters"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,28 +15,28 @@
|
|||||||
"createAccountButton": "Créer le compte",
|
"createAccountButton": "Créer le compte",
|
||||||
"passwordRequirementsHeader": "Mot de passe requis",
|
"passwordRequirementsHeader": "Mot de passe requis",
|
||||||
"successHeader": "Succes!",
|
"successHeader": "Succes!",
|
||||||
"successContinueButton": "Continuer"
|
"successContinueButton": "Continuer",
|
||||||
},
|
"validationStrings": {
|
||||||
"validationStrings": {
|
"length": {
|
||||||
"length": {
|
"singular": "Doit avoir au moins {n} caractère",
|
||||||
"singular": "Doit avoir au moins {n} caractère",
|
"plural": "Doit avoir au moins {n} caractères"
|
||||||
"plural": "Doit avoir au moins {n} caractères"
|
},
|
||||||
},
|
"uppercase": {
|
||||||
"uppercase": {
|
"singular": "Doit avoir au moins {n} caractère majuscule",
|
||||||
"singular": "Doit avoir au moins {n} caractère majuscule",
|
"plural": "Must have at least {n} caractères majuscules"
|
||||||
"plural": "Must have at least {n} caractères majuscules"
|
},
|
||||||
},
|
"lowercase": {
|
||||||
"lowercase": {
|
"singular": "Doit avoir au moins {n} caractère minuscule",
|
||||||
"singular": "Doit avoir au moins {n} caractère minuscule",
|
"plural": "Doit avoir au moins {n} caractères minuscules"
|
||||||
"plural": "Doit avoir au moins {n} caractères minuscules"
|
},
|
||||||
},
|
"number": {
|
||||||
"number": {
|
"singular": "Doit avoir au moins {n} nombre",
|
||||||
"singular": "Doit avoir au moins {n} nombre",
|
"plural": "Doit avoir au moins {n} nombres"
|
||||||
"plural": "Doit avoir au moins {n} nombres"
|
},
|
||||||
},
|
"special": {
|
||||||
"special": {
|
"singular": "Doit avoir au moins {n} caractère spécial",
|
||||||
"singular": "Doit avoir au moins {n} caractère spécial",
|
"plural": "Doit avoir au moins {n} caractères spéciaux"
|
||||||
"plural": "Doit avoir au moins {n} caractères spéciaux"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,28 +14,28 @@
|
|||||||
"createAccountButton": "Maak account aan",
|
"createAccountButton": "Maak account aan",
|
||||||
"passwordRequirementsHeader": "Wachtwoordvereisten",
|
"passwordRequirementsHeader": "Wachtwoordvereisten",
|
||||||
"successHeader": "Succes!",
|
"successHeader": "Succes!",
|
||||||
"successContinueButton": "Doorgaan"
|
"successContinueButton": "Doorgaan",
|
||||||
},
|
"validationStrings": {
|
||||||
"validationStrings": {
|
"length": {
|
||||||
"length": {
|
"singular": "Moet ten minste {n} teken bevatten",
|
||||||
"singular": "Moet ten minste {n} teken bevatten",
|
"plural": "Moet ten minste {n} tekens bevatten"
|
||||||
"plural": "Moet ten minste {n} tekens bevatten"
|
},
|
||||||
},
|
"uppercase": {
|
||||||
"uppercase": {
|
"singular": "Moet ten minste {n} hoofdletter bevatten",
|
||||||
"singular": "Moet ten minste {n} hoofdletter bevatten",
|
"plural": "Moet ten minste {n} hoofdletters bevatten"
|
||||||
"plural": "Moet ten minste {n} hoofdletters bevatten"
|
},
|
||||||
},
|
"lowercase": {
|
||||||
"lowercase": {
|
"singular": "Moet ten minste {n} kleine letter bevatten",
|
||||||
"singular": "Moet ten minste {n} kleine letter bevatten",
|
"plural": "Moet ten minste {n} kleine letters bevatten"
|
||||||
"plural": "Moet ten minste {n} kleine letters bevatten"
|
},
|
||||||
},
|
"number": {
|
||||||
"number": {
|
"singular": "Moet ten minste {n} cijfer bevatten",
|
||||||
"singular": "Moet ten minste {n} cijfer bevatten",
|
"plural": "Moet ten minste {n} cijfers bevatten"
|
||||||
"plural": "Moet ten minste {n} cijfers bevatten"
|
},
|
||||||
},
|
"special": {
|
||||||
"special": {
|
"singular": "Moet ten minste {n} bijzonder teken bevatten",
|
||||||
"singular": "Moet ten minste {n} bijzonder teken bevatten",
|
"plural": "Moet ten minste {n} bijzondere tekens bevatten"
|
||||||
"plural": "Moet ten minste {n} bijzondere tekens bevatten"
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
main.go
3
main.go
@ -569,6 +569,7 @@ func start(asDaemon, firstCall bool) {
|
|||||||
|
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
router.Use(static.Serve("/", static.LocalFile(filepath.Join(app.localPath, "web"), false)))
|
router.Use(static.Serve("/", static.LocalFile(filepath.Join(app.localPath, "web"), false)))
|
||||||
|
router.Use(static.Serve("/lang/", static.LocalFile(filepath.Join(app.localPath, "lang"), false)))
|
||||||
app.loadHTML(router)
|
app.loadHTML(router)
|
||||||
router.NoRoute(app.NoRouteHandler)
|
router.NoRoute(app.NoRouteHandler)
|
||||||
if debugMode {
|
if debugMode {
|
||||||
@ -579,7 +580,7 @@ func start(asDaemon, firstCall bool) {
|
|||||||
router.GET("/", app.AdminPage)
|
router.GET("/", app.AdminPage)
|
||||||
router.GET("/accounts", app.AdminPage)
|
router.GET("/accounts", app.AdminPage)
|
||||||
router.GET("/settings", app.AdminPage)
|
router.GET("/settings", app.AdminPage)
|
||||||
router.GET("/lang/:page/:file", app.ServeLang)
|
|
||||||
router.GET("/lang/:page", app.GetLanguages)
|
router.GET("/lang/:page", app.GetLanguages)
|
||||||
router.GET("/token/login", app.getTokenLogin)
|
router.GET("/token/login", app.getTokenLogin)
|
||||||
router.GET("/token/refresh", app.getTokenRefresh)
|
router.GET("/token/refresh", app.getTokenRefresh)
|
||||||
|
@ -147,7 +147,7 @@ type setting struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type section struct {
|
type section struct {
|
||||||
Meta langMeta `json:"meta"`
|
Meta meta `json:"meta"`
|
||||||
Order []string `json:"order"`
|
Order []string `json:"order"`
|
||||||
Settings map[string]setting `json:"settings"`
|
Settings map[string]setting `json:"settings"`
|
||||||
}
|
}
|
||||||
|
360
storage.go
360
storage.go
@ -20,6 +20,30 @@ type Storage struct {
|
|||||||
lang Lang
|
lang Lang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EmailLang map[string]map[string]map[string]interface{} // Map of lang codes to email name to fields
|
||||||
|
|
||||||
|
func (el *EmailLang) format(lang, email, field string, vals ...string) string {
|
||||||
|
text := (*el)[lang][email][field].(string)
|
||||||
|
for _, val := range vals {
|
||||||
|
text = strings.Replace(text, "{n}", val, 1)
|
||||||
|
}
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
func (el *EmailLang) get(lang, email, field string) string { return (*el)[lang][email][field].(string) }
|
||||||
|
|
||||||
|
type Lang struct {
|
||||||
|
chosenFormLang string
|
||||||
|
chosenAdminLang string
|
||||||
|
chosenEmailLang string
|
||||||
|
AdminPath string
|
||||||
|
Admin map[string]map[string]interface{}
|
||||||
|
AdminJSON map[string]string
|
||||||
|
FormPath string
|
||||||
|
Form map[string]map[string]interface{}
|
||||||
|
EmailPath string
|
||||||
|
Email EmailLang
|
||||||
|
}
|
||||||
|
|
||||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
@ -43,202 +67,6 @@ type Invite struct {
|
|||||||
Profile string `json:"profile"`
|
Profile string `json:"profile"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Lang struct {
|
|
||||||
chosenFormLang string
|
|
||||||
chosenAdminLang string
|
|
||||||
chosenEmailLang string
|
|
||||||
AdminPath string
|
|
||||||
Admin adminLangs
|
|
||||||
AdminJSON map[string]string
|
|
||||||
FormPath string
|
|
||||||
Form formLangs
|
|
||||||
EmailPath string
|
|
||||||
Email emailLangs
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) loadLang() (err error) {
|
|
||||||
err = st.loadLangAdmin()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = st.loadLangForm()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = st.loadLangEmail()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a given language has missing values, fill it in with the english value.
|
|
||||||
func patchLang(english, other *langSection) {
|
|
||||||
for n, ev := range *english {
|
|
||||||
if v, ok := (*other)[n]; !ok || v == "" {
|
|
||||||
(*other)[n] = ev
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func patchQuantityStrings(english, other *map[string]quantityString) {
|
|
||||||
for n, ev := range *english {
|
|
||||||
qs, ok := (*other)[n]
|
|
||||||
if !ok {
|
|
||||||
(*other)[n] = ev
|
|
||||||
return
|
|
||||||
} else if qs.Singular == "" {
|
|
||||||
qs.Singular = ev.Singular
|
|
||||||
} else if (*other)[n].Plural == "" {
|
|
||||||
qs.Plural = ev.Plural
|
|
||||||
}
|
|
||||||
(*other)[n] = qs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) loadLangAdmin() error {
|
|
||||||
st.lang.Admin = map[string]adminLang{}
|
|
||||||
var english adminLang
|
|
||||||
load := func(fname string) error {
|
|
||||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
|
||||||
lang := adminLang{}
|
|
||||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.AdminPath, fname))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if substituteStrings != "" {
|
|
||||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(f, &lang)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fname != "en-us.json" {
|
|
||||||
patchLang(&english.Strings, &lang.Strings)
|
|
||||||
patchLang(&english.Notifications, &lang.Notifications)
|
|
||||||
patchQuantityStrings(&english.QuantityStrings, &lang.QuantityStrings)
|
|
||||||
}
|
|
||||||
stringAdmin, err := json.Marshal(lang)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lang.JSON = string(stringAdmin)
|
|
||||||
st.lang.Admin[index] = lang
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := load("en-us.json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
english = st.lang.Admin["en-us"]
|
|
||||||
files, err := ioutil.ReadDir(st.lang.AdminPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, f := range files {
|
|
||||||
if f.Name() != "en-us.json" {
|
|
||||||
err = load(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) loadLangForm() error {
|
|
||||||
st.lang.Form = map[string]formLang{}
|
|
||||||
var english formLang
|
|
||||||
load := func(fname string) error {
|
|
||||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
|
||||||
lang := formLang{}
|
|
||||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.FormPath, fname))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if substituteStrings != "" {
|
|
||||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(f, &lang)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fname != "en-us.json" {
|
|
||||||
patchLang(&english.Strings, &lang.Strings)
|
|
||||||
patchQuantityStrings(&english.ValidationStrings, &lang.ValidationStrings)
|
|
||||||
}
|
|
||||||
validationStrings, err := json.Marshal(lang.ValidationStrings)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
lang.validationStringsJSON = string(validationStrings)
|
|
||||||
st.lang.Form[index] = lang
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := load("en-us.json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
english = st.lang.Form["en-us"]
|
|
||||||
files, err := ioutil.ReadDir(st.lang.FormPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, f := range files {
|
|
||||||
if f.Name() != "en-us.json" {
|
|
||||||
err = load(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (st *Storage) loadLangEmail() error {
|
|
||||||
st.lang.Email = map[string]emailLang{}
|
|
||||||
var english emailLang
|
|
||||||
load := func(fname string) error {
|
|
||||||
index := strings.TrimSuffix(fname, filepath.Ext(fname))
|
|
||||||
lang := emailLang{}
|
|
||||||
f, err := ioutil.ReadFile(filepath.Join(st.lang.EmailPath, fname))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if substituteStrings != "" {
|
|
||||||
f = []byte(strings.ReplaceAll(string(f), "Jellyfin", substituteStrings))
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(f, &lang)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if fname != "en-us.json" {
|
|
||||||
patchLang(&english.UserCreated, &lang.UserCreated)
|
|
||||||
patchLang(&english.InviteExpiry, &lang.InviteExpiry)
|
|
||||||
patchLang(&english.PasswordReset, &lang.PasswordReset)
|
|
||||||
patchLang(&english.UserDeleted, &lang.UserDeleted)
|
|
||||||
patchLang(&english.InviteEmail, &lang.InviteEmail)
|
|
||||||
}
|
|
||||||
st.lang.Email[index] = lang
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err := load("en-us.json")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
english = st.lang.Email["en-us"]
|
|
||||||
files, err := ioutil.ReadDir(st.lang.EmailPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, f := range files {
|
|
||||||
if f.Name() != "en-us.json" {
|
|
||||||
err = load(f.Name())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type Invites map[string]Invite
|
type Invites map[string]Invite
|
||||||
|
|
||||||
func (st *Storage) loadInvites() error {
|
func (st *Storage) loadInvites() error {
|
||||||
@ -249,75 +77,77 @@ func (st *Storage) storeInvites() error {
|
|||||||
return storeJSON(st.invite_path, st.invites)
|
return storeJSON(st.invite_path, st.invites)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (st *Storage) loadLang() error {
|
func (st *Storage) loadLang() error {
|
||||||
// loadData := func(path string, stringJson bool) (map[string]string, map[string]map[string]interface{}, error) {
|
loadData := func(path string, stringJson bool) (map[string]string, map[string]map[string]interface{}, error) {
|
||||||
// files, err := ioutil.ReadDir(path)
|
files, err := ioutil.ReadDir(path)
|
||||||
// outString := map[string]string{}
|
outString := map[string]string{}
|
||||||
// out := map[string]map[string]interface{}{}
|
out := map[string]map[string]interface{}{}
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, nil, err
|
return nil, nil, err
|
||||||
// }
|
}
|
||||||
// for _, f := range files {
|
for _, f := range files {
|
||||||
// index := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
index := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
|
||||||
// var data map[string]interface{}
|
var data map[string]interface{}
|
||||||
// var file []byte
|
var file []byte
|
||||||
// var err error
|
var err error
|
||||||
// file, err = ioutil.ReadFile(filepath.Join(path, f.Name()))
|
file, err = ioutil.ReadFile(filepath.Join(path, f.Name()))
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// file = []byte("{}")
|
file = []byte("{}")
|
||||||
// }
|
}
|
||||||
// // Replace Jellyfin with something if necessary
|
// Replace Jellyfin with something if necessary
|
||||||
// if substituteStrings != "" {
|
if substituteStrings != "" {
|
||||||
// fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
|
fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
|
||||||
// file = []byte(fileString)
|
file = []byte(fileString)
|
||||||
// }
|
}
|
||||||
// err = json.Unmarshal(file, &data)
|
err = json.Unmarshal(file, &data)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// log.Printf("ERROR: Failed to read \"%s\": %s", path, err)
|
log.Printf("ERROR: Failed to read \"%s\": %s", path, err)
|
||||||
// return nil, nil, err
|
return nil, nil, err
|
||||||
// }
|
}
|
||||||
// if stringJson {
|
if stringJson {
|
||||||
// stringJSON, err := json.Marshal(data)
|
stringJSON, err := json.Marshal(data)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return nil, nil, err
|
return nil, nil, err
|
||||||
// }
|
}
|
||||||
// outString[index] = string(stringJSON)
|
outString[index] = string(stringJSON)
|
||||||
// }
|
}
|
||||||
// out[index] = data
|
out[index] = data
|
||||||
//
|
|
||||||
// }
|
}
|
||||||
// return outString, out, nil
|
return outString, out, nil
|
||||||
// }
|
}
|
||||||
// _, form, err := loadData(st.lang.FormPath, false)
|
_, form, err := loadData(st.lang.FormPath, false)
|
||||||
// if err != nil {
|
if err != nil {
|
||||||
// return err
|
return err
|
||||||
// }
|
}
|
||||||
// for index, lang := range form {
|
for index, lang := range form {
|
||||||
// validationStrings := lang["validationStrings"].(map[string]interface{})
|
strings := lang["strings"].(map[string]interface{})
|
||||||
// vS, err := json.Marshal(validationStrings)
|
validationStrings := strings["validationStrings"].(map[string]interface{})
|
||||||
// if err != nil {
|
vS, err := json.Marshal(validationStrings)
|
||||||
// return err
|
if err != nil {
|
||||||
// }
|
return err
|
||||||
// lang["validationStrings"] = string(vS)
|
}
|
||||||
// form[index] = lang
|
strings["validationStrings"] = string(vS)
|
||||||
// }
|
lang["strings"] = strings
|
||||||
// st.lang.Form = form
|
form[index] = lang
|
||||||
// adminJSON, admin, err := loadData(st.lang.AdminPath, true)
|
}
|
||||||
// st.lang.Admin = admin
|
st.lang.Form = form
|
||||||
// st.lang.AdminJSON = adminJSON
|
adminJSON, admin, err := loadData(st.lang.AdminPath, true)
|
||||||
//
|
st.lang.Admin = admin
|
||||||
// _, emails, err := loadData(st.lang.EmailPath, false)
|
st.lang.AdminJSON = adminJSON
|
||||||
// fixedEmails := map[string]map[string]map[string]interface{}{}
|
|
||||||
// for lang, e := range emails {
|
_, emails, err := loadData(st.lang.EmailPath, false)
|
||||||
// f := map[string]map[string]interface{}{}
|
fixedEmails := map[string]map[string]map[string]interface{}{}
|
||||||
// for field, vals := range e {
|
for lang, e := range emails {
|
||||||
// f[field] = vals.(map[string]interface{})
|
f := map[string]map[string]interface{}{}
|
||||||
// }
|
for field, vals := range e {
|
||||||
// fixedEmails[lang] = f
|
f[field] = vals.(map[string]interface{})
|
||||||
// }
|
}
|
||||||
// st.lang.Email = fixedEmails
|
fixedEmails[lang] = f
|
||||||
// return err
|
}
|
||||||
// }
|
st.lang.Email = fixedEmails
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (st *Storage) loadEmails() error {
|
func (st *Storage) loadEmails() error {
|
||||||
return loadJSON(st.emails_path, &st.emails)
|
return loadJSON(st.emails_path, &st.emails)
|
||||||
|
33
views.go
33
views.go
@ -45,7 +45,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
|||||||
lang := gc.Query("lang")
|
lang := gc.Query("lang")
|
||||||
if lang == "" {
|
if lang == "" {
|
||||||
lang = app.storage.lang.chosenAdminLang
|
lang = app.storage.lang.chosenAdminLang
|
||||||
} else if _, ok := app.storage.lang.Admin[lang]; !ok {
|
} else if _, ok := app.storage.lang.Form[lang]; !ok {
|
||||||
lang = app.storage.lang.chosenAdminLang
|
lang = app.storage.lang.chosenAdminLang
|
||||||
}
|
}
|
||||||
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
|
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
|
||||||
@ -61,9 +61,9 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
|||||||
"commit": COMMIT,
|
"commit": COMMIT,
|
||||||
"ombiEnabled": ombiEnabled,
|
"ombiEnabled": ombiEnabled,
|
||||||
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
||||||
"strings": app.storage.lang.Admin[lang].Strings,
|
"strings": app.storage.lang.Admin[lang]["strings"],
|
||||||
"quantityStrings": app.storage.lang.Admin[lang].QuantityStrings,
|
"quantityStrings": app.storage.lang.Admin[lang]["quantityStrings"],
|
||||||
"language": app.storage.lang.Admin[lang].JSON,
|
"language": app.storage.lang.AdminJSON[lang],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,19 +84,18 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
|
|||||||
email = ""
|
email = ""
|
||||||
}
|
}
|
||||||
gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{
|
gcHTML(gc, http.StatusOK, "form-loader.html", gin.H{
|
||||||
"urlBase": app.URLBase,
|
"urlBase": app.URLBase,
|
||||||
"cssClass": app.cssClass,
|
"cssClass": app.cssClass,
|
||||||
"contactMessage": app.config.Section("ui").Key("contact_message").String(),
|
"contactMessage": app.config.Section("ui").Key("contact_message").String(),
|
||||||
"helpMessage": app.config.Section("ui").Key("help_message").String(),
|
"helpMessage": app.config.Section("ui").Key("help_message").String(),
|
||||||
"successMessage": app.config.Section("ui").Key("success_message").String(),
|
"successMessage": app.config.Section("ui").Key("success_message").String(),
|
||||||
"jfLink": app.config.Section("jellyfin").Key("public_server").String(),
|
"jfLink": app.config.Section("jellyfin").Key("public_server").String(),
|
||||||
"validate": app.config.Section("password_validation").Key("enabled").MustBool(false),
|
"validate": app.config.Section("password_validation").Key("enabled").MustBool(false),
|
||||||
"requirements": app.validator.getCriteria(),
|
"requirements": app.validator.getCriteria(),
|
||||||
"email": email,
|
"email": email,
|
||||||
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
"username": !app.config.Section("email").Key("no_username").MustBool(false),
|
||||||
"strings": app.storage.lang.Form[lang].Strings,
|
"lang": app.storage.lang.Form[lang]["strings"],
|
||||||
"validationStrings": app.storage.lang.Form[lang].validationStringsJSON,
|
"code": code,
|
||||||
"code": code,
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
gcHTML(gc, 404, "invalidCode.html", gin.H{
|
gcHTML(gc, 404, "invalidCode.html", gin.H{
|
||||||
|
Loading…
Reference in New Issue
Block a user