refactor template code (#7860)
Этот коммит содержится в:
коммит произвёл
Harrison Healey
родитель
7eb7509393
Коммит
5cf45d2155
@@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mattermost/mattermost-server/app"
|
"github.com/mattermost/mattermost-server/app"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/utils"
|
|
||||||
|
|
||||||
_ "github.com/nicksnyder/go-i18n/i18n"
|
_ "github.com/nicksnyder/go-i18n/i18n"
|
||||||
)
|
)
|
||||||
@@ -113,8 +112,6 @@ func Init(a *app.App, root *mux.Router) *API {
|
|||||||
// 404 on any api route before web.go has a chance to serve it
|
// 404 on any api route before web.go has a chance to serve it
|
||||||
root.Handle("/api/{anything:.*}", http.HandlerFunc(Handle404))
|
root.Handle("/api/{anything:.*}", http.HandlerFunc(Handle404))
|
||||||
|
|
||||||
utils.InitHTML()
|
|
||||||
|
|
||||||
a.InitEmailBatching()
|
a.InitEmailBatching()
|
||||||
|
|
||||||
if *a.Config().ServiceSettings.EnableAPIv3 {
|
if *a.Config().ServiceSettings.EnableAPIv3 {
|
||||||
|
|||||||
@@ -1040,7 +1040,7 @@ func updateMfa(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := app.SendMfaChangeEmail(user.Email, activate, user.Locale, utils.GetSiteURL()); err != nil {
|
if err := c.App.SendMfaChangeEmail(user.Email, activate, user.Locale, utils.GetSiteURL()); err != nil {
|
||||||
l4g.Error(err.Error())
|
l4g.Error(err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -222,8 +222,6 @@ func Init(a *app.App, root *mux.Router, full bool) *API {
|
|||||||
|
|
||||||
// REMOVE CONDITION WHEN APIv3 REMOVED
|
// REMOVE CONDITION WHEN APIv3 REMOVED
|
||||||
if full {
|
if full {
|
||||||
utils.InitHTML()
|
|
||||||
|
|
||||||
a.InitEmailBatching()
|
a.InitEmailBatching()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
app/app.go
18
app/app.go
@@ -4,6 +4,7 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -52,7 +53,8 @@ type App struct {
|
|||||||
configFile string
|
configFile string
|
||||||
newStore func() store.Store
|
newStore func() store.Store
|
||||||
|
|
||||||
sessionCache *utils.Cache
|
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
||||||
|
sessionCache *utils.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
var appCount = 0
|
var appCount = 0
|
||||||
@@ -94,6 +96,12 @@ func New(options ...Option) *App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if htmlTemplateWatcher, err := utils.NewHTMLTemplateWatcher("templates"); err != nil {
|
||||||
|
l4g.Error(utils.T("api.api.init.parsing_templates.error"), err)
|
||||||
|
} else {
|
||||||
|
app.htmlTemplateWatcher = htmlTemplateWatcher
|
||||||
|
}
|
||||||
|
|
||||||
app.Srv.Store = app.newStore()
|
app.Srv.Store = app.newStore()
|
||||||
app.initJobs()
|
app.initJobs()
|
||||||
|
|
||||||
@@ -125,6 +133,10 @@ func (a *App) Shutdown() {
|
|||||||
a.Srv.Store.Close()
|
a.Srv.Store.Close()
|
||||||
a.Srv = nil
|
a.Srv = nil
|
||||||
|
|
||||||
|
if a.htmlTemplateWatcher != nil {
|
||||||
|
a.htmlTemplateWatcher.Close()
|
||||||
|
}
|
||||||
|
|
||||||
l4g.Info(utils.T("api.server.stop_server.stopped.info"))
|
l4g.Info(utils.T("api.server.stop_server.stopped.info"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,6 +339,10 @@ func (a *App) WaitForGoroutines() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) HTMLTemplates() *template.Template {
|
||||||
|
return a.htmlTemplateWatcher.Templates()
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
|
func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
|
||||||
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
|
err := model.NewAppError("Handle404", "api.context.404.app_error", nil, "", http.StatusNotFound)
|
||||||
err.Translate(utils.T)
|
err.Translate(utils.T)
|
||||||
|
|||||||
@@ -66,7 +66,6 @@ func setupTestHelper(enterprise bool) *TestHelper {
|
|||||||
}
|
}
|
||||||
th.App.StartServer()
|
th.App.StartServer()
|
||||||
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ServiceSettings.ListenAddress = prevListenAddress })
|
||||||
utils.InitHTML()
|
|
||||||
utils.EnableDebugLogForTest()
|
utils.EnableDebugLogForTest()
|
||||||
th.App.Srv.Store.MarkSystemRanUnitTests()
|
th.App.Srv.Store.MarkSystemRanUnitTests()
|
||||||
|
|
||||||
|
|||||||
54
app/email.go
54
app/email.go
@@ -10,6 +10,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
l4g "github.com/alecthomas/log4go"
|
l4g "github.com/alecthomas/log4go"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/utils"
|
"github.com/mattermost/mattermost-server/utils"
|
||||||
)
|
)
|
||||||
@@ -21,7 +23,7 @@ func (a *App) SendChangeUsernameEmail(oldUsername, newUsername, email, locale, s
|
|||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
||||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
|
bodyPage := a.NewEmailTemplate("email_change_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
|
bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.username_change_body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.username_change_body.info",
|
||||||
@@ -43,7 +45,7 @@ func (a *App) SendEmailChangeVerifyEmail(newUserEmail, locale, siteURL, token st
|
|||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
||||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("email_change_verify_body", locale)
|
bodyPage := a.NewEmailTemplate("email_change_verify_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title")
|
bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title")
|
||||||
bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info",
|
bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info",
|
||||||
@@ -65,7 +67,7 @@ func (a *App) SendEmailChangeEmail(oldEmail, newEmail, locale, siteURL string) *
|
|||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
||||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("email_change_body", locale)
|
bodyPage := a.NewEmailTemplate("email_change_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
|
bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.email_change_body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.email_change_body.info",
|
||||||
@@ -88,7 +90,7 @@ func (a *App) SendVerifyEmail(userEmail, locale, siteURL, token string) *model.A
|
|||||||
subject := T("api.templates.verify_subject",
|
subject := T("api.templates.verify_subject",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("verify_body", locale)
|
bodyPage := a.NewEmailTemplate("verify_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": url.Host})
|
bodyPage.Props["Title"] = T("api.templates.verify_body.title", map[string]interface{}{"ServerURL": url.Host})
|
||||||
bodyPage.Props["Info"] = T("api.templates.verify_body.info")
|
bodyPage.Props["Info"] = T("api.templates.verify_body.info")
|
||||||
@@ -108,7 +110,7 @@ func (a *App) SendSignInChangeEmail(email, method, locale, siteURL string) *mode
|
|||||||
subject := T("api.templates.signin_change_email.subject",
|
subject := T("api.templates.signin_change_email.subject",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("signin_change_body", locale)
|
bodyPage := a.NewEmailTemplate("signin_change_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
|
bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.signin_change_email.body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.signin_change_email.body.info",
|
||||||
@@ -130,7 +132,7 @@ func (a *App) SendWelcomeEmail(userId string, email string, verified bool, local
|
|||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
||||||
"ServerURL": rawUrl.Host})
|
"ServerURL": rawUrl.Host})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("welcome_body", locale)
|
bodyPage := a.NewEmailTemplate("welcome_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": rawUrl.Host})
|
bodyPage.Props["Title"] = T("api.templates.welcome_body.title", map[string]interface{}{"ServerURL": rawUrl.Host})
|
||||||
bodyPage.Props["Info"] = T("api.templates.welcome_body.info")
|
bodyPage.Props["Info"] = T("api.templates.welcome_body.info")
|
||||||
@@ -167,7 +169,7 @@ func (a *App) SendPasswordChangeEmail(email, method, locale, siteURL string) *mo
|
|||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"],
|
||||||
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
"TeamDisplayName": a.Config().TeamSettings.SiteName})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
|
bodyPage := a.NewEmailTemplate("password_change_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
|
bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.password_change_body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.password_change_body.info",
|
||||||
@@ -180,13 +182,13 @@ func (a *App) SendPasswordChangeEmail(email, method, locale, siteURL string) *mo
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendUserAccessTokenAddedEmail(email, locale string) *model.AppError {
|
func (a *App) SendUserAccessTokenAddedEmail(email, locale string) *model.AppError {
|
||||||
T := utils.GetUserTranslations(locale)
|
T := utils.GetUserTranslations(locale)
|
||||||
|
|
||||||
subject := T("api.templates.user_access_token_subject",
|
subject := T("api.templates.user_access_token_subject",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("password_change_body", locale)
|
bodyPage := a.NewEmailTemplate("password_change_body", locale)
|
||||||
bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title")
|
bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.user_access_token_body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.user_access_token_body.info",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "SiteURL": utils.GetSiteURL()})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"], "SiteURL": utils.GetSiteURL()})
|
||||||
@@ -198,7 +200,7 @@ func SendUserAccessTokenAddedEmail(email, locale string) *model.AppError {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError) {
|
func (a *App) SendPasswordResetEmail(email string, token *model.Token, locale, siteURL string) (bool, *model.AppError) {
|
||||||
|
|
||||||
T := utils.GetUserTranslations(locale)
|
T := utils.GetUserTranslations(locale)
|
||||||
|
|
||||||
@@ -207,7 +209,7 @@ func SendPasswordResetEmail(email string, token *model.Token, locale, siteURL st
|
|||||||
subject := T("api.templates.reset_subject",
|
subject := T("api.templates.reset_subject",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("reset_body", locale)
|
bodyPage := a.NewEmailTemplate("reset_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = T("api.templates.reset_body.title")
|
bodyPage.Props["Title"] = T("api.templates.reset_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info", nil)
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(T, "api.templates.reset_body.info", nil)
|
||||||
@@ -221,13 +223,13 @@ func SendPasswordResetEmail(email string, token *model.Token, locale, siteURL st
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError {
|
func (a *App) SendMfaChangeEmail(email string, activated bool, locale, siteURL string) *model.AppError {
|
||||||
T := utils.GetUserTranslations(locale)
|
T := utils.GetUserTranslations(locale)
|
||||||
|
|
||||||
subject := T("api.templates.mfa_change_subject",
|
subject := T("api.templates.mfa_change_subject",
|
||||||
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("mfa_change_body", locale)
|
bodyPage := a.NewEmailTemplate("mfa_change_body", locale)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
|
|
||||||
bodyText := ""
|
bodyText := ""
|
||||||
@@ -258,7 +260,7 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, invites []st
|
|||||||
"TeamDisplayName": team.DisplayName,
|
"TeamDisplayName": team.DisplayName,
|
||||||
"SiteName": utils.ClientCfg["SiteName"]})
|
"SiteName": utils.ClientCfg["SiteName"]})
|
||||||
|
|
||||||
bodyPage := utils.NewHTMLTemplate("invite_body", model.DEFAULT_LOCALE)
|
bodyPage := a.NewEmailTemplate("invite_body", model.DEFAULT_LOCALE)
|
||||||
bodyPage.Props["SiteURL"] = siteURL
|
bodyPage.Props["SiteURL"] = siteURL
|
||||||
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
|
bodyPage.Props["Title"] = utils.T("api.templates.invite_body.title")
|
||||||
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info",
|
bodyPage.Html["Info"] = utils.TranslateAsHtml(utils.T, "api.templates.invite_body.info",
|
||||||
@@ -288,3 +290,27 @@ func (a *App) SendInviteEmails(team *model.Team, senderName string, invites []st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) NewEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||||
|
t := utils.NewHTMLTemplate(a.HTMLTemplates(), name)
|
||||||
|
|
||||||
|
var localT i18n.TranslateFunc
|
||||||
|
if locale != "" {
|
||||||
|
localT = utils.GetUserTranslations(locale)
|
||||||
|
} else {
|
||||||
|
localT = utils.T
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Props["Footer"] = localT("api.templates.email_footer")
|
||||||
|
|
||||||
|
if *a.Config().EmailSettings.FeedbackOrganization != "" {
|
||||||
|
t.Props["Organization"] = localT("api.templates.email_organization") + *a.Config().EmailSettings.FeedbackOrganization
|
||||||
|
} else {
|
||||||
|
t.Props["Organization"] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Html["EmailInfo"] = utils.TranslateAsHtml(localT, "api.templates.email_info",
|
||||||
|
map[string]interface{}{"SupportEmail": *a.Config().SupportSettings.SupportEmail, "SiteName": a.Config().TeamSettings.SiteName})
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func (a *App) sendBatchedEmailNotification(userId string, notifications []*batch
|
|||||||
"Day": tm.Day(),
|
"Day": tm.Day(),
|
||||||
})
|
})
|
||||||
|
|
||||||
body := utils.NewHTMLTemplate("post_batched_body", user.Locale)
|
body := a.NewEmailTemplate("post_batched_body", user.Locale)
|
||||||
body.Props["SiteURL"] = *a.Config().ServiceSettings.SiteURL
|
body.Props["SiteURL"] = *a.Config().ServiceSettings.SiteURL
|
||||||
body.Props["Posts"] = template.HTML(contents)
|
body.Props["Posts"] = template.HTML(contents)
|
||||||
body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
|
body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
|
||||||
@@ -250,9 +250,9 @@ func (a *App) renderBatchedPost(notification *batchedNotification, channel *mode
|
|||||||
// don't include message contents if email notification contents type is set to generic
|
// don't include message contents if email notification contents type is set to generic
|
||||||
var template *utils.HTMLTemplate
|
var template *utils.HTMLTemplate
|
||||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||||
template = utils.NewHTMLTemplate("post_batched_post_full", userLocale)
|
template = a.NewEmailTemplate("post_batched_post_full", userLocale)
|
||||||
} else {
|
} else {
|
||||||
template = utils.NewHTMLTemplate("post_batched_post_generic", userLocale)
|
template = a.NewEmailTemplate("post_batched_post_generic", userLocale)
|
||||||
}
|
}
|
||||||
|
|
||||||
template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
|
template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
|
||||||
|
|||||||
@@ -413,10 +413,10 @@ func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post,
|
|||||||
// only include message contents in notification email if email notification contents type is set to full
|
// only include message contents in notification email if email notification contents type is set to full
|
||||||
var bodyPage *utils.HTMLTemplate
|
var bodyPage *utils.HTMLTemplate
|
||||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||||
bodyPage = utils.NewHTMLTemplate("post_body_full", recipient.Locale)
|
bodyPage = a.NewEmailTemplate("post_body_full", recipient.Locale)
|
||||||
bodyPage.Props["PostMessage"] = a.GetMessageForNotification(post, translateFunc)
|
bodyPage.Props["PostMessage"] = a.GetMessageForNotification(post, translateFunc)
|
||||||
} else {
|
} else {
|
||||||
bodyPage = utils.NewHTMLTemplate("post_body_generic", recipient.Locale)
|
bodyPage = a.NewEmailTemplate("post_body_generic", recipient.Locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyPage.Props["SiteURL"] = utils.GetSiteURL()
|
bodyPage.Props["SiteURL"] = utils.GetSiteURL()
|
||||||
|
|||||||
@@ -247,7 +247,7 @@ func (a *App) CreateUserAccessToken(token *model.UserAccessToken) (*model.UserAc
|
|||||||
l4g.Error(result.Err.Error())
|
l4g.Error(result.Err.Error())
|
||||||
} else {
|
} else {
|
||||||
user := result.Data.(*model.User)
|
user := result.Data.(*model.User)
|
||||||
if err := SendUserAccessTokenAddedEmail(user.Email, user.Locale); err != nil {
|
if err := a.SendUserAccessTokenAddedEmail(user.Email, user.Locale); err != nil {
|
||||||
l4g.Error(err.Error())
|
l4g.Error(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1065,7 +1065,7 @@ func (a *App) UpdateMfa(activate bool, userId, token string) *model.AppError {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := SendMfaChangeEmail(user.Email, activate, user.Locale, utils.GetSiteURL()); err != nil {
|
if err := a.SendMfaChangeEmail(user.Email, activate, user.Locale, utils.GetSiteURL()); err != nil {
|
||||||
l4g.Error(err.Error())
|
l4g.Error(err.Error())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1160,7 +1160,7 @@ func (a *App) SendPasswordReset(email string, siteURL string) (bool, *model.AppE
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := SendPasswordResetEmail(user.Email, token, user.Locale, siteURL); err != nil {
|
if _, err := a.SendPasswordResetEmail(user.Email, token, user.Locale, siteURL); err != nil {
|
||||||
return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError)
|
return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Message, http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/mattermost-server/app"
|
"github.com/mattermost/mattermost-server/app"
|
||||||
"github.com/mattermost/mattermost-server/model"
|
"github.com/mattermost/mattermost-server/model"
|
||||||
"github.com/mattermost/mattermost-server/utils"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -260,8 +259,6 @@ func userInviteCmdF(cmd *cobra.Command, args []string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.InitHTML()
|
|
||||||
|
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return errors.New("Expected at least two arguments. See help text for details.")
|
return errors.New("Expected at least two arguments. See help text for details.")
|
||||||
}
|
}
|
||||||
|
|||||||
116
utils/html.go
116
utils/html.go
@@ -6,55 +6,60 @@ package utils
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
l4g "github.com/alecthomas/log4go"
|
l4g "github.com/alecthomas/log4go"
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/nicksnyder/go-i18n/i18n"
|
"github.com/nicksnyder/go-i18n/i18n"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global storage for templates
|
type HTMLTemplateWatcher struct {
|
||||||
var htmlTemplates *template.Template
|
templates atomic.Value
|
||||||
|
stop chan struct{}
|
||||||
type HTMLTemplate struct {
|
stopped chan struct{}
|
||||||
TemplateName string
|
|
||||||
Props map[string]interface{}
|
|
||||||
Html map[string]template.HTML
|
|
||||||
Locale string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitHTML() {
|
func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) {
|
||||||
InitHTMLWithDir("templates")
|
templatesDir, _ := FindDir(directory)
|
||||||
}
|
|
||||||
|
|
||||||
func InitHTMLWithDir(dir string) {
|
|
||||||
|
|
||||||
if htmlTemplates != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
templatesDir, _ := FindDir(dir)
|
|
||||||
l4g.Debug(T("api.api.init.parsing_templates.debug"), templatesDir)
|
l4g.Debug(T("api.api.init.parsing_templates.debug"), templatesDir)
|
||||||
var err error
|
|
||||||
if htmlTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
ret := &HTMLTemplateWatcher{
|
||||||
l4g.Error(T("api.api.init.parsing_templates.error"), err)
|
stop: make(chan struct{}),
|
||||||
|
stopped: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch the templates folder for changes.
|
|
||||||
watcher, err := fsnotify.NewWatcher()
|
watcher, err := fsnotify.NewWatcher()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l4g.Error(T("web.create_dir.error"), err)
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = watcher.Add(templatesDir); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if htmlTemplates, err := template.ParseGlob(templatesDir + "*.html"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
ret.templates.Store(htmlTemplates)
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer close(ret.stopped)
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
case <-ret.stop:
|
||||||
|
return
|
||||||
case event := <-watcher.Events:
|
case event := <-watcher.Events:
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
l4g.Info(T("web.reparse_templates.info"), event.Name)
|
l4g.Info(T("web.reparse_templates.info"), event.Name)
|
||||||
if htmlTemplates, err = template.ParseGlob(templatesDir + "*.html"); err != nil {
|
if htmlTemplates, err := template.ParseGlob(templatesDir + "*.html"); err != nil {
|
||||||
l4g.Error(T("web.parsing_templates.error"), err)
|
l4g.Error(T("web.parsing_templates.error"), err)
|
||||||
|
} else {
|
||||||
|
ret.templates.Store(htmlTemplates)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case err := <-watcher.Errors:
|
case err := <-watcher.Errors:
|
||||||
@@ -63,57 +68,42 @@ func InitHTMLWithDir(dir string) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = watcher.Add(templatesDir)
|
return ret, nil
|
||||||
if err != nil {
|
|
||||||
l4g.Error(T("web.watcher_fail.error"), err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTMLTemplate(templateName string, locale string) *HTMLTemplate {
|
func (w *HTMLTemplateWatcher) Templates() *template.Template {
|
||||||
|
return w.templates.Load().(*template.Template)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *HTMLTemplateWatcher) Close() {
|
||||||
|
close(w.stop)
|
||||||
|
<-w.stopped
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTMLTemplate struct {
|
||||||
|
Templates *template.Template
|
||||||
|
TemplateName string
|
||||||
|
Props map[string]interface{}
|
||||||
|
Html map[string]template.HTML
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHTMLTemplate(templates *template.Template, templateName string) *HTMLTemplate {
|
||||||
return &HTMLTemplate{
|
return &HTMLTemplate{
|
||||||
|
Templates: templates,
|
||||||
TemplateName: templateName,
|
TemplateName: templateName,
|
||||||
Props: make(map[string]interface{}),
|
Props: make(map[string]interface{}),
|
||||||
Html: make(map[string]template.HTML),
|
Html: make(map[string]template.HTML),
|
||||||
Locale: locale,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HTMLTemplate) addDefaultProps() {
|
|
||||||
var localT i18n.TranslateFunc
|
|
||||||
if len(t.Locale) > 0 {
|
|
||||||
localT = GetUserTranslations(t.Locale)
|
|
||||||
} else {
|
|
||||||
localT = T
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Props["Footer"] = localT("api.templates.email_footer")
|
|
||||||
|
|
||||||
if *Cfg.EmailSettings.FeedbackOrganization != "" {
|
|
||||||
t.Props["Organization"] = localT("api.templates.email_organization") + *Cfg.EmailSettings.FeedbackOrganization
|
|
||||||
} else {
|
|
||||||
t.Props["Organization"] = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Html["EmailInfo"] = TranslateAsHtml(localT, "api.templates.email_info",
|
|
||||||
map[string]interface{}{"SupportEmail": *Cfg.SupportSettings.SupportEmail, "SiteName": Cfg.TeamSettings.SiteName})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *HTMLTemplate) Render() string {
|
func (t *HTMLTemplate) Render() string {
|
||||||
t.addDefaultProps()
|
|
||||||
|
|
||||||
var text bytes.Buffer
|
var text bytes.Buffer
|
||||||
|
t.RenderToWriter(&text)
|
||||||
if err := htmlTemplates.ExecuteTemplate(&text, t.TemplateName, t); err != nil {
|
|
||||||
l4g.Error(T("api.api.render.error"), t.TemplateName, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return text.String()
|
return text.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *HTMLTemplate) RenderToWriter(w http.ResponseWriter) error {
|
func (t *HTMLTemplate) RenderToWriter(w io.Writer) error {
|
||||||
t.addDefaultProps()
|
if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil {
|
||||||
|
|
||||||
if err := htmlTemplates.ExecuteTemplate(w, t.TemplateName, t); err != nil {
|
|
||||||
l4g.Error(T("api.api.render.error"), t.TemplateName, err)
|
l4g.Error(T("api.api.render.error"), t.TemplateName, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,50 +4,144 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/bundle"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/language"
|
||||||
|
"github.com/nicksnyder/go-i18n/i18n/translation"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/mattermost/mattermost-server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTranslateAsHtml(t *testing.T) {
|
var htmlTestTranslationBundle *bundle.Bundle
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
htmlTestTranslationBundle = bundle.New()
|
||||||
|
fooBold, _ := translation.NewTranslation(map[string]interface{}{
|
||||||
|
"id": "foo.bold",
|
||||||
|
"translation": "<b>{{ .Foo }}</b>",
|
||||||
|
})
|
||||||
|
htmlTestTranslationBundle.AddTranslation(&language.Language{Tag: "en"}, fooBold)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTMLTemplateWatcher(t *testing.T) {
|
||||||
TranslationsPreInit()
|
TranslationsPreInit()
|
||||||
|
|
||||||
translateFunc := TfuncWithFallback("en")
|
dir, err := ioutil.TempDir("", "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
expected := "To finish updating your email address for YOUR TEAM HERE, please click the link below to confirm this is the right address."
|
require.NoError(t, os.Mkdir(filepath.Join(dir, "templates"), 0700))
|
||||||
if actual := TranslateAsHtml(translateFunc, "api.templates.email_change_verify_body.info",
|
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "templates", "foo.html"), []byte(`{{ define "foo" }}foo{{ end }}`), 0600))
|
||||||
map[string]interface{}{"TeamDisplayName": "YOUR TEAM HERE"}); actual != template.HTML(expected) {
|
|
||||||
t.Fatalf("Incorrectly translated template, got %v, expected %v", actual, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
expected = "To finish updating your email address for <b>YOUR TEAM HERE</b>, please click the link below to confirm this is the right address."
|
prevDir, err := os.Getwd()
|
||||||
if actual := TranslateAsHtml(translateFunc, "api.templates.email_change_verify_body.info",
|
require.NoError(t, err)
|
||||||
map[string]interface{}{"TeamDisplayName": "<b>YOUR TEAM HERE</b>"}); actual != template.HTML(expected) {
|
defer os.Chdir(prevDir)
|
||||||
t.Fatalf("Incorrectly translated template, got %v, expected %v", actual, expected)
|
os.Chdir(dir)
|
||||||
|
|
||||||
|
watcher, err := NewHTMLTemplateWatcher("templates")
|
||||||
|
require.NotNil(t, watcher)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
tpl := NewHTMLTemplate(watcher.Templates(), "foo")
|
||||||
|
assert.Equal(t, "foo", tpl.Render())
|
||||||
|
|
||||||
|
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "templates", "foo.html"), []byte(`{{ define "foo" }}bar{{ end }}`), 0600))
|
||||||
|
|
||||||
|
for i := 0; i < 30; i++ {
|
||||||
|
tpl = NewHTMLTemplate(watcher.Templates(), "foo")
|
||||||
|
if tpl.Render() == "bar" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
}
|
}
|
||||||
|
assert.Equal(t, "bar", tpl.Render())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTMLTemplateWatcher_BadDirectory(t *testing.T) {
|
||||||
|
TranslationsPreInit()
|
||||||
|
watcher, err := NewHTMLTemplateWatcher("notarealdirectory")
|
||||||
|
assert.Nil(t, watcher)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTMLTemplate(t *testing.T) {
|
||||||
|
tpl := template.New("test")
|
||||||
|
_, err := tpl.Parse(`{{ define "foo" }}foo{{ .Props.Bar }}{{ end }}`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
htmlTemplate := NewHTMLTemplate(tpl, "foo")
|
||||||
|
htmlTemplate.Props["Bar"] = "bar"
|
||||||
|
assert.Equal(t, "foobar", htmlTemplate.Render())
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
require.NoError(t, htmlTemplate.RenderToWriter(buf))
|
||||||
|
assert.Equal(t, "foobar", buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHTMLTemplate_RenderError(t *testing.T) {
|
||||||
|
tpl := template.New("test")
|
||||||
|
_, err := tpl.Parse(`{{ define "foo" }}foo{{ .Foo.Bar }}bar{{ end }}`)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
htmlTemplate := NewHTMLTemplate(tpl, "foo")
|
||||||
|
assert.Equal(t, "foo", htmlTemplate.Render())
|
||||||
|
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
assert.Error(t, htmlTemplate.RenderToWriter(buf))
|
||||||
|
assert.Equal(t, "foo", buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTranslateAsHtml(t *testing.T) {
|
||||||
|
assert.EqualValues(t, "<b><i>foo</i></b>", TranslateAsHtml(i18n.TranslateFunc(htmlTestTranslationBundle.MustTfunc("en")), "foo.bold", map[string]interface{}{
|
||||||
|
"Foo": "<i>foo</i>",
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEscapeForHtml(t *testing.T) {
|
func TestEscapeForHtml(t *testing.T) {
|
||||||
input := "abc"
|
for name, tc := range map[string]struct {
|
||||||
expected := "abc"
|
In interface{}
|
||||||
if actual := escapeForHtml(input).(string); actual != expected {
|
Expected interface{}
|
||||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", input, actual, expected)
|
}{
|
||||||
}
|
"NoHTML": {
|
||||||
|
In: "abc",
|
||||||
input = "<b>abc</b>"
|
Expected: "abc",
|
||||||
expected = "<b>abc</b>"
|
},
|
||||||
if actual := escapeForHtml(input).(string); actual != expected {
|
"String": {
|
||||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", input, actual, expected)
|
In: "<b>abc</b>",
|
||||||
}
|
Expected: "<b>abc</b>",
|
||||||
|
},
|
||||||
inputMap := map[string]interface{}{
|
"StringPointer": {
|
||||||
"abc": "abc",
|
In: model.NewString("<b>abc</b>"),
|
||||||
"123": "<b>123</b>",
|
Expected: "<b>abc</b>",
|
||||||
}
|
},
|
||||||
expectedMap := map[string]interface{}{
|
"Map": {
|
||||||
"abc": "abc",
|
In: map[string]interface{}{
|
||||||
"123": "<b>123</b>",
|
"abc": "abc",
|
||||||
}
|
"123": "<b>123</b>",
|
||||||
if actualMap := escapeForHtml(inputMap).(map[string]interface{}); actualMap["abc"] != expectedMap["abc"] || actualMap["123"] != expectedMap["123"] {
|
},
|
||||||
t.Fatalf("incorrectly escaped %v, got %v expected %v", inputMap, actualMap, expectedMap)
|
Expected: map[string]interface{}{
|
||||||
|
"abc": "abc",
|
||||||
|
"123": "<b>123</b>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Unsupported": {
|
||||||
|
In: struct{ string }{"<b>abc</b>"},
|
||||||
|
Expected: "",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tc.Expected, escapeForHtml(tc.In))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,13 +81,12 @@ func CheckBrowserCompatability(c *api.Context, r *http.Request) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
func root(c *api.Context, w http.ResponseWriter, r *http.Request) {
|
||||||
if !CheckBrowserCompatability(c, r) {
|
if !CheckBrowserCompatability(c, r) {
|
||||||
w.Header().Set("Cache-Control", "no-store")
|
w.Header().Set("Cache-Control", "no-store")
|
||||||
page := utils.NewHTMLTemplate("unsupported_browser", c.Locale)
|
page := utils.NewHTMLTemplate(c.App.HTMLTemplates(), "unsupported_browser")
|
||||||
page.Props["Title"] = c.T("web.error.unsupported_browser.title")
|
page.Props["Title"] = c.T("web.error.unsupported_browser.title")
|
||||||
page.Props["Message"] = c.T("web.error.unsupported_browser.message")
|
page.Props["Message"] = c.T("web.error.unsupported_browser.message")
|
||||||
page.RenderToWriter(w)
|
page.RenderToWriter(w)
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user