Skip to content
Snippets Groups Projects
Commit 644296d6 authored by Brett Walker's avatar Brett Walker Committed by Douwe Maan
Browse files

Resolve "Wiki page attachments not rendered properly"

parent 734b8f93
No related branches found
No related tags found
1 merge request!10495Merge Requests - Assignee
Loading
Loading
@@ -8,8 +8,9 @@ module Banzai
#
# Based on Banzai::Filter::AutolinkFilter
#
# CommonMark does not allow spaces in the url portion of a link.
# For example, `[example](page slug)` is not valid. However,
# CommonMark does not allow spaces in the url portion of a link/url.
# For example, `[example](page slug)` is not valid.
# Neither is `![example](test image.jpg)`. However,
# in our wikis, we support (via RedCarpet) this type of link, allowing
# wiki pages to be easily linked by their title. This filter adds that functionality.
# The intent is for this to only be used in Wikis - in general, we want
Loading
Loading
@@ -20,10 +21,17 @@ module Banzai
 
# Pattern to match a standard markdown link
#
# Rubular: http://rubular.com/r/z9EAHxYmKI
LINK_PATTERN = /\[([^\]]+)\]\(([^)"]+)(?: \"([^\"]+)\")?\)/
# Text matching LINK_PATTERN inside these elements will not be linked
# Rubular: http://rubular.com/r/2EXEQ49rg5
LINK_OR_IMAGE_PATTERN = %r{
(?<preview_operator>!)?
\[(?<text>.+?)\]
\(
(?<new_link>.+?)
(?<title>\ ".+?")?
\)
}x
# Text matching LINK_OR_IMAGE_PATTERN inside these elements will not be linked
IGNORE_PARENTS = %w(a code kbd pre script style).to_set
 
# The XPath query to use for finding text nodes to parse.
Loading
Loading
@@ -38,7 +46,7 @@ module Banzai
doc.xpath(TEXT_QUERY).each do |node|
content = node.to_html
 
next unless content.match(LINK_PATTERN)
next unless content.match(LINK_OR_IMAGE_PATTERN)
 
html = spaced_link_filter(content)
 
Loading
Loading
@@ -53,25 +61,37 @@ module Banzai
private
 
def spaced_link_match(link)
match = LINK_PATTERN.match(link)
return link unless match && match[1] && match[2]
match = LINK_OR_IMAGE_PATTERN.match(link)
return link unless match
 
# escape the spaces in the url so that it's a valid markdown link,
# then run it through the markdown processor again, let it do its magic
text = match[1]
new_link = match[2].gsub(' ', '%20')
title = match[3] ? " \"#{match[3]}\"" : ''
html = Banzai::Filter::MarkdownFilter.call("[#{text}](#{new_link}#{title})", context)
html = Banzai::Filter::MarkdownFilter.call(transform_markdown(match), context)
 
# link is wrapped in a <p>, so strip that off
html.sub('<p>', '').chomp('</p>')
end
 
def spaced_link_filter(text)
Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_PATTERN) do |link, left:, right:|
Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_OR_IMAGE_PATTERN) do |link, left:, right:|
spaced_link_match(link)
end
end
def transform_markdown(match)
preview_operator, text, new_link, title = process_match(match)
"#{preview_operator}[#{text}](#{new_link}#{title})"
end
def process_match(match)
[
match[:preview_operator],
match[:text],
match[:new_link].gsub(' ', '%20'),
match[:title]
]
end
end
end
end
Loading
Loading
@@ -5,7 +5,7 @@ module Banzai
@filters ||= begin
super.insert_after(Filter::TableOfContentsFilter, Filter::GollumTagsFilter)
.insert_before(Filter::TaskListFilter, Filter::WikiLinkFilter)
.insert_before(Filter::WikiLinkFilter, Filter::SpacedLinkFilter)
.insert_before(Filter::VideoLinkFilter, Filter::SpacedLinkFilter)
end
end
end
Loading
Loading
Loading
Loading
@@ -3,49 +3,73 @@ require 'spec_helper'
describe Banzai::Filter::SpacedLinkFilter do
include FilterSpecHelper
 
let(:link) { '[example](page slug)' }
let(:link) { '[example](page slug)' }
let(:image) { '![example](img test.jpg)' }
 
it 'converts slug with spaces to a link' do
doc = filter("See #{link}")
context 'when a link is detected' do
it 'converts slug with spaces to a link' do
doc = filter("See #{link}")
 
