Handle AsciiDoc better, reuse HTML pipeline filters (fixes #9263)

This commit is contained in:
Jakub Jirutka 2015-05-13 01:07:48 +02:00
parent dc348baf18
commit 8dbc4746fe
9 changed files with 184 additions and 6 deletions

View file

@ -222,8 +222,12 @@ module ApplicationHelper
end
def render_markup(file_name, file_content)
GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
if asciidoc?(file_name)
asciidoc(file_content)
else
GitHub::Markup.render(file_name, file_content).
force_encoding(file_content.encoding).html_safe
end
rescue RuntimeError
simple_format(file_content)
end
@ -236,6 +240,10 @@ module ApplicationHelper
Gitlab::MarkdownHelper.gitlab_markdown?(filename)
end
def asciidoc?(filename)
Gitlab::MarkdownHelper.asciidoc?(filename)
end
# Overrides ActionView::Helpers::UrlHelper#link_to to add `rel="nofollow"` to
# external links
def link_to(name = nil, options = nil, html_options = {})

View file

@ -56,6 +56,16 @@ module GitlabMarkdownHelper
@markdown.render(text).html_safe
end
def asciidoc(text)
Gitlab::Asciidoc.render(text, {
commit: @commit,
project: @project,
project_wiki: @project_wiki,
requested_path: @path,
ref: @ref
})
end
# Return the first line of +text+, up to +max_chars+, after parsing the line
# as Markdown. HTML tags in the parsed output are not counted toward the
# +max_chars+ limit. If the length limit falls within a tag's contents, then
@ -67,8 +77,11 @@ module GitlabMarkdownHelper
end
def render_wiki_content(wiki_page)
if wiki_page.format == :markdown
case wiki_page.format
when :markdown
markdown(wiki_page.content)
when :asciidoc
asciidoc(wiki_page.content)
else
wiki_page.formatted_content.html_safe
end

View file

@ -27,6 +27,8 @@ module TreeHelper
def render_readme(readme)
if gitlab_markdown?(readme.name)
preserve(markdown(readme.data))
elsif asciidoc?(readme.name)
asciidoc(readme.data)
elsif markup?(readme.name)
render_markup(readme.name, readme.data)
else

60
lib/gitlab/asciidoc.rb Normal file
View file

@ -0,0 +1,60 @@
require 'asciidoctor'
require 'html/pipeline'
module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
# the resulting HTML through HTML pipeline filters.
module Asciidoc
# Provide autoload paths for filters to prevent a circular dependency error
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
'env-gitlab', 'source-highlighter=html-pipeline'
].freeze
# Public: Converts the provided Asciidoc markup into HTML.
#
# input - the source text in Asciidoc format
# context - a Hash with the template context:
# :commit
# :project
# :project_wiki
# :requested_path
# :ref
# asciidoc_opts - a Hash of options to pass to the Asciidoctor converter
# html_opts - a Hash of options for HTML output:
# :xhtml - output XHTML instead of HTML
#
def self.render(input, context, asciidoc_opts = {}, html_opts = {})
asciidoc_opts = asciidoc_opts.reverse_merge(
safe: :secure,
backend: html_opts[:xhtml] ? :xhtml5 : :html5,
attributes: []
)
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
html = ::Asciidoctor.convert(input, asciidoc_opts)
if context[:project]
result = HTML::Pipeline.new(filters).call(html, context)
save_opts = html_opts[:xhtml] ?
Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0
html = result[:output].to_html(save_with: save_opts)
end
html.html_safe
end
private
def self.filters
[
Gitlab::Markdown::RelativeLinkFilter
]
end
end
end

View file

