Commit 3b690891 authored by Robert Speicher's avatar Robert Speicher

Basic support for an Atom-specific rendering pipeline

parent 4bd92e68
...@@ -47,15 +47,16 @@ module GitlabMarkdownHelper ...@@ -47,15 +47,16 @@ module GitlabMarkdownHelper
def markdown(text, context = {}) def markdown(text, context = {})
return unless text.present? return unless text.present?
context.merge!( context.reverse_merge!(
path: @path, path: @path,
pipeline: :default,
project: @project, project: @project,
project_wiki: @project_wiki, project_wiki: @project_wiki,
ref: @ref ref: @ref
) )
html = Gitlab::Markdown.render(text, context) html = Gitlab::Markdown.render(text, context)
Gitlab::Markdown.post_process(html, current_user) Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], user: current_user)
end end
# TODO (rspeicher): Remove all usages of this helper and just call `markdown` # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
...@@ -63,15 +64,16 @@ module GitlabMarkdownHelper ...@@ -63,15 +64,16 @@ module GitlabMarkdownHelper
def gfm(text, options = {}) def gfm(text, options = {})
return unless text.present? return unless text.present?
options.merge!( options.reverse_merge!(
path: @path, path: @path,
pipeline: :default,
project: @project, project: @project,
project_wiki: @project_wiki, project_wiki: @project_wiki,
ref: @ref ref: @ref
) )
html = Gitlab::Markdown.gfm(text, options) html = Gitlab::Markdown.gfm(text, options)
Gitlab::Markdown.post_process(html, current_user) Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], user: current_user)
end end
def asciidoc(text) def asciidoc(text)
......
%div{xmlns: "http://www.w3.org/1999/xhtml"} %div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(issue.description, xhtml: true, reference_only_path: false, project: issue.project) = markdown(issue.description, pipeline: :atom, project: issue.project)
%div{xmlns: "http://www.w3.org/1999/xhtml"} %div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(merge_request.description, xhtml: true, reference_only_path: false, project: merge_request.project) = markdown(merge_request.description, pipeline: :atom, project: merge_request.project)
%div{xmlns: "http://www.w3.org/1999/xhtml"} %div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown(note.note, xhtml: true, reference_only_path: false, project: note.project) = markdown(note.note, pipeline: :atom, project: note.project)
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
%i %i
at at
= commit[:timestamp].to_time.to_s(:short) = commit[:timestamp].to_time.to_s(:short)
%blockquote= markdown(escape_once(commit[:message]), xhtml: true, reference_only_path: false, project: event.project) %blockquote= markdown(escape_once(commit[:message]), pipeline: :atom, project: event.project)
- if event.commits_count > 15 - if event.commits_count > 15
%p %p
%i %i
......
...@@ -7,6 +7,14 @@ module Gitlab ...@@ -7,6 +7,14 @@ module Gitlab
module Markdown module Markdown
# Convert a Markdown String into an HTML-safe String of HTML # Convert a Markdown String into an HTML-safe String of HTML
# #
# Note that while the returned HTML will have been sanitized of dangerous
# HTML, it may post a risk of information leakage if it's not also passed
# through `post_process`.
#
# Also note that the returned String is always HTML, not XHTML. Views
# requiring XHTML, such as Atom feeds, need to call `post_process` on the
# result, providing the appropriate `pipeline` option.
#
# markdown - Markdown String # markdown - Markdown String
# context - Hash of context options passed to our HTML Pipeline # context - Hash of context options passed to our HTML Pipeline
# #
...@@ -38,15 +46,19 @@ module Gitlab ...@@ -38,15 +46,19 @@ module Gitlab
# permission to make (`RedactorFilter`). # permission to make (`RedactorFilter`).
# #
# html - String to process # html - String to process
# for_user - User state # options - Hash of options to customize output
# :pipeline - Symbol pipeline type
# :user - User object
# #
# Returns an HTML-safe String # Returns an HTML-safe String
def self.post_process(html, for_user) def self.post_process(html, options)
result = post_processor.call(html, current_user: for_user) doc = post_processor.to_document(html, current_user: options[:user])
result[:output]. if options[:pipeline] == :atom
to_html. doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
html_safe else
doc.to_html
end.html_safe
end end
# Provide autoload paths for filters to prevent a circular dependency error # Provide autoload paths for filters to prevent a circular dependency error
...@@ -68,26 +80,20 @@ module Gitlab ...@@ -68,26 +80,20 @@ module Gitlab
autoload :TaskListFilter, 'gitlab/markdown/task_list_filter' autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter' autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
# Public: Parse the provided text with GitLab-Flavored Markdown # Public: Parse the provided HTML with GitLab-Flavored Markdown
# #
# text - the source text # html - HTML String
# options - A Hash of options used to customize output (default: {}): # options - A Hash of options used to customize output (default: {})
# :xhtml - output XHTML instead of HTML # :no_header_anchors - Disable header anchors in TableOfContentsFilter
# :reference_only_path - Use relative path for reference links # :path - Current path String
def self.gfm(text, options = {}) # :pipeline - Symbol pipeline type
return text if text.nil? # :project - Current Project object
# :project_wiki - Current ProjectWiki object
# Duplicate the string so we don't alter the original, then call to_str # :ref - Current ref String
# to cast it back to a String instead of a SafeBuffer. This is required #
# for gsub calls to work as we need them to. # Returns an HTML-safe String
text = text.dup.to_str def self.gfm(html, options = {})
return '' unless html.present?
options.reverse_merge!(
xhtml: false,
reference_only_path: true,
project: options[:project],
current_user: options[:current_user]
)
@pipeline ||= HTML::Pipeline.new(filters) @pipeline ||= HTML::Pipeline.new(filters)
...@@ -96,47 +102,39 @@ module Gitlab ...@@ -96,47 +102,39 @@ module Gitlab
pipeline: options[:pipeline], pipeline: options[:pipeline],
# EmojiFilter # EmojiFilter
asset_root: Gitlab.config.gitlab.url,
asset_host: Gitlab::Application.config.asset_host, asset_host: Gitlab::Application.config.asset_host,
asset_root: Gitlab.config.gitlab.url,
# TableOfContentsFilter
no_header_anchors: options[:no_header_anchors],
# ReferenceFilter # ReferenceFilter
only_path: options[:reference_only_path], only_path: only_path_pipeline?(options[:pipeline]),
project: options[:project], project: options[:project],
# RelativeLinkFilter # RelativeLinkFilter
project_wiki: options[:project_wiki],
ref: options[:ref], ref: options[:ref],
requested_path: options[:path], requested_path: options[:path],
project_wiki: options[:project_wiki]
}
result = @pipeline.call(text, context)
save_options = 0 # TableOfContentsFilter
if options[:xhtml] no_header_anchors: options[:no_header_anchors]
save_options |= Nokogiri::XML::Node::SaveOptions::AS_XHTML }
end
text = result[:output].to_html(save_with: save_options)
text.html_safe @pipeline.to_html(html, context).html_safe
end end
private private
def self.renderer # Check if a pipeline enables the `only_path` context option
@markdown ||= begin #
renderer = Redcarpet::Render::HTML.new # Returns Boolean
Redcarpet::Markdown.new(renderer, redcarpet_options) def self.only_path_pipeline?(pipeline)
case pipeline
when :atom, :email
false
else
true
end end
end end
def self.post_processor
@post_processor ||= HTML::Pipeline.new([Gitlab::Markdown::RedactorFilter])
end
def self.redcarpet_options def self.redcarpet_options
# https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
@redcarpet_options ||= { @redcarpet_options ||= {
...@@ -151,6 +149,17 @@ module Gitlab ...@@ -151,6 +149,17 @@ module Gitlab
}.freeze }.freeze
end end
def self.renderer
@markdown ||= begin
renderer = Redcarpet::Render::HTML.new
Redcarpet::Markdown.new(renderer, redcarpet_options)
end
end
def self.post_processor
@post_processor ||= HTML::Pipeline.new([Gitlab::Markdown::RedactorFilter])
end
# Filters used in our pipeline # Filters used in our pipeline
# #
# SanitizationFilter should come first so that all generated reference HTML # SanitizationFilter should come first so that all generated reference HTML
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment