FIX: allow emoji class when crawling embedded content, add rspc-html-matchers
This commit is contained in:
parent
85daf56ed4
commit
b7803fc68e
1
Gemfile
1
Gemfile
|
@ -120,6 +120,7 @@ group :test, :development do
|
||||||
gem 'simplecov', require: false
|
gem 'simplecov', require: false
|
||||||
gem 'timecop'
|
gem 'timecop'
|
||||||
gem 'rspec-given'
|
gem 'rspec-given'
|
||||||
|
gem 'rspec-html-matchers'
|
||||||
gem 'pry-nav'
|
gem 'pry-nav'
|
||||||
gem 'spork-rails'
|
gem 'spork-rails'
|
||||||
gem 'byebug', require: ENV['RM_INFO'].nil?
|
gem 'byebug', require: ENV['RM_INFO'].nil?
|
||||||
|
|
|
@ -337,6 +337,9 @@ GEM
|
||||||
rspec-given (3.5.4)
|
rspec-given (3.5.4)
|
||||||
given_core (= 3.5.4)
|
given_core (= 3.5.4)
|
||||||
rspec (>= 2.12)
|
rspec (>= 2.12)
|
||||||
|
rspec-html-matchers (0.7.0)
|
||||||
|
nokogiri (~> 1)
|
||||||
|
rspec (~> 3)
|
||||||
rspec-logsplit (0.1.3)
|
rspec-logsplit (0.1.3)
|
||||||
rspec-mocks (3.2.1)
|
rspec-mocks (3.2.1)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
@ -511,6 +514,7 @@ DEPENDENCIES
|
||||||
rmmseg-cpp
|
rmmseg-cpp
|
||||||
rspec (~> 3.2.0)
|
rspec (~> 3.2.0)
|
||||||
rspec-given
|
rspec-given
|
||||||
|
rspec-html-matchers
|
||||||
rspec-rails
|
rspec-rails
|
||||||
rtlit
|
rtlit
|
||||||
ruby-readability
|
ruby-readability
|
||||||
|
|
|
@ -53,6 +53,10 @@
|
||||||
{{embedding-setting field="embed_blacklist_selector"
|
{{embedding-setting field="embed_blacklist_selector"
|
||||||
value=embedding.embed_blacklist_selector
|
value=embedding.embed_blacklist_selector
|
||||||
placeholder=".ad-unit, header"}}
|
placeholder=".ad-unit, header"}}
|
||||||
|
|
||||||
|
{{embedding-setting field="embed_classname_whitelist"
|
||||||
|
value=embedding.embed_classname_whitelist
|
||||||
|
placeholder="emoji, classname"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class='embedding-secondary'>
|
<div class='embedding-secondary'>
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Embedding < OpenStruct
|
||||||
embed_truncate
|
embed_truncate
|
||||||
embed_whitelist_selector
|
embed_whitelist_selector
|
||||||
embed_blacklist_selector
|
embed_blacklist_selector
|
||||||
|
embed_classname_whitelist
|
||||||
feed_polling_enabled
|
feed_polling_enabled
|
||||||
feed_polling_url
|
feed_polling_url
|
||||||
embed_username_key_from_feed)
|
embed_username_key_from_feed)
|
||||||
|
|
|
@ -69,12 +69,13 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
original_uri = URI.parse(url)
|
original_uri = URI.parse(url)
|
||||||
opts = {
|
opts = {
|
||||||
tags: %w[div p code pre h1 h2 h3 b em i strong a img ul li ol blockquote],
|
tags: %w[div p code pre h1 h2 h3 b em i strong a img ul li ol blockquote],
|
||||||
attributes: %w[href src],
|
attributes: %w[href src class],
|
||||||
remove_empty_nodes: false
|
remove_empty_nodes: false
|
||||||
}
|
}
|
||||||
|
|
||||||
opts[:whitelist] = SiteSetting.embed_whitelist_selector if SiteSetting.embed_whitelist_selector.present?
|
opts[:whitelist] = SiteSetting.embed_whitelist_selector if SiteSetting.embed_whitelist_selector.present?
|
||||||
opts[:blacklist] = SiteSetting.embed_blacklist_selector if SiteSetting.embed_blacklist_selector.present?
|
opts[:blacklist] = SiteSetting.embed_blacklist_selector if SiteSetting.embed_blacklist_selector.present?
|
||||||
|
embed_classname_whitelist = SiteSetting.embed_classname_whitelist if SiteSetting.embed_classname_whitelist.present?
|
||||||
|
|
||||||
doc = Readability::Document.new(open(url).read, opts)
|
doc = Readability::Document.new(open(url).read, opts)
|
||||||
|
|
||||||
|
@ -96,6 +97,16 @@ class TopicEmbed < ActiveRecord::Base
|
||||||
# If there is a mistyped URL, just do nothing
|
# If there is a mistyped URL, just do nothing
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# only allow classes in the whitelist
|
||||||
|
allowed_classes = if embed_classname_whitelist.blank? then [] else embed_classname_whitelist.split(/[ ,]+/i) end
|
||||||
|
doc.search('[class]:not([class=""])').each do |node|
|
||||||
|
classes = node[:class].split(' ').select{ |classname| allowed_classes.include?(classname) }
|
||||||
|
if classes.length === 0
|
||||||
|
node.delete('class')
|
||||||
|
else
|
||||||
|
node[:class] = classes.join(' ')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
[title, doc.to_html]
|
[title, doc.to_html]
|
||||||
|
|
|
@ -812,6 +812,9 @@ embedding:
|
||||||
embed_blacklist_selector:
|
embed_blacklist_selector:
|
||||||
default: ''
|
default: ''
|
||||||
hidden: true
|
hidden: true
|
||||||
|
embed_classname_whitelist:
|
||||||
|
default: 'emoji'
|
||||||
|
hidden: true
|
||||||
|
|
||||||
legal:
|
legal:
|
||||||
tos_url:
|
tos_url:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
require 'stringio'
|
||||||
|
|
||||||
describe TopicEmbed do
|
describe TopicEmbed do
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ describe TopicEmbed do
|
||||||
expect(post.cooked).to eq(post.raw)
|
expect(post.cooked).to eq(post.raw)
|
||||||
|
|
||||||
# It converts relative URLs to absolute
|
# It converts relative URLs to absolute
|
||||||
expect(post.cooked.start_with?("hello world new post <a href=\"http://eviltrout.com/hello\">hello</a> <img src=\"http://eviltrout.com/images/wat.jpg\">")).to eq(true)
|
expect(post.cooked).to have_tag('a', with: { href: 'http://eviltrout.com/hello' })
|
||||||
|
expect(post.cooked).to have_tag('img', with: { src: 'http://eviltrout.com/images/wat.jpg' })
|
||||||
|
|
||||||
expect(post.topic.has_topic_embed?).to eq(true)
|
expect(post.topic.has_topic_embed?).to eq(true)
|
||||||
expect(TopicEmbed.where(topic_id: post.topic_id)).to be_present
|
expect(TopicEmbed.where(topic_id: post.topic_id)).to be_present
|
||||||
|
@ -58,4 +60,78 @@ describe TopicEmbed do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '.find_remote' do
|
||||||
|
|
||||||
|
context 'post with allowed classes "foo" and "emoji"' do
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:url) { 'http://eviltrout.com/123' }
|
||||||
|
let(:contents) { "my normal size emoji <p class='foo'>Hi</p> <img class='emoji other foo' src='/images/smiley.jpg'>" }
|
||||||
|
let!(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||||
|
let!(:file) { StringIO.new }
|
||||||
|
|
||||||
|
content = ''
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
SiteSetting.stubs(:embed_classname_whitelist).returns 'emoji , foo'
|
||||||
|
file.stubs(:read).returns contents
|
||||||
|
TopicEmbed.stubs(:open).returns file
|
||||||
|
title, content = TopicEmbed.find_remote(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'img node has emoji class' do
|
||||||
|
expect(content).to have_tag('img', with: { class: 'emoji' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'img node has foo class' do
|
||||||
|
expect(content).to have_tag('img', with: { class: 'foo' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'p node has foo class' do
|
||||||
|
expect(content).to have_tag('p', with: { class: 'foo' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'nodes removes classes other than emoji' do
|
||||||
|
expect(content).to have_tag('img', without: { class: 'other' })
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'post with no allowed classes' do
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:url) { 'http://eviltrout.com/123' }
|
||||||
|
let(:contents) { "my normal size emoji <p class='foo'>Hi</p> <img class='emoji other foo' src='/images/smiley.jpg'>" }
|
||||||
|
let!(:embeddable_host) { Fabricate(:embeddable_host) }
|
||||||
|
let!(:file) { StringIO.new }
|
||||||
|
|
||||||
|
content = ''
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
SiteSetting.stubs(:embed_classname_whitelist).returns ' '
|
||||||
|
file.stubs(:read).returns contents
|
||||||
|
TopicEmbed.stubs(:open).returns file
|
||||||
|
title, content = TopicEmbed.find_remote(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'img node doesn\'t have emoji class' do
|
||||||
|
expect(content).to have_tag('img', without: { class: 'emoji' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'img node doesn\'t have foo class' do
|
||||||
|
expect(content).to have_tag('img', without: { class: 'foo' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'p node doesn\'t foo class' do
|
||||||
|
expect(content).to have_tag('p', without: { class: 'foo' })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'img node doesn\'t have other class' do
|
||||||
|
expect(content).to have_tag('img', without: { class: 'other' })
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -42,6 +42,7 @@ Spork.prefork do
|
||||||
config.fail_fast = ENV['RSPEC_FAIL_FAST'] == "1"
|
config.fail_fast = ENV['RSPEC_FAIL_FAST'] == "1"
|
||||||
config.include Helpers
|
config.include Helpers
|
||||||
config.include MessageBus
|
config.include MessageBus
|
||||||
|
config.include RSpecHtmlMatchers
|
||||||
config.mock_framework = :mocha
|
config.mock_framework = :mocha
|
||||||
config.order = 'random'
|
config.order = 'random'
|
||||||
config.infer_spec_type_from_file_location!
|
config.infer_spec_type_from_file_location!
|
||||||
|
|
Loading…
Reference in New Issue