diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee index e9b19457dfe8..584ecfc70318 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_action/email_reply.coffee @@ -148,7 +148,7 @@ class EmailReply extends App.Controller selected = App.Utils.text2html(selected) if selected - quote_header = @fullQuoteHeader(article) + quote_header = @replyQuoteHeader(article) selected = "


#{quote_header}#{selected}

" @@ -196,7 +196,7 @@ class EmailReply extends App.Controller body = App.Utils.textCleanup(article.body) body = App.Utils.text2html(body) - quote_header = @fullQuoteHeader(article) + quote_header = App.FullQuoteHeader.fullQuoteHeaderForward(article) body = "
---Begin forwarded message:---

#{quote_header}#{body}

" @@ -339,7 +339,7 @@ class EmailReply extends App.Controller true - @fullQuoteHeader: (article) -> + @replyQuoteHeader: (article) -> if !App.Config.get('ui_ticket_zoom_article_email_full_quote_header') return '' @@ -348,4 +348,5 @@ class EmailReply extends App.Controller App.i18n.translateInline('On %s, %s wrote:', date, name) + '

' + App.Config.set('200-EmailReply', EmailReply, 'TicketZoomArticleAction') diff --git a/app/assets/javascripts/app/lib/app_post/full_quote_header.coffee b/app/assets/javascripts/app/lib/app_post/full_quote_header.coffee new file mode 100644 index 000000000000..63b7a77615d2 --- /dev/null +++ b/app/assets/javascripts/app/lib/app_post/full_quote_header.coffee @@ -0,0 +1,79 @@ +class App.FullQuoteHeader + @fullQuoteHeaderForward: (article) -> + if !App.Config.get('ui_ticket_zoom_article_email_full_quote_header') + return '' + + output = document.createElement('div') + + data = { + Subject: article.subject + Date: App.i18n.translateTimestamp(article.created_at) + From: @fullQuoteHeaderForwardFrom(article) + To: @fullQuoteHeaderForwardTo(article) + CC: @fullQuoteHeaderForwardCC(article) + } + + for key, value of data + if value + output.append App.i18n.translateContent(key), ': ', value, document.createElement('br') + + output.append document.createElement('br') + + output.outerHTML + + @fullQuoteHeaderForwardFrom: (article) -> + user_id = article.origin_by_id || article.created_by_id + + @fullQuoteHeaderEnsurePrivacy(user_id) || @fullQuoteHeaderEnsurePrivacy(article.from) || article.from + + @fullQuoteHeaderForwardTo: (article) -> + if article.type.name is 'email' || article.type.name is 'web' + @fullQuoteHeaderEnsurePrivacy(article.to) || article.to + else if article.sender.name is 'Customer' && article.type.name is 'phone' + if email_address_id = App.Group.findByAttribute('name', article.to)?.email_address_id + App.EmailAddress.find(email_address_id).displayName() + else + article.to + else if article.sender.name is 'Agent' && article.type.name is 'phone' + ticket = App.Ticket.find article.ticket_id + @fullQuoteHeaderEnsurePrivacy(ticket.customer_id) || @fullQuoteHeaderEnsurePrivacy(article.to) || article.to + else + article.to + + @fullQuoteHeaderForwardCC: (article) -> + return if !article.cc + + article + .cc + .split(',') + .map (elem) -> + elem.trim() + .map (elem) => + @fullQuoteHeaderEnsurePrivacy(elem) || elem + .join(', ') + + @fullQuoteHeaderEnsurePrivacyParseInput: (input) -> + switch typeof input + when 'number' + App.User.find input + when 'string' + if email = @fullQuoteHeaderExtractEmail(input) + App.User.findByAttribute('email', email) + when 'object' + input + + @fullQuoteHeaderEnsurePrivacy: (input) => + user = @fullQuoteHeaderEnsurePrivacyParseInput(input) + + return if !user + + output = "#{user.displayName()}" + + if !user.permission('ticket.agent') && user.email + output += " <#{user.email}>" + + output + + @fullQuoteHeaderExtractEmail: (input) -> + if match = input.match(/]+)(>?)/) + match[1] diff --git a/spec/system/ticket/update/full_quote_header_spec.rb b/spec/system/ticket/update/full_quote_header_spec.rb index 75519e83c311..4b0ae8f4a548 100644 --- a/spec/system/ticket/update/full_quote_header_spec.rb +++ b/spec/system/ticket/update/full_quote_header_spec.rb @@ -23,7 +23,7 @@ click_forward within(:richtext) do - expect(page).to contain_full_quote(ticket_article) + expect(page).to contain_full_quote(ticket_article).formatted_for(:forward) end end end @@ -33,7 +33,21 @@ highlight_and_click_reply within(:richtext) do - expect(page).to contain_full_quote(ticket_article) + expect(page).to contain_full_quote(ticket_article).formatted_for(:reply) + end + end + end + + context 'when customer is agent' do + let(:customer) { create(:agent) } + + it 'includes OP without email when forwarding' do + within(:active_content) do + click_forward + + within(:richtext) do + expect(page).to contain_full_quote(ticket_article).formatted_for(:forward).ensuring_privacy(true) + end end end end @@ -47,7 +61,7 @@ click_forward within(:richtext) do - expect(page).not_to contain_full_quote(ticket_article) + expect(page).not_to contain_full_quote(ticket_article).formatted_for(:forward) end end end @@ -57,7 +71,7 @@ highlight_and_click_reply within(:richtext) do - expect(page).not_to contain_full_quote(ticket_article) + expect(page).not_to contain_full_quote(ticket_article).formatted_for(:reply) end end end @@ -82,11 +96,65 @@ def highlight_and_click_reply define :contain_full_quote do match do - citation.has_text?(name) && citation.has_no_text?(email) && citation.has_text?(timestamp) + confirm_content && confirm_style end match_when_negated do - citation.has_no_text?(name) && citation.has_no_text?(email) && citation.has_no_text?(timestamp) + confirm_no_content + end + + # sets expected quote format + # @param [Symbol] :forward or :reply, defaults to :reply if not set + chain :formatted_for do |style| + @style = style + end + + def style + @style || :reply # rubocop:disable RSpec/InstanceVariable + end + + # sets expected privacy level + # @param [Boolean] defaults to false if not set + chain :ensuring_privacy do |flag| + @ensuring_privacy = flag + end + + def ensure_privacy? + @ensuring_privacy || false # rubocop:disable RSpec/InstanceVariable + end + + def confirm_content + case style + when :reply + confirm_content_reply + when :forward + confirm_content_forward + end + end + + def confirm_content_reply + citation.has_text?(name) && citation.has_no_text?(email) && citation.has_text?(timestamp_reply) + end + + def confirm_content_forward + if ensure_privacy? + citation.has_text?(name) && citation.has_no_text?(email) && citation.has_text?(timestamp_forward) + else + citation.has_text?(name) && citation.has_text?(email) && citation.has_text?(timestamp_forward) + end + end + + def confirm_no_content + citation.has_no_text?(name) && citation.has_no_text?(email) && citation.has_no_text?(timestamp_reply) && citation.has_no_text?(timestamp_forward) + end + + def confirm_style + case style + when :forward + citation.text.match?(/Subject(.+)\nDate(.+)/) + when :reply + citation.text.match?(/^On(.+)wrote:$/) + end end def citation @@ -101,11 +169,18 @@ def email expected.created_by.email end - def timestamp + def timestamp_reply expected .created_at .in_time_zone('Europe/London') .strftime('%A, %B %1d, %Y, %1I:%M:%S %p') end + + def timestamp_forward + expected + .created_at + .in_time_zone('Europe/London') + .strftime('%m/%d/%Y %H:%M') + end end end