mirror of
https://github.com/hrfee/jfa-go.git
synced 2025-01-01 05:50:12 +00:00
Compare commits
3 Commits
e8b1cca9ca
...
d772e43e44
Author | SHA1 | Date | |
---|---|---|---|
d772e43e44 | |||
8fdab39b18 | |||
f7d2771263 |
12
api.go
12
api.go
@ -1570,6 +1570,7 @@ func (app *appContext) GetEmail(gc *gin.Context) {
|
||||
var err error
|
||||
var msg *Email
|
||||
var variables []string
|
||||
var conditionals []string
|
||||
var values map[string]interface{}
|
||||
var writeVars func(variables []string)
|
||||
newEmail := false
|
||||
@ -1661,16 +1662,21 @@ func (app *appContext) GetEmail(gc *gin.Context) {
|
||||
// app.storage.customEmails.InviteEmail = content
|
||||
} else if id == "WelcomeEmail" {
|
||||
content = app.storage.customEmails.WelcomeEmail.Content
|
||||
conditionals = []string{"{yourAccountWillExpire}"}
|
||||
app.storage.customEmails.WelcomeEmail.Conditionals = conditionals
|
||||
if content == "" {
|
||||
newEmail = true
|
||||
msg, err = app.email.constructWelcome("", time.Time{}, app, true)
|
||||
content = msg.Text
|
||||
conditionals = []string{"{yourAccountWillExpire}"}
|
||||
} else {
|
||||
variables = app.storage.customEmails.WelcomeEmail.Variables
|
||||
}
|
||||
writeVars = func(variables []string) { app.storage.customEmails.WelcomeEmail.Variables = variables }
|
||||
writeVars = func(variables []string) {
|
||||
app.storage.customEmails.WelcomeEmail.Variables = variables
|
||||
}
|
||||
// app.storage.customEmails.WelcomeEmail = content
|
||||
values = app.email.welcomeValues(username, time.Time{}, app, false, true)
|
||||
values = app.email.welcomeValues(username, time.Now(), app, false, true)
|
||||
} else if id == "EmailConfirmation" {
|
||||
content = app.storage.customEmails.EmailConfirmation.Content
|
||||
if content == "" {
|
||||
@ -1734,7 +1740,7 @@ func (app *appContext) GetEmail(gc *gin.Context) {
|
||||
respondBool(500, false, gc)
|
||||
return
|
||||
}
|
||||
gc.JSON(200, customEmailDTO{Content: content, Variables: variables, Values: values, HTML: email.HTML, Plaintext: email.Text})
|
||||
gc.JSON(200, customEmailDTO{Content: content, Variables: variables, Conditionals: conditionals, Values: values, HTML: email.HTML, Plaintext: email.Text})
|
||||
}
|
||||
|
||||
// @Summary Returns whether there's a new update, and extra info if there is.
|
||||
|
144
email.go
144
email.go
@ -274,13 +274,12 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
|
||||
var err error
|
||||
template := emailer.confirmationValues(code, username, key, app, noSub)
|
||||
if app.storage.customEmails.EmailConfirmation.Enabled {
|
||||
content := app.storage.customEmails.EmailConfirmation.Content
|
||||
for _, v := range app.storage.customEmails.EmailConfirmation.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.EmailConfirmation.Content,
|
||||
app.storage.customEmails.EmailConfirmation.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "email_confirmation", "email_", template)
|
||||
@ -346,13 +345,12 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
||||
template := emailer.inviteValues(code, invite, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.InviteEmail.Enabled {
|
||||
content := app.storage.customEmails.InviteEmail.Content
|
||||
for _, v := range app.storage.customEmails.InviteEmail.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.InviteEmail.Content,
|
||||
app.storage.customEmails.InviteEmail.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "invite_emails", "email_", template)
|
||||
@ -386,13 +384,12 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
|
||||
var err error
|
||||
template := emailer.expiryValues(code, invite, app, noSub)
|
||||
if app.storage.customEmails.InviteExpiry.Enabled {
|
||||
content := app.storage.customEmails.InviteExpiry.Content
|
||||
for _, v := range app.storage.customEmails.InviteExpiry.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.InviteExpiry.Content,
|
||||
app.storage.customEmails.InviteExpiry.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "notifications", "expiry_", template)
|
||||
@ -441,13 +438,12 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
|
||||
template := emailer.createdValues(code, username, address, invite, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.UserCreated.Enabled {
|
||||
content := app.storage.customEmails.UserCreated.Content
|
||||
for _, v := range app.storage.customEmails.UserCreated.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserCreated.Content,
|
||||
app.storage.customEmails.UserCreated.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "notifications", "created_", template)
|
||||
@ -516,13 +512,12 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext, noSub
|
||||
template := emailer.resetValues(pwr, app, noSub)
|
||||
var err error
|
||||
if app.storage.customEmails.PasswordReset.Enabled {
|
||||
content := app.storage.customEmails.PasswordReset.Content
|
||||
for _, v := range app.storage.customEmails.PasswordReset.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.PasswordReset.Content,
|
||||
app.storage.customEmails.PasswordReset.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "password_resets", "email_", template)
|
||||
@ -558,13 +553,12 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext, noSub b
|
||||
var err error
|
||||
template := emailer.deletedValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserDeleted.Enabled {
|
||||
content := app.storage.customEmails.UserDeleted.Content
|
||||
for _, v := range app.storage.customEmails.UserDeleted.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserDeleted.Content,
|
||||
app.storage.customEmails.UserDeleted.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "deletion", "email_", template)
|
||||
@ -600,13 +594,12 @@ func (emailer *Emailer) constructDisabled(reason string, app *appContext, noSub
|
||||
var err error
|
||||
template := emailer.disabledValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserDisabled.Enabled {
|
||||
content := app.storage.customEmails.UserDisabled.Content
|
||||
for _, v := range app.storage.customEmails.UserDisabled.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserDisabled.Content,
|
||||
app.storage.customEmails.UserDisabled.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "disable_enable", "disabled_", template)
|
||||
@ -642,13 +635,12 @@ func (emailer *Emailer) constructEnabled(reason string, app *appContext, noSub b
|
||||
var err error
|
||||
template := emailer.enabledValues(reason, app, noSub)
|
||||
if app.storage.customEmails.UserEnabled.Enabled {
|
||||
content := app.storage.customEmails.UserEnabled.Content
|
||||
for _, v := range app.storage.customEmails.UserEnabled.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserEnabled.Content,
|
||||
app.storage.customEmails.UserEnabled.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "disable_enable", "enabled_", template)
|
||||
@ -677,12 +669,14 @@ func (emailer *Emailer) welcomeValues(username string, expiry time.Time, app *ap
|
||||
template["username"] = username
|
||||
template["message"] = app.config.Section("email").Key("message").String()
|
||||
exp := app.formatDatetime(expiry)
|
||||
if custom {
|
||||
template["yourAccountWillExpire"] = exp
|
||||
} else if !expiry.IsZero() {
|
||||
template["yourAccountWillExpire"] = emailer.lang.WelcomeEmail.template("yourAccountWillExpire", tmpl{
|
||||
"date": exp,
|
||||
})
|
||||
if !expiry.IsZero() {
|
||||
if custom {
|
||||
template["yourAccountWillExpire"] = exp
|
||||
} else if !expiry.IsZero() {
|
||||
template["yourAccountWillExpire"] = emailer.lang.WelcomeEmail.template("yourAccountWillExpire", tmpl{
|
||||
"date": exp,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return template
|
||||
@ -705,13 +699,12 @@ func (emailer *Emailer) constructWelcome(username string, expiry time.Time, app
|
||||
})
|
||||
}
|
||||
if app.storage.customEmails.WelcomeEmail.Enabled {
|
||||
content := app.storage.customEmails.WelcomeEmail.Content
|
||||
for _, v := range app.storage.customEmails.WelcomeEmail.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.WelcomeEmail.Content,
|
||||
app.storage.customEmails.WelcomeEmail.Variables,
|
||||
app.storage.customEmails.WelcomeEmail.Conditionals,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "welcome_email", "email_", template)
|
||||
@ -741,13 +734,12 @@ func (emailer *Emailer) constructUserExpired(app *appContext, noSub bool) (*Emai
|
||||
var err error
|
||||
template := emailer.userExpiredValues(app, noSub)
|
||||
if app.storage.customEmails.UserExpired.Enabled {
|
||||
content := app.storage.customEmails.UserExpired.Content
|
||||
for _, v := range app.storage.customEmails.UserExpired.Variables {
|
||||
replaceWith, ok := template[v[1:len(v)-1]]
|
||||
if ok {
|
||||
content = strings.ReplaceAll(content, v, replaceWith.(string))
|
||||
}
|
||||
}
|
||||
content := templateEmail(
|
||||
app.storage.customEmails.UserExpired.Content,
|
||||
app.storage.customEmails.UserExpired.Variables,
|
||||
nil,
|
||||
template,
|
||||
)
|
||||
email, err = emailer.constructTemplate(email.Subject, content, app)
|
||||
} else {
|
||||
email.HTML, email.Text, err = emailer.construct(app, "user_expiry", "email_", template)
|
||||
|
@ -183,6 +183,8 @@
|
||||
<div class="col flex-col content mt-half">
|
||||
<span class="label supra" for="editor-variables" id="label-editor-variables">{{ .strings.variables }}</span>
|
||||
<div id="editor-variables"></div>
|
||||
<span class="label supra" for="editor-conditionals" id="label-editor-conditionals">{{ .strings.conditionals }}</span>
|
||||
<div id="editor-conditionals"></div>
|
||||
<label class="label supra" for="textarea-editor">{{ .strings.message }}</label>
|
||||
<textarea id="textarea-editor" class="textarea full-width flex-auto ~neutral !normal mt-half monospace"></textarea>
|
||||
<p class="support mt-half mb-1">{{ .strings.markdownSupported }}</p>
|
||||
|
@ -49,6 +49,7 @@
|
||||
"subject": "Email Subject",
|
||||
"message": "Message",
|
||||
"variables": "Variables",
|
||||
"conditionals": "Conditionals",
|
||||
"preview": "Preview",
|
||||
"reset": "Reset",
|
||||
"edit": "Edit",
|
||||
|
11
models.go
11
models.go
@ -214,11 +214,12 @@ type emailTestDTO struct {
|
||||
}
|
||||
|
||||
type customEmailDTO struct {
|
||||
Content string `json:"content"`
|
||||
Variables []string `json:"variables"`
|
||||
Values map[string]interface{} `json:"values"`
|
||||
HTML string `json:"html"`
|
||||
Plaintext string `json:"plaintext"`
|
||||
Content string `json:"content"`
|
||||
Variables []string `json:"variables"`
|
||||
Conditionals []string `json:"conditionals"`
|
||||
Values map[string]interface{} `json:"values"`
|
||||
HTML string `json:"html"`
|
||||
Plaintext string `json:"plaintext"`
|
||||
}
|
||||
|
||||
type extendExpiryDTO struct {
|
||||
|
@ -43,9 +43,10 @@ type customEmails struct {
|
||||
}
|
||||
|
||||
type customEmail struct {
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Variables []string `json:"variables,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Variables []string `json:"variables,omitempty"`
|
||||
Conditionals []string `json:"conditionals,omitempty"`
|
||||
}
|
||||
|
||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||
|
118
template.go
Normal file
118
template.go
Normal file
@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func truthy(val interface{}) bool {
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return v != ""
|
||||
case bool:
|
||||
return v
|
||||
case int:
|
||||
return v != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Templater for custom emails.
|
||||
// Variables should be written as {varName}.
|
||||
// If statements should be written as {if (!)varName}...{endif}.
|
||||
// Strings are true if != "", ints are true if != 0.
|
||||
func templateEmail(content string, variables []string, conditionals []string, values map[string]interface{}) string {
|
||||
ifStart, ifEnd := -1, -1
|
||||
ifTrue := false
|
||||
invalidIf := false
|
||||
previousEnd := -2
|
||||
cStart, cEnd := -1, -1
|
||||
varStart, varEnd := -1, -1
|
||||
varName := ""
|
||||
out := ""
|
||||
for i, c := range content {
|
||||
if c == '{' {
|
||||
cStart = i + 1
|
||||
for content[cStart] == ' ' {
|
||||
cStart++
|
||||
}
|
||||
if content[cStart:cStart+3] == "if " {
|
||||
varStart = cStart + 3
|
||||
for content[varStart] == ' ' {
|
||||
varStart++
|
||||
}
|
||||
}
|
||||
if ifStart == -1 {
|
||||
out += content[previousEnd+2 : i]
|
||||
}
|
||||
if content[cStart:cStart+5] != "endif" || invalidIf {
|
||||
continue
|
||||
}
|
||||
ifEnd = i - 1
|
||||
if ifTrue {
|
||||
b := templateEmail(content[ifStart:ifEnd+1], variables, conditionals, values)
|
||||
out += b
|
||||
ifTrue = false
|
||||
}
|
||||
} else if c == '}' {
|
||||
if varStart != -1 {
|
||||
ifStart = i + 1
|
||||
varEnd = i - 1
|
||||
for content[varEnd] == ' ' {
|
||||
varEnd--
|
||||
}
|
||||
varName = content[varStart : varEnd+1]
|
||||
positive := true
|
||||
if varName[0] == '!' {
|
||||
positive = false
|
||||
varName = varName[1:]
|
||||
}
|
||||
validVar := false
|
||||
wrappedVarName := "{" + varName + "}"
|
||||
for _, v := range conditionals {
|
||||
if v == wrappedVarName {
|
||||
validVar = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if validVar {
|
||||
ifTrue = positive == truthy(values[varName])
|
||||
} else {
|
||||
invalidIf = true
|
||||
ifStart, ifEnd = -1, -1
|
||||
}
|
||||
varStart, varEnd = -1, -1
|
||||
}
|
||||
cEnd = i - 1
|
||||
for content[cEnd] == ' ' {
|
||||
cEnd--
|
||||
}
|
||||
previousEnd = i - 1
|
||||
if content[cEnd-4:cEnd+1] == "endif" && !invalidIf {
|
||||
continue
|
||||
}
|
||||
validVar := false
|
||||
varName = content[cStart : cEnd+1]
|
||||
cStart, cEnd = -1, -1
|
||||
if ifStart != -1 {
|
||||
continue
|
||||
}
|
||||
wrappedVarName := "{" + varName + "}"
|
||||
for _, v := range variables {
|
||||
if v == wrappedVarName {
|
||||
validVar = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validVar {
|
||||
out += wrappedVarName
|
||||
continue
|
||||
}
|
||||
out += fmt.Sprint(values[varName])
|
||||
}
|
||||
}
|
||||
if previousEnd+1 != len(content)-1 {
|
||||
out += content[previousEnd+2:]
|
||||
}
|
||||
if out == "" {
|
||||
return content
|
||||
}
|
||||
return out
|
||||
}
|
@ -769,6 +769,7 @@ class ombiDefaults {
|
||||
interface templateEmail {
|
||||
content: string;
|
||||
variables: string[];
|
||||
conditionals: string[];
|
||||
values: { [key: string]: string };
|
||||
html: string;
|
||||
plaintext: string;
|
||||
@ -788,6 +789,8 @@ class EmailEditor {
|
||||
private _header = document.getElementById("header-editor") as HTMLSpanElement;
|
||||
private _variables = document.getElementById("editor-variables") as HTMLDivElement;
|
||||
private _variablesLabel = document.getElementById("label-editor-variables") as HTMLElement;
|
||||
private _conditionals = document.getElementById("editor-conditionals") as HTMLDivElement;
|
||||
private _conditionalsLabel = document.getElementById("label-editor-conditionals") as HTMLElement;
|
||||
private _textArea = document.getElementById("textarea-editor") as HTMLTextAreaElement;
|
||||
private _preview = document.getElementById("editor-preview") as HTMLDivElement;
|
||||
private _previewContent: HTMLElement;
|
||||
@ -845,7 +848,7 @@ class EmailEditor {
|
||||
this._variablesLabel.classList.remove("unfocused");
|
||||
}
|
||||
this._variables.innerHTML = innerHTML
|
||||
const buttons = this._variables.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
|
||||
let buttons = this._variables.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
|
||||
for (let i = 0; i < this._templ.variables.length; i++) {
|
||||
buttons[i].innerHTML = `<span class="monospace">` + this._templ.variables[i] + `</span>`;
|
||||
buttons[i].onclick = () => {
|
||||
@ -854,6 +857,28 @@ class EmailEditor {
|
||||
// this._timeout = setTimeout(this.loadPreview, this._finishInterval);
|
||||
}
|
||||
}
|
||||
|
||||
innerHTML = '';
|
||||
for (let i = this._templ.conditionals.length-1; i >= 0; i--) {
|
||||
let ci = i % colors.length;
|
||||
innerHTML += '<span class="button ~' + colors[ci] +' !normal mb-1" style="margin-left: 0.25rem; margin-right: 0.25rem;"></span>'
|
||||
}
|
||||
if (this._templ.conditionals.length == 0) {
|
||||
this._conditionalsLabel.classList.add("unfocused");
|
||||
} else {
|
||||
this._conditionalsLabel.classList.remove("unfocused");
|
||||
}
|
||||
this._conditionals.innerHTML = innerHTML
|
||||
buttons = this._conditionals.querySelectorAll("span.button") as NodeListOf<HTMLSpanElement>;
|
||||
for (let i = 0; i < this._templ.conditionals.length; i++) {
|
||||
buttons[i].innerHTML = `<span class="monospace">{if ` + this._templ.conditionals[i].slice(1) + `</span>`;
|
||||
buttons[i].onclick = () => {
|
||||
this.insert(this._textArea, "{if " + this._templ.conditionals[i].slice(1) + "{endif}");
|
||||
this.loadPreview();
|
||||
// this._timeout = setTimeout(this.loadPreview, this._finishInterval);
|
||||
}
|
||||
}
|
||||
|
||||
window.modals.editor.show();
|
||||
}
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user