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_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)
lines = body.scrub.lines.to_a
range_start = 0
range_end = 0
# If we started with a quote, skip it
lines.each_with_index do |l, idx|
break if 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+,? \d+,?.*wrote:/)
break unless line_is_quote?(l) or l =~ /^>/ or l.blank?
range_start = idx + 1
end
lines[range_start..-1].each_with_index do |l, idx|
break if line_is_quote?(l)
# Headers on subsequent lines
break if (0..2).all? { |off| lines[idx+off] =~ REPLYING_HEADER_REGEX }
# Headers on the same line
break if REPLYING_HEADER_LABELS.count { |lbl| l.include? lbl } >= 3
range_end = idx
range_end = range_start + idx
end
lines[0..range_end].join.strip
lines[range_start..range_end].join.strip
end
def wrap_body_in_quote(user_email)

View file

@ -42,15 +42,13 @@ describe Email::Receiver do
end
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
expect(test_parse_body(fixture_file("emails/hebrew.eml"))).to eq("שלום")
end
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
expect(test_parse_body(fixture_file("emails/big5.eml"))).to eq("媽!我上電視了!")
end
@ -149,12 +147,30 @@ the lazy dog. The quick brown fox jumps over the lazy dog."
)
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
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
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
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.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
@ -312,7 +328,7 @@ This is a link http://example.com"
receiver.process
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)
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