diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb index e11156999..2a622ae0b 100644 --- a/app/helpers/formatting_helper.rb +++ b/app/helpers/formatting_helper.rb @@ -14,7 +14,7 @@ module FormattingHelper end def status_content_format(status) - html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : [])) + html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []), content_type: status.content_type) end def account_bio_format(account) diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb new file mode 100644 index 000000000..5ce87d306 --- /dev/null +++ b/app/lib/advanced_text_formatter.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +class AdvancedTextFormatter < TextFormatter + class HTMLRenderer < Redcarpet::Render::HTML + def initialize(options, &block) + super(options) + @format_link = block + end + + def block_code(code, _language) + <<~HTML.squish +
#{h(code).gsub("\n", '
')}
+ HTML
+ end
+
+ def autolink(link, link_type)
+ return link if link_type == :email
+ @format_link.call(link)
+ end
+ end
+
+ # @param [String] text
+ # @param [Hash] options
+ # @option options [Boolean] :multiline
+ # @option options [Boolean] :with_domains
+ # @option options [Boolean] :with_rel_me
+ # @option options [Arraytext
' + end + end + + context 'given text containing line feeds' do + let(:text) { "line\nfeed" } + + it 'removes line feeds' do + is_expected.not_to include "\n" + end + end + + context 'given some inline code using backticks' do + let(:text) { 'test `foo` bar' } + + it 'formats code using' do
+ is_expected.to include 'test foo
bar'
+ end
+ end
+
+ context 'given some quote' do
+ let(:text) { "> foo\n\nbar" }
+
+ it 'formats code using ' do
+ is_expected.to include 'foo
'
+ end
+ end
+
+ context 'given text containing linkable mentions' do
+ let(:preloaded_accounts) { [Fabricate(:account, username: 'alice')] }
+ let(:text) { '@alice' }
+
+ it 'creates a mention link' do
+ is_expected.to include '@alice'
+ end
+ end
+
+ context 'given text containing unlinkable mentions' do
+ let(:preloaded_accounts) { [] }
+ let(:text) { '@alice' }
+
+ it 'does not create a mention link' do
+ is_expected.to include '@alice'
+ end
+ end
+
+ context 'given a stand-alone medium URL' do
+ let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
+ end
+ end
+
+ context 'given a stand-alone google URL' do
+ let(:text) { 'http://google.com' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="http://google.com"'
+ end
+ end
+
+ context 'given a stand-alone URL with a newer TLD' do
+ let(:text) { 'http://example.gay' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="http://example.gay"'
+ end
+ end
+
+ context 'given a stand-alone IDN URL' do
+ let(:text) { 'https://nic.みんな/' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://nic.みんな/"'
+ end
+
+ it 'has display URL' do
+ is_expected.to include 'nic.みんな/'
+ end
+ end
+
+ context 'given a URL with a trailing period' do
+ let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
+
+ it 'matches the full URL but not the period' do
+ is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
+ end
+ end
+
+ context 'given a URL enclosed with parentheses' do
+ let(:text) { '(http://google.com/)' }
+
+ it 'matches the full URL but not the parentheses' do
+ is_expected.to include 'href="http://google.com/"'
+ end
+ end
+
+ context 'given a URL with a trailing exclamation point' do
+ let(:text) { 'http://www.google.com!' }
+
+ it 'matches the full URL but not the exclamation point' do
+ is_expected.to include 'href="http://www.google.com"'
+ end
+ end
+
+ context 'given a URL with a trailing single quote' do
+ let(:text) { "http://www.google.com'" }
+
+ it 'matches the full URL but not the single quote' do
+ is_expected.to include 'href="http://www.google.com"'
+ end
+ end
+ end
+
+ context 'given a URL with a trailing angle bracket' do
+ let(:text) { 'http://www.google.com>' }
+
+ it 'matches the full URL but not the angle bracket' do
+ is_expected.to include 'href="http://www.google.com"'
+ end
+ end
+
+ context 'given a URL with a query string' do
+ context 'with escaped unicode character' do
+ let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
+ end
+ end
+
+ context 'with unicode character' do
+ let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"'
+ end
+ end
+
+ context 'with unicode character at the end' do
+ let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
+ end
+ end
+
+ context 'with escaped and not escaped unicode characters' do
+ let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
+
+ it 'preserves escaped unicode characters' do
+ is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"'
+ end
+ end
+
+ context 'given a URL with parentheses in it' do
+ let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
+
+ it 'matches the full URL' do
+ is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
+ end
+ end
+
+ context 'given a URL in quotation marks' do
+ let(:text) { '"https://example.com/"' }
+
+ it 'does not match the quotation marks' do
+ is_expected.to include 'href="https://example.com/"'
+ end
+ end
+
+ context 'given a URL in angle brackets' do
+ let(:text) { ' ' }
+
+ it 'does not match the angle brackets' do
+ is_expected.to include 'href="https://example.com/"'
+ end
+ end
+
+ context 'given a URL containing unsafe code (XSS attack, invisible part)' do
+ let(:text) { %q{http://example.com/blahblahblahblah/a} }
+
+ it 'does not include the HTML in the URL' do
+ is_expected.to include '"http://example.com/blahblahblahblah/a"'
+ end
+
+ it 'does not include a script tag' do
+ is_expected.to_not include '' }
+
+ it 'does not include a script tag' do
+ is_expected.to_not include '