discourse/lib/email/message_builder.rb
2016-04-07 22:21:17 +08:00

222 lines
7.5 KiB
Ruby

# Builds a Mail::Message we can use for sending. Optionally supports using a template
# for the body and subject
module Email
module BuildEmailHelper
def build_email(*builder_args)
builder = Email::MessageBuilder.new(*builder_args)
headers(builder.header_args) if builder.header_args.present?
mail(builder.build_args).tap { |message|
if message && h = builder.html_part
message.html_part = h
end
}
end
end
class MessageBuilder
attr_reader :template_args
REPLY_TO_AUTO_GENERATED_HEADER_KEY = "X-Discourse-Reply-to-Auto-Generated".freeze
REPLY_TO_AUTO_GENERATED_HEADER_VALUE = "marked".freeze
def initialize(to, opts=nil)
@to = to
@opts = opts || {}
@template_args = {
site_name: SiteSetting.email_prefix.presence || SiteSetting.title,
base_url: Discourse.base_url,
user_preferences_url: "#{Discourse.base_url}/my/preferences",
}.merge!(@opts)
if @template_args[:url].present?
@template_args[:header_instructions] = I18n.t('user_notifications.header_instructions', locale: @opts[:locale])
if @opts[:include_respond_instructions] == false
@template_args[:respond_instructions] = ''
else
if @opts[:only_reply_by_email]
string = "user_notifications.only_reply_by_email"
else
string = allow_reply_by_email? ? "user_notifications.reply_by_email" : "user_notifications.visit_link_to_respond"
string << "_pm" if @opts[:private_reply]
end
@template_args[:respond_instructions] = I18n.t(string, @template_args)
end
end
end
def subject
if @opts[:use_site_subject]
subject = String.new(SiteSetting.email_subject)
subject.gsub!("%{site_name}", @template_args[:site_name])
subject.gsub!("%{optional_re}", @opts[:add_re_to_subject] ? I18n.t('subject_re', template_args) : '')
subject.gsub!("%{optional_pm}", @opts[:private_reply] ? I18n.t('subject_pm', template_args) : '')
subject.gsub!("%{optional_cat}", @template_args[:show_category_in_subject] ? "[#{@template_args[:show_category_in_subject]}] " : '')
subject.gsub!("%{topic_title}", @template_args[:topic_title]) if @template_args[:topic_title] # must be last for safety
else
subject = @opts[:subject]
subject = I18n.t("#{@opts[:template]}.subject_template", template_args) if @opts[:template]
end
subject
end
def html_part
return unless html_override = @opts[:html_override]
if @opts[:add_unsubscribe_link]
unsubscribe_link = PrettyText.cook(I18n.t('unsubscribe_link', template_args), sanitize: false).html_safe
html_override.gsub!("%{unsubscribe_link}", unsubscribe_link)
if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link]
unsubscribe_via_email_link = PrettyText.cook(I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname, locale: @opts[:locale]), sanitize: false).html_safe
html_override.gsub!("%{unsubscribe_via_email_link}", unsubscribe_via_email_link)
else
html_override.gsub!("%{unsubscribe_via_email_link}", "")
end
else
html_override.gsub!("%{unsubscribe_link}", "")
html_override.gsub!("%{unsubscribe_via_email_link}", "")
end
header_instructions = @template_args[:header_instructions]
if header_instructions.present?
header_instructions = PrettyText.cook(header_instructions, sanitize: false).html_safe
html_override.gsub!("%{header_instructions}", header_instructions)
else
html_override.gsub!("%{header_instructions}", "")
end
if response_instructions = @template_args[:respond_instructions]
respond_instructions = PrettyText.cook(response_instructions, sanitize: false).html_safe
html_override.gsub!("%{respond_instructions}", respond_instructions)
else
html_override.gsub!("%{respond_instructions}", "")
end
styled = Email::Styles.new(html_override, @opts)
styled.format_basic
if style = @opts[:style]
styled.send "format_#{style}"
end
Mail::Part.new do
content_type 'text/html; charset=UTF-8'
body styled.to_html
end
end
def body
body = @opts[:body]
body = I18n.t("#{@opts[:template]}.text_body_template", template_args).dup if @opts[:template]
if @opts[:add_unsubscribe_link]
body << "\n"
body << I18n.t('unsubscribe_link', template_args)
if SiteSetting.unsubscribe_via_email_footer && @opts[:add_unsubscribe_via_email_link]
body << I18n.t('unsubscribe_via_email_link', hostname: Discourse.current_hostname, locale: @opts[:locale])
end
end
body
end
def build_args
{ to: @to,
subject: subject,
body: body,
charset: 'UTF-8',
from: from_value }
end
def header_args
result = {}
if @opts[:add_unsubscribe_link]
result['List-Unsubscribe'] = "<#{template_args[:user_preferences_url]}>"
end
if @opts[:mark_as_reply_to_auto_generated]
result[REPLY_TO_AUTO_GENERATED_HEADER_KEY] = REPLY_TO_AUTO_GENERATED_HEADER_VALUE
end
result['X-Discourse-Post-Id'] = @opts[:post_id].to_s if @opts[:post_id]
result['X-Discourse-Topic-Id'] = @opts[:topic_id].to_s if @opts[:topic_id]
if allow_reply_by_email?
result['X-Discourse-Reply-Key'] = reply_key
result['Reply-To'] = reply_by_email_address
else
result['Reply-To'] = from_value
end
result.merge(MessageBuilder.custom_headers(SiteSetting.email_custom_headers))
end
def self.custom_headers(string)
result = {}
string.split('|').each { |item|
header = item.split(':', 2)
if header.length == 2
name = header[0].strip
value = header[1].strip
result[name] = value if name.length > 0 && value.length > 0
end
} unless string.nil?
result
end
protected
def reply_key
@reply_key ||= SecureRandom.hex(16)
end
def allow_reply_by_email?
SiteSetting.reply_by_email_enabled? &&
reply_by_email_address.present? &&
@opts[:allow_reply_by_email]
end
def private_reply?
allow_reply_by_email? && @opts[:private_reply]
end
def from_value
return @from_value if @from_value
@from_value = @opts[:from] || SiteSetting.notification_email
@from_value = alias_email(@from_value)
end
def reply_by_email_address
return @reply_by_email_address if @reply_by_email_address
return nil unless SiteSetting.reply_by_email_address.present?
@reply_by_email_address = SiteSetting.reply_by_email_address.dup
@reply_by_email_address.gsub!("%{reply_key}", reply_key)
@reply_by_email_address = if private_reply?
alias_email(@reply_by_email_address)
else
site_alias_email(@reply_by_email_address)
end
end
def alias_email(source)
return source if @opts[:from_alias].blank? && SiteSetting.email_site_title.blank?
if !@opts[:from_alias].blank?
"#{Email.cleanup_alias(@opts[:from_alias])} <#{source}>"
else
"#{Email.cleanup_alias(SiteSetting.email_site_title)} <#{source}>"
end
end
def site_alias_email(source)
"#{Email.cleanup_alias(SiteSetting.email_site_title.presence || SiteSetting.title)} <#{source}>"
end
end
end