@ -9,7 +9,7 @@ module Gitlab
# Returns boolean
def markup?(filename)
filename.downcase.end_with?(*%w(.textile .rdoc .org .creole .wiki
.mediawiki .rst .adoc .asciidoc .asc))
.mediawiki .rst .adoc .ad .asciidoc))
end
# Public: Determines if a given filename is compatible with
@ -22,6 +22,15 @@ module Gitlab
filename.downcase.end_with?(*%w(.mdown .md .markdown))
end
# Public: Determines if the given filename has AsciiDoc extension.
#
# filename - Filename string to check
#
# Returns boolean
def asciidoc?(filename)
filename.downcase.end_with?(*%w(.adoc .ad .asciidoc))
end
def previewable?(filename)
gitlab_markdown?(filename) || markup?(filename)
end

View file

@ -261,12 +261,19 @@ describe ApplicationHelper do
end
end
describe 'markup_render' do
describe 'render_markup' do
let(:content) { 'Noël' }
it 'should preserve encoding' do
expect(content.encoding.name).to eq('UTF-8')
expect(render_markup('foo.rst', content).encoding.name).to eq('UTF-8')
end
it "should delegate to #asciidoc when file name corresponds to AsciiDoc" do
expect(self).to receive(:asciidoc?).with('foo.adoc').and_return(true)
expect(self).to receive(:asciidoc).and_return('NOEL')
expect(render_markup('foo.adoc', content)).to eq('NOEL')
end
end
end

View file

@ -110,6 +110,14 @@ describe GitlabMarkdownHelper do
helper.render_wiki_content(@wiki)
end
it "should use Asciidoctor for asciidoc files" do
allow(@wiki).to receive(:format).and_return(:asciidoc)
expect(helper).to receive(:asciidoc).with('wiki content')
helper.render_wiki_content(@wiki)
end
it "should use the Gollum renderer for all other file types" do
allow(@wiki).to receive(:format).and_return(:rdoc)
formatted_content_stub = double('formatted_content')

View file

@ -0,0 +1,59 @@
require 'spec_helper'
require 'nokogiri'
module Gitlab
describe Asciidoc do
let(:input) { '<b>ascii</b>' }
let(:context) { {} }
let(:html) { 'H<sub>2</sub>O' }
context "without project" do
it "should convert the input using Asciidoctor and default options" do
expected_asciidoc_opts = { safe: :secure, backend: :html5,
attributes: described_class::DEFAULT_ADOC_ATTRS }
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
expect( render(input, context) ).to eql html
end
context "with asciidoc_opts" do
let(:asciidoc_opts) { {safe: :safe, attributes: ['foo']} }
it "should merge the options with default ones" do
expected_asciidoc_opts = { safe: :safe, backend: :html5,
attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo'] }
expect(Asciidoctor).to receive(:convert)
.with(input, expected_asciidoc_opts).and_return(html)
render(input, context, asciidoc_opts)
end
end
end
context "with project in context" do
let(:context) { {project: create(:project)} }
it "should filter converted input via HTML pipeline and return result" do
filtered_html = '<b>ASCII</b>'
allow(Asciidoctor).to receive(:convert).and_return(html)
expect_any_instance_of(HTML::Pipeline).to receive(:call)
.with(html, context)
.and_return(output: Nokogiri::HTML.fragment(filtered_html))
expect( render('foo', context) ).to eql filtered_html
end
end
def render(*args)
described_class.render(*args)
end
end
end

View file

@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::MarkdownHelper do
describe '#markup?' do
%w(textile rdoc org creole wiki
mediawiki rst adoc asciidoc asc).each do |type|
mediawiki rst adoc ad asciidoc).each do |type|
it "returns true for #{type} files" do
expect(Gitlab::MarkdownHelper.markup?("README.#{type}")).to be_truthy
end
@ -25,4 +25,16 @@ describe Gitlab::MarkdownHelper do
expect(Gitlab::MarkdownHelper.gitlab_markdown?('README.rb')).not_to be_truthy
end
end
describe '#asciidoc?' do
%w(adoc ad asciidoc ADOC).each do |type|
it "returns true for #{type} files" do
expect(Gitlab::MarkdownHelper.asciidoc?("README.#{type}")).to be_truthy
end
end
it 'returns false when given a non-asciidoc filename' do
expect(Gitlab::MarkdownHelper.asciidoc?('README.rb')).not_to be_truthy
end
end
end