Extracting html templates into a library (#16946)
* Extracting html templates into a library * Moving tests to the right place * Fixing tests * Addressing PR review comments * Addressing PR review comments * Replacing attomic with RWMutex * Returning errors as channel for Templates watcher * Address PR review comments * Other small fixes * Simplifying NewWithWatcher * Addressing PR review comments * Making error handling on rendering templates more robust * Fixing tests * Changing how we return errors * Fixing shadow variables * Addressing PR review comments * Logging errors from the outside of sendNotificationEmail * Fixing lock in shutdown * Fixing the resource copy for commands tests temporary directories * Removing unused import * A couple of tiny fixes
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
58dce5930e
Коммит
95b0809850
44
app/app.go
44
app/app.go
@@ -6,7 +6,6 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -21,6 +20,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/services/timezones"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/templates"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
)
|
||||
|
||||
@@ -148,12 +148,8 @@ func (a *App) TelemetryId() string {
|
||||
return a.Srv().TelemetryId()
|
||||
}
|
||||
|
||||
func (s *Server) HTMLTemplates() *template.Template {
|
||||
if s.htmlTemplateWatcher != nil {
|
||||
return s.htmlTemplateWatcher.Templates()
|
||||
}
|
||||
|
||||
return nil
|
||||
func (s *Server) TemplatesContainer() *templates.Container {
|
||||
return s.htmlTemplateWatcher
|
||||
}
|
||||
|
||||
func (a *App) Handle404(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -467,25 +463,25 @@ func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User,
|
||||
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.missing_server.app_error", nil, i18n.T("api.context.invalid_param.app_error", map[string]interface{}{"Name": "SMTPServer"}), http.StatusInternalServerError)
|
||||
}
|
||||
T := i18n.GetUserTranslations(sender.Locale)
|
||||
bodyPage := a.Srv().EmailService.newEmailTemplate("warn_metric_ack", sender.Locale)
|
||||
bodyPage.Props["ContactNameHeader"] = T("api.templates.warn_metric_ack.body.contact_name_header")
|
||||
bodyPage.Props["ContactNameValue"] = sender.GetFullName()
|
||||
bodyPage.Props["ContactEmailHeader"] = T("api.templates.warn_metric_ack.body.contact_email_header")
|
||||
bodyPage.Props["ContactEmailValue"] = sender.Email
|
||||
data := a.Srv().EmailService.newEmailTemplateData(sender.Locale)
|
||||
data.Props["ContactNameHeader"] = T("api.templates.warn_metric_ack.body.contact_name_header")
|
||||
data.Props["ContactNameValue"] = sender.GetFullName()
|
||||
data.Props["ContactEmailHeader"] = T("api.templates.warn_metric_ack.body.contact_email_header")
|
||||
data.Props["ContactEmailValue"] = sender.Email
|
||||
|
||||
//same definition as the active users count metric displayed in the SystemConsole Analytics section
|
||||
registeredUsersCount, cerr := a.Srv().Store.User().Count(model.UserCountOptions{})
|
||||
if cerr != nil {
|
||||
mlog.Warn("Error retrieving the number of registered users", mlog.Err(cerr))
|
||||
} else {
|
||||
bodyPage.Props["RegisteredUsersHeader"] = T("api.templates.warn_metric_ack.body.registered_users_header")
|
||||
bodyPage.Props["RegisteredUsersValue"] = registeredUsersCount
|
||||
data.Props["RegisteredUsersHeader"] = T("api.templates.warn_metric_ack.body.registered_users_header")
|
||||
data.Props["RegisteredUsersValue"] = registeredUsersCount
|
||||
}
|
||||
bodyPage.Props["SiteURLHeader"] = T("api.templates.warn_metric_ack.body.site_url_header")
|
||||
bodyPage.Props["SiteURL"] = a.GetSiteURL()
|
||||
bodyPage.Props["TelemetryIdHeader"] = T("api.templates.warn_metric_ack.body.diagnostic_id_header")
|
||||
bodyPage.Props["TelemetryIdValue"] = a.TelemetryId()
|
||||
bodyPage.Props["Footer"] = T("api.templates.warn_metric_ack.footer")
|
||||
data.Props["SiteURLHeader"] = T("api.templates.warn_metric_ack.body.site_url_header")
|
||||
data.Props["SiteURL"] = a.GetSiteURL()
|
||||
data.Props["TelemetryIdHeader"] = T("api.templates.warn_metric_ack.body.diagnostic_id_header")
|
||||
data.Props["TelemetryIdValue"] = a.TelemetryId()
|
||||
data.Props["Footer"] = T("api.templates.warn_metric_ack.footer")
|
||||
|
||||
warnMetricStatus, warnMetricDisplayTexts := a.getWarnMetricStatusAndDisplayTextsForId(warnMetricId, T, false)
|
||||
if warnMetricStatus == nil {
|
||||
@@ -493,10 +489,16 @@ func (a *App) NotifyAndSetWarnMetricAck(warnMetricId string, sender *model.User,
|
||||
}
|
||||
|
||||
subject := T("api.templates.warn_metric_ack.subject")
|
||||
bodyPage.Props["Title"] = warnMetricDisplayTexts.EmailBody
|
||||
data.Props["Title"] = warnMetricDisplayTexts.EmailBody
|
||||
|
||||
mailConfig := a.Srv().MailServiceConfig()
|
||||
if err := mailservice.SendMailUsingConfig(model.MM_SUPPORT_ADVISOR_ADDRESS, subject, bodyPage.Render(), mailConfig, false, sender.Email); err != nil {
|
||||
|
||||
body, err := a.Srv().TemplatesContainer().RenderToString("warn_metric_ack", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := mailservice.SendMailUsingConfig(model.MM_SUPPORT_ADVISOR_ADDRESS, subject, body, mailConfig, false, sender.Email); err != nil {
|
||||
return model.NewAppError("NotifyAndSetWarnMetricAck", "api.email.send_warn_metric_ack.failure.app_error", map[string]interface{}{"Error": err.Error()}, "", http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
687
app/email.go
687
app/email.go
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -22,7 +23,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/services/mailservice"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/templates"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -94,14 +95,19 @@ func (es *EmailService) sendChangeUsernameEmail(newUsername, email, locale, site
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("email_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.username_change_body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.username_change_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.username_change_body.title")
|
||||
data.Props["Info"] = T("api.templates.username_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "NewUsername": newUsername})
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("email_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("sendChangeUsernameEmail", "api.user.send_email_change_username_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -117,15 +123,20 @@ func (es *EmailService) sendEmailChangeVerifyEmail(newUserEmail, locale, siteURL
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("email_change_verify_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.email_change_verify_body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.email_change_verify_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.email_change_verify_body.title")
|
||||
data.Props["Info"] = T("api.templates.email_change_verify_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
|
||||
bodyPage.Props["VerifyUrl"] = link
|
||||
bodyPage.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button")
|
||||
data.Props["VerifyUrl"] = link
|
||||
data.Props["VerifyButton"] = T("api.templates.email_change_verify_body.button")
|
||||
|
||||
if err := es.sendMail(newUserEmail, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("email_change_verify_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(newUserEmail, subject, body); err != nil {
|
||||
return model.NewAppError("sendEmailChangeVerifyEmail", "api.user.send_email_change_verify_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -139,14 +150,19 @@ func (es *EmailService) sendEmailChangeEmail(oldEmail, newEmail, locale, siteURL
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("email_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.email_change_body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.email_change_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.email_change_body.title")
|
||||
data.Props["Info"] = T("api.templates.email_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "NewEmail": newEmail})
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(oldEmail, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("email_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(oldEmail, subject, body); err != nil {
|
||||
return model.NewAppError("sendEmailChangeEmail", "api.user.send_email_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -166,20 +182,25 @@ func (es *EmailService) sendVerifyEmail(userEmail, locale, siteURL, token, redir
|
||||
subject := T("api.templates.verify_subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("verify_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.verify_body.title")
|
||||
bodyPage.Props["SubTitle1"] = T("api.templates.verify_body.subTitle1")
|
||||
bodyPage.Props["ServerURL"] = T("api.templates.verify_body.serverURL", map[string]interface{}{"ServerURL": serverURL})
|
||||
bodyPage.Props["SubTitle2"] = T("api.templates.verify_body.subTitle2")
|
||||
bodyPage.Props["ButtonURL"] = link
|
||||
bodyPage.Props["Button"] = T("api.templates.verify_body.button")
|
||||
bodyPage.Props["Info"] = T("api.templates.verify_body.info")
|
||||
bodyPage.Props["Info1"] = T("api.templates.verify_body.info1")
|
||||
bodyPage.Props["QuestionTitle"] = T("api.templates.questions_footer.title")
|
||||
bodyPage.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.verify_body.title")
|
||||
data.Props["SubTitle1"] = T("api.templates.verify_body.subTitle1")
|
||||
data.Props["ServerURL"] = T("api.templates.verify_body.serverURL", map[string]interface{}{"ServerURL": serverURL})
|
||||
data.Props["SubTitle2"] = T("api.templates.verify_body.subTitle2")
|
||||
data.Props["ButtonURL"] = link
|
||||
data.Props["Button"] = T("api.templates.verify_body.button")
|
||||
data.Props["Info"] = T("api.templates.verify_body.info")
|
||||
data.Props["Info1"] = T("api.templates.verify_body.info1")
|
||||
data.Props["QuestionTitle"] = T("api.templates.questions_footer.title")
|
||||
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
||||
|
||||
if err := es.sendMail(userEmail, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("verify_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(userEmail, subject, body); err != nil {
|
||||
return model.NewAppError("SendVerifyEmail", "api.user.send_verify_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -192,14 +213,19 @@ func (es *EmailService) SendSignInChangeEmail(email, method, locale, siteURL str
|
||||
subject := T("api.templates.signin_change_email.subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("signin_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.signin_change_email.body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.signin_change_email.body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.signin_change_email.body.title")
|
||||
data.Props["Info"] = T("api.templates.signin_change_email.body.info",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName, "Method": method})
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("signin_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("SendSignInChangeEmail", "api.user.send_sign_in_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -222,22 +248,22 @@ func (es *EmailService) sendWelcomeEmail(userID string, email string, verified b
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"ServerURL": serverURL})
|
||||
|
||||
bodyPage := es.newEmailTemplate("welcome_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.welcome_body.title")
|
||||
bodyPage.Props["SubTitle1"] = T("api.templates.welcome_body.subTitle1")
|
||||
bodyPage.Props["ServerURL"] = T("api.templates.welcome_body.serverURL", map[string]interface{}{"ServerURL": serverURL})
|
||||
bodyPage.Props["SubTitle2"] = T("api.templates.welcome_body.subTitle2")
|
||||
bodyPage.Props["Button"] = T("api.templates.welcome_body.button")
|
||||
bodyPage.Props["Info"] = T("api.templates.welcome_body.info")
|
||||
bodyPage.Props["Info1"] = T("api.templates.welcome_body.info1")
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.welcome_body.title")
|
||||
data.Props["SubTitle1"] = T("api.templates.welcome_body.subTitle1")
|
||||
data.Props["ServerURL"] = T("api.templates.welcome_body.serverURL", map[string]interface{}{"ServerURL": serverURL})
|
||||
data.Props["SubTitle2"] = T("api.templates.welcome_body.subTitle2")
|
||||
data.Props["Button"] = T("api.templates.welcome_body.button")
|
||||
data.Props["Info"] = T("api.templates.welcome_body.info")
|
||||
data.Props["Info1"] = T("api.templates.welcome_body.info1")
|
||||
data.Props["SiteURL"] = siteURL
|
||||
|
||||
if *es.srv.Config().NativeAppSettings.AppDownloadLink != "" {
|
||||
bodyPage.Props["AppDownloadTitle"] = T("api.templates.welcome_body.app_download_title")
|
||||
bodyPage.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info")
|
||||
bodyPage.Props["AppDownloadButton"] = T("api.templates.welcome_body.app_download_button")
|
||||
bodyPage.Props["AppDownloadLink"] = *es.srv.Config().NativeAppSettings.AppDownloadLink
|
||||
data.Props["AppDownloadTitle"] = T("api.templates.welcome_body.app_download_title")
|
||||
data.Props["AppDownloadInfo"] = T("api.templates.welcome_body.app_download_info")
|
||||
data.Props["AppDownloadButton"] = T("api.templates.welcome_body.app_download_button")
|
||||
data.Props["AppDownloadLink"] = *es.srv.Config().NativeAppSettings.AppDownloadLink
|
||||
}
|
||||
|
||||
if !verified && *es.srv.Config().EmailSettings.RequireEmailVerification {
|
||||
@@ -249,10 +275,15 @@ func (es *EmailService) sendWelcomeEmail(userID string, email string, verified b
|
||||
if redirect != "" {
|
||||
link += fmt.Sprintf("&redirect_to=%s", redirect)
|
||||
}
|
||||
bodyPage.Props["ButtonURL"] = link
|
||||
data.Props["ButtonURL"] = link
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("welcome_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("sendWelcomeEmail", "api.user.send_welcome_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -266,30 +297,35 @@ func (es *EmailService) SendCloudWelcomeEmail(userEmail, locale, teamInviteID, w
|
||||
|
||||
workSpacePath := fmt.Sprintf("https://%s.cloud.mattermost.com", workSpaceName)
|
||||
|
||||
bodyPage := es.newEmailTemplate("cloud_welcome_email", locale)
|
||||
bodyPage.Props["Title"] = T("api.templates.cloud_welcome_email.title", map[string]interface{}{"WorkSpace": workSpaceName})
|
||||
bodyPage.Props["SubTitle"] = T("api.templates.cloud_welcome_email.subtitle")
|
||||
bodyPage.Props["SubTitleInfo"] = T("api.templates.cloud_welcome_email.subtitle_info")
|
||||
bodyPage.Props["Info"] = T("api.templates.cloud_welcome_email.info")
|
||||
bodyPage.Props["Info2"] = T("api.templates.cloud_welcome_email.info2")
|
||||
bodyPage.Props["WorkSpacePath"] = workSpacePath
|
||||
bodyPage.Props["DNS"] = dns
|
||||
bodyPage.Props["InviteInfo"] = T("api.templates.cloud_welcome_email.invite_info")
|
||||
bodyPage.Props["InviteSubInfo"] = T("api.templates.cloud_welcome_email.invite_sub_info", map[string]interface{}{"WorkSpace": workSpaceName})
|
||||
bodyPage.Props["InviteSubInfoLink"] = fmt.Sprintf("%s/signup_user_complete/?id=%s", workSpacePath, teamInviteID)
|
||||
bodyPage.Props["AddAppsInfo"] = T("api.templates.cloud_welcome_email.add_apps_info")
|
||||
bodyPage.Props["AddAppsSubInfo"] = T("api.templates.cloud_welcome_email.add_apps_sub_info")
|
||||
bodyPage.Props["AppMarketPlace"] = T("api.templates.cloud_welcome_email.app_market_place")
|
||||
bodyPage.Props["AppMarketPlaceLink"] = "https://integrations.mattermost.com/"
|
||||
bodyPage.Props["DownloadMMInfo"] = T("api.templates.cloud_welcome_email.download_mm_info")
|
||||
bodyPage.Props["SignInSubInfo"] = T("api.templates.cloud_welcome_email.signin_sub_info")
|
||||
bodyPage.Props["MMApps"] = T("api.templates.cloud_welcome_email.mm_apps")
|
||||
bodyPage.Props["SignInSubInfo2"] = T("api.templates.cloud_welcome_email.signin_sub_info2")
|
||||
bodyPage.Props["DownloadMMAppsLink"] = "https://mattermost.com/download/"
|
||||
bodyPage.Props["Button"] = T("api.templates.cloud_welcome_email.button")
|
||||
bodyPage.Props["GettingStartedQuestions"] = T("api.templates.cloud_welcome_email.start_questions")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["Title"] = T("api.templates.cloud_welcome_email.title", map[string]interface{}{"WorkSpace": workSpaceName})
|
||||
data.Props["SubTitle"] = T("api.templates.cloud_welcome_email.subtitle")
|
||||
data.Props["SubTitleInfo"] = T("api.templates.cloud_welcome_email.subtitle_info")
|
||||
data.Props["Info"] = T("api.templates.cloud_welcome_email.info")
|
||||
data.Props["Info2"] = T("api.templates.cloud_welcome_email.info2")
|
||||
data.Props["WorkSpacePath"] = workSpacePath
|
||||
data.Props["DNS"] = dns
|
||||
data.Props["InviteInfo"] = T("api.templates.cloud_welcome_email.invite_info")
|
||||
data.Props["InviteSubInfo"] = T("api.templates.cloud_welcome_email.invite_sub_info", map[string]interface{}{"WorkSpace": workSpaceName})
|
||||
data.Props["InviteSubInfoLink"] = fmt.Sprintf("%s/signup_user_complete/?id=%s", workSpacePath, teamInviteID)
|
||||
data.Props["AddAppsInfo"] = T("api.templates.cloud_welcome_email.add_apps_info")
|
||||
data.Props["AddAppsSubInfo"] = T("api.templates.cloud_welcome_email.add_apps_sub_info")
|
||||
data.Props["AppMarketPlace"] = T("api.templates.cloud_welcome_email.app_market_place")
|
||||
data.Props["AppMarketPlaceLink"] = "https://integrations.mattermost.com/"
|
||||
data.Props["DownloadMMInfo"] = T("api.templates.cloud_welcome_email.download_mm_info")
|
||||
data.Props["SignInSubInfo"] = T("api.templates.cloud_welcome_email.signin_sub_info")
|
||||
data.Props["MMApps"] = T("api.templates.cloud_welcome_email.mm_apps")
|
||||
data.Props["SignInSubInfo2"] = T("api.templates.cloud_welcome_email.signin_sub_info2")
|
||||
data.Props["DownloadMMAppsLink"] = "https://mattermost.com/download/"
|
||||
data.Props["Button"] = T("api.templates.cloud_welcome_email.button")
|
||||
data.Props["GettingStartedQuestions"] = T("api.templates.cloud_welcome_email.start_questions")
|
||||
|
||||
if err := es.sendMail(userEmail, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("cloud_welcome_email", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendCloudWelcomeEmail", "api.user.send_cloud_welcome_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(userEmail, subject, body); err != nil {
|
||||
return model.NewAppError("SendCloudWelcomeEmail", "api.user.send_cloud_welcome_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -303,14 +339,19 @@ func (es *EmailService) sendPasswordChangeEmail(email, method, locale, siteURL s
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"TeamDisplayName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("password_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.password_change_body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.password_change_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.password_change_body.title")
|
||||
data.Props["Info"] = T("api.templates.password_change_body.info",
|
||||
map[string]interface{}{"TeamDisplayName": es.srv.Config().TeamSettings.SiteName, "TeamURL": siteURL, "Method": method})
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("password_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("sendPasswordChangeEmail", "api.user.send_password_change_email_and_forget.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -323,14 +364,19 @@ func (es *EmailService) sendUserAccessTokenAddedEmail(email, locale, siteURL str
|
||||
subject := T("api.templates.user_access_token_subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("password_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.user_access_token_body.title")
|
||||
bodyPage.Props["Info"] = T("api.templates.user_access_token_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.user_access_token_body.title")
|
||||
data.Props["Info"] = T("api.templates.user_access_token_body.info",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName, "SiteURL": siteURL})
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("password_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("sendUserAccessTokenAddedEmail", "api.user.send_user_access_token.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -345,17 +391,22 @@ func (es *EmailService) SendPasswordResetEmail(email string, token *model.Token,
|
||||
subject := T("api.templates.reset_subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("reset_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.reset_body.title")
|
||||
bodyPage.Props["SubTitle"] = T("api.templates.reset_body.subTitle")
|
||||
bodyPage.Props["Info"] = T("api.templates.reset_body.info")
|
||||
bodyPage.Props["ButtonURL"] = link
|
||||
bodyPage.Props["Button"] = T("api.templates.reset_body.button")
|
||||
bodyPage.Props["QuestionTitle"] = T("api.templates.questions_footer.title")
|
||||
bodyPage.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.reset_body.title")
|
||||
data.Props["SubTitle"] = T("api.templates.reset_body.subTitle")
|
||||
data.Props["Info"] = T("api.templates.reset_body.info")
|
||||
data.Props["ButtonURL"] = link
|
||||
data.Props["Button"] = T("api.templates.reset_body.button")
|
||||
data.Props["QuestionTitle"] = T("api.templates.questions_footer.title")
|
||||
data.Props["QuestionInfo"] = T("api.templates.questions_footer.info")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("reset_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -368,19 +419,24 @@ func (es *EmailService) sendMfaChangeEmail(email string, activated bool, locale,
|
||||
subject := T("api.templates.mfa_change_subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("mfa_change_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
|
||||
if activated {
|
||||
bodyPage.Props["Info"] = T("api.templates.mfa_activated_body.info", map[string]interface{}{"SiteURL": siteURL})
|
||||
bodyPage.Props["Title"] = T("api.templates.mfa_activated_body.title")
|
||||
data.Props["Info"] = T("api.templates.mfa_activated_body.info", map[string]interface{}{"SiteURL": siteURL})
|
||||
data.Props["Title"] = T("api.templates.mfa_activated_body.title")
|
||||
} else {
|
||||
bodyPage.Props["Info"] = T("api.templates.mfa_deactivated_body.info", map[string]interface{}{"SiteURL": siteURL})
|
||||
bodyPage.Props["Title"] = T("api.templates.mfa_deactivated_body.title")
|
||||
data.Props["Info"] = T("api.templates.mfa_deactivated_body.info", map[string]interface{}{"SiteURL": siteURL})
|
||||
data.Props["Title"] = T("api.templates.mfa_deactivated_body.title")
|
||||
}
|
||||
bodyPage.Props["Warning"] = T("api.templates.email_warning")
|
||||
data.Props["Warning"] = T("api.templates.email_warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("mfa_change_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("SendMfaChangeEmail", "api.user.send_mfa_change_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -411,34 +467,39 @@ func (es *EmailService) SendInviteEmails(team *model.Team, senderName string, se
|
||||
"TeamDisplayName": team.DisplayName,
|
||||
"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("invite_body", "")
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = i18n.T("api.templates.invite_body.title", map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
bodyPage.Props["SubTitle"] = i18n.T("api.templates.invite_body.subTitle")
|
||||
bodyPage.Props["Button"] = i18n.T("api.templates.invite_body.button")
|
||||
bodyPage.Props["SenderName"] = senderName
|
||||
bodyPage.Props["InviteFooterTitle"] = i18n.T("api.templates.invite_body_footer.title")
|
||||
bodyPage.Props["InviteFooterInfo"] = i18n.T("api.templates.invite_body_footer.info")
|
||||
bodyPage.Props["InviteFooterLearnMore"] = i18n.T("api.templates.invite_body_footer.learn_more")
|
||||
data := es.newEmailTemplateData("")
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = i18n.T("api.templates.invite_body.title", map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
data.Props["SubTitle"] = i18n.T("api.templates.invite_body.subTitle")
|
||||
data.Props["Button"] = i18n.T("api.templates.invite_body.button")
|
||||
data.Props["SenderName"] = senderName
|
||||
data.Props["InviteFooterTitle"] = i18n.T("api.templates.invite_body_footer.title")
|
||||
data.Props["InviteFooterInfo"] = i18n.T("api.templates.invite_body_footer.info")
|
||||
data.Props["InviteFooterLearnMore"] = i18n.T("api.templates.invite_body_footer.learn_more")
|
||||
|
||||
token := model.NewToken(
|
||||
TokenTypeTeamInvitation,
|
||||
model.MapToJson(map[string]string{"teamId": team.Id, "email": invite}),
|
||||
)
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = invite
|
||||
props["display_name"] = team.DisplayName
|
||||
props["name"] = team.Name
|
||||
data := model.MapToJson(props)
|
||||
tokenProps := make(map[string]string)
|
||||
tokenProps["email"] = invite
|
||||
tokenProps["display_name"] = team.DisplayName
|
||||
tokenProps["name"] = team.Name
|
||||
tokenData := model.MapToJson(tokenProps)
|
||||
|
||||
if err := es.srv.Store.Token().Save(token); err != nil {
|
||||
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
bodyPage.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
|
||||
data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
|
||||
|
||||
if err := es.sendMail(invite, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("invite_body", data)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
|
||||
}
|
||||
|
||||
if err := es.sendMail(invite, subject, body); err != nil {
|
||||
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
@@ -470,19 +531,19 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode
|
||||
"TeamDisplayName": team.DisplayName,
|
||||
"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("invite_body", "")
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = i18n.T("api.templates.invite_body.title", map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
bodyPage.Props["SubTitle"] = i18n.T("api.templates.invite_body_guest.subTitle")
|
||||
bodyPage.Props["Button"] = i18n.T("api.templates.invite_body.button")
|
||||
bodyPage.Props["SenderName"] = senderName
|
||||
bodyPage.Props["Message"] = ""
|
||||
data := es.newEmailTemplateData("")
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = i18n.T("api.templates.invite_body.title", map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName})
|
||||
data.Props["SubTitle"] = i18n.T("api.templates.invite_body_guest.subTitle")
|
||||
data.Props["Button"] = i18n.T("api.templates.invite_body.button")
|
||||
data.Props["SenderName"] = senderName
|
||||
data.Props["Message"] = ""
|
||||
if message != "" {
|
||||
bodyPage.Props["Message"] = message
|
||||
data.Props["Message"] = message
|
||||
}
|
||||
bodyPage.Props["InviteFooterTitle"] = i18n.T("api.templates.invite_body_footer.title")
|
||||
bodyPage.Props["InviteFooterInfo"] = i18n.T("api.templates.invite_body_footer.info")
|
||||
bodyPage.Props["InviteFooterLearnMore"] = i18n.T("api.templates.invite_body_footer.learn_more")
|
||||
data.Props["InviteFooterTitle"] = i18n.T("api.templates.invite_body_footer.title")
|
||||
data.Props["InviteFooterInfo"] = i18n.T("api.templates.invite_body_footer.info")
|
||||
data.Props["InviteFooterLearnMore"] = i18n.T("api.templates.invite_body_footer.learn_more")
|
||||
|
||||
channelIDs := []string{}
|
||||
for _, channel := range channels {
|
||||
@@ -499,20 +560,20 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode
|
||||
}),
|
||||
)
|
||||
|
||||
props := make(map[string]string)
|
||||
props["email"] = invite
|
||||
props["display_name"] = team.DisplayName
|
||||
props["name"] = team.Name
|
||||
data := model.MapToJson(props)
|
||||
tokenProps := make(map[string]string)
|
||||
tokenProps["email"] = invite
|
||||
tokenProps["display_name"] = team.DisplayName
|
||||
tokenProps["name"] = team.Name
|
||||
tokenData := model.MapToJson(tokenProps)
|
||||
|
||||
if err := es.srv.Store.Token().Save(token); err != nil {
|
||||
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
bodyPage.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token))
|
||||
data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
|
||||
|
||||
if !*es.srv.Config().EmailSettings.SendEmailNotifications {
|
||||
mlog.Info("sending invitation ", mlog.String("to", invite), mlog.String("link", bodyPage.Props["Link"].(string)))
|
||||
mlog.Info("sending invitation ", mlog.String("to", invite), mlog.String("link", data.Props["ButtomURL"].(string)))
|
||||
}
|
||||
|
||||
embeddedFiles := make(map[string]io.Reader)
|
||||
@@ -524,7 +585,12 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode
|
||||
}
|
||||
}
|
||||
|
||||
if nErr := es.sendMailWithEmbeddedFiles(invite, subject, bodyPage.Render(), embeddedFiles); nErr != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("invite_body", data)
|
||||
if err != nil {
|
||||
mlog.Error("Failed to send invite email successfully", mlog.Err(err))
|
||||
}
|
||||
|
||||
if nErr := es.sendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles); nErr != nil {
|
||||
mlog.Error("Failed to send invite email successfully", mlog.Err(nErr))
|
||||
}
|
||||
}
|
||||
@@ -532,32 +598,32 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode
|
||||
return nil
|
||||
}
|
||||
|
||||
func (es *EmailService) newEmailTemplate(name, locale string) *utils.HTMLTemplate {
|
||||
t := utils.NewHTMLTemplate(es.srv.HTMLTemplates(), name)
|
||||
|
||||
func (es *EmailService) newEmailTemplateData(locale string) templates.Data {
|
||||
var localT i18n.TranslateFunc
|
||||
if locale != "" {
|
||||
localT = i18n.GetUserTranslations(locale)
|
||||
} else {
|
||||
localT = i18n.T
|
||||
}
|
||||
|
||||
t.Props["Footer"] = localT("api.templates.email_footer")
|
||||
t.Props["FooterV2"] = localT("api.templates.email_footer_v2")
|
||||
organization := ""
|
||||
|
||||
if *es.srv.Config().EmailSettings.FeedbackOrganization != "" {
|
||||
t.Props["Organization"] = localT("api.templates.email_organization") + *es.srv.Config().EmailSettings.FeedbackOrganization
|
||||
} else {
|
||||
t.Props["Organization"] = ""
|
||||
organization = localT("api.templates.email_organization") + *es.srv.Config().EmailSettings.FeedbackOrganization
|
||||
}
|
||||
|
||||
t.Props["EmailInfo1"] = localT("api.templates.email_info1")
|
||||
t.Props["EmailInfo2"] = localT("api.templates.email_info2")
|
||||
t.Props["EmailInfo3"] = localT("api.templates.email_info3",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
t.Props["SupportEmail"] = *es.srv.Config().SupportSettings.SupportEmail
|
||||
|
||||
return t
|
||||
return templates.Data{
|
||||
Props: map[string]interface{}{
|
||||
"EmailInfo1": localT("api.templates.email_info1"),
|
||||
"EmailInfo2": localT("api.templates.email_info2"),
|
||||
"EmailInfo3": localT("api.templates.email_info3",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName}),
|
||||
"SupportEmail": *es.srv.Config().SupportSettings.SupportEmail,
|
||||
"Footer": localT("api.templates.email_footer"),
|
||||
"FooterV2": localT("api.templates.email_footer_v2"),
|
||||
"Organization": organization,
|
||||
},
|
||||
HTML: map[string]template.HTML{},
|
||||
}
|
||||
}
|
||||
|
||||
func (es *EmailService) SendDeactivateAccountEmail(email string, locale, siteURL string) *model.AppError {
|
||||
@@ -569,14 +635,19 @@ func (es *EmailService) SendDeactivateAccountEmail(email string, locale, siteURL
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName,
|
||||
"ServerURL": serverURL})
|
||||
|
||||
bodyPage := es.newEmailTemplate("deactivate_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": serverURL})
|
||||
bodyPage.Props["Info"] = T("api.templates.deactivate_body.info",
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.deactivate_body.title", map[string]interface{}{"ServerURL": serverURL})
|
||||
data.Props["Info"] = T("api.templates.deactivate_body.info",
|
||||
map[string]interface{}{"SiteURL": siteURL})
|
||||
bodyPage.Props["Warning"] = T("api.templates.deactivate_body.warning")
|
||||
data.Props["Warning"] = T("api.templates.deactivate_body.warning")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("deactivate_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendDeactivateEmail", "api.user.send_deactivate_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("SendDeactivateEmail", "api.user.send_deactivate_email_and_forget.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -595,13 +666,18 @@ func (es *EmailService) SendRemoveExpiredLicenseEmail(email string, locale, site
|
||||
subject := T("api.templates.remove_expired_license.subject",
|
||||
map[string]interface{}{"SiteName": es.srv.Config().TeamSettings.SiteName})
|
||||
|
||||
bodyPage := es.newEmailTemplate("remove_expired_license", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.remove_expired_license.body.title")
|
||||
bodyPage.Props["Link"] = renewalLink
|
||||
bodyPage.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.remove_expired_license.body.title")
|
||||
data.Props["Link"] = renewalLink
|
||||
data.Props["LinkButton"] = T("api.templates.remove_expired_license.body.renew_button")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, nErr := es.srv.TemplatesContainer().RenderToString("remove_expired_license", data)
|
||||
if nErr != nil {
|
||||
return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("SendRemoveExpiredLicenseEmail", "api.license.remove_expired_license.failed.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -667,17 +743,22 @@ func (es *EmailService) SendAtUserLimitWarningEmail(email string, locale string,
|
||||
|
||||
subject := T("api.templates.at_limit_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("reached_user_limit_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.at_limit_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.at_limit_info1")
|
||||
bodyPage.Props["Info2"] = T("api.templates.at_limit_info2")
|
||||
bodyPage.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.at_limit_title")
|
||||
data.Props["Info1"] = T("api.templates.at_limit_info1")
|
||||
data.Props["Info2"] = T("api.templates.at_limit_info2")
|
||||
data.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("reached_user_limit_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendAtUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendAtUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -688,25 +769,30 @@ func (es *EmailService) SendAtUserLimitWarningEmail(email string, locale string,
|
||||
func (es *EmailService) SendUpgradeEmail(user, email, locale, siteURL, action string) (bool, *model.AppError) {
|
||||
T := i18n.GetUserTranslations(locale)
|
||||
|
||||
bodyPage := es.newEmailTemplate("cloud_upgrade_request_email", locale)
|
||||
subject := T("api.templates.upgrade_request_subject")
|
||||
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["Info5"] = T("api.templates.at_limit_info5")
|
||||
data.Props["BillingPath"] = "admin_console/billing/subscription"
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if action == model.InviteLimitation {
|
||||
bodyPage.Props["Title"] = T("api.templates.upgrade_request_title", map[string]interface{}{"UserName": user})
|
||||
bodyPage.Props["Info4"] = T("api.templates.upgrade_request_info4")
|
||||
data.Props["Title"] = T("api.templates.upgrade_request_title", map[string]interface{}{"UserName": user})
|
||||
data.Props["Info4"] = T("api.templates.upgrade_request_info4")
|
||||
} else {
|
||||
bodyPage.Props["Title"] = T("api.templates.upgrade_request_title2")
|
||||
bodyPage.Props["Info4"] = T("api.templates.upgrade_request_info4_2")
|
||||
data.Props["Title"] = T("api.templates.upgrade_request_title2")
|
||||
data.Props["Info4"] = T("api.templates.upgrade_request_info4_2")
|
||||
}
|
||||
|
||||
subject := T("api.templates.upgrade_request_subject")
|
||||
bodyPage.Props["Info5"] = T("api.templates.at_limit_info5")
|
||||
bodyPage.Props["BillingPath"] = "admin_console/billing/subscription"
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("cloud_upgrade_request_email", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendUpgradeEmail", "api.user.send_upgrade_request_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendUpgradeEmail", "api.user.send_upgrade_request_email.error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -718,17 +804,22 @@ func (es *EmailService) SendOverUserLimitWarningEmail(email string, locale strin
|
||||
|
||||
subject := T("api.templates.over_limit_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("reached_user_limit_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_info1")
|
||||
bodyPage.Props["Info2"] = T("api.templates.over_limit_info2")
|
||||
bodyPage.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_info1")
|
||||
data.Props["Info2"] = T("api.templates.over_limit_info2")
|
||||
data.Props["Button"] = T("api.templates.upgrade_mattermost_cloud")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("reached_user_limit_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -740,20 +831,25 @@ func (es *EmailService) SendOverUserLimitThirtyDayWarningEmail(email string, loc
|
||||
|
||||
subject := T("api.templates.over_limit_30_days_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_30_days_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_30_days_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_30_days_info1")
|
||||
bodyPage.Props["Info2"] = T("api.templates.over_limit_30_days_info2")
|
||||
bodyPage.Props["Info2Item1"] = T("api.templates.over_limit_30_days_info2_item1")
|
||||
bodyPage.Props["Info2Item2"] = T("api.templates.over_limit_30_days_info2_item2")
|
||||
bodyPage.Props["Info2Item3"] = T("api.templates.over_limit_30_days_info2_item3")
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_30_days_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_30_days_info1")
|
||||
data.Props["Info2"] = T("api.templates.over_limit_30_days_info2")
|
||||
data.Props["Info2Item1"] = T("api.templates.over_limit_30_days_info2_item1")
|
||||
data.Props["Info2Item2"] = T("api.templates.over_limit_30_days_info2_item2")
|
||||
data.Props["Info2Item3"] = T("api.templates.over_limit_30_days_info2_item3")
|
||||
data.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_30_days_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -765,19 +861,24 @@ func (es *EmailService) SendOverUserLimitNinetyDayWarningEmail(email string, loc
|
||||
|
||||
subject := T("api.templates.over_limit_90_days_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_90_days_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_90_days_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_90_days_info1", map[string]interface{}{"OverLimitDate": overLimitDate})
|
||||
bodyPage.Props["Info2"] = T("api.templates.over_limit_90_days_info2")
|
||||
bodyPage.Props["Info3"] = T("api.templates.over_limit_90_days_info3")
|
||||
bodyPage.Props["Info4"] = T("api.templates.over_limit_90_days_info4")
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_90_days_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_90_days_info1", map[string]interface{}{"OverLimitDate": overLimitDate})
|
||||
data.Props["Info2"] = T("api.templates.over_limit_90_days_info2")
|
||||
data.Props["Info3"] = T("api.templates.over_limit_90_days_info3")
|
||||
data.Props["Info4"] = T("api.templates.over_limit_90_days_info4")
|
||||
data.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_90_days_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -789,17 +890,22 @@ func (es *EmailService) SendOverUserLimitWorkspaceSuspendedWarningEmail(email st
|
||||
|
||||
subject := T("api.templates.over_limit_suspended_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_workspace_suspended_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_suspended_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_suspended_info1")
|
||||
bodyPage.Props["Info2"] = T("api.templates.over_limit_suspended_info2")
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_suspended_contact_support")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_suspended_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_suspended_info1")
|
||||
data.Props["Info2"] = T("api.templates.over_limit_suspended_info2")
|
||||
data.Props["Button"] = T("api.templates.over_limit_suspended_contact_support")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_workspace_suspended_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -811,16 +917,21 @@ func (es *EmailService) SendOverUserFourteenDayWarningEmail(email string, locale
|
||||
|
||||
subject := T("api.templates.over_limit_14_days_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_7_days_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_14_days_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_14_days_info1", map[string]interface{}{"OverLimitDate": overLimitDate})
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_14_days_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_14_days_info1", map[string]interface{}{"OverLimitDate": overLimitDate})
|
||||
data.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_7_days_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -832,16 +943,21 @@ func (es *EmailService) SendOverUserSevenDayWarningEmail(email string, locale st
|
||||
|
||||
subject := T("api.templates.over_limit_7_days_subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_7_days_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.over_limit_7_days_title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.over_limit_7_days_info1")
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.over_limit_7_days_title")
|
||||
data.Props["Info1"] = T("api.templates.over_limit_7_days_info1")
|
||||
data.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_7_days_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -852,15 +968,20 @@ func (es *EmailService) SendSuspensionEmailToSupport(email string, installationI
|
||||
// Localization not needed
|
||||
|
||||
subject := fmt.Sprintf("Cloud Installation %s Scheduled Suspension", installationID)
|
||||
bodyPage := es.newEmailTemplate("over_user_limit_support_body", "en")
|
||||
bodyPage.Props["CustomerID"] = customerID
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["SubscriptionID"] = subscriptionID
|
||||
bodyPage.Props["InstallationID"] = installationID
|
||||
bodyPage.Props["SuspensionDate"] = time.Now().AddDate(0, 0, 61).Format("2006-01-02")
|
||||
bodyPage.Props["UserCount"] = userCount
|
||||
data := es.newEmailTemplateData("en")
|
||||
data.Props["CustomerID"] = customerID
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["SubscriptionID"] = subscriptionID
|
||||
data.Props["InstallationID"] = installationID
|
||||
data.Props["SuspensionDate"] = time.Now().AddDate(0, 0, 61).Format("2006-01-02")
|
||||
data.Props["UserCount"] = userCount
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("over_user_limit_support_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendOverUserLimitWarningEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -872,20 +993,25 @@ func (es *EmailService) SendPaymentFailedEmail(email string, locale string, fail
|
||||
|
||||
subject := T("api.templates.payment_failed.subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("payment_failed_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.payment_failed.title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.payment_failed.info1", map[string]interface{}{"CardBrand": failedPayment.CardBrand, "LastFour": failedPayment.LastFour})
|
||||
bodyPage.Props["Info2"] = T("api.templates.payment_failed.info2")
|
||||
bodyPage.Props["Info3"] = T("api.templates.payment_failed.info3")
|
||||
bodyPage.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.payment_failed.title")
|
||||
data.Props["Info1"] = T("api.templates.payment_failed.info1", map[string]interface{}{"CardBrand": failedPayment.CardBrand, "LastFour": failedPayment.LastFour})
|
||||
data.Props["Info2"] = T("api.templates.payment_failed.info2")
|
||||
data.Props["Info3"] = T("api.templates.payment_failed.info3")
|
||||
data.Props["Button"] = T("api.templates.over_limit_fix_now")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
bodyPage.Props["FailedReason"] = failedPayment.FailureMessage
|
||||
data.Props["FailedReason"] = failedPayment.FailureMessage
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("payment_failed_body", data)
|
||||
if err != nil {
|
||||
return false, model.NewAppError("SendPaymentFailedEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return false, model.NewAppError("SendPaymentFailedEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
@@ -897,17 +1023,22 @@ func (es *EmailService) SendNoCardPaymentFailedEmail(email string, locale string
|
||||
|
||||
subject := T("api.templates.payment_failed_no_card.subject")
|
||||
|
||||
bodyPage := es.newEmailTemplate("payment_failed_no_card_body", locale)
|
||||
bodyPage.Props["SiteURL"] = siteURL
|
||||
bodyPage.Props["Title"] = T("api.templates.payment_failed_no_card.title")
|
||||
bodyPage.Props["Info1"] = T("api.templates.payment_failed_no_card.info1")
|
||||
bodyPage.Props["Info3"] = T("api.templates.payment_failed_no_card.info3")
|
||||
bodyPage.Props["Button"] = T("api.templates.payment_failed_no_card.button")
|
||||
bodyPage.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
data := es.newEmailTemplateData(locale)
|
||||
data.Props["SiteURL"] = siteURL
|
||||
data.Props["Title"] = T("api.templates.payment_failed_no_card.title")
|
||||
data.Props["Info1"] = T("api.templates.payment_failed_no_card.info1")
|
||||
data.Props["Info3"] = T("api.templates.payment_failed_no_card.info3")
|
||||
data.Props["Button"] = T("api.templates.payment_failed_no_card.button")
|
||||
data.Props["EmailUs"] = T("api.templates.email_us_anytime_at")
|
||||
|
||||
bodyPage.Props["Footer"] = T("api.templates.copyright")
|
||||
data.Props["Footer"] = T("api.templates.copyright")
|
||||
|
||||
if err := es.sendMail(email, subject, bodyPage.Render()); err != nil {
|
||||
body, err := es.srv.TemplatesContainer().RenderToString("payment_failed_no_card_body", data)
|
||||
if err != nil {
|
||||
return model.NewAppError("SendPaymentFailedEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
if err := es.sendMail(email, subject, body); err != nil {
|
||||
return model.NewAppError("SendPaymentFailedEmail", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -222,7 +221,13 @@ func (es *EmailService) sendBatchedEmailNotification(userID string, notification
|
||||
emailNotificationContentsType = *es.srv.Config().EmailSettings.EmailNotificationContentsType
|
||||
}
|
||||
|
||||
contents += es.renderBatchedPost(notification, channel, sender, *es.srv.Config().ServiceSettings.SiteURL, displayNameFormat, translateFunc, user.Locale, emailNotificationContentsType)
|
||||
postContent, err := es.renderBatchedPost(notification, channel, sender, *es.srv.Config().ServiceSettings.SiteURL, displayNameFormat, translateFunc, user.Locale, emailNotificationContentsType)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to render post for batched email notification template", mlog.Err(err))
|
||||
continue
|
||||
}
|
||||
|
||||
contents += postContent
|
||||
}
|
||||
|
||||
tm := time.Unix(notifications[0].post.CreateAt/1000, 0)
|
||||
@@ -234,34 +239,39 @@ func (es *EmailService) sendBatchedEmailNotification(userID string, notification
|
||||
"Day": tm.Day(),
|
||||
})
|
||||
|
||||
body := es.newEmailTemplate("post_batched_body", user.Locale)
|
||||
body.Props["SiteURL"] = *es.srv.Config().ServiceSettings.SiteURL
|
||||
body.Props["Posts"] = template.HTML(contents)
|
||||
body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
|
||||
data := es.newEmailTemplateData(user.Locale)
|
||||
data.Props["SiteURL"] = *es.srv.Config().ServiceSettings.SiteURL
|
||||
data.Props["Posts"] = template.HTML(contents)
|
||||
data.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
|
||||
|
||||
if nErr := es.sendNotificationMail(user.Email, subject, body.Render()); nErr != nil {
|
||||
body, err2 := es.srv.TemplatesContainer().RenderToString("post_batched_body", data)
|
||||
if err2 != nil {
|
||||
mlog.Warn("Unable build the batched email notification template", mlog.Err(err2))
|
||||
return
|
||||
}
|
||||
|
||||
if nErr := es.sendNotificationMail(user.Email, subject, body); nErr != nil {
|
||||
mlog.Warn("Unable to send batched email notification", mlog.String("email", user.Email), mlog.Err(nErr))
|
||||
}
|
||||
}
|
||||
|
||||
func (es *EmailService) renderBatchedPost(notification *batchedNotification, channel *model.Channel, sender *model.User, siteURL string, displayNameFormat string, translateFunc i18n.TranslateFunc, userLocale string, emailNotificationContentsType string) string {
|
||||
func (es *EmailService) renderBatchedPost(notification *batchedNotification, channel *model.Channel, sender *model.User, siteURL string, displayNameFormat string, translateFunc i18n.TranslateFunc, userLocale string, emailNotificationContentsType string) (string, error) {
|
||||
// don't include message contents if email notification contents type is set to generic
|
||||
var template *utils.HTMLTemplate
|
||||
var templateName = "post_batched_post_generic"
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
template = es.newEmailTemplate("post_batched_post_full", userLocale)
|
||||
} else {
|
||||
template = es.newEmailTemplate("post_batched_post_generic", userLocale)
|
||||
templateName = "post_batched_post_full"
|
||||
}
|
||||
|
||||
template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
|
||||
template.Props["PostMessage"] = es.srv.GetMessageForNotification(notification.post, translateFunc)
|
||||
template.Props["PostLink"] = siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
||||
template.Props["SenderName"] = sender.GetDisplayName(displayNameFormat)
|
||||
data := es.newEmailTemplateData(userLocale)
|
||||
data.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
|
||||
data.Props["PostMessage"] = es.srv.GetMessageForNotification(notification.post, translateFunc)
|
||||
data.Props["PostLink"] = siteURL + "/" + notification.teamName + "/pl/" + notification.post.Id
|
||||
data.Props["SenderName"] = sender.GetDisplayName(displayNameFormat)
|
||||
|
||||
tm := time.Unix(notification.post.CreateAt/1000, 0)
|
||||
timezone, _ := tm.Zone()
|
||||
|
||||
template.Props["Date"] = translateFunc("api.email_batching.render_batched_post.date", map[string]interface{}{
|
||||
data.Props["Date"] = translateFunc("api.email_batching.render_batched_post.date", map[string]interface{}{
|
||||
"Year": tm.Year(),
|
||||
"Month": translateFunc(tm.Month().String()),
|
||||
"Day": tm.Day(),
|
||||
@@ -271,17 +281,17 @@ func (es *EmailService) renderBatchedPost(notification *batchedNotification, cha
|
||||
})
|
||||
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
template.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.direct_message")
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.direct_message")
|
||||
} else if channel.Type == model.CHANNEL_GROUP {
|
||||
template.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.group_message")
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.group_message")
|
||||
} else {
|
||||
// don't include channel name if email notification contents type is set to generic
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
template.Props["ChannelName"] = channel.DisplayName
|
||||
data.Props["ChannelName"] = channel.DisplayName
|
||||
} else {
|
||||
template.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.notification")
|
||||
data.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.notification")
|
||||
}
|
||||
}
|
||||
|
||||
return template.Render()
|
||||
return es.srv.TemplatesContainer().RenderToString(templateName, data)
|
||||
}
|
||||
|
||||
@@ -304,7 +304,8 @@ func TestRenderBatchedPostGeneric(t *testing.T) {
|
||||
return translationID
|
||||
}
|
||||
|
||||
var rendered = th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_GENERIC)
|
||||
rendered, err := th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_GENERIC)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, rendered, post.Message, "Rendered email should not contain post contents when email notification contents type is set to Generic.")
|
||||
}
|
||||
|
||||
@@ -329,6 +330,7 @@ func TestRenderBatchedPostFull(t *testing.T) {
|
||||
return translationID
|
||||
}
|
||||
|
||||
var rendered = th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_FULL)
|
||||
rendered, err := th.Server.EmailService.renderBatchedPost(notification, channel, sender, "http://localhost:8065", "", translateFunc, "en", model.EMAIL_NOTIFICATION_CONTENTS_FULL)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, rendered, post.Message, "Rendered email should contain post contents when email notification contents type is set to Full.")
|
||||
}
|
||||
|
||||
@@ -231,7 +231,10 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
}
|
||||
|
||||
if a.userAllowsEmail(profileMap[id], channelMemberNotifyPropsMap[id], post) {
|
||||
a.sendNotificationEmail(notification, profileMap[id], team)
|
||||
err := a.sendNotificationEmail(notification, profileMap[id], team)
|
||||
if err != nil {
|
||||
mlog.Warn("Unable to send notification email.", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"fmt"
|
||||
"html"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -16,17 +15,17 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (a *App) sendNotificationEmail(notification *PostNotification, user *model.User, team *model.Team) *model.AppError {
|
||||
func (a *App) sendNotificationEmail(notification *PostNotification, user *model.User, team *model.Team) error {
|
||||
channel := notification.Channel
|
||||
post := notification.Post
|
||||
|
||||
if channel.IsGroupOrDirect() {
|
||||
teams, err := a.Srv().Store.Team().GetTeamsByUserId(user.Id)
|
||||
if err != nil {
|
||||
return model.NewAppError("sendNotificationEmail", "app.team.get_all.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
return errors.Wrap(err, "unable to get user teams")
|
||||
}
|
||||
|
||||
// if the recipient isn't in the current user's team, just pick one
|
||||
@@ -97,7 +96,10 @@ func (a *App) sendNotificationEmail(notification *PostNotification, user *model.
|
||||
}
|
||||
|
||||
landingURL := a.GetSiteURL() + "/landing#/" + team.Name
|
||||
var bodyText = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc)
|
||||
var bodyText, err = a.getNotificationEmailBody(user, post, channel, channelName, senderName, team.Name, landingURL, emailNotificationContentsType, useMilitaryTime, translateFunc)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "unable to render the email notification template")
|
||||
}
|
||||
|
||||
a.Srv().Go(func() {
|
||||
if nErr := a.Srv().EmailService.sendNotificationMail(user.Email, html.UnescapeString(subjectText), bodyText); nErr != nil {
|
||||
@@ -163,11 +165,12 @@ func getGroupMessageNotificationEmailSubject(user *model.User, post *model.Post,
|
||||
/**
|
||||
* Computes the email body for notification messages
|
||||
*/
|
||||
func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post, channel *model.Channel, channelName string, senderName string, teamName string, landingURL string, emailNotificationContentsType string, useMilitaryTime bool, translateFunc i18n.TranslateFunc) string {
|
||||
func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post, channel *model.Channel, channelName string, senderName string, teamName string, landingURL string, emailNotificationContentsType string, useMilitaryTime bool, translateFunc i18n.TranslateFunc) (string, error) {
|
||||
// only include message contents in notification email if email notification contents type is set to full
|
||||
var bodyPage *utils.HTMLTemplate
|
||||
var templateName = "post_body_generic"
|
||||
data := a.Srv().EmailService.newEmailTemplateData(recipient.Locale)
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyPage = a.Srv().EmailService.newEmailTemplate("post_body_full", recipient.Locale)
|
||||
templateName = "post_body_full"
|
||||
postMessage := a.GetMessageForNotification(post, translateFunc)
|
||||
postMessage = html.EscapeString(postMessage)
|
||||
normalizedPostMessage, err := a.generateHyperlinkForChannels(postMessage, teamName, landingURL)
|
||||
@@ -175,16 +178,14 @@ func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post,
|
||||
mlog.Warn("Encountered error while generating hyperlink for channels", mlog.String("team_name", teamName), mlog.Err(err))
|
||||
normalizedPostMessage = postMessage
|
||||
}
|
||||
bodyPage.Props["PostMessage"] = template.HTML(normalizedPostMessage)
|
||||
} else {
|
||||
bodyPage = a.Srv().EmailService.newEmailTemplate("post_body_generic", recipient.Locale)
|
||||
data.Props["PostMessage"] = template.HTML(normalizedPostMessage)
|
||||
}
|
||||
|
||||
bodyPage.Props["SiteURL"] = a.GetSiteURL()
|
||||
data.Props["SiteURL"] = a.GetSiteURL()
|
||||
if teamName != "select_team" {
|
||||
bodyPage.Props["TeamLink"] = landingURL + "/pl/" + post.Id
|
||||
data.Props["TeamLink"] = landingURL + "/pl/" + post.Id
|
||||
} else {
|
||||
bodyPage.Props["TeamLink"] = landingURL
|
||||
data.Props["TeamLink"] = landingURL
|
||||
}
|
||||
|
||||
t := getFormattedPostTime(recipient, post, useMilitaryTime, translateFunc)
|
||||
@@ -198,51 +199,51 @@ func (a *App) getNotificationEmailBody(recipient *model.User, post *model.Post,
|
||||
}
|
||||
if channel.Type == model.CHANNEL_DIRECT {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.full")
|
||||
bodyPage.Props["Info1"] = ""
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.full")
|
||||
data.Props["Info1"] = ""
|
||||
info["SenderName"] = senderName
|
||||
bodyPage.Props["Info2"] = translateFunc("app.notification.body.text.direct.full", info)
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.direct.full", info)
|
||||
} else {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.generic", map[string]interface{}{
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.direct.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
bodyPage.Props["Info"] = translateFunc("app.notification.body.text.direct.generic", info)
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.direct.generic", info)
|
||||
}
|
||||
} else if channel.Type == model.CHANNEL_GROUP {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.full")
|
||||
bodyPage.Props["Info1"] = translateFunc("app.notification.body.text.group_message.full",
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.full")
|
||||
data.Props["Info1"] = translateFunc("app.notification.body.text.group_message.full",
|
||||
map[string]interface{}{
|
||||
"ChannelName": channelName,
|
||||
})
|
||||
info["SenderName"] = senderName
|
||||
bodyPage.Props["Info2"] = translateFunc("app.notification.body.text.group_message.full2", info)
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.group_message.full2", info)
|
||||
} else {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.generic", map[string]interface{}{
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.group_message.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
bodyPage.Props["Info"] = translateFunc("app.notification.body.text.group_message.generic", info)
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.group_message.generic", info)
|
||||
}
|
||||
} else {
|
||||
if emailNotificationContentsType == model.EMAIL_NOTIFICATION_CONTENTS_FULL {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.full")
|
||||
bodyPage.Props["Info1"] = translateFunc("app.notification.body.text.notification.full",
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.full")
|
||||
data.Props["Info1"] = translateFunc("app.notification.body.text.notification.full",
|
||||
map[string]interface{}{
|
||||
"ChannelName": channelName,
|
||||
})
|
||||
info["SenderName"] = senderName
|
||||
bodyPage.Props["Info2"] = translateFunc("app.notification.body.text.notification.full2", info)
|
||||
data.Props["Info2"] = translateFunc("app.notification.body.text.notification.full2", info)
|
||||
} else {
|
||||
bodyPage.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.generic", map[string]interface{}{
|
||||
data.Props["BodyText"] = translateFunc("app.notification.body.intro.notification.generic", map[string]interface{}{
|
||||
"SenderName": senderName,
|
||||
})
|
||||
bodyPage.Props["Info"] = translateFunc("app.notification.body.text.notification.generic", info)
|
||||
data.Props["Info"] = translateFunc("app.notification.body.text.notification.generic", info)
|
||||
}
|
||||
}
|
||||
|
||||
bodyPage.Props["Button"] = translateFunc("api.templates.post_body.button")
|
||||
data.Props["Button"] = translateFunc("api.templates.post_body.button")
|
||||
|
||||
return bodyPage.Render()
|
||||
return a.Srv().TemplatesContainer().RenderToString(templateName, data)
|
||||
}
|
||||
|
||||
type formattedPostTime struct {
|
||||
|
||||
@@ -91,7 +91,8 @@ func TestGetNotificationEmailBodyFullNotificationPublicChannel(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification.", fmt.Sprintf("Expected email text 'You have a new notification. Got %s", body))
|
||||
require.Contains(t, body, "Channel: "+channel.DisplayName, "Expected email text 'Channel: %s'. Got %s", channel.DisplayName, body)
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
@@ -123,7 +124,8 @@ func TestGetNotificationEmailBodyFullNotificationGroupChannel(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Group Message.", fmt.Sprintf("Expected email text 'You have a new Group Message. Got "+body))
|
||||
require.Contains(t, body, "Channel: ChannelName", fmt.Sprintf("Expected email text 'Channel: ChannelName'. Got %s", body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
@@ -155,7 +157,8 @@ func TestGetNotificationEmailBodyFullNotificationPrivateChannel(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification.", fmt.Sprintf("Expected email text 'You have a new notification. Got "+body))
|
||||
require.Contains(t, body, "Channel: "+channel.DisplayName, fmt.Sprintf("Expected email text 'Channel: "+channel.DisplayName+"'. Got "+body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
@@ -187,7 +190,8 @@ func TestGetNotificationEmailBodyFullNotificationDirectChannel(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Direct Message.", fmt.Sprintf("Expected email text 'You have a new Direct Message. Got "+body))
|
||||
require.Contains(t, body, senderName+" - ", fmt.Sprintf("Expected email text '%s - '. Got %s", senderName, body))
|
||||
require.Contains(t, body, post.Message, fmt.Sprintf("Expected email text '%s'. Got %s", post.Message, body))
|
||||
@@ -222,7 +226,8 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeWithTimezone(t *testi
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
require.NoError(t, err)
|
||||
r, _ := regexp.Compile("E([S|D]+)T")
|
||||
zone := r.FindString(body)
|
||||
require.Contains(t, body, "sender - 9:43 AM "+zone+", April 25", fmt.Sprintf("Expected email text 'sender - 9:43 AM %s, April 25'. Got %s", zone, body))
|
||||
@@ -274,7 +279,8 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTimeNoTimezone(t *testing
|
||||
err = tmp.Execute(&text, fmt.Sprintf("sender - %s:%s %s, %s %s", formattedTime.Hour, formattedTime.Minute, formattedTime.TimeZone, formattedTime.Month, formattedTime.Day))
|
||||
require.NoError(t, err)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
postTimeLine := text.String()
|
||||
require.Contains(t, body, postTimeLine, fmt.Sprintf("Expected email text '%s'. Got %s", postTimeLine, body))
|
||||
}
|
||||
@@ -307,7 +313,8 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime12Hour(t *testing.T)
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, false, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "sender - 2:30 PM", fmt.Sprintf("Expected email text 'sender - 2:30 PM'. Got %s", body))
|
||||
require.Contains(t, body, "April 25", fmt.Sprintf("Expected email text 'April 25'. Got %s", body))
|
||||
}
|
||||
@@ -340,7 +347,8 @@ func TestGetNotificationEmailBodyFullNotificationLocaleTime24Hour(t *testing.T)
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "sender - 14:30", fmt.Sprintf("Expected email text 'sender - 14:30'. Got %s", body))
|
||||
require.Contains(t, body, "April 25", fmt.Sprintf("Expected email text 'April 25'. Got %s", body))
|
||||
}
|
||||
@@ -370,7 +378,8 @@ func TestGetNotificationEmailBodyGenericNotificationPublicChannel(t *testing.T)
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification from "+senderName, fmt.Sprintf("Expected email text 'You have a new notification from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "Channel: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
@@ -401,7 +410,8 @@ func TestGetNotificationEmailBodyGenericNotificationGroupChannel(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Group Message from "+senderName, fmt.Sprintf("Expected email text 'You have a new Group Message from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
@@ -432,7 +442,8 @@ func TestGetNotificationEmailBodyGenericNotificationPrivateChannel(t *testing.T)
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new notification from "+senderName, fmt.Sprintf("Expected email text 'You have a new notification from %s'. Got %s", senderName, body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
@@ -463,7 +474,8 @@ func TestGetNotificationEmailBodyGenericNotificationDirectChannel(t *testing.T)
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, "You have a new Direct Message from "+senderName, fmt.Sprintf("Expected email text 'You have a new Direct Message from "+senderName+"'. Got "+body))
|
||||
require.False(t, strings.Contains(body, "CHANNEL: "+channel.DisplayName), fmt.Sprintf("Did not expect email text 'CHANNEL: %s'. Got %s", channel.DisplayName, body))
|
||||
require.False(t, strings.Contains(body, post.Message), fmt.Sprintf("Did not expect email text '%s'. Got %s", post.Message, body))
|
||||
@@ -496,9 +508,10 @@ func TestGetNotificationEmailEscapingChars(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
channelName, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotContains(t, body, message)
|
||||
}
|
||||
@@ -539,9 +552,10 @@ func TestGetNotificationEmailBodyPublicChannelMention(t *testing.T) {
|
||||
channelStoreMock.On("GetByNames", "test", []string{ch.Name}, true).Return([]*model.Channel{ch}, nil)
|
||||
storeMock.On("Channel").Return(&channelStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
mention := "~" + ch.Name
|
||||
assert.Contains(t, body, "<a href='"+channelURL+"'>"+mention+"</a>")
|
||||
@@ -604,9 +618,10 @@ func TestGetNotificationEmailBodyMultiPublicChannelMention(t *testing.T) {
|
||||
channelStoreMock.On("GetByNames", "test", []string{ch.Name, ch2.Name, ch3.Name}, true).Return([]*model.Channel{ch, ch2, ch3}, nil)
|
||||
storeMock.On("Channel").Return(&channelStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
channelURL2 := teamURL + "/channels/" + ch2.Name
|
||||
channelURL3 := teamURL + "/channels/" + ch3.Name
|
||||
@@ -652,9 +667,10 @@ func TestGetNotificationEmailBodyPrivateChannelMention(t *testing.T) {
|
||||
channelStoreMock.On("GetByNames", "test", []string{ch.Name}, true).Return([]*model.Channel{ch}, nil)
|
||||
storeMock.On("Channel").Return(&channelStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, ch,
|
||||
ch.Name, senderName, teamName, teamURL,
|
||||
emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
channelURL := teamURL + "/channels/" + ch.Name
|
||||
mention := "~" + ch.Name
|
||||
assert.NotContains(t, body, "<a href='"+channelURL+"'>"+mention+"</a>")
|
||||
@@ -797,7 +813,8 @@ func TestLandingLink(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, teamURL, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
|
||||
}
|
||||
|
||||
@@ -826,6 +843,7 @@ func TestLandingLinkPermalink(t *testing.T) {
|
||||
teamStoreMock.On("GetByName", "testteam").Return(&model.Team{Name: "testteam"}, nil)
|
||||
storeMock.On("Team").Return(&teamStoreMock)
|
||||
|
||||
body := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
body, err := th.App.getNotificationEmailBody(recipient, post, channel, channelName, senderName, teamName, teamURL, emailNotificationContentsType, true, translateFunc)
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, body, teamURL+"/pl/"+post.Id, fmt.Sprintf("Expected email text '%s'. Got %s", teamURL, body))
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/i18n"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/templates"
|
||||
"github.com/mattermost/mattermost-server/v5/store"
|
||||
"github.com/mattermost/mattermost-server/v5/store/localcachelayer"
|
||||
"github.com/mattermost/mattermost-server/v5/store/retrylayer"
|
||||
@@ -65,6 +66,7 @@ import (
|
||||
"github.com/mattermost/mattermost-server/v5/store/sqlstore"
|
||||
"github.com/mattermost/mattermost-server/v5/store/timerlayer"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/utils/fileutils"
|
||||
)
|
||||
|
||||
var MaxNotificationsPerChannelDefault int64 = 1000000
|
||||
@@ -133,7 +135,7 @@ type Server struct {
|
||||
|
||||
newStore func() (store.Store, error)
|
||||
|
||||
htmlTemplateWatcher *utils.HTMLTemplateWatcher
|
||||
htmlTemplateWatcher *templates.Container
|
||||
sessionCache cache.Cache
|
||||
seenPendingPostIdsCache cache.Cache
|
||||
statusCache cache.Cache
|
||||
@@ -391,9 +393,19 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if htmlTemplateWatcher, err2 := utils.NewHTMLTemplateWatcher("templates"); err2 != nil {
|
||||
mlog.Error("Failed to parse server templates", mlog.Err(err2))
|
||||
templatesDir, ok := fileutils.FindDir("templates")
|
||||
if !ok {
|
||||
mlog.Error("Failed find server templates", mlog.String("directory", "templates"))
|
||||
} else {
|
||||
htmlTemplateWatcher, errorsChan, err2 := templates.NewWithWatcher(templatesDir)
|
||||
if err2 != nil {
|
||||
return nil, errors.Wrap(err2, "cannot initialize server templates")
|
||||
}
|
||||
s.Go(func() {
|
||||
for err2 := range errorsChan {
|
||||
mlog.Warn("Server templates error", mlog.Err(err2))
|
||||
}
|
||||
})
|
||||
s.htmlTemplateWatcher = htmlTemplateWatcher
|
||||
}
|
||||
|
||||
@@ -865,13 +877,10 @@ func (s *Server) Shutdown() {
|
||||
// Push notification hub needs to be shutdown after HTTP server
|
||||
// to prevent stray requests from generating a push notification after it's shut down.
|
||||
s.StopPushNotificationsHubWorkers()
|
||||
s.htmlTemplateWatcher.Close()
|
||||
|
||||
s.WaitForGoroutines()
|
||||
|
||||
if s.htmlTemplateWatcher != nil {
|
||||
s.htmlTemplateWatcher.Close()
|
||||
}
|
||||
|
||||
if s.advancedLogListenerCleanup != nil {
|
||||
s.advancedLogListenerCleanup()
|
||||
s.advancedLogListenerCleanup = nil
|
||||
|
||||
@@ -38,7 +38,7 @@ type testHelper struct {
|
||||
|
||||
// Setup creates an instance of testHelper.
|
||||
func Setup(t testing.TB) *testHelper {
|
||||
dir, err := ioutil.TempDir("", "testHelper")
|
||||
dir, err := testlib.SetupTestResources()
|
||||
if err != nil {
|
||||
panic("failed to create temporary directory: " + err.Error())
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func Setup(t testing.TB) *testHelper {
|
||||
|
||||
// Setup creates an instance of testHelper.
|
||||
func SetupWithStoreMock(t testing.TB) *testHelper {
|
||||
dir, err := ioutil.TempDir("", "testHelper")
|
||||
dir, err := testlib.SetupTestResources()
|
||||
if err != nil {
|
||||
panic("failed to create temporary directory: " + err.Error())
|
||||
}
|
||||
|
||||
@@ -9,9 +9,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/utils/fileutils"
|
||||
)
|
||||
|
||||
func TestConfigFlag(t *testing.T) {
|
||||
@@ -19,16 +16,14 @@ func TestConfigFlag(t *testing.T) {
|
||||
defer th.TearDown()
|
||||
dir := th.TemporaryDirectory()
|
||||
|
||||
i18n, ok := fileutils.FindDir("i18n")
|
||||
require.True(t, ok)
|
||||
require.NoError(t, utils.CopyDir(i18n, filepath.Join(dir, "i18n")))
|
||||
|
||||
prevDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(prevDir)
|
||||
os.Chdir(dir)
|
||||
|
||||
t.Run("version without a config file should fail", func(t *testing.T) {
|
||||
err := os.RemoveAll("config")
|
||||
require.NoError(t, err)
|
||||
th.SetAutoConfig(false)
|
||||
defer th.SetAutoConfig(true)
|
||||
require.Error(t, th.RunCommand(t, "version"))
|
||||
|
||||
143
shared/templates/templates.go
Обычный файл
143
shared/templates/templates.go
Обычный файл
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
// Container represents a set of templates that can be render
|
||||
type Container struct {
|
||||
templates *template.Template
|
||||
mutex sync.RWMutex
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
watch bool
|
||||
}
|
||||
|
||||
// Data contains the data used to populate the template variables, it has Props
|
||||
// that can be of any type and HTML that only can be `template.HTML` types.
|
||||
type Data struct {
|
||||
Props map[string]interface{}
|
||||
HTML map[string]template.HTML
|
||||
}
|
||||
|
||||
// NewFromTemplates creates a new templates container using a
|
||||
// `template.Template` object
|
||||
func NewFromTemplate(templates *template.Template) *Container {
|
||||
return &Container{templates: templates}
|
||||
}
|
||||
|
||||
// New creates a new templates container scanning a directory.
|
||||
func New(directory string) (*Container, error) {
|
||||
c := &Container{}
|
||||
|
||||
htmlTemplates, err := template.ParseGlob(filepath.Join(directory, "*.html"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c.templates = htmlTemplates
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewWithWatcher creates a new templates container scanning a directory and
|
||||
// watch the directory filesystem changes to apply them to the loaded
|
||||
// templates. This function returns the container and an errors channel to pass
|
||||
// all errors that can happen during the watch process, or an regular error if
|
||||
// we fail to create the templates or the watcher. The caller must consume the
|
||||
// returned errors channel to ensure not blocking the watch process.
|
||||
func NewWithWatcher(directory string) (*Container, <-chan error, error) {
|
||||
htmlTemplates, err := template.ParseGlob(filepath.Join(directory, "*.html"))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = watcher.Add(directory)
|
||||
if err != nil {
|
||||
watcher.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
c := &Container{
|
||||
templates: htmlTemplates,
|
||||
watch: true,
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
}
|
||||
errors := make(chan error)
|
||||
|
||||
go func() {
|
||||
defer close(errors)
|
||||
defer close(c.stopped)
|
||||
defer watcher.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.stop:
|
||||
return
|
||||
case event := <-watcher.Events:
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
if htmlTemplates, err := template.ParseGlob(filepath.Join(directory, "*.html")); err != nil {
|
||||
errors <- err
|
||||
} else {
|
||||
c.mutex.Lock()
|
||||
c.templates = htmlTemplates
|
||||
c.mutex.Unlock()
|
||||
}
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
errors <- err
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return c, errors, nil
|
||||
}
|
||||
|
||||
// Close stops the templates watcher of the container in case you have created
|
||||
// it with watch parameter set to true
|
||||
func (c *Container) Close() {
|
||||
c.mutex.RLock()
|
||||
defer c.mutex.RUnlock()
|
||||
if c.watch {
|
||||
close(c.stop)
|
||||
<-c.stopped
|
||||
}
|
||||
}
|
||||
|
||||
// RenderToString renders the template referenced with the template name using
|
||||
// the data provided and return a string with the result
|
||||
func (c *Container) RenderToString(templateName string, data Data) (string, error) {
|
||||
var text bytes.Buffer
|
||||
if err := c.Render(&text, templateName, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return text.String(), nil
|
||||
}
|
||||
|
||||
// RenderToString renders the template referenced with the template name using
|
||||
// the data provided and write it to the writer provided
|
||||
func (c *Container) Render(w io.Writer, templateName string, data Data) error {
|
||||
c.mutex.RLock()
|
||||
htmlTemplates := c.templates
|
||||
c.mutex.RUnlock()
|
||||
|
||||
if err := htmlTemplates.ExecuteTemplate(w, templateName, data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
115
shared/templates/templates_test.go
Обычный файл
115
shared/templates/templates_test.go
Обычный файл
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package templates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTMLTemplateWatcher(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
require.NoError(t, os.Mkdir(filepath.Join(dir, "templates"), 0700))
|
||||
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "templates", "foo.html"), []byte(`{{ define "foo" }}foo{{ end }}`), 0600))
|
||||
|
||||
prevDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(prevDir)
|
||||
os.Chdir(dir)
|
||||
|
||||
watcher, errChan, err := NewWithWatcher("templates")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, watcher)
|
||||
select {
|
||||
case msg := <-errChan:
|
||||
err = msg
|
||||
default:
|
||||
err = nil
|
||||
}
|
||||
require.NoError(t, err)
|
||||
defer watcher.Close()
|
||||
|
||||
text, err := watcher.RenderToString("foo", Data{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "foo", text)
|
||||
|
||||
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "templates", "foo.html"), []byte(`{{ define "foo" }}bar{{ end }}`), 0600))
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
text, err := watcher.RenderToString("foo", Data{})
|
||||
return text == "bar" && err == nil
|
||||
}, time.Millisecond*1000, time.Millisecond*50)
|
||||
}
|
||||
|
||||
func TestNewWithWatcher_BadDirectory(t *testing.T) {
|
||||
watcher, errChan, err := NewWithWatcher("notarealdirectory")
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, watcher)
|
||||
assert.Nil(t, errChan)
|
||||
}
|
||||
|
||||
func TestNew_BadDirectory(t *testing.T) {
|
||||
watcher, err := New("notarealdirectory")
|
||||
assert.Nil(t, watcher)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
tpl := template.New("test")
|
||||
_, err := tpl.Parse(`{{ define "foo" }}foo{{ .Props.Bar }}{{ end }}`)
|
||||
require.NoError(t, err)
|
||||
mt := NewFromTemplate(tpl)
|
||||
|
||||
data := Data{
|
||||
Props: map[string]interface{}{
|
||||
"Bar": "bar",
|
||||
},
|
||||
}
|
||||
text, err := mt.RenderToString("foo", data)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "foobar", text)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
require.NoError(t, mt.Render(buf, "foo", data))
|
||||
assert.Equal(t, "foobar", buf.String())
|
||||
}
|
||||
|
||||
func TestRenderError(t *testing.T) {
|
||||
tpl := template.New("test")
|
||||
_, err := tpl.Parse(`{{ define "foo" }}foo{{ .Foo.Bar }}bar{{ end }}`)
|
||||
require.NoError(t, err)
|
||||
mt := NewFromTemplate(tpl)
|
||||
|
||||
text, err := mt.RenderToString("foo", Data{})
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "", text)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
assert.Error(t, mt.Render(buf, "foo", Data{}))
|
||||
assert.Equal(t, "foo", buf.String())
|
||||
}
|
||||
|
||||
func TestRenderUnknownTemplate(t *testing.T) {
|
||||
tpl := template.New("")
|
||||
mt := NewFromTemplate(tpl)
|
||||
|
||||
text, err := mt.RenderToString("foo", Data{})
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, "", text)
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
assert.Error(t, mt.Render(buf, "foo", Data{}))
|
||||
assert.Equal(t, "", buf.String())
|
||||
}
|
||||
@@ -87,6 +87,7 @@ func getTestResourcesToSetup() []testResourceDetails {
|
||||
|
||||
var testResourcesToSetup = []testResourceDetails{
|
||||
{root, "mattermost-server", resourceTypeFolder, actionSymlink},
|
||||
{"go.mod", "go.mod", resourceTypeFile, actionSymlink},
|
||||
{"i18n", "i18n", resourceTypeFolder, actionSymlink},
|
||||
{"templates", "templates", resourceTypeFolder, actionSymlink},
|
||||
{"tests", "tests", resourceTypeFolder, actionSymlink},
|
||||
|
||||
118
utils/html.go
118
utils/html.go
@@ -1,118 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/shared/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/utils/fileutils"
|
||||
)
|
||||
|
||||
type HTMLTemplateWatcher struct {
|
||||
templates atomic.Value
|
||||
stop chan struct{}
|
||||
stopped chan struct{}
|
||||
}
|
||||
|
||||
func NewHTMLTemplateWatcher(directory string) (*HTMLTemplateWatcher, error) {
|
||||
templatesDir, _ := fileutils.FindDir(directory)
|
||||
mlog.Debug("Parsing server templates", mlog.String("templates_directory", templatesDir))
|
||||
|
||||
ret := &HTMLTemplateWatcher{
|
||||
stop: make(chan struct{}),
|
||||
stopped: make(chan struct{}),
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = watcher.Add(templatesDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.templates.Store(htmlTemplates)
|
||||
|
||||
go func() {
|
||||
defer close(ret.stopped)
|
||||
defer watcher.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ret.stop:
|
||||
return
|
||||
case event := <-watcher.Events:
|
||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||
mlog.Info("Re-parsing templates because of modified file", mlog.String("file_name", event.Name))
|
||||
if htmlTemplates, err := template.ParseGlob(filepath.Join(templatesDir, "*.html")); err != nil {
|
||||
mlog.Error("Failed to parse templates.", mlog.Err(err))
|
||||
} else {
|
||||
ret.templates.Store(htmlTemplates)
|
||||
}
|
||||
}
|
||||
case err := <-watcher.Errors:
|
||||
mlog.Error("Failed in directory watcher", mlog.Err(err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
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{
|
||||
Templates: templates,
|
||||
TemplateName: templateName,
|
||||
Props: make(map[string]interface{}),
|
||||
HTML: make(map[string]template.HTML),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *HTMLTemplate) Render() string {
|
||||
var text bytes.Buffer
|
||||
t.RenderToWriter(&text)
|
||||
return text.String()
|
||||
}
|
||||
|
||||
func (t *HTMLTemplate) RenderToWriter(w io.Writer) error {
|
||||
if t.Templates == nil {
|
||||
return errors.New("no html templates")
|
||||
}
|
||||
|
||||
if err := t.Templates.ExecuteTemplate(w, t.TemplateName, t); err != nil {
|
||||
mlog.Warn("Error rendering template", mlog.String("template_name", t.TemplateName), mlog.Err(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"html/template"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestHTMLTemplateWatcher(t *testing.T) {
|
||||
TranslationsPreInit()
|
||||
|
||||
dir, err := ioutil.TempDir("", "")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
require.NoError(t, os.Mkdir(filepath.Join(dir, "templates"), 0700))
|
||||
require.NoError(t, ioutil.WriteFile(filepath.Join(dir, "templates", "foo.html"), []byte(`{{ define "foo" }}foo{{ end }}`), 0600))
|
||||
|
||||
prevDir, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
defer os.Chdir(prevDir)
|
||||
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())
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/avct/uasurfer"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/utils"
|
||||
"github.com/mattermost/mattermost-server/v5/shared/templates"
|
||||
)
|
||||
|
||||
// MattermostApp describes downloads for the Mattermost App
|
||||
@@ -46,7 +46,13 @@ type SystemBrowser struct {
|
||||
|
||||
func renderUnsupportedBrowser(app app.AppIface, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
page := utils.NewHTMLTemplate(app.Srv().HTMLTemplates(), "unsupported_browser")
|
||||
|
||||
data := templates.Data{
|
||||
Props: map[string]interface{}{
|
||||
"DownloadAppOrUpgradeBrowserString": app.T("web.error.unsupported_browser.download_app_or_upgrade_browser"),
|
||||
"LearnMoreString": app.T("web.error.unsupported_browser.learn_more"),
|
||||
},
|
||||
}
|
||||
|
||||
// User Agent info
|
||||
ua := uasurfer.Parse(r.UserAgent())
|
||||
@@ -57,18 +63,16 @@ func renderUnsupportedBrowser(app app.AppIface, w http.ResponseWriter, r *http.R
|
||||
|
||||
// Basic heading translations
|
||||
if isSafari {
|
||||
page.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support_version")
|
||||
data.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support_version")
|
||||
} else {
|
||||
page.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support")
|
||||
data.Props["NoLongerSupportString"] = app.T("web.error.unsupported_browser.no_longer_support")
|
||||
}
|
||||
page.Props["DownloadAppOrUpgradeBrowserString"] = app.T("web.error.unsupported_browser.download_app_or_upgrade_browser")
|
||||
page.Props["LearnMoreString"] = app.T("web.error.unsupported_browser.learn_more")
|
||||
|
||||
// Mattermost app version
|
||||
if isWindows {
|
||||
page.Props["App"] = renderMattermostAppWindows(app)
|
||||
data.Props["App"] = renderMattermostAppWindows(app)
|
||||
} else if isMacOSX {
|
||||
page.Props["App"] = renderMattermostAppMac(app)
|
||||
data.Props["App"] = renderMattermostAppMac(app)
|
||||
}
|
||||
|
||||
// Browsers to download
|
||||
@@ -78,14 +82,14 @@ func renderUnsupportedBrowser(app app.AppIface, w http.ResponseWriter, r *http.R
|
||||
if isSafari {
|
||||
browsers = append(browsers, renderBrowserSafari(app))
|
||||
}
|
||||
page.Props["Browsers"] = browsers
|
||||
data.Props["Browsers"] = browsers
|
||||
|
||||
// If on Windows 10, show link to Edge
|
||||
if isWindows10 {
|
||||
page.Props["SystemBrowser"] = renderSystemBrowserEdge(app, r)
|
||||
data.Props["SystemBrowser"] = renderSystemBrowserEdge(app, r)
|
||||
}
|
||||
|
||||
page.RenderToWriter(w)
|
||||
app.Srv().TemplatesContainer().Render(w, "unsupported_browser", data)
|
||||
}
|
||||
|
||||
func renderMattermostAppMac(app app.AppIface) MattermostApp {
|
||||
|
||||
Ссылка в новой задаче
Block a user