mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-06 01:42:13 +00:00
feat: e-mail templates (#1158)
* View definition added * Get templates and texts from the database. * Fill in texts in templates * Fill in texts in templates * Client API added * Weekly backup * Weekly backup * Daily backup * Weekly backup * Tests added * Corrections from merge branch * Fixes from pull request review
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
@@ -24,10 +25,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
notificationTable = "notification.notifications"
|
||||
NotifyUserID = "NOTIFICATION"
|
||||
labelPolicyTableOrg = "management.label_policies"
|
||||
labelPolicyTableDef = "adminapi.label_policies"
|
||||
notificationTable = "notification.notifications"
|
||||
NotifyUserID = "NOTIFICATION"
|
||||
labelPolicyTableOrg = "management.label_policies"
|
||||
labelPolicyTableDef = "adminapi.label_policies"
|
||||
mailTemplateTableOrg = "management.mail_templates"
|
||||
mailTemplateTableDef = "adminapi.mail_templates"
|
||||
mailTextTableOrg = "management.mail_texts"
|
||||
mailTextTableDef = "adminapi.mail_texts"
|
||||
mailTextTypeDomainClaimed = "DomainClaimed"
|
||||
mailTextTypeInitCode = "InitCode"
|
||||
mailTextTypePasswordReset = "PasswordReset"
|
||||
mailTextTypeVerifyEmail = "VerifyEmail"
|
||||
mailTextTypeVerifyPhone = "VerifyPhone"
|
||||
)
|
||||
|
||||
type Notification struct {
|
||||
@@ -135,11 +145,22 @@ func (n *Notification) handleInitUserCode(event *models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := n.getMailTemplate(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendUserInitCode(n.statikDir, n.i18n, user, initCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
|
||||
text, err := n.getMailText(context.Background(), mailTextTypeInitCode, user.PreferredLanguage[len(user.PreferredLanguage)-2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = types.SendUserInitCode(string(template.Template), text, user, initCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -163,11 +184,21 @@ func (n *Notification) handlePasswordCode(event *models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := n.getMailTemplate(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendPasswordCode(n.statikDir, n.i18n, user, pwCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
|
||||
text, err := n.getMailText(context.Background(), mailTextTypePasswordReset, user.PreferredLanguage[len(user.PreferredLanguage)-2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendPasswordCode(string(template.Template), text, user, pwCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -191,11 +222,22 @@ func (n *Notification) handleEmailVerificationCode(event *models.Event) (err err
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := n.getMailTemplate(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := n.view.NotifyUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendEmailVerificationCode(n.statikDir, n.i18n, user, emailCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
|
||||
text, err := n.getMailText(context.Background(), mailTextTypeVerifyEmail, user.PreferredLanguage[len(user.PreferredLanguage)-2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = types.SendEmailVerificationCode(string(template.Template), text, user, emailCode, n.systemDefaults, n.AesCrypto, colors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -238,7 +280,21 @@ func (n *Notification) handleDomainClaimed(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendDomainClaimed(n.statikDir, n.i18n, user, data["userName"], n.systemDefaults)
|
||||
colors, err := n.getLabelPolicy(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
template, err := n.getMailTemplate(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
text, err := n.getMailText(context.Background(), mailTextTypeDomainClaimed, user.PreferredLanguage[len(user.PreferredLanguage)-2:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = types.SendDomainClaimed(string(template.Template), text, user, data["userName"], n.systemDefaults, colors)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -306,3 +362,39 @@ func (n *Notification) getLabelPolicy(ctx context.Context) (*iam_model.LabelPoli
|
||||
}
|
||||
return iam_es_model.LabelPolicyViewToModel(policy), err
|
||||
}
|
||||
|
||||
// Read organization specific template
|
||||
func (n *Notification) getMailTemplate(ctx context.Context) (*iam_model.MailTemplateView, error) {
|
||||
// read from Org
|
||||
template, err := n.view.MailTemplateByAggregateID(authz.GetCtxData(ctx).OrgID, mailTemplateTableOrg)
|
||||
if errors.IsNotFound(err) {
|
||||
// read from default
|
||||
template, err = n.view.MailTemplateByAggregateID(n.systemDefaults.IamID, mailTemplateTableDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
template.Default = true
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iam_es_model.MailTemplateViewToModel(template), err
|
||||
}
|
||||
|
||||
// Read organization specific texts
|
||||
func (n *Notification) getMailText(ctx context.Context, textType string, language string) (*iam_model.MailTextView, error) {
|
||||
// read from Org
|
||||
mailText, err := n.view.MailTextByIDs(authz.GetCtxData(ctx).OrgID, textType, language, mailTextTableOrg)
|
||||
if errors.IsNotFound(err) {
|
||||
// read from default
|
||||
mailText, err = n.view.MailTextByIDs(n.systemDefaults.IamID, textType, language, mailTextTableDef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mailText.Default = true
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return iam_es_model.MailTextViewToModel(mailText), err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/iam/repository/view"
|
||||
"github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
)
|
||||
|
||||
func (v *View) MailTemplateByAggregateID(aggregateID string, mailTemplateTableVar string) (*model.MailTemplateView, error) {
|
||||
return view.GetMailTemplateByAggregateID(v.Db, mailTemplateTableVar, aggregateID)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/iam/repository/view"
|
||||
"github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
)
|
||||
|
||||
func (v *View) MailTextByIDs(aggregateID string, textType string, language string, mailTextTableVar string) (*model.MailTextView, error) {
|
||||
return view.GetMailTextByIDs(v.Db, mailTextTableVar, aggregateID, textType, language)
|
||||
}
|
||||
@@ -12,23 +12,21 @@ const (
|
||||
templateFileName = "template.html"
|
||||
)
|
||||
|
||||
func GetParsedTemplate(dir http.FileSystem, contentData interface{}) (string, error) {
|
||||
template, err := ParseTemplateFile(dir, "", contentData)
|
||||
func GetParsedTemplate(mailhtml string, contentData interface{}) (string, error) {
|
||||
template, err := ParseTemplateFile(mailhtml, contentData)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return ParseTemplateText(template, contentData)
|
||||
}
|
||||
|
||||
func ParseTemplateFile(dir http.FileSystem, fileName string, data interface{}) (string, error) {
|
||||
if fileName == "" {
|
||||
fileName = templateFileName
|
||||
}
|
||||
template, err := readFile(dir, fileName)
|
||||
func ParseTemplateFile(mailhtml string, data interface{}) (string, error) {
|
||||
tmpl, err := template.New("tmpl").Parse(mailhtml)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return parseTemplate(template, data)
|
||||
|
||||
return parseTemplate(tmpl, data)
|
||||
}
|
||||
|
||||
func ParseTemplateText(text string, data interface{}) (string, error) {
|
||||
@@ -63,3 +61,20 @@ func readFile(dir http.FileSystem, fileName string) (*template.Template, error)
|
||||
}
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
func readFileFromDatabase(dir http.FileSystem, fileName string) (*template.Template, error) {
|
||||
f, err := dir.Open(templatesPath + "/" + fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
content, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmpl, err := template.New(fileName).Parse(string(content))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tmpl, nil
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"html"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/notification/templates"
|
||||
view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
)
|
||||
@@ -15,7 +15,7 @@ type DomainClaimedData struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
func SendDomainClaimed(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults) error {
|
||||
func SendDomainClaimed(mailhtml string, text *iam_model.MailTextView, user *view_model.NotifyUser, username string, systemDefaults systemdefaults.SystemDefaults, colors *iam_model.LabelPolicyView) error {
|
||||
url, err := templates.ParseTemplateText(systemDefaults.Notifications.Endpoints.DomainClaimed, &UrlData{UserID: user.ID})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -27,11 +27,28 @@ func SendDomainClaimed(dir http.FileSystem, i18n *i18n.Translator, user *view_mo
|
||||
"TempUsername": username,
|
||||
"Domain": strings.Split(user.LastEmail, "@")[1],
|
||||
}
|
||||
systemDefaults.Notifications.TemplateData.DomainClaimed.Translate(i18n, args, user.PreferredLanguage)
|
||||
data := &DomainClaimedData{TemplateData: systemDefaults.Notifications.TemplateData.DomainClaimed, URL: url}
|
||||
template, err := templates.GetParsedTemplate(dir, data)
|
||||
|
||||
text.Greeting, err = templates.ParseTemplateText(text.Greeting, args)
|
||||
text.Text, err = templates.ParseTemplateText(text.Text, args)
|
||||
text.Text = html.UnescapeString(text.Text)
|
||||
|
||||
emailCodeData := &DomainClaimedData{
|
||||
TemplateData: templates.TemplateData{
|
||||
Title: text.Title,
|
||||
PreHeader: text.PreHeader,
|
||||
Subject: text.Subject,
|
||||
Greeting: text.Greeting,
|
||||
Text: html.UnescapeString(text.Text),
|
||||
Href: url,
|
||||
ButtonText: text.ButtonText,
|
||||
PrimaryColor: colors.PrimaryColor,
|
||||
SecondaryColor: colors.SecondaryColor,
|
||||
},
|
||||
URL: url,
|
||||
}
|
||||
template, err := templates.GetParsedTemplate(mailhtml, emailCodeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return generateEmail(user, systemDefaults.Notifications.TemplateData.DomainClaimed.Subject, template, systemDefaults.Notifications, true)
|
||||
return generateEmail(user, text.Subject, template, systemDefaults.Notifications, true)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"html"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/notification/templates"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
@@ -17,7 +16,7 @@ type EmailVerificationCodeData struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
func SendEmailVerificationCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
func SendEmailVerificationCode(mailhtml string, text *iam_model.MailTextView, user *view_model.NotifyUser, code *es_model.EmailCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
codeString, err := crypto.DecryptString(code.Code, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -31,15 +30,29 @@ func SendEmailVerificationCode(dir http.FileSystem, i18n *i18n.Translator, user
|
||||
"LastName": user.LastName,
|
||||
"Code": codeString,
|
||||
}
|
||||
systemDefaults.Notifications.TemplateData.VerifyEmail.Translate(i18n, args, user.PreferredLanguage)
|
||||
emailCodeData := &EmailVerificationCodeData{TemplateData: systemDefaults.Notifications.TemplateData.VerifyEmail, URL: url}
|
||||
|
||||
// Set the color in initCodeData
|
||||
emailCodeData.PrimaryColor = colors.PrimaryColor
|
||||
emailCodeData.SecondaryColor = colors.SecondaryColor
|
||||
template, err := templates.GetParsedTemplate(dir, emailCodeData)
|
||||
text.Greeting, err = templates.ParseTemplateText(text.Greeting, args)
|
||||
text.Text, err = templates.ParseTemplateText(text.Text, args)
|
||||
text.Text = html.UnescapeString(text.Text)
|
||||
|
||||
emailCodeData := &EmailVerificationCodeData{
|
||||
TemplateData: templates.TemplateData{
|
||||
Title: text.Title,
|
||||
PreHeader: text.PreHeader,
|
||||
Subject: text.Subject,
|
||||
Greeting: text.Greeting,
|
||||
Text: html.UnescapeString(text.Text),
|
||||
Href: url,
|
||||
ButtonText: text.ButtonText,
|
||||
PrimaryColor: colors.PrimaryColor,
|
||||
SecondaryColor: colors.SecondaryColor,
|
||||
},
|
||||
URL: url,
|
||||
}
|
||||
|
||||
template, err := templates.GetParsedTemplate(mailhtml, emailCodeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return generateEmail(user, systemDefaults.Notifications.TemplateData.VerifyEmail.Subject, template, systemDefaults.Notifications, true)
|
||||
return generateEmail(user, text.Subject, template, systemDefaults.Notifications, true)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"html"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/notification/templates"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
@@ -23,7 +22,7 @@ type UrlData struct {
|
||||
PasswordSet bool
|
||||
}
|
||||
|
||||
func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
func SendUserInitCode(mailhtml string, text *iam_model.MailTextView, user *view_model.NotifyUser, code *es_model.InitUserCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
codeString, err := crypto.DecryptString(code.Code, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -38,15 +37,28 @@ func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_mod
|
||||
"Code": codeString,
|
||||
"PreferredLoginName": user.PreferredLoginName,
|
||||
}
|
||||
systemDefaults.Notifications.TemplateData.InitCode.Translate(i18n, args, user.PreferredLanguage)
|
||||
initCodeData := &InitCodeEmailData{TemplateData: systemDefaults.Notifications.TemplateData.InitCode, URL: url}
|
||||
|
||||
// Set the color in initCodeData
|
||||
initCodeData.PrimaryColor = colors.PrimaryColor
|
||||
initCodeData.SecondaryColor = colors.SecondaryColor
|
||||
template, err := templates.GetParsedTemplate(dir, initCodeData)
|
||||
text.Greeting, err = templates.ParseTemplateText(text.Greeting, args)
|
||||
text.Text, err = templates.ParseTemplateText(text.Text, args)
|
||||
text.Text = html.UnescapeString(text.Text)
|
||||
|
||||
emailCodeData := &InitCodeEmailData{
|
||||
TemplateData: templates.TemplateData{
|
||||
Title: text.Title,
|
||||
PreHeader: text.PreHeader,
|
||||
Subject: text.Subject,
|
||||
Greeting: text.Greeting,
|
||||
Text: html.UnescapeString(text.Text),
|
||||
Href: url,
|
||||
ButtonText: text.ButtonText,
|
||||
PrimaryColor: colors.PrimaryColor,
|
||||
SecondaryColor: colors.SecondaryColor,
|
||||
},
|
||||
URL: url,
|
||||
}
|
||||
template, err := templates.GetParsedTemplate(mailhtml, emailCodeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return generateEmail(user, systemDefaults.Notifications.TemplateData.InitCode.Subject, template, systemDefaults.Notifications, true)
|
||||
return generateEmail(user, text.Subject, template, systemDefaults.Notifications, true)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"html"
|
||||
|
||||
"github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/notification/templates"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
@@ -19,7 +18,7 @@ type PasswordCodeData struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
func SendPasswordCode(dir http.FileSystem, i18n *i18n.Translator, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
func SendPasswordCode(mailhtml string, text *iam_model.MailTextView, user *view_model.NotifyUser, code *es_model.PasswordCode, systemDefaults systemdefaults.SystemDefaults, alg crypto.EncryptionAlgorithm, colors *iam_model.LabelPolicyView) error {
|
||||
codeString, err := crypto.DecryptString(code.Code, alg)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -33,15 +32,30 @@ func SendPasswordCode(dir http.FileSystem, i18n *i18n.Translator, user *view_mod
|
||||
"LastName": user.LastName,
|
||||
"Code": codeString,
|
||||
}
|
||||
systemDefaults.Notifications.TemplateData.PasswordReset.Translate(i18n, args, user.PreferredLanguage)
|
||||
passwordCodeData := &PasswordCodeData{TemplateData: systemDefaults.Notifications.TemplateData.PasswordReset, FirstName: user.FirstName, LastName: user.LastName, URL: url}
|
||||
|
||||
// Set the color in initCodeData
|
||||
passwordCodeData.PrimaryColor = colors.PrimaryColor
|
||||
passwordCodeData.SecondaryColor = colors.SecondaryColor
|
||||
template, err := templates.GetParsedTemplate(dir, passwordCodeData)
|
||||
text.Greeting, err = templates.ParseTemplateText(text.Greeting, args)
|
||||
text.Text, err = templates.ParseTemplateText(text.Text, args)
|
||||
text.Text = html.UnescapeString(text.Text)
|
||||
|
||||
emailCodeData := &PasswordCodeData{
|
||||
TemplateData: templates.TemplateData{
|
||||
Title: text.Title,
|
||||
PreHeader: text.PreHeader,
|
||||
Subject: text.Subject,
|
||||
Greeting: text.Greeting,
|
||||
Text: html.UnescapeString(text.Text),
|
||||
Href: url,
|
||||
ButtonText: text.ButtonText,
|
||||
PrimaryColor: colors.PrimaryColor,
|
||||
SecondaryColor: colors.SecondaryColor,
|
||||
},
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
URL: url,
|
||||
}
|
||||
template, err := templates.GetParsedTemplate(mailhtml, emailCodeData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return generateEmail(user, systemDefaults.Notifications.TemplateData.PasswordReset.Subject, template, systemDefaults.Notifications, false)
|
||||
return generateEmail(user, text.Subject, template, systemDefaults.Notifications, true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user