From 41ab05ca6637a2eb799ec13ef177b6954098fece Mon Sep 17 00:00:00 2001 From: Jyoti Patel <36148363+jp0707@users.noreply.github.com> Date: Wed, 10 Mar 2021 02:18:32 -0500 Subject: [PATCH] [GH-15906][MM-22844] Redesign reset password and invite emails (#17029) Automatic Merge --- app/email.go | 36 +-- i18n/en.json | 38 ++- templates/invite_body.html | 558 ++++++++++++++++++++++++++++++---- templates/invite_body.mjml | 37 +++ templates/partials/card.mjml | 15 + templates/partials/style.mjml | 78 ++++- templates/reset_body.html | 480 ++++++++++++++++++++++++++--- templates/reset_body.mjml | 21 ++ templates/verify_body.html | 78 ++++- templates/welcome_body.html | 78 ++++- 10 files changed, 1291 insertions(+), 128 deletions(-) create mode 100644 templates/invite_body.mjml create mode 100644 templates/partials/card.mjml create mode 100644 templates/reset_body.mjml diff --git a/app/email.go b/app/email.go index e5af249ec7..9ca379dc29 100644 --- a/app/email.go +++ b/app/email.go @@ -308,10 +308,12 @@ func (es *EmailService) SendPasswordResetEmail(email string, token *model.Token, bodyPage := es.newEmailTemplate("reset_body", locale) bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Title"] = T("api.templates.reset_body.title") - bodyPage.Props["Info1"] = i18n.TranslateAsHTML(T, "api.templates.reset_body.info1", nil) - bodyPage.Props["Info2"] = T("api.templates.reset_body.info2") - bodyPage.Props["ResetUrl"] = link + 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") if err := es.sendMail(email, subject, bodyPage.Render()); err != nil { return false, model.NewAppError("SendPasswordReset", "api.user.send_password_reset.send.app_error", nil, "err="+err.Error(), http.StatusInternalServerError) @@ -371,13 +373,13 @@ func (es *EmailService) SendInviteEmails(team *model.Team, senderName string, se bodyPage := es.newEmailTemplate("invite_body", "") bodyPage.Props["SiteURL"] = siteURL - bodyPage.Props["Title"] = i18n.T("api.templates.invite_body.title") - bodyPage.HTML["Info"] = i18n.TranslateAsHTML(i18n.T, "api.templates.invite_body.info", - map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName}) + 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.HTML["ExtraInfo"] = i18n.TranslateAsHTML(i18n.T, "api.templates.invite_body.extra_info", - map[string]interface{}{"TeamDisplayName": team.DisplayName}) - bodyPage.Props["TeamURL"] = siteURL + "/" + team.Name + 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") token := model.NewToken( TokenTypeTeamInvitation, @@ -394,7 +396,7 @@ func (es *EmailService) SendInviteEmails(team *model.Team, senderName string, se mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) continue } - bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) + bodyPage.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) if err := es.sendMail(invite, subject, bodyPage.Render()); err != nil { mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) @@ -430,19 +432,17 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode bodyPage := es.newEmailTemplate("invite_body", "") bodyPage.Props["SiteURL"] = siteURL - bodyPage.Props["Title"] = i18n.T("api.templates.invite_body.title") - bodyPage.HTML["Info"] = i18n.TranslateAsHTML(i18n.T, "api.templates.invite_body_guest.info", - map[string]interface{}{"SenderName": senderName, "TeamDisplayName": team.DisplayName}) + 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["SenderId"] = senderUserId bodyPage.Props["Message"] = "" if message != "" { bodyPage.Props["Message"] = message } - bodyPage.HTML["ExtraInfo"] = i18n.TranslateAsHTML(i18n.T, "api.templates.invite_body.extra_info", - map[string]interface{}{"TeamDisplayName": team.DisplayName}) - bodyPage.Props["TeamURL"] = siteURL + "/" + team.Name + 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") channelIDs := []string{} for _, channel := range channels { @@ -469,7 +469,7 @@ func (es *EmailService) sendGuestInviteEmails(team *model.Team, channels []*mode mlog.Error("Failed to send invite email successfully ", mlog.Err(err)) continue } - bodyPage.Props["Link"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), url.QueryEscape(token.Token)) + bodyPage.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(data), 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))) diff --git a/i18n/en.json b/i18n/en.json index b17b2008bd..24b60595e8 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -2760,23 +2760,31 @@ }, { "id": "api.templates.invite_body.button", - "translation": "Join Team" + "translation": "Join now" }, { - "id": "api.templates.invite_body.extra_info", - "translation": "Mattermost lets you share messages and files from your PC or phone, with instant search and archiving. After you’ve joined [[{{.TeamDisplayName}}]], you can sign-in to your new team and access these features anytime from the web address:" - }, - { - "id": "api.templates.invite_body.info", - "translation": "[[{{.SenderName}}]], has invited you to join [[{{.TeamDisplayName}}]]." + "id": "api.templates.invite_body.subTitle", + "translation": "Start collaborating with your team on Mattermost" }, { "id": "api.templates.invite_body.title", - "translation": "You've been invited" + "translation": "{{ .SenderName }} invited you to join the {{ .TeamDisplayName }} team." }, { - "id": "api.templates.invite_body_guest.info", - "translation": "[[{{.SenderName}}]], has invited you to join team [[{{.TeamDisplayName}}]] as a guest." + "id": "api.templates.invite_body_footer.info", + "translation": "Mattermost is a flexible, open source messaging platform that enables secure team collaboration." + }, + { + "id": "api.templates.invite_body_footer.learn_more", + "translation": "Learn more" + }, + { + "id": "api.templates.invite_body_footer.title", + "translation": "What is Mattermost?" + }, + { + "id": "api.templates.invite_body_guest.subTitle", + "translation": "You were invited as a guest to collaborate with the team" }, { "id": "api.templates.invite_guest_subject", @@ -3003,16 +3011,16 @@ "translation": "Reset Password" }, { - "id": "api.templates.reset_body.info1", - "translation": "To change your password, click \"Reset Password\" below." + "id": "api.templates.reset_body.info", + "translation": "The password reset link expires in 24 hours." }, { - "id": "api.templates.reset_body.info2", - "translation": "If you did not mean to reset your password, please ignore this email and your password will remain the same. The password reset link expires in 24 hours." + "id": "api.templates.reset_body.subTitle", + "translation": "Click the button below to reset your password. If you didn’t request this, you can safely ignore this email." }, { "id": "api.templates.reset_body.title", - "translation": "You requested a password reset" + "translation": "Reset Your Password" }, { "id": "api.templates.reset_subject", diff --git a/templates/invite_body.html b/templates/invite_body.html index 99361ae76a..58bd019a40 100644 --- a/templates/invite_body.html +++ b/templates/invite_body.html @@ -1,61 +1,515 @@ {{define "invite_body"}} - - - - -
- - - + + +
- - - + + +
- + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + +
+ +
+ + + + - -
+ +
+ + + + - - - + + +
+ + + + + + +
+ +
- - - + +
-

{{.Props.Title}}

-

{{.HTML.Info}}

-

- {{.Props.Button}} -

- {{if .Props.Message}} - - - - - -
- - -

{{.Props.SenderName}}

-

{{.Props.Message}}

-
- {{end}} -
-

{{.HTML.ExtraInfo}}

{{.Props.TeamURL}}

+
+ + +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + + + + +
+
{{.Props.Title}}
+
+
{{.Props.SubTitle}}
+
+ + + + +
+ + {{.Props.Button}} + +
+
+
+ +
+
+ + {{if .Props.Message}} +
+ +
+ + + + + + +
+ +
+ +
+ + + + + + +
+ + + + - + + +
+
+
+
+ +
+ + + + + + + + + +
+
{{.Props.SenderName}}
+
+
{{.Props.Message}}
+
+
+ +
+ +
+
+ +
+ {{else}} + +
+ + + + - -
+ +
+ + + + - - - {{template "email_footer" . }} - -
+ + - {{template "email_info" . }} + +
+ +
-
-
-
+
+
+ +
+ + + {{end}} + +
+ + + + + + +
+ +
+ + + + + + + + + + + + +
+
{{.Props.InviteFooterTitle}}
+
+
{{.Props.InviteFooterInfo}}
+
+ +
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+
{{.Props.Organization}} + {{.Props.FooterV2}} +
+
+
+ +
+
+ +
+ + + + + {{end}} diff --git a/templates/invite_body.mjml b/templates/invite_body.mjml new file mode 100644 index 0000000000..d3e70c7f2d --- /dev/null +++ b/templates/invite_body.mjml @@ -0,0 +1,37 @@ + + + + + + + + + {{if .Props.Message}} +
+ +
+ {{else}} + + + + + + {{end}} + + + + {{.Props.InviteFooterTitle}} + + + {{.Props.InviteFooterInfo}} + + + + {{.Props.InviteFooterLearnMore}} + + + + +
+
+
diff --git a/templates/partials/card.mjml b/templates/partials/card.mjml new file mode 100644 index 0000000000..e3b0cc6aa6 --- /dev/null +++ b/templates/partials/card.mjml @@ -0,0 +1,15 @@ + + + + + + + + {{.Props.SenderName}} + + + {{.Props.Message}} + + + + \ No newline at end of file diff --git a/templates/partials/style.mjml b/templates/partials/style.mjml index e2345b143d..7a93d522fc 100644 --- a/templates/partials/style.mjml +++ b/templates/partials/style.mjml @@ -61,7 +61,11 @@ font-size:14px !important; line-height:20px !important; color:#3D3C40 !important; - padding:0px !important; + padding:0px 48px 0px 48px !important; + } + + .footerInfo a { + color:#166DE0 !important; } .appDownloadButton a{ @@ -82,6 +86,52 @@ padding:8px 24px 8px 24px !important; } + .postCard { + padding:0px 24px 40px 24px !important; + } + + .messageCard { + background:#FFFFFF !important; + border:1px solid rgba(61, 60, 64, 0.08) !important; + box-sizing:border-box !important; + box-shadow:0px 8px 24px rgba(0, 0, 0, 0.12) !important; + border-radius:4px !important; + padding:32px !important; + } + + .messageAvatar img { + width:32px !important; + height:32px !important; + padding:0px !important; + border-radius:32px !important; + } + + .messageAvatarCol { + width:32px !important; + } + + .senderName div { + text-align:left !important; + font-weight:600 !important; + font-size:14px !important; + line-height:20px !important; + color:#3D3C40 !important; + padding:0px 0px 4px 0px !important; + } + + .senderMessage div { + text-align:left !important; + font-size:14px !important; + line-height:20px !important; + color:#3D3C40 !important; + padding:0px !important; + } + + .senderInfoCol { + width:394px !important; + padding:0px 0px 0px 12px !important; + } + @media all and (min-width: 541px) { .emailBody { padding:32px !important; @@ -92,12 +142,38 @@ .emailBody { padding:16px !important; } + + .messageCard { + padding:16px !important; + } + + .senderInfoCol { + width:80% !important; + padding:0px 0px 0px 12px !important; + } } @media all and (max-width: 400px) { .emailBody { padding:0px !important; } + + .footerInfo div { + padding:0px !important; + } + + .messageCard { + padding:16px !important; + } + + .postCard { + padding:0px 0px 40px 0px !important; + } + + .senderInfoCol { + width:80% !important; + padding:0px 0px 0px 12px !important; + } } diff --git a/templates/reset_body.html b/templates/reset_body.html index 21bb756ffc..33edf1cfa1 100644 --- a/templates/reset_body.html +++ b/templates/reset_body.html @@ -1,46 +1,446 @@ {{define "reset_body"}} - - - - - + + +
- - - + + +
- - - + + + + + + + +
- + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + - -
+ +
+ + + + + + +
+ +
+ + + + - - - + +
+ + + + + + +
+ +
- - - - - - {{template "email_info" . }} - -
-

{{.Props.Title}}

-

{{.Props.Info1}}
{{.Props.Info2}}

-

- {{.Props.Button}} -

-
+
+
+ +
+
+ +
+ + + + - -
+ +
+ + + + - - - {{template "email_footer" . }} - -
+
{{.Props.Title}}
-
-
+
+
{{.Props.SubTitle}}
+
+ + + + +
+ + {{.Props.Button}} + +
+
+ + +
+ + +
+ + + + + + +
+ +
+ + + + + + + + + +
+ + + + + + +
+ +
+
+
{{.Props.Info}}
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + + + + +
+
{{.Props.QuestionTitle}}
+
+
{{.Props.QuestionInfo}} + + {{.Props.SupportEmail}} +
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + + + +
+
{{.Props.Organization}} + {{.Props.FooterV2}} +
+
+
+ +
+
+ +
+ + + + + {{end}} diff --git a/templates/reset_body.mjml b/templates/reset_body.mjml new file mode 100644 index 0000000000..06bd55c508 --- /dev/null +++ b/templates/reset_body.mjml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + {{.Props.Info}} + + + + + + s + + diff --git a/templates/verify_body.html b/templates/verify_body.html index 67ee5dee89..d6fe66f461 100644 --- a/templates/verify_body.html +++ b/templates/verify_body.html @@ -137,7 +137,11 @@ font-size: 14px !important; line-height: 20px !important; color: #3D3C40 !important; - padding: 0px !important; + padding: 0px 48px 0px 48px !important; + } + + .footerInfo a { + color: #166DE0 !important; } .appDownloadButton a { @@ -158,6 +162,52 @@ padding: 8px 24px 8px 24px !important; } + .postCard { + padding: 0px 24px 40px 24px !important; + } + + .messageCard { + background: #FFFFFF !important; + border: 1px solid rgba(61, 60, 64, 0.08) !important; + box-sizing: border-box !important; + box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12) !important; + border-radius: 4px !important; + padding: 32px !important; + } + + .messageAvatar img { + width: 32px !important; + height: 32px !important; + padding: 0px !important; + border-radius: 32px !important; + } + + .messageAvatarCol { + width: 32px !important; + } + + .senderName div { + text-align: left !important; + font-weight: 600 !important; + font-size: 14px !important; + line-height: 20px !important; + color: #3D3C40 !important; + padding: 0px 0px 4px 0px !important; + } + + .senderMessage div { + text-align: left !important; + font-size: 14px !important; + line-height: 20px !important; + color: #3D3C40 !important; + padding: 0px !important; + } + + .senderInfoCol { + width: 394px !important; + padding: 0px 0px 0px 12px !important; + } + @media all and (min-width: 541px) { .emailBody { padding: 32px !important; @@ -168,12 +218,38 @@ .emailBody { padding: 16px !important; } + + .messageCard { + padding: 16px !important; + } + + .senderInfoCol { + width: 80% !important; + padding: 0px 0px 0px 12px !important; + } } @media all and (max-width: 400px) { .emailBody { padding: 0px !important; } + + .footerInfo div { + padding: 0px !important; + } + + .messageCard { + padding: 16px !important; + } + + .postCard { + padding: 0px 0px 40px 0px !important; + } + + .senderInfoCol { + width: 80% !important; + padding: 0px 0px 0px 12px !important; + } } diff --git a/templates/welcome_body.html b/templates/welcome_body.html index 7ad55108a7..b444f6aaf0 100644 --- a/templates/welcome_body.html +++ b/templates/welcome_body.html @@ -137,7 +137,11 @@ font-size: 14px !important; line-height: 20px !important; color: #3D3C40 !important; - padding: 0px !important; + padding: 0px 48px 0px 48px !important; + } + + .footerInfo a { + color: #166DE0 !important; } .appDownloadButton a { @@ -158,6 +162,52 @@ padding: 8px 24px 8px 24px !important; } + .postCard { + padding: 0px 24px 40px 24px !important; + } + + .messageCard { + background: #FFFFFF !important; + border: 1px solid rgba(61, 60, 64, 0.08) !important; + box-sizing: border-box !important; + box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.12) !important; + border-radius: 4px !important; + padding: 32px !important; + } + + .messageAvatar img { + width: 32px !important; + height: 32px !important; + padding: 0px !important; + border-radius: 32px !important; + } + + .messageAvatarCol { + width: 32px !important; + } + + .senderName div { + text-align: left !important; + font-weight: 600 !important; + font-size: 14px !important; + line-height: 20px !important; + color: #3D3C40 !important; + padding: 0px 0px 4px 0px !important; + } + + .senderMessage div { + text-align: left !important; + font-size: 14px !important; + line-height: 20px !important; + color: #3D3C40 !important; + padding: 0px !important; + } + + .senderInfoCol { + width: 394px !important; + padding: 0px 0px 0px 12px !important; + } + @media all and (min-width: 541px) { .emailBody { padding: 32px !important; @@ -168,12 +218,38 @@ .emailBody { padding: 16px !important; } + + .messageCard { + padding: 16px !important; + } + + .senderInfoCol { + width: 80% !important; + padding: 0px 0px 0px 12px !important; + } } @media all and (max-width: 400px) { .emailBody { padding: 0px !important; } + + .footerInfo div { + padding: 0px !important; + } + + .messageCard { + padding: 16px !important; + } + + .postCard { + padding: 0px 0px 40px 0px !important; + } + + .senderInfoCol { + width: 80% !important; + padding: 0px 0px 0px 12px !important; + } }