mirror of
https://github.com/discourse/discourse.git
synced 2026-07-31 00:28:19 -05:00
UX: add preview to email templates (#36657)
Adds a preview text to the beginning of the email body based on user notification type. The value is taken from the translation key, which can be customized for each email within Admin -> Appearance -> Site Texts (ie. translation overrides). For html emails this preview text is hidden with `display: none` to prevent it appearing within the body of the email. For the plain text version of the email it will appear within the body of the email. Co-authored-by: Martin Brennan <martin@discourse.org>
This commit is contained in:
co-authored by
Martin Brennan
parent
cd858b2d64
commit
17e6ea96ab
@@ -27,11 +27,14 @@ module EmailHelper
|
||||
raw "<a href='#{Discourse.base_url_no_prefix}#{url}' style='color: ##{@anchor_color}'>#{title}</a>"
|
||||
end
|
||||
|
||||
def email_html_template
|
||||
def email_html_template(preview: nil)
|
||||
email_preview = preview.present? ? PrettyText.cook(preview, sanitize: false).html_safe : ""
|
||||
|
||||
EmailStyle
|
||||
.new
|
||||
.html
|
||||
.sub("%{email_content}") { capture { yield } }
|
||||
.gsub("%{email_preview}", email_preview)
|
||||
.gsub("%{html_lang}", html_lang)
|
||||
.gsub("%{dark_mode_meta_tags}", dark_mode_meta_tags)
|
||||
.gsub("%{dark_mode_styles}", dark_mode_styles)
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
</style>
|
||||
<![endif]-->
|
||||
|
||||
<div style='display: none'>%{email_preview}</div>
|
||||
|
||||
%{email_content}
|
||||
|
||||
<!-- prevent Gmail on iOS font size manipulation -->
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<div class='email-preview'>%{email_preview}</div>
|
||||
|
||||
<div class='header-instructions'>%{header_instructions}</div>
|
||||
|
||||
<%- if SiteSetting.private_email? %>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
<%= yield %>
|
||||
<% if defined?(html_body) %><%= html_body %><% end %>
|
||||
<% else %>
|
||||
<%= email_html_template do %>
|
||||
<% preview = defined?(email_preview) ? email_preview : nil %>
|
||||
<%= email_html_template(preview: preview) do %>
|
||||
<%= yield %>
|
||||
<% if defined?(html_body) %><%= html_body %><% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -3399,6 +3399,7 @@ en:
|
||||
|
||||
post_hidden:
|
||||
title: "Post Hidden"
|
||||
preview: "Post hidden due to flags by community members."
|
||||
subject_template: "Post hidden by community flags"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
@@ -3463,6 +3464,7 @@ en:
|
||||
|
||||
post_hidden_again:
|
||||
title: "Post Hidden again"
|
||||
preview: "Post hidden again by community members."
|
||||
subject_template: "Post hidden by community flags, staff notified"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
@@ -3557,6 +3559,7 @@ en:
|
||||
|
||||
welcome_user:
|
||||
title: "Welcome User"
|
||||
preview: "Welcome to %{site_name}!"
|
||||
subject_template: "Welcome to %{site_name}!"
|
||||
text_body_template: |
|
||||
Thanks for joining %{site_name}, and welcome!
|
||||
@@ -3586,6 +3589,7 @@ en:
|
||||
With great power comes great responsibility. If you’re new to moderating, please refer to the [Moderation Guide](https://meta.discourse.org/t/discourse-moderation-guide/63116).
|
||||
welcome_invite:
|
||||
title: "Welcome Invite"
|
||||
preview: "Welcome to %{site_name}!"
|
||||
subject_template: "Welcome to %{site_name}!"
|
||||
text_body_template: |
|
||||
Thanks for accepting your invitation to %{site_name} -- welcome!
|
||||
@@ -3994,6 +3998,7 @@ en:
|
||||
|
||||
too_many_spam_flags:
|
||||
title: "Too Many Spam Flags"
|
||||
preview: "Posts temporarily hidden due to spam flags."
|
||||
subject_template: "New account on hold"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
@@ -4016,6 +4021,7 @@ en:
|
||||
For additional guidance, please refer to our [community guidelines](%{base_url}/guidelines).
|
||||
silenced_by_staff:
|
||||
title: "Silenced by Staff"
|
||||
preview: "Account temporarily placed on hold."
|
||||
subject_template: "Account temporarily on hold"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
@@ -4065,6 +4071,7 @@ en:
|
||||
|
||||
unsilenced:
|
||||
title: "Unsilenced"
|
||||
preview: "Account no longer on hold."
|
||||
subject_template: "Account no longer on hold"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
@@ -4243,6 +4250,7 @@ en:
|
||||
|
||||
user_replied:
|
||||
title: "User Replied"
|
||||
preview: "Someone replied to your post."
|
||||
subject_template: "[%{email_prefix}] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4267,6 +4275,7 @@ en:
|
||||
|
||||
user_quoted:
|
||||
title: "User Quoted"
|
||||
preview: "Someone quoted you in a post."
|
||||
subject_template: "[%{email_prefix}] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4279,6 +4288,7 @@ en:
|
||||
|
||||
user_linked:
|
||||
title: "User Linked"
|
||||
preview: "Post linked in another topic."
|
||||
subject_template: "[%{email_prefix}] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4291,6 +4301,7 @@ en:
|
||||
|
||||
user_mentioned:
|
||||
title: "User Mentioned"
|
||||
preview: "Someone mentioned you in a post."
|
||||
subject_template: "[%{email_prefix}] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4351,6 +4362,7 @@ en:
|
||||
|
||||
user_posted:
|
||||
title: "User Posted"
|
||||
preview: "Someone replied to a topic you are Watching."
|
||||
subject_template: "[%{email_prefix}] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4387,6 +4399,7 @@ en:
|
||||
|
||||
user_posted_pm:
|
||||
title: "User Posted PM"
|
||||
preview: "Someone sent you a PM."
|
||||
subject_template: "[%{email_prefix}] [PM] %{topic_title}"
|
||||
text_body_template: |
|
||||
%{header_instructions}
|
||||
@@ -4513,6 +4526,7 @@ en:
|
||||
|
||||
forgot_password:
|
||||
title: "Forgot Password"
|
||||
preview: "Reset your password."
|
||||
subject_template: "[%{email_prefix}] Password reset"
|
||||
text_body_template: |
|
||||
Somebody asked to reset your password on [%{site_name}](%{base_url}).
|
||||
@@ -4524,6 +4538,7 @@ en:
|
||||
|
||||
email_login:
|
||||
title: "Log in via link"
|
||||
preview: "Log in via link."
|
||||
subject_template: "[%{email_prefix}] Log in via link"
|
||||
text_body_template: |
|
||||
Here's your link to log in at [%{site_name}](%{base_url}).
|
||||
@@ -4535,6 +4550,7 @@ en:
|
||||
|
||||
set_password:
|
||||
title: "Set Password"
|
||||
preview: "Set account password."
|
||||
subject_template: "[%{email_prefix}] Set Password"
|
||||
text_body_template: |
|
||||
Somebody asked to add a password to your account on [%{site_name}](%{base_url}). Alternatively, you can log in using any supported online service (Google, Facebook, etc) that is associated with this validated email address.
|
||||
@@ -4546,6 +4562,7 @@ en:
|
||||
|
||||
admin_login:
|
||||
title: "Admin Login"
|
||||
preview: "Admin login requested."
|
||||
subject_template: "[%{email_prefix}] Login"
|
||||
text_body_template: |
|
||||
Somebody asked to log in to your account on [%{site_name}](%{base_url}).
|
||||
@@ -4557,6 +4574,7 @@ en:
|
||||
|
||||
account_created:
|
||||
title: "Account Created"
|
||||
preview: "New account created for you."
|
||||
subject_template: "[%{email_prefix}] Your New Account"
|
||||
text_body_template: |
|
||||
A new account was created for you at %{site_name}
|
||||
@@ -4610,6 +4628,7 @@ en:
|
||||
|
||||
notify_old_email:
|
||||
title: "Notify Old Email"
|
||||
preview: "Email address updated."
|
||||
subject_template: "[%{email_prefix}] Your email address has been changed"
|
||||
text_body_template: |
|
||||
This is an automated message to let you know that your email address for
|
||||
@@ -4634,6 +4653,7 @@ en:
|
||||
|
||||
signup_after_approval:
|
||||
title: "Signup After Approval"
|
||||
preview: "New account approved."
|
||||
subject_template: "You've been approved on %{site_name}!"
|
||||
text_body_template: |
|
||||
Welcome to %{site_name}!
|
||||
@@ -4661,6 +4681,7 @@ en:
|
||||
|
||||
signup:
|
||||
title: "Signup"
|
||||
preview: "Confirm your account."
|
||||
subject_template: "[%{email_prefix}] Confirm your new account"
|
||||
text_body_template: |
|
||||
Welcome to %{site_name}!
|
||||
@@ -4685,6 +4706,7 @@ en:
|
||||
|
||||
suspicious_login:
|
||||
title: "New Login Alert"
|
||||
preview: "Verify your device."
|
||||
subject_template: "[%{site_name}] New Login from %{location}"
|
||||
text_body_template: |
|
||||
Hello,
|
||||
|
||||
@@ -20,6 +20,10 @@ module Email
|
||||
hostname: Discourse.current_hostname,
|
||||
}.merge!(@opts)
|
||||
|
||||
if @opts[:template].present? && I18n.exists?("#{@opts[:template]}.preview")
|
||||
@template_args[:email_preview] ||= I18n.t("#{@opts[:template]}.preview", @template_args)
|
||||
end
|
||||
|
||||
return if @template_args[:url].blank?
|
||||
|
||||
@template_args[:header_instructions] ||= I18n.t(
|
||||
@@ -150,6 +154,13 @@ module Email
|
||||
html_override.gsub!("%{unsubscribe_instructions}", "")
|
||||
end
|
||||
|
||||
if @template_args[:email_preview].present?
|
||||
email_preview = PrettyText.cook(@template_args[:email_preview], sanitize: false).html_safe
|
||||
html_override.gsub!("%{email_preview}", email_preview)
|
||||
else
|
||||
html_override.gsub!("%{email_preview}", "")
|
||||
end
|
||||
|
||||
if @template_args[:header_instructions].present?
|
||||
header_instructions =
|
||||
PrettyText.cook(@template_args[:header_instructions], sanitize: false).html_safe
|
||||
@@ -234,6 +245,10 @@ module Email
|
||||
def header_args
|
||||
result = {}
|
||||
|
||||
if @template_args[:email_preview].present?
|
||||
result["X-Discourse-Email-Preview"] = @template_args[:email_preview]
|
||||
end
|
||||
|
||||
if @opts[:add_unsubscribe_link]
|
||||
if unsubscribe_url = @template_args[:unsubscribe_url].presence
|
||||
result["List-Unsubscribe"] = "<#{unsubscribe_url}>"
|
||||
|
||||
@@ -15,6 +15,7 @@ module Email
|
||||
end
|
||||
|
||||
def html
|
||||
email_preview = @message.header["X-Discourse-Email-Preview"]&.value
|
||||
style =
|
||||
if @message.html_part
|
||||
Email::Styles.new(@message.html_part.body.to_s, @opts)
|
||||
@@ -25,6 +26,7 @@ module Email
|
||||
format: :html,
|
||||
locals: {
|
||||
html_body: PrettyText.cook(text).html_safe,
|
||||
email_preview: email_preview,
|
||||
},
|
||||
)
|
||||
Email::Styles.new(unstyled, @opts)
|
||||
|
||||
@@ -263,6 +263,7 @@ module Email
|
||||
"-moz-box-sizing:border-box;-ms-text-size-adjust:100%;-webkit-box-sizing:border-box;-webkit-text-size-adjust:100%;box-sizing:border-box;color:#0a0a0a;font-family:Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.3;margin:0;min-width:100%;padding:0;width:100%",
|
||||
)
|
||||
|
||||
style(".email-preview", "display: none;")
|
||||
style(".previous-discussion", "font-size: 17px; color: #444; margin-bottom:10px;")
|
||||
style(
|
||||
".notification-date",
|
||||
|
||||
@@ -24,6 +24,10 @@ class SystemMessage
|
||||
|
||||
params = defaults.merge(params)
|
||||
|
||||
if I18n.exists?("system_messages.#{type}.preview", @recipient.effective_locale)
|
||||
params[:email_preview] = I18n.t("system_messages.#{type}.preview", params)
|
||||
end
|
||||
|
||||
title =
|
||||
params[:message_title] ||
|
||||
I18n.with_locale(@recipient.effective_locale) do
|
||||
@@ -102,6 +106,7 @@ class SystemMessage
|
||||
end,
|
||||
site_password: "",
|
||||
base_url: Discourse.base_url,
|
||||
email_preview: "",
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -60,12 +60,19 @@ RSpec.describe UserNotifications do
|
||||
describe ".signup" do
|
||||
subject(:email) { UserNotifications.signup(user) }
|
||||
|
||||
let(:email_html) { Email::Renderer.new(email).html }
|
||||
|
||||
it "works" do
|
||||
expect(email.to).to eq([user.email])
|
||||
expect(email.subject).to be_present
|
||||
expect(email.from).to eq([SiteSetting.notification_email])
|
||||
expect(email.body).to be_present
|
||||
end
|
||||
|
||||
it "shows the correct preview text in html" do
|
||||
preview_text = I18n.t("user_notifications.signup.preview")
|
||||
expect(email_html.scan(/#{preview_text}/).count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".forgot_password" do
|
||||
@@ -77,6 +84,18 @@ RSpec.describe UserNotifications do
|
||||
expect(email.from).to eq([SiteSetting.notification_email])
|
||||
expect(email.body).to be_present
|
||||
end
|
||||
|
||||
it "shows the correct preview text in forgot password" do
|
||||
preview_text = I18n.t("user_notifications.forgot_password.preview")
|
||||
expect(Email::Renderer.new(email).html.scan(/#{preview_text}/).count).to eq(1)
|
||||
end
|
||||
|
||||
it "shows the correct preview text when setting password" do
|
||||
email = UserNotifications.forgot_password(Fabricate(:user, password: nil))
|
||||
preview_text = I18n.t("user_notifications.set_password.preview")
|
||||
|
||||
expect(Email::Renderer.new(email).html.scan(/#{preview_text}/).count).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".post_approved" do
|
||||
@@ -125,6 +144,8 @@ RSpec.describe UserNotifications do
|
||||
Fabricate(:email_token, user: user, scope: EmailToken.scopes[:email_login]).token
|
||||
end
|
||||
|
||||
let(:email_html) { Email::Renderer.new(email).html }
|
||||
|
||||
it "generates the right email" do
|
||||
expect(email.to).to eq([user.email])
|
||||
expect(email.from).to eq([SiteSetting.notification_email])
|
||||
@@ -133,12 +154,16 @@ RSpec.describe UserNotifications do
|
||||
I18n.t("user_notifications.email_login.subject_template", email_prefix: SiteSetting.title),
|
||||
)
|
||||
|
||||
preview_text = I18n.t("user_notifications.email_login.preview")
|
||||
expect(email_html.scan(/#{preview_text}/).count).to eq(1)
|
||||
|
||||
expect(email.body.to_s).to match(
|
||||
I18n.t(
|
||||
"user_notifications.email_login.text_body_template",
|
||||
site_name: SiteSetting.title,
|
||||
base_url: Discourse.base_url,
|
||||
email_token: email_token,
|
||||
email_preview: preview_text,
|
||||
),
|
||||
)
|
||||
end
|
||||
@@ -530,6 +555,9 @@ RSpec.describe UserNotifications do
|
||||
expect(mail.subject).to match(/Taggie/)
|
||||
|
||||
mail_html = mail.html_part.body.to_s
|
||||
preview_text = I18n.t("user_notifications.user_replied.preview")
|
||||
|
||||
expect(mail_html.scan(/#{preview_text}/).count).to eq(1)
|
||||
|
||||
expect(mail_html.scan(/My super duper cool topic/).count).to eq(1)
|
||||
expect(mail_html.scan(/In Reply To/).count).to eq(1)
|
||||
@@ -755,6 +783,9 @@ RSpec.describe UserNotifications do
|
||||
# subject should not include category name
|
||||
expect(mail.subject).not_to match(/Uncategorized/)
|
||||
|
||||
preview_text = I18n.t("user_notifications.user_posted.preview")
|
||||
expect(mail.html_part.body.to_s.scan(/#{preview_text}/).count).to eq(1)
|
||||
|
||||
# 1 respond to links as no context by default
|
||||
expect(mail.html_part.body.to_s.scan(/to respond/).count).to eq(1)
|
||||
|
||||
@@ -847,6 +878,10 @@ RSpec.describe UserNotifications do
|
||||
# subject should include "[PM]"
|
||||
expect(mail.subject).to include("[PM] ")
|
||||
|
||||
# note that translation key differs from method name (ie. user_posted_pm)
|
||||
preview_text = I18n.t("user_notifications.user_posted_pm.preview")
|
||||
expect(mail.html_part.body.to_s.scan(/#{preview_text}/).count).to eq(1)
|
||||
|
||||
# 1 "visit message" link
|
||||
expect(mail.html_part.body.to_s.scan(/Visit Message/).count).to eq(1)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user