Use Gitlab::Markdown.render with :pipeline option rather than different methods
This commit is contained in:
parent
4a5b77188e
commit
ed41333a6e
|
|
@ -20,7 +20,7 @@ module GitlabMarkdownHelper
|
|||
end
|
||||
|
||||
user = current_user if defined?(current_user)
|
||||
gfm_body = Gitlab::Markdown.gfm(escaped_body, project: @project, current_user: user)
|
||||
gfm_body = Gitlab::Markdown.render(escaped_body, project: @project, current_user: user, pipeline: :single_line)
|
||||
|
||||
fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
|
||||
if fragment.children.size == 1 && fragment.children[0].name == 'a'
|
||||
|
|
@ -48,37 +48,20 @@ module GitlabMarkdownHelper
|
|||
def markdown(text, context = {})
|
||||
return "" unless text.present?
|
||||
|
||||
context.reverse_merge!(
|
||||
path: @path,
|
||||
pipeline: :default,
|
||||
project: @project,
|
||||
project_wiki: @project_wiki,
|
||||
ref: @ref
|
||||
)
|
||||
|
||||
user = current_user if defined?(current_user)
|
||||
|
||||
context[:project] ||= @project
|
||||
|
||||
html = Gitlab::Markdown.render(text, context)
|
||||
Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user)
|
||||
end
|
||||
|
||||
# TODO (rspeicher): Remove all usages of this helper and just call `markdown`
|
||||
# with a custom pipeline depending on the content being rendered
|
||||
def gfm(text, options = {})
|
||||
return "" unless text.present?
|
||||
context.merge!(
|
||||
current_user: (current_user if defined?(current_user)),
|
||||
|
||||
options.reverse_merge!(
|
||||
path: @path,
|
||||
pipeline: :default,
|
||||
project: @project,
|
||||
project_wiki: @project_wiki,
|
||||
ref: @ref
|
||||
# RelativeLinkFilter
|
||||
requested_path: @path,
|
||||
project_wiki: @project_wiki,
|
||||
ref: @ref
|
||||
)
|
||||
|
||||
user = current_user if defined?(current_user)
|
||||
|
||||
html = Gitlab::Markdown.gfm(text, options)
|
||||
Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user)
|
||||
Gitlab::Markdown.post_process(html, context)
|
||||
end
|
||||
|
||||
def asciidoc(text)
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
.commit-row-title
|
||||
= link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: ''
|
||||
·
|
||||
= gfm event_commit_title(commit[:message]), project: project
|
||||
= markdown event_commit_title(commit[:message]), project: project, pipeline: :single_line
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@
|
|||
|
||||
.commit-box.gray-content-block.middle-block
|
||||
%h3.commit-title
|
||||
= gfm escape_once(@commit.title)
|
||||
= markdown escape_once(@commit.title), pipeline: :single_line
|
||||
- if @commit.description.present?
|
||||
%pre.commit-description
|
||||
= preserve(gfm(escape_once(@commit.description)))
|
||||
= preserve(markdown(escape_once(@commit.description), pipeline: :single_line))
|
||||
|
||||
:coffeescript
|
||||
$(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
- if commit.description?
|
||||
.commit-row-description.js-toggle-content
|
||||
%pre
|
||||
= preserve(gfm(escape_once(commit.description)))
|
||||
= preserve(markdown(escape_once(commit.description), pipeline: :single_line))
|
||||
|
||||
.commit-row-info
|
||||
= commit_author_link(commit, avatar: true, size: 24)
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
.gray-content-block.middle-block
|
||||
%h2.issue-title
|
||||
= gfm escape_once(@issue.title)
|
||||
= markdown escape_once(@issue.title), pipeline: :single_line
|
||||
%div
|
||||
- if @issue.description.present?
|
||||
.description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
.gray-content-block.middle-block
|
||||
%h2.issue-title
|
||||
= gfm escape_once(@merge_request.title)
|
||||
= markdown escape_once(@merge_request.title), pipeline: :single_line
|
||||
|
||||
%div
|
||||
- if @merge_request.description.present?
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@
|
|||
%i.fa.fa-check
|
||||
Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)}
|
||||
= succeed '.' do
|
||||
!= gfm(issues_sentence(@closes_issues))
|
||||
!= markdown issues_sentence(@closes_issues), pipeline: :gfm
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
%span All issues for this milestone are closed. You may close milestone now.
|
||||
|
||||
%h3.issue-title
|
||||
= gfm escape_once(@milestone.title)
|
||||
= markdown escape_once(@milestone.title), pipeline: :single_line
|
||||
%div
|
||||
- if @milestone.description.present?
|
||||
.description
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
= link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do
|
||||
%code= commit.short_id
|
||||
= image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
|
||||
= gfm escape_once(truncate(commit.title, length: 40))
|
||||
= markdown escape_once(truncate(commit.title, length: 40)), pipeline: :single_line
|
||||
%td
|
||||
%span.pull-right.cgray
|
||||
= time_ago_with_tooltip(commit.committed_date)
|
||||
|
|
|
|||
|
|
@ -19,51 +19,15 @@ module Gitlab
|
|||
# context - Hash of context options passed to our HTML Pipeline
|
||||
#
|
||||
# Returns an HTML-safe String
|
||||
def self.render(markdown, context = {})
|
||||
html = renderer.render(markdown)
|
||||
html = gfm(html, context)
|
||||
def self.render(text, context = {})
|
||||
pipeline = context[:pipeline] || :full
|
||||
|
||||
html.html_safe
|
||||
end
|
||||
html_pipeline = html_pipelines[pipeline]
|
||||
|
||||
# Convert a Markdown String into HTML without going through the HTML
|
||||
# Pipeline.
|
||||
#
|
||||
# Note that because the pipeline is skipped, SanitizationFilter is as well.
|
||||
# Do not output the result of this method to the user.
|
||||
#
|
||||
# markdown - Markdown String
|
||||
#
|
||||
# Returns a String
|
||||
def self.render_without_gfm(markdown)
|
||||
renderer.render(markdown)
|
||||
end
|
||||
transformers = get_context_transformers(pipeline)
|
||||
context = transformers.reduce(context) { |context, transformer| transformer.call(context) }
|
||||
|
||||
# Perform post-processing on an HTML String
|
||||
#
|
||||
# This method is used to perform state-dependent changes to a String of
|
||||
# HTML, such as removing references that the current user doesn't have
|
||||
# permission to make (`RedactorFilter`).
|
||||
#
|
||||
# html - String to process
|
||||
# options - Hash of options to customize output
|
||||
# :pipeline - Symbol pipeline type
|
||||
# :project - Project
|
||||
# :user - User object
|
||||
#
|
||||
# Returns an HTML-safe String
|
||||
def self.post_process(html, options)
|
||||
context = {
|
||||
project: options[:project],
|
||||
current_user: options[:user]
|
||||
}
|
||||
doc = post_processor.to_document(html, context)
|
||||
|
||||
if options[:pipeline] == :atom
|
||||
doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
|
||||
else
|
||||
doc.to_html
|
||||
end.html_safe
|
||||
html_pipeline.to_html(text, context)
|
||||
end
|
||||
|
||||
# Provide autoload paths for filters to prevent a circular dependency error
|
||||
|
|
@ -75,6 +39,7 @@ module Gitlab
|
|||
autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter'
|
||||
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
|
||||
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
|
||||
autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter'
|
||||
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
|
||||
autoload :RedactorFilter, 'gitlab/markdown/redactor_filter'
|
||||
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
|
||||
|
|
@ -85,98 +50,38 @@ module Gitlab
|
|||
autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
|
||||
autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
|
||||
|
||||
# Public: Parse the provided HTML with GitLab-Flavored Markdown
|
||||
# Perform post-processing on an HTML String
|
||||
#
|
||||
# html - HTML String
|
||||
# options - A Hash of options used to customize output (default: {})
|
||||
# :no_header_anchors - Disable header anchors in TableOfContentsFilter
|
||||
# :path - Current path String
|
||||
# :pipeline - Symbol pipeline type
|
||||
# :project - Current Project object
|
||||
# :project_wiki - Current ProjectWiki object
|
||||
# :ref - Current ref String
|
||||
# This method is used to perform state-dependent changes to a String of
|
||||
# HTML, such as removing references that the current user doesn't have
|
||||
# permission to make (`RedactorFilter`).
|
||||
#
|
||||
# html - String to process
|
||||
# context - Hash of options to customize output
|
||||
# :pipeline - Symbol pipeline type
|
||||
# :project - Project
|
||||
# :user - User object
|
||||
#
|
||||
# Returns an HTML-safe String
|
||||
def self.gfm(html, options = {})
|
||||
return '' unless html.present?
|
||||
def self.post_process(html, context)
|
||||
doc = html_pipelines[:post_process].to_document(html, context)
|
||||
|
||||
@pipeline ||= HTML::Pipeline.new(filters)
|
||||
|
||||
context = {
|
||||
# SanitizationFilter
|
||||
pipeline: options[:pipeline],
|
||||
|
||||
# EmojiFilter
|
||||
asset_host: Gitlab::Application.config.asset_host,
|
||||
asset_root: Gitlab.config.gitlab.base_url,
|
||||
|
||||
# ReferenceFilter
|
||||
only_path: only_path_pipeline?(options[:pipeline]),
|
||||
project: options[:project],
|
||||
|
||||
# RelativeLinkFilter
|
||||
project_wiki: options[:project_wiki],
|
||||
ref: options[:ref],
|
||||
requested_path: options[:path],
|
||||
|
||||
# TableOfContentsFilter
|
||||
no_header_anchors: options[:no_header_anchors]
|
||||
}
|
||||
|
||||
@pipeline.to_html(html, context).html_safe
|
||||
if context[:xhtml]
|
||||
doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
|
||||
else
|
||||
doc.to_html
|
||||
end.html_safe
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Check if a pipeline enables the `only_path` context option
|
||||
#
|
||||
# Returns Boolean
|
||||
def self.only_path_pipeline?(pipeline)
|
||||
case pipeline
|
||||
when :atom, :email
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def self.redcarpet_options
|
||||
# https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
|
||||
@redcarpet_options ||= {
|
||||
fenced_code_blocks: true,
|
||||
footnotes: true,
|
||||
lax_spacing: true,
|
||||
no_intra_emphasis: true,
|
||||
space_after_headers: true,
|
||||
strikethrough: true,
|
||||
superscript: true,
|
||||
tables: true
|
||||
}.freeze
|
||||
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
|
||||
#
|
||||
# SanitizationFilter should come first so that all generated reference HTML
|
||||
# goes through untouched.
|
||||
#
|
||||
# See https://github.com/jch/html-pipeline#filters for more filters.
|
||||
def self.filters
|
||||
[
|
||||
FILTERS = {
|
||||
plain_markdown: [
|
||||
Gitlab::Markdown::MarkdownFilter
|
||||
],
|
||||
gfm: [
|
||||
Gitlab::Markdown::SyntaxHighlightFilter,
|
||||
Gitlab::Markdown::SanitizationFilter,
|
||||
|
||||
Gitlab::Markdown::RelativeLinkFilter,
|
||||
Gitlab::Markdown::EmojiFilter,
|
||||
Gitlab::Markdown::TableOfContentsFilter,
|
||||
Gitlab::Markdown::AutolinkFilter,
|
||||
|
|
@ -192,7 +97,90 @@ module Gitlab
|
|||
Gitlab::Markdown::LabelReferenceFilter,
|
||||
|
||||
Gitlab::Markdown::TaskListFilter
|
||||
],
|
||||
|
||||
full: [:plain_markdown, :gfm],
|
||||
atom: :full,
|
||||
email: :full,
|
||||
description: :full,
|
||||
single_line: :gfm,
|
||||
|
||||
post_process: [
|
||||
Gitlab::Markdown::RelativeLinkFilter,
|
||||
Gitlab::Markdown::RedactorFilter
|
||||
]
|
||||
}
|
||||
|
||||
CONTEXT_TRANSFORMERS = {
|
||||
gfm: {
|
||||
only_path: true,
|
||||
|
||||
# EmojiFilter
|
||||
asset_host: Gitlab::Application.config.asset_host,
|
||||
asset_root: Gitlab.config.gitlab.base_url
|
||||
},
|
||||
full: :gfm,
|
||||
|
||||
atom: [
|
||||
:full,
|
||||
{
|
||||
only_path: false,
|
||||
xhtml: true
|
||||
}
|
||||
],
|
||||
email: [
|
||||
:full,
|
||||
{
|
||||
only_path: false
|
||||
}
|
||||
],
|
||||
description: [
|
||||
:full,
|
||||
{
|
||||
# SanitizationFilter
|
||||
inline_sanitization: true
|
||||
}
|
||||
],
|
||||
single_line: :gfm,
|
||||
|
||||
post_process: {
|
||||
post_process: true
|
||||
}
|
||||
}
|
||||
|
||||
def self.html_pipelines
|
||||
@html_pipelines ||= Hash.new do |hash, pipeline|
|
||||
filters = get_filters(pipeline)
|
||||
HTML::Pipeline.new(filters)
|
||||
end
|
||||
end
|
||||
|
||||
def self.get_filters(pipelines)
|
||||
Array.wrap(pipelines).flat_map do |pipeline|
|
||||
case pipeline
|
||||
when Class
|
||||
pipeline
|
||||
when Symbol
|
||||
get_filters(FILTERS[pipeline])
|
||||
when Array
|
||||
get_filters(pipeline)
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
def self.get_context_transformers(pipelines)
|
||||
Array.wrap(pipelines).flat_map do |pipeline|
|
||||
case pipeline
|
||||
when Hash
|
||||
->(context) { context.merge(pipeline) }
|
||||
when Proc
|
||||
pipeline
|
||||
when Symbol
|
||||
get_context_transformers(CONTEXT_TRANSFORMERS[pipeline])
|
||||
when Array
|
||||
get_context_transformers(pipeline)
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
module Gitlab
|
||||
module Markdown
|
||||
class MarkdownFilter < HTML::Pipeline::TextFilter
|
||||
def initialize(text, context = nil, result = nil)
|
||||
super text, context, result
|
||||
@text = @text.gsub "\r", ''
|
||||
end
|
||||
|
||||
def call
|
||||
html = self.class.renderer.render(@text)
|
||||
html.rstrip!
|
||||
html
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.redcarpet_options
|
||||
# https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
|
||||
@redcarpet_options ||= {
|
||||
fenced_code_blocks: true,
|
||||
footnotes: true,
|
||||
lax_spacing: true,
|
||||
no_intra_emphasis: true,
|
||||
space_after_headers: true,
|
||||
strikethrough: true,
|
||||
superscript: true,
|
||||
tables: true
|
||||
}.freeze
|
||||
end
|
||||
|
||||
def self.renderer
|
||||
@renderer ||= begin
|
||||
renderer = Redcarpet::Render::HTML.new
|
||||
Redcarpet::Markdown.new(renderer, redcarpet_options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -16,7 +16,7 @@ module Gitlab
|
|||
def call
|
||||
return doc unless linkable_files?
|
||||
|
||||
doc.search('a').each do |el|
|
||||
doc.search('a:not(.gfm)').each do |el|
|
||||
process_link_attr el.attribute('href')
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ module Gitlab
|
|||
def whitelist
|
||||
# Descriptions are more heavily sanitized, allowing only a few elements.
|
||||
# See http://git.io/vkuAN
|
||||
if pipeline == :description
|
||||
if context[:inline_sanitization]
|
||||
whitelist = LIMITED
|
||||
whitelist[:elements] -= %w(pre code img ol ul li)
|
||||
else
|
||||
|
|
@ -25,10 +25,6 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def pipeline
|
||||
context[:pipeline] || :default
|
||||
end
|
||||
|
||||
def customized?(transformers)
|
||||
transformers.last.source_location[0] == __FILE__
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ module Gitlab
|
|||
|
||||
def analyze(text)
|
||||
references.clear
|
||||
@text = Gitlab::Markdown.render_without_gfm(text)
|
||||
|
||||
@document = Gitlab::Markdown.render(text, project: project)
|
||||
end
|
||||
|
||||
%i(user label issue merge_request snippet commit commit_range).each do |type|
|
||||
|
|
@ -44,20 +45,13 @@ module Gitlab
|
|||
filter = Gitlab::Markdown.const_get(klass)
|
||||
|
||||
context = {
|
||||
project: project,
|
||||
current_user: current_user,
|
||||
|
||||
# We don't actually care about the links generated
|
||||
only_path: true,
|
||||
ignore_blockquotes: true,
|
||||
|
||||
# ReferenceGathererFilter
|
||||
project: project,
|
||||
current_user: current_user,
|
||||
load_lazy_references: false,
|
||||
reference_filter: filter
|
||||
}
|
||||
|
||||
pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
|
||||
result = pipeline.call(@text)
|
||||
result = self.class.pipeline.call(@document, context)
|
||||
|
||||
values = result[:references][filter_type].uniq
|
||||
|
||||
|
|
@ -67,5 +61,9 @@ module Gitlab
|
|||
|
||||
values
|
||||
end
|
||||
|
||||
def self.pipeline
|
||||
@pipeline ||= HTML::Pipeline.new([Gitlab::Markdown::ReferenceGathererFilter])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue