QuestionMarkdown adjustments

* Using `Redcarpet::Render::StripDown` as base now
* Support for autolinks and named links in Markdown
* Named links in questions just return the actual link as text
* Fixed codestyle in files
This commit is contained in:
Andreas Nedbal 2022-01-16 22:13:07 +01:00 committed by Andreas Nedbal
parent 0a52c09684
commit 3bdca34c2d
3 changed files with 93 additions and 66 deletions

View File

@ -1,10 +1,16 @@
require 'uri' # frozen_string_literal: true
class QuestionMarkdown < Redcarpet::Render::Base require "uri"
class QuestionMarkdown < Redcarpet::Render::StripDown
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include SharedMarkers include SharedMarkers
def paragraph(text) def paragraph(text)
"<p>#{text}</p>" "<p>#{text}</p>"
end end
end
def link(link, _title, _content)
process_link(link)
end
end

View File

@ -1,15 +1,34 @@
# frozen_string_literal: true
module SharedMarkers module SharedMarkers
include ActionView::Helpers::TagHelper include ActionView::Helpers::TagHelper
def autolink(link, _link_type) def process_link(link, text = nil)
href = if ALLOWED_HOSTS_IN_MARKDOWN.include?(URI(link).host) href = if ALLOWED_HOSTS_IN_MARKDOWN.include?(URI(link).host) || URI(link).relative?
link link
else else
linkfilter_path(url: link) linkfilter_path(url: link)
end end
content_tag(:a, link, href: href, target: "_blank", rel: "nofollow") options = { href: href }
unless URI(link).relative?
options = options.merge({
target: "_blank",
rel: "nofollow"
})
end
content_tag(:a, text.nil? ? link : text, options)
rescue rescue
link link
end end
end
def autolink(link, _link_type)
process_link(link)
end
def link(link, _title, content)
process_link(link, content)
end
end

View File

@ -1,124 +1,126 @@
# frozen_string_literal: true
require "rails_helper" require "rails_helper"
describe MarkdownHelper, :type => :helper do describe MarkdownHelper, type: :helper do
before do before do
stub_const("APP_CONFIG", { stub_const("APP_CONFIG", {
'hostname' => 'example.com', "hostname" => "example.com",
'https' => true, "https" => true,
'items_per_page' => 5, "items_per_page" => 5,
'allowed_hosts' => [ "allowed_hosts" => [
'twitter.com' "twitter.com"
] ]
}) })
end end
describe '#markdown' do describe "#markdown" do
it 'should return the expected text' do it "should return the expected text" do
expect(markdown('**Strong text**')).to eq('<p><strong>Strong text</strong></p>') expect(markdown("**Strong text**")).to eq("<p><strong>Strong text</strong></p>")
end end
it 'should transform mentions into links' do it "should transform mentions into links" do
expect(markdown('@jake_weary')).to eq('<p><a href="/jake_weary">@jake_weary</a></p>') expect(markdown("@jake_weary")).to eq('<p><a href="/jake_weary">@jake_weary</a></p>')
end end
end end
describe '#strip_markdown' do describe "#strip_markdown" do
it 'should not return formatted text' do it "should not return formatted text" do
expect(strip_markdown('**Strong text**')).to eq('Strong text') expect(strip_markdown("**Strong text**".dup)).to eq("Strong text")
end end
end end
describe '#twitter_markdown' do describe "#twitter_markdown" do
context 'mentioned user has Twitter connected' do context "mentioned user has Twitter connected" do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:service) { Services::Twitter.create(user: user) } let(:service) { Services::Twitter.create(user: user) }
before do before do
service.nickname = 'test' service.nickname = "test"
service.save! service.save!
end end
it 'should transform a internal mention to a Twitter mention' do it "should transform a internal mention to a Twitter mention" do
expect(twitter_markdown("@#{user.screen_name}")).to eq('@test') expect(twitter_markdown("@#{user.screen_name}")).to eq("@test")
end end
end end
context 'mentioned user doesnt have Twitter connected' do context "mentioned user doesnt have Twitter connected" do
it 'should not transform the mention' do it "should not transform the mention" do
expect(twitter_markdown('@test')).to eq('test') expect(twitter_markdown("@test")).to eq("test")
end end
end end
end end
describe '#question_markdown' do describe "#question_markdown" do
it 'should link allowed links without the linkfilter' do it "should link allowed links without the linkfilter" do
expect(question_markdown('https://twitter.com/retrospring')).to eq('<p><a href="https://twitter.com/retrospring" target="_blank" rel="nofollow">https://twitter.com/retrospring</a></p>') expect(question_markdown("https://twitter.com/retrospring")).to eq('<p><a href="https://twitter.com/retrospring" target="_blank" rel="nofollow">https://twitter.com/retrospring</a></p>')
end end
it 'should link untrusted links with the linkfilter' do it "should link untrusted links with the linkfilter" do
expect(question_markdown('https://rrerr.net')).to eq('<p><a href="/linkfilter?url=https%3A%2F%2Frrerr.net" target="_blank" rel="nofollow">https://rrerr.net</a></p>') expect(question_markdown("https://rrerr.net")).to eq('<p><a href="/linkfilter?url=https%3A%2F%2Frrerr.net" target="_blank" rel="nofollow">https://rrerr.net</a></p>')
end end
it 'should not process any markup aside of links' do it "should not process any markup aside of links" do
expect(question_markdown('**your account has been disabled**, [click here to enable it again](https://evil.example.com)')). to eq('<p>**your account has been disabled**, [click here to enable it again](<a href="/linkfilter?url=https%3A%2F%2Fevil.example.com" target="_blank" rel="nofollow">https://evil.example.com</a>)</p>') expect(question_markdown("**your account has been disabled**, [click here to enable it again](https://evil.example.com)")).to eq('<p>your account has been disabled, <a href="/linkfilter?url=https%3A%2F%2Fevil.example.com" target="_blank" rel="nofollow">https://evil.example.com</a></p>')
end end
it 'should not raise an exception if an invalid link is processed' do it "should not raise an exception if an invalid link is processed" do
expect{ question_markdown('https://example.com/example.質問') }.not_to raise_error expect { question_markdown("https://example.com/example.質問") }.not_to raise_error
end end
it 'should not process invalid links' do it "should not process invalid links" do
expect(question_markdown('https://example.com/example.質問')).to eq('<p>https://example.com/example.質問</p>') expect(question_markdown("https://example.com/example.質問")).to eq("<p>https://example.com/example.質問</p>")
end end
end end
describe '#raw_markdown' do describe "#raw_markdown" do
it 'should return the expected text' do it "should return the expected text" do
expect(raw_markdown('# Heading')).to eq("<h1>Heading</h1>\n") expect(raw_markdown("# Heading")).to eq("<h1>Heading</h1>\n")
end end
end end
describe '#get_markdown' do describe "#get_markdown" do
it 'should return the contents of the specified file' do it "should return the contents of the specified file" do
expect(get_markdown('spec/fixtures/markdown/test.md')).to eq("# Heading") expect(get_markdown("spec/fixtures/markdown/test.md")).to eq("# Heading")
end end
it 'should return an error message on error' do it "should return an error message on error" do
expect(get_markdown('non/existant/path.md')).to eq("# Error reading #{Rails.root}/non/existant/path.md") expect(get_markdown("non/existant/path.md")).to eq("# Error reading #{Rails.root.join('non/existant/path.md')}")
end end
end end
describe '#markdown_io' do describe "#markdown_io" do
it 'should return the expected text' do it "should return the expected text" do
expect(markdown_io('spec/fixtures/markdown/io.md')).to eq('<p><strong>Strong text</strong></p>') expect(markdown_io("spec/fixtures/markdown/io.md")).to eq("<p><strong>Strong text</strong></p>")
end end
end end
describe '#strip_markdown_io' do describe "#strip_markdown_io" do
it 'should return the expected text' do it "should return the expected text" do
expect(strip_markdown_io('spec/fixtures/markdown/io.md')).to eq('Strong text') expect(strip_markdown_io("spec/fixtures/markdown/io.md")).to eq("Strong text")
end end
end end
describe '#twitter_markdown_io' do describe "#twitter_markdown_io" do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:service) { Services::Twitter.create(user: user) } let(:service) { Services::Twitter.create(user: user) }
before do before do
user.screen_name = 'test' user.screen_name = "test"
user.save! user.save!
service.nickname = 'ObamaGaming' service.nickname = "ObamaGaming"
service.save! service.save!
end end
it 'should return the expected text' do it "should return the expected text" do
expect(twitter_markdown_io('spec/fixtures/markdown/twitter.md')).to eq('@ObamaGaming') expect(twitter_markdown_io("spec/fixtures/markdown/twitter.md")).to eq("@ObamaGaming")
end end
end end
describe '#raw_markdown_io' do describe "#raw_markdown_io" do
it 'should return the expected text' do it "should return the expected text" do
expect(raw_markdown_io('spec/fixtures/markdown/test.md')).to eq("<h1>Heading</h1>\n") expect(raw_markdown_io("spec/fixtures/markdown/test.md")).to eq("<h1>Heading</h1>\n")
end end
end end
end end