summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrett Walker <bwalker@gitlab.com>2018-08-16 20:42:07 -0500
committerBrett Walker <bwalker@gitlab.com>2018-08-20 08:00:47 -0500
commit036a52ac691e57f82ad5ad8e24d868bd13719558 (patch)
treedf44e086721bbd64d0139ceb64a98470a6a75a08
parent3c80adf5c8486315fa84ac237177c38b9ae625c9 (diff)
downloadgitlab-ce-036a52ac691e57f82ad5ad8e24d868bd13719558.tar.gz
add SpacedLinkFilter for wiki links with spaces
-rw-r--r--lib/banzai/filter/spaced_link_filter.rb72
-rw-r--r--lib/banzai/pipeline/wiki_pipeline.rb1
2 files changed, 73 insertions, 0 deletions
diff --git a/lib/banzai/filter/spaced_link_filter.rb b/lib/banzai/filter/spaced_link_filter.rb
new file mode 100644
index 00000000000..1d21b8174ab
--- /dev/null
+++ b/lib/banzai/filter/spaced_link_filter.rb
@@ -0,0 +1,72 @@
+require 'uri'
+
+module Banzai
+ module Filter
+ # HTML Filter for markdown links with spaces in the URLs
+ #
+ # 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,
+ # in our wikis, we support (via RedCarpet) this type of link, allowing
+ # wiki pages to be easily linked bby their title. This filter adds that functionality.
+ # The intent is for this to only be used in Wikis - in general, we want
+ # to adhere to CommonMark's spec.
+ #
+ class SpacedLinkFilter < HTML::Pipeline::Filter
+ include ActionView::Helpers::TagHelper
+
+ # 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
+ IGNORE_PARENTS = %w(a code kbd pre script style).to_set
+
+ # The XPath query to use for finding text nodes to parse.
+ TEXT_QUERY = %Q(descendant-or-self::text()[
+ not(#{IGNORE_PARENTS.map { |p| "ancestor::#{p}" }.join(' or ')})
+ and contains(., ']\(')
+ ]).freeze
+
+ def call
+ return doc if context[:markdown_engine] == :redcarpet
+
+ doc.xpath(TEXT_QUERY).each do |node|
+ content = node.to_html
+
+ next unless content.match(LINK_PATTERN)
+
+ html = spaced_link_filter(content)
+
+ next if html == content
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
+ private
+
+ def spaced_link_match(link)
+ match = LINK_PATTERN.match(link)
+ if match && match[1] && match[2]
+ # 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 it's magic
+ new_link = "[#{match[1]}](#{match[2].gsub(' ', '%20')})"
+ Banzai::Filter::MarkdownFilter.new(new_link, context).call
+ else
+ link
+ end
+ end
+
+ def spaced_link_filter(text)
+ Gitlab::StringRegexMarker.new(CGI.unescapeHTML(text), text.html_safe).mark(LINK_PATTERN) do |link, left:, right:|
+ spaced_link_match(link)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
index c37b8e71cb0..737ff0cc818 100644
--- a/lib/banzai/pipeline/wiki_pipeline.rb
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -5,6 +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)
end
end
end