Commit ed41333a authored by Douwe Maan's avatar Douwe Maan

Use Gitlab::Markdown.render with :pipeline option rather than different methods

parent 4a5b7718
...@@ -20,7 +20,7 @@ module GitlabMarkdownHelper ...@@ -20,7 +20,7 @@ module GitlabMarkdownHelper
end end
user = current_user if defined?(current_user) 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) fragment = Nokogiri::HTML::DocumentFragment.parse(gfm_body)
if fragment.children.size == 1 && fragment.children[0].name == 'a' if fragment.children.size == 1 && fragment.children[0].name == 'a'
...@@ -48,37 +48,20 @@ module GitlabMarkdownHelper ...@@ -48,37 +48,20 @@ module GitlabMarkdownHelper
def markdown(text, context = {}) def markdown(text, context = {})
return "" unless text.present? return "" unless text.present?
context.reverse_merge!( context[:project] ||= @project
path: @path,
pipeline: :default,
project: @project,
project_wiki: @project_wiki,
ref: @ref
)
user = current_user if defined?(current_user)
html = Gitlab::Markdown.render(text, context) 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` context.merge!(
# with a custom pipeline depending on the content being rendered current_user: (current_user if defined?(current_user)),
def gfm(text, options = {})
return "" unless text.present?
options.reverse_merge!( # RelativeLinkFilter
path: @path, requested_path: @path,
pipeline: :default,
project: @project,
project_wiki: @project_wiki, project_wiki: @project_wiki,
ref: @ref ref: @ref
) )
user = current_user if defined?(current_user) Gitlab::Markdown.post_process(html, context)
html = Gitlab::Markdown.gfm(text, options)
Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user)
end end
def asciidoc(text) def asciidoc(text)
......
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
.commit-row-title .commit-row-title
= link_to truncate_sha(commit[:id]), namespace_project_commit_path(project.namespace, project, commit[:id]), class: "commit_short_id", alt: '' = 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 @@ ...@@ -50,10 +50,10 @@
.commit-box.gray-content-block.middle-block .commit-box.gray-content-block.middle-block
%h3.commit-title %h3.commit-title
= gfm escape_once(@commit.title) = markdown escape_once(@commit.title), pipeline: :single_line
- if @commit.description.present? - if @commit.description.present?
%pre.commit-description %pre.commit-description
= preserve(gfm(escape_once(@commit.description))) = preserve(markdown(escape_once(@commit.description), pipeline: :single_line))
:coffeescript :coffeescript
$(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}") $(".commit-info-row.branches").load("#{branches_namespace_project_commit_path(@project.namespace, @project, @commit.id)}")
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
- if commit.description? - if commit.description?
.commit-row-description.js-toggle-content .commit-row-description.js-toggle-content
%pre %pre
= preserve(gfm(escape_once(commit.description))) = preserve(markdown(escape_once(commit.description), pipeline: :single_line))
.commit-row-info .commit-row-info
= commit_author_link(commit, avatar: true, size: 24) = commit_author_link(commit, avatar: true, size: 24)
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
.gray-content-block.middle-block .gray-content-block.middle-block
%h2.issue-title %h2.issue-title
= gfm escape_once(@issue.title) = markdown escape_once(@issue.title), pipeline: :single_line
%div %div
- if @issue.description.present? - if @issue.description.present?
.description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''} .description{class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : ''}
......
.gray-content-block.middle-block .gray-content-block.middle-block
%h2.issue-title %h2.issue-title
= gfm escape_once(@merge_request.title) = markdown escape_once(@merge_request.title), pipeline: :single_line
%div %div
- if @merge_request.description.present? - if @merge_request.description.present?
......
...@@ -24,4 +24,4 @@ ...@@ -24,4 +24,4 @@
%i.fa.fa-check %i.fa.fa-check
Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)} Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)}
= succeed '.' do = succeed '.' do
!= gfm(issues_sentence(@closes_issues)) != markdown issues_sentence(@closes_issues), pipeline: :gfm
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
%span All issues for this milestone are closed. You may close milestone now. %span All issues for this milestone are closed. You may close milestone now.
%h3.issue-title %h3.issue-title
= gfm escape_once(@milestone.title) = markdown escape_once(@milestone.title), pipeline: :single_line
%div %div
- if @milestone.description.present? - if @milestone.description.present?
.description .description
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do = link_to namespace_project_commits_path(@project.namespace, @project, commit.id) do
%code= commit.short_id %code= commit.short_id
= image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' = 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 %td
%span.pull-right.cgray %span.pull-right.cgray
= time_ago_with_tooltip(commit.committed_date) = time_ago_with_tooltip(commit.committed_date)
...@@ -19,51 +19,15 @@ module Gitlab ...@@ -19,51 +19,15 @@ module Gitlab
# context - Hash of context options passed to our HTML Pipeline # context - Hash of context options passed to our HTML Pipeline
# #
# Returns an HTML-safe String # Returns an HTML-safe String
def self.render(markdown, context = {}) def self.render(text, context = {})
html = renderer.render(markdown) pipeline = context[:pipeline] || :full
html = gfm(html, context)
html.html_safe html_pipeline = html_pipelines[pipeline]
end
# Convert a Markdown String into HTML without going through the HTML transformers = get_context_transformers(pipeline)
# Pipeline. context = transformers.reduce(context) { |context, transformer| transformer.call(context) }
#
# 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
# 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 html_pipeline.to_html(text, context)
doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
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
...@@ -75,6 +39,7 @@ module Gitlab ...@@ -75,6 +39,7 @@ module Gitlab
autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter' autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter'
autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter' autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter' autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
autoload :MarkdownFilter, 'gitlab/markdown/markdown_filter'
autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter' autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
autoload :RedactorFilter, 'gitlab/markdown/redactor_filter' autoload :RedactorFilter, 'gitlab/markdown/redactor_filter'
autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter' autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
...@@ -85,98 +50,38 @@ module Gitlab ...@@ -85,98 +50,38 @@ 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 HTML with GitLab-Flavored Markdown # Perform post-processing on an HTML String
# #
# html - HTML String # This method is used to perform state-dependent changes to a String of
# options - A Hash of options used to customize output (default: {}) # HTML, such as removing references that the current user doesn't have
# :no_header_anchors - Disable header anchors in TableOfContentsFilter # permission to make (`RedactorFilter`).
# :path - Current path String #
# html - String to process
# context - Hash of options to customize output
# :pipeline - Symbol pipeline type # :pipeline - Symbol pipeline type
# :project - Current Project object # :project - Project
# :project_wiki - Current ProjectWiki object # :user - User object
# :ref - Current ref String
# #
# Returns an HTML-safe String # Returns an HTML-safe String
def self.gfm(html, options = {}) def self.post_process(html, context)
return '' unless html.present? 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
end
private if context[:xhtml]
doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
# 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 else
true doc.to_html
end end.html_safe
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 end
# Filters used in our pipeline private
# FILTERS = {
# SanitizationFilter should come first so that all generated reference HTML plain_markdown: [
# goes through untouched. Gitlab::Markdown::MarkdownFilter
# ],
# See https://github.com/jch/html-pipeline#filters for more filters. gfm: [
def self.filters
[
Gitlab::Markdown::SyntaxHighlightFilter, Gitlab::Markdown::SyntaxHighlightFilter,
Gitlab::Markdown::SanitizationFilter, Gitlab::Markdown::SanitizationFilter,
Gitlab::Markdown::RelativeLinkFilter,
Gitlab::Markdown::EmojiFilter, Gitlab::Markdown::EmojiFilter,
Gitlab::Markdown::TableOfContentsFilter, Gitlab::Markdown::TableOfContentsFilter,
Gitlab::Markdown::AutolinkFilter, Gitlab::Markdown::AutolinkFilter,
...@@ -192,7 +97,90 @@ module Gitlab ...@@ -192,7 +97,90 @@ module Gitlab
Gitlab::Markdown::LabelReferenceFilter, Gitlab::Markdown::LabelReferenceFilter,
Gitlab::Markdown::TaskListFilter 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 end
end end
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 ...@@ -16,7 +16,7 @@ module Gitlab
def call def call
return doc unless linkable_files? 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') process_link_attr el.attribute('href')
end end
......
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
def whitelist def whitelist
# Descriptions are more heavily sanitized, allowing only a few elements. # Descriptions are more heavily sanitized, allowing only a few elements.
# See http://git.io/vkuAN # See http://git.io/vkuAN
if pipeline == :description if context[:inline_sanitization]
whitelist = LIMITED whitelist = LIMITED
whitelist[:elements] -= %w(pre code img ol ul li) whitelist[:elements] -= %w(pre code img ol ul li)
else else
...@@ -25,10 +25,6 @@ module Gitlab ...@@ -25,10 +25,6 @@ module Gitlab
private private
def pipeline
context[:pipeline] || :default
end
def customized?(transformers) def customized?(transformers)
transformers.last.source_location[0] == __FILE__ transformers.last.source_location[0] == __FILE__
end end
......
...@@ -13,7 +13,8 @@ module Gitlab ...@@ -13,7 +13,8 @@ module Gitlab
def analyze(text) def analyze(text)
references.clear references.clear
@text = Gitlab::Markdown.render_without_gfm(text)
@document = Gitlab::Markdown.render(text, project: project)
end end
%i(user label issue merge_request snippet commit commit_range).each do |type| %i(user label issue merge_request snippet commit commit_range).each do |type|
...@@ -46,18 +47,11 @@ module Gitlab ...@@ -46,18 +47,11 @@ module Gitlab
context = { context = {
project: project, project: project,
current_user: current_user, current_user: current_user,
# We don't actually care about the links generated
only_path: true,
ignore_blockquotes: true,
# ReferenceGathererFilter
load_lazy_references: false, load_lazy_references: false,
reference_filter: filter reference_filter: filter
} }
pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context) result = self.class.pipeline.call(@document, context)
result = pipeline.call(@text)
values = result[:references][filter_type].uniq values = result[:references][filter_type].uniq
...@@ -67,5 +61,9 @@ module Gitlab ...@@ -67,5 +61,9 @@ module Gitlab
values values
end end
def self.pipeline
@pipeline ||= HTML::Pipeline.new([Gitlab::Markdown::ReferenceGathererFilter])
end
end end
end end
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