Email parsing uses Traditional Markdown Linebreaks by default. Added JS tests for line breaks.
This commit is contained in:
parent
bfdbf373f3
commit
bb908d5913
|
@ -122,7 +122,8 @@ Discourse.Markdown = {
|
||||||
});
|
});
|
||||||
|
|
||||||
// newline prediction in trivial cases
|
// newline prediction in trivial cases
|
||||||
if (!Discourse.SiteSettings.traditional_markdown_linebreaks) {
|
var linebreaks = opts.traditional_markdown_linebreaks || Discourse.SiteSettings.traditional_markdown_linebreaks;
|
||||||
|
if (!linebreaks) {
|
||||||
converter.hooks.chain("preConversion", function(text) {
|
converter.hooks.chain("preConversion", function(text) {
|
||||||
return text.replace(/(^[\w<][^\n]*\n+)/gim, function(t) {
|
return text.replace(/(^[\w<][^\n]*\n+)/gim, function(t) {
|
||||||
if (t.match(/\n{2}/gim)) return t;
|
if (t.match(/\n{2}/gim)) return t;
|
||||||
|
|
|
@ -34,8 +34,8 @@ class Post < ActiveRecord::Base
|
||||||
|
|
||||||
validates_with ::Validators::PostValidator
|
validates_with ::Validators::PostValidator
|
||||||
|
|
||||||
# We can pass a hash of image sizes when saving to prevent crawling those images
|
# We can pass several creating options to a post via attributes
|
||||||
attr_accessor :image_sizes, :quoted_post_numbers, :no_bump, :invalidate_oneboxes
|
attr_accessor :image_sizes, :quoted_post_numbers, :no_bump, :invalidate_oneboxes, :cooking_options
|
||||||
|
|
||||||
SHORT_POST_CHARS = 1200
|
SHORT_POST_CHARS = 1200
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,8 @@ module Email
|
||||||
creator = PostCreator.new(email_log.user,
|
creator = PostCreator.new(email_log.user,
|
||||||
raw: @body,
|
raw: @body,
|
||||||
topic_id: @email_log.topic_id,
|
topic_id: @email_log.topic_id,
|
||||||
reply_to_post_number: @email_log.post.post_number)
|
reply_to_post_number: @email_log.post.post_number,
|
||||||
|
cooking_options: {traditional_markdown_linebreaks: true})
|
||||||
|
|
||||||
creator.create
|
creator.create
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,11 +32,13 @@ class PostCreator
|
||||||
# target_usernames - comma delimited list of usernames for membership (private message)
|
# target_usernames - comma delimited list of usernames for membership (private message)
|
||||||
# target_group_names - comma delimited list of groups for membership (private message)
|
# target_group_names - comma delimited list of groups for membership (private message)
|
||||||
# meta_data - Topic meta data hash
|
# meta_data - Topic meta data hash
|
||||||
|
# cooking_options - Options for rendering the text
|
||||||
|
#
|
||||||
def initialize(user, opts)
|
def initialize(user, opts)
|
||||||
# TODO: we should reload user in case it is tainted, should take in a user_id as opposed to user
|
# TODO: we should reload user in case it is tainted, should take in a user_id as opposed to user
|
||||||
# If we don't do this we introduce a rather risky dependency
|
# If we don't do this we introduce a rather risky dependency
|
||||||
@user = user
|
@user = user
|
||||||
@opts = opts
|
@opts = opts || {}
|
||||||
@spam = false
|
@spam = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -87,14 +89,17 @@ class PostCreator
|
||||||
end
|
end
|
||||||
|
|
||||||
post.post_number ||= Topic.next_post_number(post.topic_id, post.reply_to_post_number.present?)
|
post.post_number ||= Topic.next_post_number(post.topic_id, post.reply_to_post_number.present?)
|
||||||
post.cooked ||= post.cook(post.raw, topic_id: post.topic_id)
|
|
||||||
|
cooking_options = post.cooking_options || {}
|
||||||
|
cooking_options[:topic_id] = post.topic_id
|
||||||
|
|
||||||
|
post.cooked ||= post.cook(post.raw, cooking_options)
|
||||||
post.sort_order = post.post_number
|
post.sort_order = post.post_number
|
||||||
DiscourseEvent.trigger(:before_create_post, post)
|
DiscourseEvent.trigger(:before_create_post, post)
|
||||||
post.last_version_at ||= Time.now
|
post.last_version_at ||= Time.now
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.after_create_tasks(post)
|
def self.after_create_tasks(post)
|
||||||
Rails.logger.info (">" * 30) + "#{post.no_bump} #{post.created_at}"
|
|
||||||
# Update attributes on the topic - featured users and last posted.
|
# Update attributes on the topic - featured users and last posted.
|
||||||
attrs = {last_posted_at: post.created_at, last_post_user_id: post.user_id}
|
attrs = {last_posted_at: post.created_at, last_post_user_id: post.user_id}
|
||||||
attrs[:bumped_at] = post.created_at unless post.no_bump
|
attrs[:bumped_at] = post.created_at unless post.no_bump
|
||||||
|
@ -183,14 +188,13 @@ class PostCreator
|
||||||
user: @user,
|
user: @user,
|
||||||
reply_to_post_number: @opts[:reply_to_post_number])
|
reply_to_post_number: @opts[:reply_to_post_number])
|
||||||
|
|
||||||
post.post_type = @opts[:post_type] if @opts[:post_type].present?
|
# Attributes we pass through to the post instance if present
|
||||||
post.no_bump = @opts[:no_bump] if @opts[:no_bump].present?
|
[:post_type, :no_bump, :cooking_options, :image_sizes, :acting_user, :invalidate_oneboxes].each do |a|
|
||||||
post.extract_quoted_post_numbers
|
post.send("#{a}=", @opts[a]) if @opts[a].present?
|
||||||
post.acting_user = @opts[:acting_user] if @opts[:acting_user].present?
|
end
|
||||||
post.created_at = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present?
|
|
||||||
|
|
||||||
post.image_sizes = @opts[:image_sizes] if @opts[:image_sizes].present?
|
post.extract_quoted_post_numbers
|
||||||
post.invalidate_oneboxes = @opts[:invalidate_oneboxes] if @opts[:invalidate_oneboxes].present?
|
post.created_at = Time.zone.parse(@opts[:created_at].to_s) if @opts[:created_at].present?
|
||||||
@post = post
|
@post = post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -40,16 +40,6 @@ stripped from my reply?")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "with a content boundary" do
|
|
||||||
let(:bounded_email) { File.read("#{Rails.root}/spec/fixtures/emails/boundary.eml") }
|
|
||||||
let(:receiver) { Email::Receiver.new(bounded_email) }
|
|
||||||
|
|
||||||
it "does something" do
|
|
||||||
receiver.process
|
|
||||||
expect(receiver.body).to eq("I'll look into it, thanks!")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "with a valid email" do
|
describe "with a valid email" do
|
||||||
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
|
let(:reply_key) { "59d8df8370b7e95c5a49fbf86aeb2c93" }
|
||||||
let(:valid_reply) { File.read("#{Rails.root}/spec/fixtures/emails/valid_reply.eml") }
|
let(:valid_reply) { File.read("#{Rails.root}/spec/fixtures/emails/valid_reply.eml") }
|
||||||
|
@ -83,7 +73,11 @@ greatest show ever created. Everyone should watch it.
|
||||||
EmailLog.expects(:for).with(reply_key).returns(email_log)
|
EmailLog.expects(:for).with(reply_key).returns(email_log)
|
||||||
|
|
||||||
creator = mock
|
creator = mock
|
||||||
PostCreator.expects(:new).with(instance_of(User), has_entry(raw: reply_body)).returns(creator)
|
PostCreator.expects(:new).with(instance_of(User),
|
||||||
|
has_entries(raw: reply_body,
|
||||||
|
cooking_options: {traditional_markdown_linebreaks: true}))
|
||||||
|
.returns(creator)
|
||||||
|
|
||||||
creator.expects(:create)
|
creator.expects(:create)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,20 @@ describe PostCreator do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "cooking options" do
|
||||||
|
let(:raw) { "this is my awesome message body hello world" }
|
||||||
|
|
||||||
|
it "passes the cooking options through correctly" do
|
||||||
|
creator = PostCreator.new(user,
|
||||||
|
title: 'hi there welcome to my topic',
|
||||||
|
raw: raw,
|
||||||
|
cooking_options: { traditional_markdown_linebreaks: true })
|
||||||
|
|
||||||
|
Post.any_instance.expects(:cook).with(raw, has_key(:traditional_markdown_linebreaks)).returns(raw)
|
||||||
|
creator.create
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# integration test ... minimise db work
|
# integration test ... minimise db work
|
||||||
context 'private message' do
|
context 'private message' do
|
||||||
let(:target_user1) { Fabricate(:coding_horror) }
|
let(:target_user1) { Fabricate(:coding_horror) }
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
/*global sanitizeHtml:true */
|
/*global sanitizeHtml:true */
|
||||||
|
|
||||||
module("Discourse.Markdown");
|
module("Discourse.Markdown", {
|
||||||
|
setup: function() {
|
||||||
|
Discourse.SiteSettings.traditional_markdown_linebreaks = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var cooked = function(input, expected, text) {
|
var cooked = function(input, expected, text) {
|
||||||
equal(Discourse.Markdown.cook(input, {mentionLookup: false }), expected, text);
|
equal(Discourse.Markdown.cook(input, {mentionLookup: false }), expected, text);
|
||||||
|
@ -12,7 +16,24 @@ var cookedOptions = function(input, opts, expected, text) {
|
||||||
|
|
||||||
test("basic cooking", function() {
|
test("basic cooking", function() {
|
||||||
cooked("hello", "<p>hello</p>", "surrounds text with paragraphs");
|
cooked("hello", "<p>hello</p>", "surrounds text with paragraphs");
|
||||||
cooked("1\n2\n3", "<p>1 <br>\n2 <br>\n3</p>", "automatically handles trivial newlines");
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Line Breaks", function() {
|
||||||
|
|
||||||
|
var input = "1\n2\n3";
|
||||||
|
cooked(input, "<p>1 <br>\n2 <br>\n3</p>", "automatically handles trivial newlines");
|
||||||
|
|
||||||
|
var traditionalOutput = "<p>1\n2\n3</p>";
|
||||||
|
|
||||||
|
cookedOptions(input,
|
||||||
|
{traditional_markdown_linebreaks: true},
|
||||||
|
traditionalOutput,
|
||||||
|
"It supports traditional markdown via an option")
|
||||||
|
|
||||||
|
Discourse.SiteSettings.traditional_markdown_linebreaks = true;
|
||||||
|
cooked(input, traditionalOutput, "It supports traditional markdown via a Site Setting")
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Links", function() {
|
test("Links", function() {
|
||||||
|
|
Loading…
Reference in New Issue