chore: Forward outgoing email message
This commit is contained in:
@@ -1,48 +1,255 @@
|
||||
# Handles formatting and preparation of forwarded email messages
|
||||
class Messages::ForwardedMessageBuilder
|
||||
def initialize(message_id)
|
||||
@message_id = message_id
|
||||
module Messages::ForwardedMessageFormatter
|
||||
def self.parse_from_field(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /(.*)<(.*)>/ ? Regexp.last_match(2).strip : from_field
|
||||
end
|
||||
|
||||
def perform
|
||||
return {} unless @message_id
|
||||
return basic_attributes unless valid_forwarded_data?
|
||||
def self.format_plain_text_to_html(text)
|
||||
ERB::Util.html_escape(text.to_s).gsub("\n", '<br>')
|
||||
end
|
||||
|
||||
build_forwarded_attributes
|
||||
def self.strip_markdown(text)
|
||||
return '' if text.blank?
|
||||
|
||||
text.gsub(/\*\*?(.*?)\*\*?|_(.*?)_/) { |_m| Regexp.last_match(1) || Regexp.last_match(2) }
|
||||
end
|
||||
|
||||
def self.convert_markdown_to_html(text)
|
||||
return '' if text.blank?
|
||||
|
||||
html = text.to_s
|
||||
html.gsub!(/\*\*?(.*?)\*\*?/) do |m|
|
||||
m.start_with?('**') ? "<b>#{Regexp.last_match(1)}</b>" : "<i>#{Regexp.last_match(1)}</i>"
|
||||
end
|
||||
html.gsub(/_(.*?)_/, '<i>\1</i>')
|
||||
end
|
||||
|
||||
def self.extract_email(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /<(.*)>/ ? Regexp.last_match(1) : from_field
|
||||
end
|
||||
|
||||
def self.format_date_string(date_str)
|
||||
return '' if date_str.blank?
|
||||
|
||||
begin
|
||||
parsed_date = DateTime.now
|
||||
parsed_date.strftime('%a, %b %-d, %Y at %-l:%M %p')
|
||||
rescue StandardError
|
||||
date_str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Messages::ForwardedMessageHtmlBuilder
|
||||
def html_wrapper(content)
|
||||
base = '<div dir="ltr">'
|
||||
if content.blank?
|
||||
"#{base}#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
else
|
||||
"#{base}#{content}<br><br>#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
end
|
||||
end
|
||||
|
||||
def forwarded_header_html
|
||||
from_value = formatted_info[:from].to_s
|
||||
email = Messages::ForwardedMessageFormatter.extract_email(from_value)
|
||||
|
||||
[
|
||||
'<div class="gmail_quote gmail_quote_container">',
|
||||
'<div dir="ltr" class="gmail_attr">---------- Forwarded message ---------<br>',
|
||||
build_email_html(email),
|
||||
build_field_html('Date', formatted_info[:date]),
|
||||
build_field_html('Subject', formatted_info[:subject]),
|
||||
build_to_html(formatted_info[:to]),
|
||||
'</div><br><br>'
|
||||
].compact.join
|
||||
end
|
||||
|
||||
def build_email_html(email)
|
||||
return nil if email.blank?
|
||||
|
||||
"From: <<a href=\"mailto:#{email}\">#{email}</a>><br>"
|
||||
end
|
||||
|
||||
def build_field_html(label, value)
|
||||
value.present? ? "#{label}: #{value}<br>" : nil
|
||||
end
|
||||
|
||||
def build_to_html(to_email)
|
||||
return nil if to_email.blank?
|
||||
|
||||
"To: <<a href=\"mailto:#{to_email}\">#{to_email}</a>><br>"
|
||||
end
|
||||
|
||||
def forwarded_body_html
|
||||
# Return HTML content directly if available
|
||||
return email_data.dig('html_content', 'full') if email_data&.dig('html_content', 'full').present?
|
||||
|
||||
# Otherwise format plain text to HTML
|
||||
content = if email_data&.dig('text_content', 'full').present?
|
||||
email_data.dig('text_content', 'full')
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
|
||||
Messages::ForwardedMessageFormatter.format_plain_text_to_html(content)
|
||||
end
|
||||
end
|
||||
|
||||
module Messages::ForwardedMessageContentBuilder
|
||||
def forwarded_header_text
|
||||
return '' unless formatted_info.values.any?
|
||||
|
||||
[
|
||||
"\n\n---------- Forwarded message ---------",
|
||||
("From: #{formatted_info[:from]}" if formatted_info[:from]),
|
||||
("Date: #{formatted_info[:date]}" if formatted_info[:date]),
|
||||
("Subject: #{formatted_info[:subject]}" if formatted_info[:subject]),
|
||||
("To: <#{formatted_info[:to]}>" if formatted_info[:to]),
|
||||
"\n"
|
||||
].compact.join("\n")
|
||||
end
|
||||
|
||||
def forwarded_body_text
|
||||
return forwarded_message.content.to_s if email_data.blank?
|
||||
|
||||
text = email_data.dig('text_content', 'full')
|
||||
html = email_data.dig('html_content', 'full')
|
||||
if text.present?
|
||||
text
|
||||
elsif html.present?
|
||||
ActionView::Base.full_sanitizer.sanitize(html)
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def formatted_content(original_content = '')
|
||||
return original_content.to_s unless valid_forwarded_data?
|
||||
return original_content.to_s if forwarded_message.blank?
|
||||
|
||||
original_content.to_s + forwarded_header_text + forwarded_body_text
|
||||
end
|
||||
|
||||
def formatted_html_content(original_content = '')
|
||||
return original_content unless valid_forwarded_data?
|
||||
return original_content if forwarded_message.blank?
|
||||
|
||||
html_content = convert_markdown_to_html(original_content)
|
||||
html_wrapper(html_content)
|
||||
html_wrapper(Messages::ForwardedMessageFormatter.convert_markdown_to_html(original_content))
|
||||
end
|
||||
end
|
||||
|
||||
module Messages::ForwardedMessageDataHandler
|
||||
def prepare_email_data
|
||||
initialize_email_data
|
||||
end
|
||||
|
||||
def initialize_email_data
|
||||
data = base_email_data
|
||||
add_content_fields(data)
|
||||
add_header_fields(data)
|
||||
data
|
||||
end
|
||||
|
||||
def base_email_data
|
||||
email_data.present? ? email_data.dup || {} : {}
|
||||
end
|
||||
|
||||
def add_content_fields(data)
|
||||
data['html_content'] ||= {}
|
||||
data['text_content'] ||= {}
|
||||
end
|
||||
|
||||
def add_header_fields(data)
|
||||
data['from'] = [email_from_inbox] # Always overwrite with inbox email
|
||||
data['to'] = Array(@params[:to_emails]) if @params && @params[:to_emails].present?
|
||||
data['subject'] ||= subject
|
||||
data['date'] ||= Time.zone.now.to_s
|
||||
end
|
||||
|
||||
def formatted_info
|
||||
{
|
||||
from: inbox_email.to_s,
|
||||
date: Messages::ForwardedMessageFormatter.format_date_string(email_date),
|
||||
subject: subject.to_s,
|
||||
to: recipient_email.to_s
|
||||
}
|
||||
end
|
||||
|
||||
def inbox_email
|
||||
email_from_data || email_from_inbox
|
||||
end
|
||||
|
||||
def email_from_data
|
||||
return nil unless email_data_has_from?
|
||||
|
||||
from_field = email_data['from'].first.to_s
|
||||
Messages::ForwardedMessageFormatter.parse_from_field(from_field)
|
||||
end
|
||||
|
||||
def email_data_has_from?
|
||||
email_data.present? &&
|
||||
email_data['from'].present? &&
|
||||
email_data['from'].first.present?
|
||||
end
|
||||
|
||||
def email_from_inbox
|
||||
inbox = forwarded_message&.conversation&.inbox
|
||||
return nil if inbox.blank? || inbox.channel_type != 'Channel::Email'
|
||||
|
||||
email = inbox.channel&.email
|
||||
return nil if email.blank?
|
||||
|
||||
email
|
||||
end
|
||||
|
||||
def recipient_email
|
||||
return email_data&.dig('to', 0) if email_data&.dig('to', 0).present?
|
||||
|
||||
forwarded_message&.content_attributes&.dig('to_emails', 0).presence
|
||||
end
|
||||
|
||||
def subject
|
||||
email_data&.dig('subject').presence ||
|
||||
forwarded_message&.conversation&.additional_attributes&.dig('subject').presence ||
|
||||
'No Subject'
|
||||
end
|
||||
|
||||
def email_date
|
||||
email_data&.dig('date').presence || Time.zone.now.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class Messages::ForwardedMessageBuilder
|
||||
include Messages::ForwardedMessageFormatter
|
||||
include Messages::ForwardedMessageHtmlBuilder
|
||||
include Messages::ForwardedMessageContentBuilder
|
||||
include Messages::ForwardedMessageDataHandler
|
||||
|
||||
def initialize(message_id, params = {})
|
||||
@message_id = message_id
|
||||
@params = params || {}
|
||||
end
|
||||
|
||||
def perform
|
||||
return {} unless @message_id
|
||||
return basic_attributes if forwarded_message.blank?
|
||||
|
||||
build_forwarded_attributes
|
||||
end
|
||||
|
||||
def forwarded_email_data(original_content = '')
|
||||
return {} unless valid_forwarded_data?
|
||||
return {} if forwarded_message.blank?
|
||||
|
||||
data = prepare_email_data
|
||||
full_content = formatted_content(original_content)
|
||||
stripped = Messages::ForwardedMessageFormatter.strip_markdown(original_content.to_s)
|
||||
html_full = formatted_html_content(original_content)
|
||||
|
||||
data['html_content'].merge!({
|
||||
'quoted' => strip_markdown(original_content.to_s),
|
||||
'reply' => full_content,
|
||||
'full' => formatted_html_content(original_content)
|
||||
})
|
||||
data['html_content'].merge!('quoted' => stripped, 'reply' => full_content, 'full' => html_full)
|
||||
data['text_content'].merge!('quoted' => original_content.to_s, 'reply' => full_content, 'full' => full_content)
|
||||
|
||||
data['text_content'].merge!({
|
||||
'quoted' => original_content.to_s,
|
||||
'reply' => full_content,
|
||||
'full' => full_content
|
||||
})
|
||||
|
||||
# Copy over any attachment data if present
|
||||
data['attachments'] = forwarded_message.attachments.map(&:serializable_hash) if forwarded_message.attachments.present?
|
||||
|
||||
data
|
||||
@@ -54,106 +261,8 @@ class Messages::ForwardedMessageBuilder
|
||||
|
||||
private
|
||||
|
||||
def valid_forwarded_data?
|
||||
forwarded_message && email_data.present?
|
||||
end
|
||||
|
||||
def html_wrapper(content)
|
||||
base = '<div dir="ltr">'
|
||||
|
||||
if content.blank?
|
||||
"#{base}#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
else
|
||||
"#{base}#{content}<br><br>#{forwarded_header_html}#{forwarded_body_html}</div>"
|
||||
end
|
||||
end
|
||||
|
||||
def build_forwarded_attributes
|
||||
{
|
||||
content_attributes: {
|
||||
forwarded_message_id: @message_id,
|
||||
email: prepare_email_data
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def forwarded_header_text
|
||||
[
|
||||
"\n\n---------- Forwarded message ---------",
|
||||
"From: #{formatted_info[:from]}",
|
||||
"Date: #{formatted_info[:date]}",
|
||||
"Subject: #{formatted_info[:subject]}",
|
||||
"To: <#{formatted_info[:to]}>\n\n"
|
||||
].join("\n")
|
||||
end
|
||||
|
||||
def forwarded_body_text
|
||||
text_content = email_data.dig('text_content', 'full')
|
||||
html_content = email_data.dig('html_content', 'full')
|
||||
|
||||
if text_content.present?
|
||||
text_content
|
||||
elsif html_content.present?
|
||||
ActionView::Base.full_sanitizer.sanitize(html_content)
|
||||
else
|
||||
forwarded_message.content.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def forwarded_header_html
|
||||
email = extract_email(formatted_info[:from])
|
||||
name = formatted_info[:from].split(' <').first
|
||||
|
||||
[
|
||||
'<div class="gmail_quote gmail_quote_container">',
|
||||
'<div dir="ltr" class="gmail_attr">---------- Forwarded message ---------<br>',
|
||||
"From: <strong class=\"gmail_sendername\" dir=\"auto\">#{name}</strong> ",
|
||||
"<span dir=\"auto\"><<a href=\"mailto:#{email}\">#{email}</a>></span><br>",
|
||||
"Date: #{formatted_info[:date]}<br>",
|
||||
"Subject: #{formatted_info[:subject]}<br>",
|
||||
"To: <<a href=\"mailto:#{formatted_info[:to]}\">#{formatted_info[:to]}</a>><br>",
|
||||
'</div><br><br>'
|
||||
].join
|
||||
end
|
||||
|
||||
def forwarded_body_html
|
||||
if email_data.dig('html_content', 'full').present?
|
||||
email_data.dig('html_content', 'full')
|
||||
elsif email_data.dig('text_content', 'full').present?
|
||||
"<pre>#{ERB::Util.html_escape(email_data.dig('text_content', 'full'))}</pre>"
|
||||
else
|
||||
"<pre>#{ERB::Util.html_escape(forwarded_message.content.to_s)}</pre>"
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_email_data
|
||||
data = email_data.dup || {}
|
||||
data['html_content'] ||= {}
|
||||
data['text_content'] ||= {}
|
||||
data
|
||||
end
|
||||
|
||||
def strip_markdown(text)
|
||||
return '' if text.blank?
|
||||
|
||||
text.gsub(/\*\*?(.*?)\*\*?|_(.*?)_/) { |_m| ::Regexp.last_match(1) || ::Regexp.last_match(2) }
|
||||
end
|
||||
|
||||
def convert_markdown_to_html(text)
|
||||
return '' if text.blank?
|
||||
|
||||
html = text.to_s
|
||||
html = html.gsub(/\*\*?(.*?)\*\*?/) do |m|
|
||||
m.start_with?('**') ? "<b>#{::Regexp.last_match(1)}</b>" : "<i>#{::Regexp.last_match(1)}</i>"
|
||||
end
|
||||
|
||||
html.gsub(/_(.*?)_/, '<i>\1</i>')
|
||||
end
|
||||
|
||||
def extract_email(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
from_field =~ /<(.*)>/ ? ::Regexp.last_match(1) : from_field
|
||||
{ content_attributes: { forwarded_message_id: @message_id, email: prepare_email_data } }
|
||||
end
|
||||
|
||||
def forwarded_message
|
||||
@@ -167,38 +276,4 @@ class Messages::ForwardedMessageBuilder
|
||||
def basic_attributes
|
||||
{ content_attributes: { forwarded_message_id: @message_id } }
|
||||
end
|
||||
|
||||
def formatted_info
|
||||
{
|
||||
from: parse_from_field(extract_from_field),
|
||||
date: format_date_string(email_data['date'] || ''),
|
||||
subject: email_data['subject'] || '',
|
||||
to: email_data['to']&.first || ''
|
||||
}
|
||||
end
|
||||
|
||||
def extract_from_field
|
||||
email_data['from']&.first.to_s
|
||||
end
|
||||
|
||||
def parse_from_field(from_field)
|
||||
return '' if from_field.blank?
|
||||
|
||||
if from_field =~ /(.*)<(.*)>/
|
||||
"#{::Regexp.last_match(1).strip} <#{::Regexp.last_match(2).strip}>"
|
||||
else
|
||||
from_field
|
||||
end
|
||||
end
|
||||
|
||||
def format_date_string(date_str)
|
||||
return '' if date_str.blank?
|
||||
|
||||
begin
|
||||
parsed_date = DateTime.now # Use current date
|
||||
parsed_date.strftime('%a, %b %-d, %Y at %-l:%M %p')
|
||||
rescue StandardError
|
||||
date_str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -53,7 +53,7 @@ const currentChat = useMapGetter('getSelectedChat');
|
||||
const currentUser = useMapGetter('getCurrentUser');
|
||||
const globalConfig = useMapGetter('globalConfig/get');
|
||||
|
||||
const fromEmail = computed(() => props.message?.to?.[0] || props.inbox?.email);
|
||||
const fromEmail = computed(() => props.inbox?.email);
|
||||
|
||||
const fullHTML = computed(() => {
|
||||
return (
|
||||
|
||||
+1
-7
@@ -39,15 +39,9 @@ const showQuotedMessage = ref(false);
|
||||
/>
|
||||
<div class="px-4 pb-4 flex flex-col gap-2">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<div
|
||||
class="h-px w-20 bg-n-alpha-2 border-t border-dashed border-n-slate-12"
|
||||
/>
|
||||
<span class="font-semibold text-sm text-n-slate-12">
|
||||
<span class="text-sm text-n-slate-12">
|
||||
{{ t('FORWARD_MESSAGE_FORM.FORWARDED_MESSAGE') }}
|
||||
</span>
|
||||
<div
|
||||
class="h-px w-20 bg-n-alpha-2 border-t border-dashed border-n-slate-12"
|
||||
/>
|
||||
</div>
|
||||
<EmailMeta />
|
||||
</div>
|
||||
|
||||
@@ -245,7 +245,7 @@
|
||||
"FORWARD_MESSAGE_FORM": {
|
||||
"FROM": "From :",
|
||||
"EMAIL_EDITOR_PLACEHOLDER": "Write your message here...",
|
||||
"FORWARDED_MESSAGE": "Forwarded message",
|
||||
"FORWARDED_MESSAGE": "---------- Forwarded message ----------",
|
||||
"SHOW_QUOTED_TEXT": "Show quoted text",
|
||||
"HIDE_QUOTED_TEXT": "Hide quoted text",
|
||||
"CONTACT_SEARCH": {
|
||||
|
||||
Reference in New Issue
Block a user