expect(doc.at_css('a').text).to eq 'example'
expect(doc.at_css('a')['href']).to eq 'page%20slug'
expect(doc.at_css('p')).to eq nil
end
expect(doc.at_css('a').text).to eq 'example'
expect(doc.at_css('a')['href']).to eq 'page%20slug'
expect(doc.at_css('a')['title']).to be_nil
expect(doc.at_css('p')).to be_nil
end
 
it 'converts slug with spaces and a title to a link' do
link = '[example](page slug "title")'
doc = filter("See #{link}")
it 'converts slug with spaces and a title to a link' do
link = '[example](page slug "title")'
doc = filter("See #{link}")
 
expect(doc.at_css('a').text).to eq 'example'
expect(doc.at_css('a')['href']).to eq 'page%20slug'
expect(doc.at_css('a')['title']).to eq 'title'
expect(doc.at_css('p')).to eq nil
end
expect(doc.at_css('a').text).to eq 'example'
expect(doc.at_css('a')['href']).to eq 'page%20slug'
expect(doc.at_css('a')['title']).to eq 'title'
expect(doc.at_css('p')).to be_nil
end
 
it 'does nothing when markdown_engine is redcarpet' do
exp = act = link
expect(filter(act, markdown_engine: :redcarpet).to_html).to eq exp
end
it 'does nothing when markdown_engine is redcarpet' do
exp = act = link
expect(filter(act, markdown_engine: :redcarpet).to_html).to eq exp
end
it 'does nothing with empty text' do
link = '[](page slug)'
doc = filter("See #{link}")
expect(doc.at_css('a')).to be_nil
end
 
it 'does nothing with empty text' do
link = '[](page slug)'
doc = filter("See #{link}")
it 'does nothing with an empty slug' do
link = '[example]()'
doc = filter("See #{link}")
 
expect(doc.at_css('a')).to eq nil
expect(doc.at_css('a')).to be_nil
end
end
 
it 'does nothing with an empty slug' do
link = '[example]()'
doc = filter("See #{link}")
context 'when an image is detected' do
it 'converts slug with spaces to an iamge' do
doc = filter("See #{image}")
expect(doc.at_css('img')['src']).to eq 'img%20test.jpg'
expect(doc.at_css('img')['alt']).to eq 'example'
expect(doc.at_css('p')).to be_nil
end
it 'converts slug with spaces and a title to an image' do
image = '![example](img test.jpg "title")'
doc = filter("See #{image}")
 
expect(doc.at_css('a')).to eq nil
expect(doc.at_css('img')['src']).to eq 'img%20test.jpg'
expect(doc.at_css('img')['alt']).to eq 'example'
expect(doc.at_css('img')['title']).to eq 'title'
expect(doc.at_css('p')).to be_nil
end
end
 
it 'converts multiple URLs' do
link1 = '[first](slug one)'
link2 = '[second](http://example.com/slug two)'
doc = filter("See #{link1} and #{link2}")
doc = filter("See #{link1} and #{image} and #{link2}")
 
found_links = doc.css('a')
 
Loading
Loading
@@ -54,6 +78,12 @@ describe Banzai::Filter::SpacedLinkFilter do
expect(found_links[0]['href']).to eq 'slug%20one'
expect(found_links[1].text).to eq 'second'
expect(found_links[1]['href']).to eq 'http://example.com/slug%20two'
found_images = doc.css('img')
expect(found_images.size).to eq(1)
expect(found_images[0]['src']).to eq 'img%20test.jpg'
expect(found_images[0]['alt']).to eq 'example'
end
 
described_class::IGNORE_PARENTS.each do |elem|
Loading
Loading
Loading
Loading
@@ -178,4 +178,25 @@ describe Banzai::Pipeline::WikiPipeline do
end
end
end
describe 'videos' do
let(:namespace) { create(:namespace, name: "wiki_link_ns") }
let(:project) { create(:project, :public, name: "wiki_link_project", namespace: namespace) }
let(:project_wiki) { ProjectWiki.new(project, double(:user)) }
let(:page) { build(:wiki_page, wiki: project_wiki, page: OpenStruct.new(url_path: 'nested/twice/start-page')) }
it 'generates video html structure' do
markdown = "![video_file](video_file_name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video_file_name.mp4"')
end
it 'rewrites and replaces video links names with white spaces to %20' do
markdown = "![video file](video file name.mp4)"
output = described_class.to_html(markdown, project: project, project_wiki: project_wiki, page_slug: page.slug)
expect(output).to include('<video src="/wiki_link_ns/wiki_link_project/wikis/nested/twice/video%20file%20name.mp4"')
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment