FIX: Allow emails to begin with a quote (but skip it!)

This commit is contained in:
Robin Ward 2015-11-18 15:22:50 -05:00
parent 85580cd243
commit 2196160549
3 changed files with 80 additions and 17 deletions

View file

@ -162,28 +162,38 @@ module Email
REPLYING_HEADER_LABELS = ['From', 'Sent', 'To', 'Subject', 'Reply To', 'Cc', 'Bcc', 'Date'] REPLYING_HEADER_LABELS = ['From', 'Sent', 'To', 'Subject', 'Reply To', 'Cc', 'Bcc', 'Date']
REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |lbl| "#{lbl}:" }) REPLYING_HEADER_REGEX = Regexp.union(REPLYING_HEADER_LABELS.map { |lbl| "#{lbl}:" })
def line_is_quote?(l)
l =~ /\A\s*\-{3,80}\s*\z/ ||
l =~ Regexp.new("\\A\\s*" + I18n.t('user_notifications.previous_discussion') + "\\s*\\Z") ||
(l =~ /via #{SiteSetting.title}(.*)\:$/) ||
# This one might be controversial but so many reply lines have years, times and end with a colon.
# Let's try it and see how well it works.
(l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) ||
(l =~ /On [\w, ]+\d+.*wrote:/)
end
def discourse_email_trimmer(body) def discourse_email_trimmer(body)
lines = body.scrub.lines.to_a lines = body.scrub.lines.to_a
range_start = 0
range_end = 0 range_end = 0
# If we started with a quote, skip it
lines.each_with_index do |l, idx| lines.each_with_index do |l, idx|
break if l =~ /\A\s*\-{3,80}\s*\z/ || break unless line_is_quote?(l) or l =~ /^>/ or l.blank?
l =~ Regexp.new("\\A\\s*" + I18n.t('user_notifications.previous_discussion') + "\\s*\\Z") || range_start = idx + 1
(l =~ /via #{SiteSetting.title}(.*)\:$/) || end
# This one might be controversial but so many reply lines have years, times and end with a colon.
# Let's try it and see how well it works. lines[range_start..-1].each_with_index do |l, idx|
(l =~ /\d{4}/ && l =~ /\d:\d\d/ && l =~ /\:$/) || break if line_is_quote?(l)
(l =~ /On \w+ \d+,? \d+,?.*wrote:/)
# Headers on subsequent lines # Headers on subsequent lines
break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX } break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX }
# Headers on the same line # Headers on the same line
break if REPLYING_HEADER_LABELS.count { |lbl| l.include? lbl } >= 3 break if REPLYING_HEADER_LABELS.count { |lbl| l.include? lbl } >= 3
range_end = range_start + idx
range_end = idx
end end
lines[0..range_end].join.strip lines[range_start..range_end].join.strip
end end
def wrap_body_in_quote(user_email) def wrap_body_in_quote(user_email)

View file

@ -42,15 +42,13 @@ describe Email::Receiver do
end end
it "supports a Hebrew reply" do it "supports a Hebrew reply" do
I18n.expects(:t).with('user_notifications.previous_discussion').returns('כלטוב') I18n.stubs(:t).with('user_notifications.previous_discussion').returns('כלטוב')
# The force_encoding call is only needed for the test - it is passed on fine to the cooked post # The force_encoding call is only needed for the test - it is passed on fine to the cooked post
expect(test_parse_body(fixture_file("emails/hebrew.eml"))).to eq("שלום") expect(test_parse_body(fixture_file("emails/hebrew.eml"))).to eq("שלום")
end end
it "supports a BIG5-encoded reply" do it "supports a BIG5-encoded reply" do
I18n.expects(:t).with('user_notifications.previous_discussion').returns('媽!我上電視了!')
# The force_encoding call is only needed for the test - it is passed on fine to the cooked post # The force_encoding call is only needed for the test - it is passed on fine to the cooked post
expect(test_parse_body(fixture_file("emails/big5.eml"))).to eq("媽!我上電視了!") expect(test_parse_body(fixture_file("emails/big5.eml"))).to eq("媽!我上電視了!")
end end
@ -149,12 +147,30 @@ the lazy dog. The quick brown fox jumps over the lazy dog."
) )
end end
it "can retrieve the first part of multiple replies" do
expect(test_parse_body(fixture_file("emails/inline_mixed.eml"))).to eq(
"The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog.
> First paragraph.
>
> Second paragraph.
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown"
)
end
it "should not include previous replies" do it "should not include previous replies" do
expect(test_parse_body(fixture_file("emails/previous_replies.eml"))).not_to match /Previous Replies/ expect(test_parse_body(fixture_file("emails/previous_replies.eml"))).not_to match(/Previous Replies/)
end end
it "strips iPhone signature" do it "strips iPhone signature" do
expect(test_parse_body(fixture_file("emails/iphone_signature.eml"))).not_to match /Sent from my iPhone/ expect(test_parse_body(fixture_file("emails/iphone_signature.eml"))).not_to match(/Sent from my iPhone/)
end end
it "properly renders email reply from gmail web client" do it "properly renders email reply from gmail web client" do
@ -288,7 +304,7 @@ This is a link http://example.com"
expect(topic.posts.count).to eq(start_count + 1) expect(topic.posts.count).to eq(start_count + 1)
expect(topic.posts.last.cooked.strip).to eq(fixture_file("emails/paragraphs.cooked").strip) expect(topic.posts.last.cooked.strip).to eq(fixture_file("emails/paragraphs.cooked").strip)
expect(topic.posts.last.cooked).not_to match /<br/ expect(topic.posts.last.cooked).not_to match(/<br/)
end end
end end
@ -312,7 +328,7 @@ This is a link http://example.com"
receiver.process receiver.process
expect(topic.posts.count).to eq(start_count + 1) expect(topic.posts.count).to eq(start_count + 1)
expect(topic.posts.last.cooked).to match /<img src=['"](\/uploads\/default\/original\/.+\.png)['"] width=['"]289['"] height=['"]126['"]>/ expect(topic.posts.last.cooked).to match(/<img src=['"](\/uploads\/default\/original\/.+\.png)['"] width=['"]289['"] height=['"]126['"]>/)
expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil) expect(Upload.find_by(sha1: upload_sha)).not_to eq(nil)
end end

37
spec/fixtures/emails/inline_mixed.eml vendored Normal file
View file

@ -0,0 +1,37 @@
In-Reply-To: <reply@discourse-app.mail>
References: <topic/36@discourse.techapj.com>
<5434ced4ee0f9_663fb0b5f76070593b@discourse-app.mail>
Date: Mon, 1 Dec 2014 20:48:40 +0530
Delivered-To: someone@googlemail.com
Subject: Re: [Discourse] [Meta] Testing reply via email
From: Walter White <walter.white@googlemail.com>
To: Discourse <reply@mail.com>
Content-Type: multipart/alternative; boundary=20cf30363f8522466905092920a6
--20cf30363f8522466905092920a6
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
On Wed, Oct 8, 2014 at 11:12 AM, techAPJ <info@unconfigured> wrote:
> techAPJ <https://meta.discourse.org/users/techapj>
> November 28
>
> Test reply.
>
> First paragraph.
>
> Second paragraph.
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown
fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog.
> First paragraph.
>
> Second paragraph.
The quick brown fox jumps over the lazy dog. The quick brown fox jumps over
the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown