summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2015-10-14 19:27:23 +0200
committerDouwe Maan <douwe@gitlab.com>2015-10-14 19:28:09 +0200
commited41333a6ecbfcc04781a47a3dc71064c92d837b (patch)
tree5494ebea76f0191288a0adfed404745a3112bf75 /lib
parent4a5b77188ec7525d1c3a1ee925c8791f841b040c (diff)
downloadgitlab-ce-ed41333a6ecbfcc04781a47a3dc71064c92d837b.tar.gz
Use Gitlab::Markdown.render with :pipeline option rather than different methods
Diffstat (limited to 'lib')
-rw-r--r--lib/gitlab/markdown.rb236
-rw-r--r--lib/gitlab/markdown/markdown_filter.rb39
-rw-r--r--lib/gitlab/markdown/relative_link_filter.rb2
-rw-r--r--lib/gitlab/markdown/sanitization_filter.rb6
-rw-r--r--lib/gitlab/reference_extractor.rb20
5 files changed, 162 insertions, 141 deletions
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
index d5b0060dd56..773ebddba2b 100644
--- a/lib/gitlab/markdown.rb
+++ b/lib/gitlab/markdown.rb
@@ -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
+ #
+ # 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 - 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
+ # 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?
-
- @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]
- }
+ def self.post_process(html, context)
+ doc = html_pipelines[:post_process].to_document(html, context)
- @pipeline.to_html(html, context).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
+ if context[:xhtml]
+ doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
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])
+ doc.to_html
+ end.html_safe
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
- [
+ private
+ 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
diff --git a/lib/gitlab/markdown/markdown_filter.rb b/lib/gitlab/markdown/markdown_filter.rb
new file mode 100644
index 00000000000..921e2a0794e
--- /dev/null
+++ b/lib/gitlab/markdown/markdown_filter.rb
@@ -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
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 6ee3d1ce039..3e9909d2f33 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -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
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/gitlab/markdown/sanitization_filter.rb
index e368de7d848..550dfafca85 100644
--- a/lib/gitlab/markdown/sanitization_filter.rb
+++ b/lib/gitlab/markdown/sanitization_filter.rb
@@ -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
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 66016ecc877..633c988d025 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -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