diff --git a/api-messages.go b/api-messages.go index f971717..dcaecc4 100644 --- a/api-messages.go +++ b/api-messages.go @@ -15,12 +15,16 @@ import ( // @Router /config/emails [get] // @Security Bearer // @tags Configuration -func (app *appContext) GetCustomEmails(gc *gin.Context) { +func (app *appContext) GetCustomContent(gc *gin.Context) { lang := gc.Query("lang") if _, ok := app.storage.lang.Email[lang]; !ok { lang = app.storage.lang.chosenEmailLang } - gc.JSON(200, emailListDTO{ + adminLang := lang + if _, ok := app.storage.lang.Admin[lang]; !ok { + adminLang = app.storage.lang.chosenAdminLang + } + list := emailListDTO{ "UserCreated": {Name: app.storage.lang.Email[lang].UserCreated["name"], Enabled: app.storage.customEmails.UserCreated.Enabled}, "InviteExpiry": {Name: app.storage.lang.Email[lang].InviteExpiry["name"], Enabled: app.storage.customEmails.InviteExpiry.Enabled}, "PasswordReset": {Name: app.storage.lang.Email[lang].PasswordReset["name"], Enabled: app.storage.customEmails.PasswordReset.Enabled}, @@ -31,13 +35,25 @@ func (app *appContext) GetCustomEmails(gc *gin.Context) { "WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.customEmails.WelcomeEmail.Enabled}, "EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.customEmails.EmailConfirmation.Enabled}, "UserExpired": {Name: app.storage.lang.Email[lang].UserExpired["name"], Enabled: app.storage.customEmails.UserExpired.Enabled}, - }) + "UserLogin": {Name: app.storage.lang.Admin[adminLang].Strings["userPageLogin"], Enabled: app.storage.userPage.Login.Enabled}, + "UserPage": {Name: app.storage.lang.Admin[adminLang].Strings["userPagePage"], Enabled: app.storage.userPage.Page.Enabled}, + } + + filter := gc.Query("filter") + if filter == "user" { + list = emailListDTO{"UserLogin": list["UserLogin"], "UserPage": list["UserPage"]} + } else { + delete(list, "UserLogin") + delete(list, "UserPage") + } + + gc.JSON(200, list) } -func (app *appContext) getCustomEmail(id string) *customEmail { +func (app *appContext) getCustomMessage(id string) *customContent { switch id { case "Announcement": - return &customEmail{} + return &customContent{} case "UserCreated": return &app.storage.customEmails.UserCreated case "InviteExpiry": @@ -58,13 +74,17 @@ func (app *appContext) getCustomEmail(id string) *customEmail { return &app.storage.customEmails.EmailConfirmation case "UserExpired": return &app.storage.customEmails.UserExpired + case "UserLogin": + return &app.storage.userPage.Login + case "UserPage": + return &app.storage.userPage.Page } return nil } // @Summary Sets the corresponding custom email. // @Produce json -// @Param customEmail body customEmail true "Content = email (in markdown)." +// @Param customEmails body customEmails true "Content = email (in markdown)." // @Success 200 {object} boolResponse // @Failure 400 {object} boolResponse // @Failure 500 {object} boolResponse @@ -72,25 +92,29 @@ func (app *appContext) getCustomEmail(id string) *customEmail { // @Router /config/emails/{id} [post] // @Security Bearer // @tags Configuration -func (app *appContext) SetCustomEmail(gc *gin.Context) { - var req customEmail +func (app *appContext) SetCustomMessage(gc *gin.Context) { + var req customContent gc.BindJSON(&req) id := gc.Param("id") if req.Content == "" { respondBool(400, false, gc) return } - email := app.getCustomEmail(id) - if email == nil { + message := app.getCustomMessage(id) + if message == nil { respondBool(400, false, gc) return } - email.Content = req.Content - email.Enabled = true + message.Content = req.Content + message.Enabled = true if app.storage.storeCustomEmails() != nil { respondBool(500, false, gc) return } + if app.storage.storeUserPageContent() != nil { + respondBool(500, false, gc) + return + } respondBool(200, true, gc) } @@ -104,7 +128,7 @@ func (app *appContext) SetCustomEmail(gc *gin.Context) { // @Router /config/emails/{id}/state/{enable/disable} [post] // @Security Bearer // @tags Configuration -func (app *appContext) SetCustomEmailState(gc *gin.Context) { +func (app *appContext) SetCustomMessageState(gc *gin.Context) { id := gc.Param("id") s := gc.Param("state") enabled := false @@ -113,20 +137,24 @@ func (app *appContext) SetCustomEmailState(gc *gin.Context) { } else if s != "disable" { respondBool(400, false, gc) } - email := app.getCustomEmail(id) - if email == nil { + message := app.getCustomMessage(id) + if message == nil { respondBool(400, false, gc) return } - email.Enabled = enabled + message.Enabled = enabled if app.storage.storeCustomEmails() != nil { respondBool(500, false, gc) return } + if app.storage.storeUserPageContent() != nil { + respondBool(500, false, gc) + return + } respondBool(200, true, gc) } -// @Summary Returns the custom email (generating it if not set) and list of used variables in it. +// @Summary Returns the custom email/message (generating it if not set) and list of used variables in it. // @Produce json // @Success 200 {object} customEmailDTO // @Failure 400 {object} boolResponse @@ -135,7 +163,7 @@ func (app *appContext) SetCustomEmailState(gc *gin.Context) { // @Router /config/emails/{id} [get] // @Security Bearer // @tags Configuration -func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) { +func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) { lang := app.storage.lang.chosenEmailLang id := gc.Param("id") var content string @@ -146,20 +174,26 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) { var values map[string]interface{} username := app.storage.lang.Email[lang].Strings.get("username") emailAddress := app.storage.lang.Email[lang].Strings.get("emailAddress") - email := app.getCustomEmail(id) - if email == nil { - app.err.Printf("Failed to get custom email with ID \"%s\"", id) + customMessage := app.getCustomMessage(id) + if customMessage == nil { + app.err.Printf("Failed to get custom message with ID \"%s\"", id) respondBool(400, false, gc) return } if id == "WelcomeEmail" { conditionals = []string{"{yourAccountWillExpire}"} - email.Conditionals = conditionals + customMessage.Conditionals = conditionals + } else if id == "UserPage" { + variables = []string{"{username}"} + customMessage.Variables = variables + } else if id == "UserLogin" { + variables = []string{} + customMessage.Variables = variables } - content = email.Content + content = customMessage.Content noContent := content == "" if !noContent { - variables = email.Variables + variables = customMessage.Variables } switch id { case "Announcement": @@ -215,12 +249,14 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) { msg, err = app.email.constructUserExpired(app, true) } values = app.email.userExpiredValues(app, false) + case "UserLogin", "UserPage": + values = map[string]interface{}{} } if err != nil { respondBool(500, false, gc) return } - if noContent && id != "Announcement" { + if noContent && id != "Announcement" && id != "UserPage" && id != "UserLogin" { content = msg.Text variables = make([]string, strings.Count(content, "{")) i := 0 @@ -239,7 +275,7 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) { i++ } } - email.Variables = variables + customMessage.Variables = variables } if variables == nil { variables = []string{} @@ -248,10 +284,21 @@ func (app *appContext) GetCustomEmailTemplate(gc *gin.Context) { respondBool(500, false, gc) return } - mail, err := app.email.constructTemplate("", "
", app) - if err != nil { + if app.storage.storeUserPageContent() != nil { respondBool(500, false, gc) - return + } + var mail *Message + if id != "UserLogin" && id != "UserPage" { + mail, err = app.email.constructTemplate("", "", app) + if err != nil { + respondBool(500, false, gc) + return + } + } else { + mail = &Message{ + HTML: "", + Markdown: "", + } } gc.JSON(200, customEmailDTO{Content: content, Variables: variables, Conditionals: conditionals, Values: values, HTML: mail.HTML, Plaintext: mail.Text}) } diff --git a/config.go b/config.go index a4316ab..7fb388c 100644 --- a/config.go +++ b/config.go @@ -46,7 +46,7 @@ func (app *appContext) loadConfig() error { key.SetValue(key.MustString(filepath.Join(app.dataPath, (key.Name() + ".json")))) } } - for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements"} { + for _, key := range []string{"user_configuration", "user_displayprefs", "user_profiles", "ombi_template", "invites", "emails", "user_template", "custom_emails", "users", "telegram_users", "discord_users", "matrix_users", "announcements", "custom_user_page_content"} { app.config.Section("files").Key(key).SetValue(app.config.Section("files").Key(key).MustString(filepath.Join(app.dataPath, (key + ".json")))) } for _, key := range []string{"matrix_sql"} { @@ -160,6 +160,12 @@ func (app *appContext) loadConfig() error { app.storage.customEmails_path = app.config.Section("files").Key("custom_emails").String() app.storage.loadCustomEmails() + app.MustSetValue("user_page", "enabled", "true") + if app.config.Section("user_page").Key("enabled").MustBool(false) { + app.storage.userPage_path = app.config.Section("files").Key("custom_user_page_content").String() + app.storage.loadUserPageContent() + } + substituteStrings = app.config.Section("jellyfin").Key("substitute_jellyfin_strings").MustString("") if substituteStrings != "" { diff --git a/config/config-base.json b/config/config-base.json index 20b6e7b..2a77336 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -1551,6 +1551,14 @@ "value": "", "description": "JSON file generated by program in settings, different from email_html/email_text. See wiki for more info." }, + "custom_user_page_content": { + "name": "Custom user page content", + "required": false, + "requires_restart": false, + "type": "text", + "value": "", + "description": "JSON file generated by program in settings, containing user page messages. See wiki for more info." + }, "telegram_users": { "name": "Telegram users", "required": false, diff --git a/css/base.css b/css/base.css index 5358312..5599eb3 100644 --- a/css/base.css +++ b/css/base.css @@ -13,6 +13,8 @@ --border-width-2: 3px; --border-width-4: 5px; --border-width-8: 8px; + + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .light { diff --git a/email.go b/email.go index 808369c..777ed25 100644 --- a/email.go +++ b/email.go @@ -24,7 +24,7 @@ import ( sMail "github.com/xhit/go-simple-mail/v2" ) -var renderer = html.NewRenderer(html.RendererOptions{Flags: html.Smartypants}) +var markdownRenderer = html.NewRenderer(html.RendererOptions{Flags: html.Smartypants}) // EmailClient implements email sending, right now via smtp, mailgun or a dummy client. type EmailClient interface { @@ -353,7 +353,7 @@ func (emailer *Emailer) constructTemplate(subject, md string, app *appContext, u subject = templateEmail(subject, []string{"{username}"}, nil, map[string]interface{}{"username": username[0]}) } email := &Message{Subject: subject} - html := markdown.ToHTML([]byte(md), nil, renderer) + html := markdown.ToHTML([]byte(md), nil, markdownRenderer) text := stripMarkdown(md) message := app.config.Section("messages").Key("message").String() var err error diff --git a/html/header.html b/html/header.html index 1babab1..db2cdab 100644 --- a/html/header.html +++ b/html/header.html @@ -1,4 +1,4 @@ - + diff --git a/html/login-modal.html b/html/login-modal.html index f640f04..3226d9e 100644 --- a/html/login-modal.html +++ b/html/login-modal.html @@ -1,11 +1,20 @@