mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-22 09:00:10 +00:00
email: add new "expiry adjusted email"
just the email at this point. Also wrote up a little guide on adding new emails on wiki.jfa-go.com.
This commit is contained in:
parent
6f5fc0948a
commit
fbbb03a47d
@ -32,6 +32,7 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
||||
"UserDeleted": {Name: app.storage.lang.Email[lang].UserDeleted["name"], Enabled: app.storage.MustGetCustomContentKey("UserDeleted").Enabled},
|
||||
"UserDisabled": {Name: app.storage.lang.Email[lang].UserDisabled["name"], Enabled: app.storage.MustGetCustomContentKey("UserDisabled").Enabled},
|
||||
"UserEnabled": {Name: app.storage.lang.Email[lang].UserEnabled["name"], Enabled: app.storage.MustGetCustomContentKey("UserEnabled").Enabled},
|
||||
"UserExpiryAdjusted": {Name: app.storage.lang.Email[lang].UserExpiryAdjusted["name"], Enabled: app.storage.MustGetCustomContentKey("UserExpiryAdjusted").Enabled},
|
||||
"InviteEmail": {Name: app.storage.lang.Email[lang].InviteEmail["name"], Enabled: app.storage.MustGetCustomContentKey("InviteEmail").Enabled},
|
||||
"WelcomeEmail": {Name: app.storage.lang.Email[lang].WelcomeEmail["name"], Enabled: app.storage.MustGetCustomContentKey("WelcomeEmail").Enabled},
|
||||
"EmailConfirmation": {Name: app.storage.lang.Email[lang].EmailConfirmation["name"], Enabled: app.storage.MustGetCustomContentKey("EmailConfirmation").Enabled},
|
||||
@ -51,39 +52,6 @@ func (app *appContext) GetCustomContent(gc *gin.Context) {
|
||||
gc.JSON(200, list)
|
||||
}
|
||||
|
||||
// No longer needed, these are stored by string keys in the database now.
|
||||
/* func (app *appContext) getCustomMessage(id string) *CustomContent {
|
||||
switch id {
|
||||
case "Announcement":
|
||||
return &CustomContent{}
|
||||
case "UserCreated":
|
||||
return &app.storage.customEmails.UserCreated
|
||||
case "InviteExpiry":
|
||||
return &app.storage.customEmails.InviteExpiry
|
||||
case "PasswordReset":
|
||||
return &app.storage.customEmails.PasswordReset
|
||||
case "UserDeleted":
|
||||
return &app.storage.customEmails.UserDeleted
|
||||
case "UserDisabled":
|
||||
return &app.storage.customEmails.UserDisabled
|
||||
case "UserEnabled":
|
||||
return &app.storage.customEmails.UserEnabled
|
||||
case "InviteEmail":
|
||||
return &app.storage.customEmails.InviteEmail
|
||||
case "WelcomeEmail":
|
||||
return &app.storage.customEmails.WelcomeEmail
|
||||
case "EmailConfirmation":
|
||||
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 content.
|
||||
// @Produce json
|
||||
// @Param CustomContent body CustomContent true "Content = email (in markdown)."
|
||||
@ -217,6 +185,11 @@ func (app *appContext) GetCustomMessageTemplate(gc *gin.Context) {
|
||||
msg, err = app.email.constructEnabled("", app, true)
|
||||
}
|
||||
values = app.email.deletedValues(app.storage.lang.Email[lang].Strings.get("reason"), app, false)
|
||||
case "UserExpiryAdjusted":
|
||||
if noContent {
|
||||
msg, err = app.email.constructExpiryAdjusted(time.Time{}, "", app, true)
|
||||
}
|
||||
values = app.email.expiryAdjustedValues(time.Now(), app.storage.lang.Email[lang].Strings.get("reason"), app, false, true)
|
||||
case "InviteEmail":
|
||||
if noContent {
|
||||
msg, err = app.email.constructInvite("", Invite{}, app, true)
|
||||
|
@ -105,6 +105,9 @@ func (app *appContext) loadConfig() error {
|
||||
app.MustSetValue("user_expiry", "email_html", "jfa-go:"+"user-expired.html")
|
||||
app.MustSetValue("user_expiry", "email_text", "jfa-go:"+"user-expired.txt")
|
||||
|
||||
app.MustSetValue("user_expiry", "adjustment_email_html", "jfa-go:"+"expiry-adjusted.html")
|
||||
app.MustSetValue("user_expiry", "adjustment_email_text", "jfa-go:"+"expiry-adjusted.txt")
|
||||
|
||||
app.MustSetValue("matrix", "topic", "Jellyfin notifications")
|
||||
app.MustSetValue("matrix", "show_on_reg", "true")
|
||||
|
||||
|
@ -1713,7 +1713,7 @@
|
||||
"order": [],
|
||||
"meta": {
|
||||
"name": "User Expiry",
|
||||
"description": "When set on an invite, users will be deleted or disabled a specified amount of time after they create their account."
|
||||
"description": "When set on an invite, users will be deleted or disabled a specified amount of time after they create their account. Expiries can also be set and extended for invididual users, optionally with a message why."
|
||||
},
|
||||
"settings": {
|
||||
"behaviour": {
|
||||
@ -1765,6 +1765,35 @@
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email in plain text"
|
||||
},
|
||||
"adjustment_subject": {
|
||||
"name": "Adjustment: email subject",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Subject of adjustment emails, sent optionally when setting/extending an expiry."
|
||||
},
|
||||
"adjustment_email_html": {
|
||||
"name": "Adjustment: Custom email (HTML)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email html"
|
||||
},
|
||||
"adjustment_email_text": {
|
||||
"name": "Adjustment: Custom email (plaintext)",
|
||||
"required": false,
|
||||
"requires_restart": false,
|
||||
"advanced": true,
|
||||
"depends_true": "messages|enabled",
|
||||
"type": "text",
|
||||
"value": "",
|
||||
"description": "Path to custom email in plain text"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
64
email.go
64
email.go
@ -741,6 +741,70 @@ func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub b
|
||||
return email, nil
|
||||
}
|
||||
|
||||
func (emailer *Emailer) expiryAdjustedValues(expiry time.Time, reason string, app *appContext, noSub bool, custom bool) map[string]interface{} {
|
||||
template := map[string]interface{}{
|
||||
"yourExpiryWasAdjusted": emailer.lang.UserExpiryAdjusted.get("yourExpiryWasAdjusted"),
|
||||
"ifPreviouslyDisabled": emailer.lang.UserExpiryAdjusted.get("ifPreviouslyDisabled"),
|
||||
"reasonString": emailer.lang.Strings.get("reason"),
|
||||
"newExpiry": "",
|
||||
"message": "",
|
||||
}
|
||||
if noSub {
|
||||
empty := []string{"reason", "newExpiry"}
|
||||
for _, v := range empty {
|
||||
template[v] = "{" + v + "}"
|
||||
}
|
||||
} else {
|
||||
template["reason"] = reason
|
||||
template["message"] = app.config.Section("messages").Key("message").String()
|
||||
exp := app.formatDatetime(expiry)
|
||||
if !expiry.IsZero() {
|
||||
if custom {
|
||||
template["newExpiry"] = exp
|
||||
} else if !expiry.IsZero() {
|
||||
template["newExpiry"] = emailer.lang.UserExpiryAdjusted.template("newExpiry", tmpl{
|
||||
"date": exp,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return template
|
||||
}
|
||||
|
||||
func (emailer *Emailer) constructExpiryAdjusted(expiry time.Time, reason string, app *appContext, noSub bool) (*Message, error) {
|
||||
email := &Message{
|
||||
Subject: app.config.Section("user_expiry").Key("adjustment_subject").MustString(emailer.lang.UserExpiryAdjusted.get("title")),
|
||||
}
|
||||
var err error
|
||||
var template map[string]interface{}
|
||||
message := app.storage.MustGetCustomContentKey("UserExpiryAdjusted")
|
||||
if message.Enabled {
|
||||
template = emailer.expiryAdjustedValues(expiry, reason, app, noSub, true)
|
||||
} else {
|
||||
template = emailer.expiryAdjustedValues(expiry, reason, app, noSub, false)
|
||||
}
|
||||
if noSub {
|
||||
template["newExpiry"] = emailer.lang.UserExpiryAdjusted.template("newExpiry", tmpl{
|
||||
"date": "{newExpiry}",
|
||||
})
|
||||
}
|
||||
if message.Enabled {
|
||||
content := templateEmail(
|
||||
message.Content,
|
||||
message.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, email.Markdown, err = emailer.construct(app, "user_expiry", "adjustment_email_", template)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return email, nil
|
||||
}
|
||||
|
||||
func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *appContext, noSub bool, custom bool) map[string]interface{} {
|
||||
template := map[string]interface{}{
|
||||
"welcome": emailer.lang.WelcomeEmail.get("welcome"),
|
||||
|
1
lang.go
1
lang.go
@ -101,6 +101,7 @@ type emailLang struct {
|
||||
UserDeleted langSection `json:"userDeleted"`
|
||||
UserDisabled langSection `json:"userDisabled"`
|
||||
UserEnabled langSection `json:"userEnabled"`
|
||||
UserExpiryAdjusted langSection `json:"userExpiryAdjusted"`
|
||||
InviteEmail langSection `json:"inviteEmail"`
|
||||
WelcomeEmail langSection `json:"welcomeEmail"`
|
||||
EmailConfirmation langSection `json:"emailConfirmation"`
|
||||
|
@ -45,6 +45,13 @@
|
||||
"title": "Your account has been re-enabled - Jellyfin",
|
||||
"yourAccountWasEnabled": "Your account was re-enabled."
|
||||
},
|
||||
"userExpiryAdjusted": {
|
||||
"name": "Expiry adjusted",
|
||||
"title": "Account expiry adjusted - Jellyfin",
|
||||
"yourExpiryWasAdjusted": "Your account's expiry date has been adjusted.",
|
||||
"ifPreviouslyDisabled": "If your account was previously disabled, it has been re-enabled.",
|
||||
"newExpiry": "Your account will now expire on {date}."
|
||||
},
|
||||
"inviteEmail": {
|
||||
"name": "Invite email",
|
||||
"title": "Invite - Jellyfin",
|
||||
|
81
mail/expiry-adjusted.mjml
Normal file
81
mail/expiry-adjusted.mjml
Normal file
@ -0,0 +1,81 @@
|
||||
<mjml>
|
||||
<mj-head>
|
||||
<mj-raw>
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<meta name="supported-color-schemes" content="light dark">
|
||||
</mj-raw>
|
||||
<mj-style>
|
||||
:root {
|
||||
Color-scheme: light dark;
|
||||
supported-color-schemes: light dark;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
Color-scheme: dark;
|
||||
.body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
[data-ogsc] .body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
[data-ogsb] .body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
Color-scheme: dark;
|
||||
.body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
[data-ogsc] .body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
[data-ogsb] .body {
|
||||
background: #242424 !important;
|
||||
background-color: #242424 !important;
|
||||
}
|
||||
}
|
||||
</mj-style>
|
||||
<mj-attributes>
|
||||
<mj-class name="bg" background-color="#101010" />
|
||||
<mj-class name="bg2" background-color="#242424" />
|
||||
<mj-class name="text" color="#cacaca" />
|
||||
<mj-class name="bold" color="rgba(255,255,255,0.87)" />
|
||||
<mj-class name="secondary" color="rgb(153,153,153)" />
|
||||
<mj-class name="blue" background-color="rgb(0,164,220)" />
|
||||
</mj-attributes>
|
||||
<mj-font name="Quicksand" href="https://fonts.googleapis.com/css2?family=Quicksand" />
|
||||
<mj-font name="Noto Sans" href="https://fonts.googleapis.com/css2?family=Noto+Sans" />
|
||||
</mj-head>
|
||||
<mj-body>
|
||||
<mj-section mj-class="bg2">
|
||||
<mj-column>
|
||||
<mj-text mj-class="bold" font-size="25px" font-family="Quicksand, Noto Sans, Helvetica, Arial, sans-serif"> {{ .jellyfin }} </mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg">
|
||||
<mj-column>
|
||||
<mj-text mj-class="text" font-size="16px" font-family="Noto Sans, Helvetica, Arial, sans-serif">
|
||||
<h3>{{ .yourExpiryWasAdjusted }}</h3>
|
||||
|
||||
<p>{{ .ifPreviouslyDisabled }}</p>
|
||||
|
||||
<h4>{{ .newExpiry }}</h4>
|
||||
|
||||
<p>{{ .reasonString }}: <i>{{ .reason }}</i></p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section mj-class="bg2">
|
||||
<mj-column>
|
||||
<mj-text mj-class="secondary" font-style="italic" font-size="14px">
|
||||
{{ .message }}
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</body>
|
||||
</mjml>
|
10
mail/expiry-adjusted.txt
Normal file
10
mail/expiry-adjusted.txt
Normal file
@ -0,0 +1,10 @@
|
||||
{{ .yourExpiryWasAdjusted }}
|
||||
|
||||
{{ .ifPreviouslyDisabled }}
|
||||
|
||||
{{ .newExpiry }}
|
||||
|
||||
{{ .reasonString }}: {{ .reason }}
|
||||
|
||||
{{ .message }}
|
||||
|
@ -17,6 +17,7 @@ func runMigrations(app *appContext) {
|
||||
linkExistingOmbiDiscordTelegram(app)
|
||||
// migrateHyphens(app)
|
||||
migrateToBadger(app)
|
||||
intialiseCustomContent(app)
|
||||
}
|
||||
|
||||
// Migrate pre-0.2.0 user templates to profiles
|
||||
@ -329,6 +330,8 @@ func migrateToBadger(app *appContext) {
|
||||
app.storage.SetCustomContentKey("UserPage", app.storage.deprecatedUserPageContent.Page)
|
||||
}
|
||||
|
||||
// Custom content not present here was added post-badger.
|
||||
|
||||
err := app.storage.db.Upsert("migrated_to_db", MigrationStatus{true})
|
||||
if err != nil {
|
||||
app.err.Fatalf("Failed to migrate to DB: %v\n", err)
|
||||
@ -336,6 +339,53 @@ func migrateToBadger(app *appContext) {
|
||||
app.info.Println("All data migrated to database. JSON files in the config folder can be deleted if you are sure all data is correct in the app. Create an issue if you have problems.")
|
||||
}
|
||||
|
||||
// Simply creates an emply CC template if not imn the DB already.
|
||||
// Add new CC types here!
|
||||
func intialiseCustomContent(app *appContext) {
|
||||
emptyCC := CustomContent{
|
||||
Enabled: false,
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserCreated"); !ok {
|
||||
app.storage.SetCustomContentKey("UserCreated", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("InviteExpiry"); !ok {
|
||||
app.storage.SetCustomContentKey("InviteExpiry", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("PasswordReset"); !ok {
|
||||
app.storage.SetCustomContentKey("PasswordReset", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserDeleted"); !ok {
|
||||
app.storage.SetCustomContentKey("UserDeleted", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserDisabled"); !ok {
|
||||
app.storage.SetCustomContentKey("UserDisabled", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserEnabled"); !ok {
|
||||
app.storage.SetCustomContentKey("UserEnabled", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("InviteEmail"); !ok {
|
||||
app.storage.SetCustomContentKey("InviteEmail", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("WelcomeEmail"); !ok {
|
||||
app.storage.SetCustomContentKey("WelcomeEmail", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("EmailConfirmation"); !ok {
|
||||
app.storage.SetCustomContentKey("EmailConfirmation", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserExpired"); !ok {
|
||||
app.storage.SetCustomContentKey("UserExpired", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserLogin"); !ok {
|
||||
app.storage.SetCustomContentKey("UserLogin", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserPage"); !ok {
|
||||
app.storage.SetCustomContentKey("UserPage", emptyCC)
|
||||
}
|
||||
if _, ok := app.storage.GetCustomContentKey("UserExpiryAdjusted"); !ok {
|
||||
app.storage.SetCustomContentKey("UserExpiryAdjusted", emptyCC)
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate between hyphenated & non-hyphenated user IDs. Doesn't seem to happen anymore, so disabled.
|
||||
// func migrateHyphens(app *appContext) {
|
||||
// checkVersion := func(version string) int {
|
||||
|
@ -616,6 +616,7 @@ type customEmails struct {
|
||||
UserDeleted CustomContent `json:"userDeleted"`
|
||||
UserDisabled CustomContent `json:"userDisabled"`
|
||||
UserEnabled CustomContent `json:"userEnabled"`
|
||||
UserExpiryAdjusted CustomContent `json:"userExpiryAdjusted"`
|
||||
InviteEmail CustomContent `json:"inviteEmail"`
|
||||
WelcomeEmail CustomContent `json:"welcomeEmail"`
|
||||
EmailConfirmation CustomContent `json:"emailConfirmation"`
|
||||
@ -1225,6 +1226,7 @@ func (st *Storage) loadLangEmail(filesystems ...fs.FS) error {
|
||||
patchLang(&lang.UserDeleted, &fallback.UserDeleted, &english.UserDeleted)
|
||||
patchLang(&lang.UserDisabled, &fallback.UserDisabled, &english.UserDisabled)
|
||||
patchLang(&lang.UserEnabled, &fallback.UserEnabled, &english.UserEnabled)
|
||||
patchLang(&lang.UserExpiryAdjusted, &fallback.UserExpiryAdjusted, &english.UserExpiryAdjusted)
|
||||
patchLang(&lang.InviteEmail, &fallback.InviteEmail, &english.InviteEmail)
|
||||
patchLang(&lang.WelcomeEmail, &fallback.WelcomeEmail, &english.WelcomeEmail)
|
||||
patchLang(&lang.EmailConfirmation, &fallback.EmailConfirmation, &english.EmailConfirmation)
|
||||
@ -1239,6 +1241,7 @@ func (st *Storage) loadLangEmail(filesystems ...fs.FS) error {
|
||||
patchLang(&lang.UserDeleted, &english.UserDeleted)
|
||||
patchLang(&lang.UserDisabled, &english.UserDisabled)
|
||||
patchLang(&lang.UserEnabled, &english.UserEnabled)
|
||||
patchLang(&lang.UserExpiryAdjusted, &english.UserExpiryAdjusted)
|
||||
patchLang(&lang.InviteEmail, &english.InviteEmail)
|
||||
patchLang(&lang.WelcomeEmail, &english.WelcomeEmail)
|
||||
patchLang(&lang.EmailConfirmation, &english.EmailConfirmation)
|
||||
|
Loading…
Reference in New Issue
Block a user