diff options
Diffstat (limited to 'lib/gitlab/markdown/filter')
20 files changed, 0 insertions, 1305 deletions
diff --git a/lib/gitlab/markdown/filter/autolink_filter.rb b/lib/gitlab/markdown/filter/autolink_filter.rb deleted file mode 100644 index c37c3bc55bf..00000000000 --- a/lib/gitlab/markdown/filter/autolink_filter.rb +++ /dev/null @@ -1,107 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'uri' - -module Gitlab - module Markdown - # HTML Filter for auto-linking URLs in HTML. - # - # Based on HTML::Pipeline::AutolinkFilter - # - # Context options: - # :autolink - Boolean, skips all processing done by this filter when false - # :link_attr - Hash of attributes for the generated links - # - class AutolinkFilter < HTML::Pipeline::Filter - include ActionView::Helpers::TagHelper - - # Pattern to match text that should be autolinked. - # - # A URI scheme begins with a letter and may contain letters, numbers, - # plus, period and hyphen. Schemes are case-insensitive but we're being - # picky here and allowing only lowercase for autolinks. - # - # See http://en.wikipedia.org/wiki/URI_scheme - # - # The negative lookbehind ensures that users can paste a URL followed by a - # period or comma for punctuation without those characters being included - # in the generated link. - # - # Rubular: http://rubular.com/r/cxjPyZc7Sb - LINK_PATTERN = %r{([a-z][a-z0-9\+\.-]+://\S+)(?<!,|\.)} - - # Text matching LINK_PATTERN inside these elements will not be linked - IGNORE_PARENTS = %w(a code kbd pre script style).to_set - - def call - return doc if context[:autolink] == false - - rinku_parse - text_parse - end - - private - - # Run the text through Rinku as a first pass - # - # This will quickly autolink http(s) and ftp links. - # - # `@doc` will be re-parsed with the HTML String from Rinku. - def rinku_parse - # Convert the options from a Hash to a String that Rinku expects - options = tag_options(link_options) - - # NOTE: We don't parse email links because it will erroneously match - # external Commit and CommitRange references. - # - # The final argument tells Rinku to link short URLs that don't include a - # period (e.g., http://localhost:3000/) - rinku = Rinku.auto_link(html, :urls, options, IGNORE_PARENTS.to_a, 1) - - # Rinku returns a String, so parse it back to a Nokogiri::XML::Document - # for further processing. - @doc = parse_html(rinku) - end - - # Autolinks any text matching LINK_PATTERN that Rinku didn't already - # replace - def text_parse - search_text_nodes(doc).each do |node| - content = node.to_html - - next if has_ancestor?(node, IGNORE_PARENTS) - next unless content.match(LINK_PATTERN) - - # If Rinku didn't link this, there's probably a good reason, so we'll - # skip it too - next if content.start_with?(*%w(http https ftp)) - - html = autolink_filter(content) - - next if html == content - - node.replace(html) - end - - doc - end - - def autolink_filter(text) - text.gsub(LINK_PATTERN) do |match| - # Remove any trailing HTML entities and store them for appending - # outside the link element. The entity must be marked HTML safe in - # order to be output literally rather than escaped. - match.gsub!(/((?:&[\w#]+;)+)\z/, '') - dropped = ($1 || '').html_safe - - options = link_options.merge(href: match) - content_tag(:a, match, options) + dropped - end - end - - def link_options - @link_options ||= context[:link_attr] || {} - end - end - end -end diff --git a/lib/gitlab/markdown/filter/commit_range_reference_filter.rb b/lib/gitlab/markdown/filter/commit_range_reference_filter.rb deleted file mode 100644 index 36b3258ef76..00000000000 --- a/lib/gitlab/markdown/filter/commit_range_reference_filter.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces commit range references with links. - # - # This filter supports cross-project references. - class CommitRangeReferenceFilter < AbstractReferenceFilter - def self.object_class - CommitRange - end - - def self.references_in(text, pattern = CommitRange.reference_pattern) - text.gsub(pattern) do |match| - yield match, $~[:commit_range], $~[:project], $~ - end - end - - def self.referenced_by(node) - project = Project.find(node.attr("data-project")) rescue nil - return unless project - - id = node.attr("data-commit-range") - range = find_object(project, id) - - return unless range - - { commit_range: range } - end - - def initialize(*args) - super - - @commit_map = {} - end - - def self.find_object(project, id) - range = CommitRange.new(id, project) - - range.valid_commits? ? range : nil - end - - def find_object(*args) - self.class.find_object(*args) - end - - def url_for_object(range, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_compare_url(project.namespace, project, - range.to_param.merge(only_path: context[:only_path])) - end - - def object_link_title(range) - range.reference_title - end - end - end -end diff --git a/lib/gitlab/markdown/filter/commit_reference_filter.rb b/lib/gitlab/markdown/filter/commit_reference_filter.rb deleted file mode 100644 index e3066a89b04..00000000000 --- a/lib/gitlab/markdown/filter/commit_reference_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces commit references with links. - # - # This filter supports cross-project references. - class CommitReferenceFilter < AbstractReferenceFilter - def self.object_class - Commit - end - - def self.references_in(text, pattern = Commit.reference_pattern) - text.gsub(pattern) do |match| - yield match, $~[:commit], $~[:project], $~ - end - end - - def self.referenced_by(node) - project = Project.find(node.attr("data-project")) rescue nil - return unless project - - id = node.attr("data-commit") - commit = find_object(project, id) - - return unless commit - - { commit: commit } - end - - def self.find_object(project, id) - if project && project.valid_repo? - project.commit(id) - end - end - - def find_object(*args) - self.class.find_object(*args) - end - - def url_for_object(commit, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_commit_url(project.namespace, project, commit, - only_path: context[:only_path]) - end - - def object_link_title(commit) - commit.link_title - end - - def object_link_text_extras(object, matches) - extras = super - - path = matches[:path] if matches.names.include?("path") - if path == '/builds' - extras.unshift "builds" - end - - extras - end - end - end -end diff --git a/lib/gitlab/markdown/filter/emoji_filter.rb b/lib/gitlab/markdown/filter/emoji_filter.rb deleted file mode 100644 index da10e4d3760..00000000000 --- a/lib/gitlab/markdown/filter/emoji_filter.rb +++ /dev/null @@ -1,80 +0,0 @@ -require 'action_controller' -require 'gitlab/markdown' -require 'gitlab_emoji' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that replaces :emoji: with images. - # - # Based on HTML::Pipeline::EmojiFilter - # - # Context options: - # :asset_root - # :asset_host - class EmojiFilter < HTML::Pipeline::Filter - IGNORED_ANCESTOR_TAGS = %w(pre code tt).to_set - - def call - search_text_nodes(doc).each do |node| - content = node.to_html - next unless content.include?(':') - next if has_ancestor?(node, IGNORED_ANCESTOR_TAGS) - - html = emoji_image_filter(content) - - next if html == content - - node.replace(html) - end - - doc - end - - # Replace :emoji: with corresponding images. - # - # text - String text to replace :emoji: in. - # - # Returns a String with :emoji: replaced with images. - def emoji_image_filter(text) - text.gsub(emoji_pattern) do |match| - name = $1 - "<img class='emoji' title=':#{name}:' alt=':#{name}:' src='#{emoji_url(name)}' height='20' width='20' align='absmiddle' />" - end - end - - private - - def emoji_url(name) - emoji_path = "emoji/#{emoji_filename(name)}" - if context[:asset_host] - # Asset host is specified. - url_to_image(emoji_path) - elsif context[:asset_root] - # Gitlab url is specified - File.join(context[:asset_root], url_to_image(emoji_path)) - else - # All other cases - url_to_image(emoji_path) - end - end - - def url_to_image(image) - ActionController::Base.helpers.url_to_image(image) - end - - # Build a regexp that matches all valid :emoji: names. - def self.emoji_pattern - @emoji_pattern ||= /:(#{Emoji.emojis_names.map { |name| Regexp.escape(name) }.join('|')}):/ - end - - def emoji_pattern - self.class.emoji_pattern - end - - def emoji_filename(name) - "#{Emoji.emoji_filename(name)}.png" - end - end - end -end diff --git a/lib/gitlab/markdown/filter/external_issue_reference_filter.rb b/lib/gitlab/markdown/filter/external_issue_reference_filter.rb deleted file mode 100644 index 14bdf5521fc..00000000000 --- a/lib/gitlab/markdown/filter/external_issue_reference_filter.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces external issue tracker references with links. - # References are ignored if the project doesn't use an external issue - # tracker. - class ExternalIssueReferenceFilter < ReferenceFilter - # Public: Find `JIRA-123` issue references in text - # - # ExternalIssueReferenceFilter.references_in(text) do |match, issue| - # "<a href=...>##{issue}</a>" - # end - # - # text - String text to search. - # - # Yields the String match and the String issue reference. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(ExternalIssue.reference_pattern) do |match| - yield match, $~[:issue] - end - end - - def call - # Early return if the project isn't using an external tracker - return doc if project.nil? || project.default_issues_tracker? - - replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content| - issue_link_filter(content) - end - - replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text| - issue_link_filter(link, link_text: text) - end - end - - # Replace `JIRA-123` issue references in text with links to the referenced - # issue's details page. - # - # text - String text to replace references in. - # - # Returns a String with `JIRA-123` references replaced with links. All - # links have `gfm` and `gfm-issue` class names attached for styling. - def issue_link_filter(text, link_text: nil) - project = context[:project] - - self.class.references_in(text) do |match, issue| - url = url_for_issue(issue, project, only_path: context[:only_path]) - - title = escape_once("Issue in #{project.external_issue_tracker.title}") - klass = reference_class(:issue) - data = data_attribute(project: project.id) - - text = link_text || match - - %(<a href="#{url}" #{data} - title="#{title}" - class="#{klass}">#{text}</a>) - end - end - - def url_for_issue(*args) - IssuesHelper.url_for_issue(*args) - end - end - end -end diff --git a/lib/gitlab/markdown/filter/external_link_filter.rb b/lib/gitlab/markdown/filter/external_link_filter.rb deleted file mode 100644 index e09dfcb83c8..00000000000 --- a/lib/gitlab/markdown/filter/external_link_filter.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML Filter to add a `rel="nofollow"` attribute to external links - # - class ExternalLinkFilter < HTML::Pipeline::Filter - def call - doc.search('a').each do |node| - link = node.attr('href') - - next unless link - - # Skip non-HTTP(S) links - next unless link.start_with?('http') - - # Skip internal links - next if link.start_with?(internal_url) - - node.set_attribute('rel', 'nofollow') - end - - doc - end - - private - - def internal_url - @internal_url ||= Gitlab.config.gitlab.url - end - end - end -end diff --git a/lib/gitlab/markdown/filter/issue_reference_filter.rb b/lib/gitlab/markdown/filter/issue_reference_filter.rb deleted file mode 100644 index 1ed69e2f431..00000000000 --- a/lib/gitlab/markdown/filter/issue_reference_filter.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces issue references with links. References to - # issues that do not exist are ignored. - # - # This filter supports cross-project references. - class IssueReferenceFilter < AbstractReferenceFilter - def self.object_class - Issue - end - - def find_object(project, id) - project.get_issue(id) - end - - def url_for_object(issue, project) - IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/filter/label_reference_filter.rb b/lib/gitlab/markdown/filter/label_reference_filter.rb deleted file mode 100644 index a2026eecaeb..00000000000 --- a/lib/gitlab/markdown/filter/label_reference_filter.rb +++ /dev/null @@ -1,96 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces label references with links. - class LabelReferenceFilter < ReferenceFilter - # Public: Find label references in text - # - # LabelReferenceFilter.references_in(text) do |match, id, name| - # "<a href=...>#{Label.find(id)}</a>" - # end - # - # text - String text to search. - # - # Yields the String match, an optional Integer label ID, and an optional - # String label name. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(Label.reference_pattern) do |match| - yield match, $~[:label_id].to_i, $~[:label_name] - end - end - - def self.referenced_by(node) - { label: LazyReference.new(Label, node.attr("data-label")) } - end - - def call - replace_text_nodes_matching(Label.reference_pattern) do |content| - label_link_filter(content) - end - - replace_link_nodes_with_href(Label.reference_pattern) do |link, text| - label_link_filter(link, link_text: text) - end - end - - # Replace label references in text with links to the label specified. - # - # text - String text to replace references in. - # - # Returns a String with label references replaced with links. All links - # have `gfm` and `gfm-label` class names attached for styling. - def label_link_filter(text, link_text: nil) - project = context[:project] - - self.class.references_in(text) do |match, id, name| - params = label_params(id, name) - - if label = project.labels.find_by(params) - url = url_for_label(project, label) - klass = reference_class(:label) - data = data_attribute( - original: link_text || match, - project: project.id, - label: label.id - ) - - text = link_text || render_colored_label(label) - - %(<a href="#{url}" #{data} - class="#{klass}">#{text}</a>) - else - match - end - end - end - - def url_for_label(project, label) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_issues_url( project.namespace, project, label_name: label.name, - only_path: context[:only_path]) - end - - def render_colored_label(label) - LabelsHelper.render_colored_label(label) - end - - # Parameters to pass to `Label.find_by` based on the given arguments - # - # id - Integer ID to pass. If present, returns {id: id} - # name - String name to pass. If `id` is absent, finds by name without - # surrounding quotes. - # - # Returns a Hash. - def label_params(id, name) - if name - { name: name.tr('"', '') } - else - { id: id } - end - end - end - end -end diff --git a/lib/gitlab/markdown/filter/markdown_filter.rb b/lib/gitlab/markdown/filter/markdown_filter.rb deleted file mode 100644 index 921e2a0794e..00000000000 --- a/lib/gitlab/markdown/filter/markdown_filter.rb +++ /dev/null @@ -1,39 +0,0 @@ -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/filter/merge_request_reference_filter.rb b/lib/gitlab/markdown/filter/merge_request_reference_filter.rb deleted file mode 100644 index 2eb77c46da7..00000000000 --- a/lib/gitlab/markdown/filter/merge_request_reference_filter.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces merge request references with links. References - # to merge requests that do not exist are ignored. - # - # This filter supports cross-project references. - class MergeRequestReferenceFilter < AbstractReferenceFilter - def self.object_class - MergeRequest - end - - def find_object(project, id) - project.merge_requests.find_by(iid: id) - end - - def url_for_object(mr, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_merge_request_url(project.namespace, project, mr, - only_path: context[:only_path]) - end - - def object_link_text_extras(object, matches) - extras = super - - path = matches[:path] if matches.names.include?("path") - case path - when '/diffs' - extras.unshift "diffs" - when '/commits' - extras.unshift "commits" - when '/builds' - extras.unshift "builds" - end - - extras - end - end - end -end diff --git a/lib/gitlab/markdown/filter/redactor_filter.rb b/lib/gitlab/markdown/filter/redactor_filter.rb deleted file mode 100644 index 33ef7ce18b5..00000000000 --- a/lib/gitlab/markdown/filter/redactor_filter.rb +++ /dev/null @@ -1,43 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that removes references to records that the current user does - # not have permission to view. - # - # Expected to be run in its own post-processing pipeline. - # - class RedactorFilter < HTML::Pipeline::Filter - def call - doc.css('a.gfm').each do |node| - unless user_can_reference?(node) - # The reference should be replaced by the original text, - # which is not always the same as the rendered text. - text = node.attr('data-original') || node.text - node.replace(text) - end - end - - doc - end - - private - - def user_can_reference?(node) - if node.has_attribute?('data-reference-filter') - reference_type = node.attr('data-reference-filter') - reference_filter = Gitlab::Markdown.const_get(reference_type) - - reference_filter.user_can_reference?(current_user, node, context) - else - true - end - end - - def current_user - context[:current_user] - end - end - end -end diff --git a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb b/lib/gitlab/markdown/filter/reference_gatherer_filter.rb deleted file mode 100644 index 62f241b4967..00000000000 --- a/lib/gitlab/markdown/filter/reference_gatherer_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that gathers all referenced records that the current user has - # permission to view. - # - # Expected to be run in its own post-processing pipeline. - # - class ReferenceGathererFilter < HTML::Pipeline::Filter - def initialize(*) - super - - result[:references] ||= Hash.new { |hash, type| hash[type] = [] } - end - - def call - doc.css('a.gfm').each do |node| - gather_references(node) - end - - load_lazy_references unless context[:load_lazy_references] == false - - doc - end - - private - - def gather_references(node) - return unless node.has_attribute?('data-reference-filter') - - reference_type = node.attr('data-reference-filter') - reference_filter = Gitlab::Markdown.const_get(reference_type) - - return if context[:reference_filter] && reference_filter != context[:reference_filter] - - return unless reference_filter.user_can_reference?(current_user, node, context) - - references = reference_filter.referenced_by(node) - return unless references - - references.each do |type, values| - Array.wrap(values).each do |value| - result[:references][type] << value - end - end - end - - # Will load all references of one type using one query. - def load_lazy_references - refs = result[:references] - refs.each do |type, values| - refs[type] = ReferenceFilter::LazyReference.load(values) - end - end - - def current_user - context[:current_user] - end - end - end -end diff --git a/lib/gitlab/markdown/filter/relative_link_filter.rb b/lib/gitlab/markdown/filter/relative_link_filter.rb deleted file mode 100644 index 81f60120fcd..00000000000 --- a/lib/gitlab/markdown/filter/relative_link_filter.rb +++ /dev/null @@ -1,157 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'uri' - -module Gitlab - module Markdown - # HTML filter that "fixes" relative links to files in a repository. - # - # Context options: - # :commit - # :project - # :project_wiki - # :ref - # :requested_path - class RelativeLinkFilter < HTML::Pipeline::Filter - def call - return doc unless linkable_files? - - doc.search('a:not(.gfm)').each do |el| - process_link_attr el.attribute('href') - end - - doc.search('img').each do |el| - process_link_attr el.attribute('src') - end - - doc - end - - protected - - def linkable_files? - context[:project_wiki].nil? && repository.try(:exists?) && !repository.empty? - end - - def process_link_attr(html_attr) - return if html_attr.blank? - - uri = URI(html_attr.value) - if uri.relative? && uri.path.present? - html_attr.value = rebuild_relative_uri(uri).to_s - end - rescue URI::Error - # noop - end - - def rebuild_relative_uri(uri) - file_path = relative_file_path(uri.path) - - uri.path = [ - relative_url_root, - context[:project].path_with_namespace, - path_type(file_path), - ref || context[:project].default_branch, # if no ref exists, point to the default branch - file_path - ].compact.join('/').squeeze('/').chomp('/') - - uri - end - - def relative_file_path(path) - nested_path = build_relative_path(path, context[:requested_path]) - file_exists?(nested_path) ? nested_path : path - end - - # Convert a relative path into its correct location based on the currently - # requested path - # - # path - Relative path String - # request_path - Currently-requested path String - # - # Examples: - # - # # File in the same directory as the current path - # build_relative_path("users.md", "doc/api/README.md") - # # => "doc/api/users.md" - # - # # File in the same directory, which is also the current path - # build_relative_path("users.md", "doc/api") - # # => "doc/api/users.md" - # - # # Going up one level to a different directory - # build_relative_path("../update/7.14-to-8.0.md", "doc/api/README.md") - # # => "doc/update/7.14-to-8.0.md" - # - # Returns a String - def build_relative_path(path, request_path) - return request_path if path.empty? - return path unless request_path - - parts = request_path.split('/') - parts.pop if path_type(request_path) != 'tree' - - while parts.length > 1 && path.start_with?('../') - parts.pop - path.sub!('../', '') - end - - parts.push(path).join('/') - end - - def file_exists?(path) - return false if path.nil? - repository.blob_at(current_sha, path).present? || - repository.tree(current_sha, path).entries.any? - end - - # Get the type of the given path - # - # path - String path to check - # - # Examples: - # - # path_type('doc/README.md') # => 'blob' - # path_type('doc/logo.png') # => 'raw' - # path_type('doc/api') # => 'tree' - # - # Returns a String - def path_type(path) - unescaped_path = Addressable::URI.unescape(path) - - if tree?(unescaped_path) - 'tree' - elsif image?(unescaped_path) - 'raw' - else - 'blob' - end - end - - def tree?(path) - repository.tree(current_sha, path).entries.any? - end - - def image?(path) - repository.blob_at(current_sha, path).try(:image?) - end - - def current_sha - context[:commit].try(:id) || - ref ? repository.commit(ref).try(:sha) : repository.head_commit.sha - end - - def relative_url_root - Gitlab.config.gitlab.relative_url_root.presence || '/' - end - - def ref - context[:ref] - end - - def repository - context[:project].try(:repository) - end - end - end -end diff --git a/lib/gitlab/markdown/filter/sanitization_filter.rb b/lib/gitlab/markdown/filter/sanitization_filter.rb deleted file mode 100644 index cf153f30622..00000000000 --- a/lib/gitlab/markdown/filter/sanitization_filter.rb +++ /dev/null @@ -1,99 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'html/pipeline/sanitization_filter' - -module Gitlab - module Markdown - # Sanitize HTML - # - # Extends HTML::Pipeline::SanitizationFilter with a custom whitelist. - class SanitizationFilter < HTML::Pipeline::SanitizationFilter - def whitelist - # Descriptions are more heavily sanitized, allowing only a few elements. - # See http://git.io/vkuAN - if context[:inline_sanitization] - whitelist = LIMITED - whitelist[:elements] -= %w(pre code img ol ul li) - else - whitelist = super - end - - customize_whitelist(whitelist) - - whitelist - end - - private - - def customized?(transformers) - transformers.last.source_location[0] == __FILE__ - end - - def customize_whitelist(whitelist) - # Only push these customizations once - return if customized?(whitelist[:transformers]) - - # Allow code highlighting - whitelist[:attributes]['pre'] = %w(class) - whitelist[:attributes]['span'] = %w(class) - - # Allow table alignment - whitelist[:attributes]['th'] = %w(style) - whitelist[:attributes]['td'] = %w(style) - - # Allow span elements - whitelist[:elements].push('span') - - # Allow any protocol in `a` elements... - whitelist[:protocols].delete('a') - - # ...but then remove links with the `javascript` protocol - whitelist[:transformers].push(remove_javascript_links) - - # Remove `rel` attribute from `a` elements - whitelist[:transformers].push(remove_rel) - - # Remove `class` attribute from non-highlight spans - whitelist[:transformers].push(clean_spans) - - whitelist - end - - def remove_javascript_links - lambda do |env| - node = env[:node] - - return unless node.name == 'a' - return unless node.has_attribute?('href') - - if node['href'].start_with?('javascript', ':javascript') - node.remove_attribute('href') - end - end - end - - def remove_rel - lambda do |env| - if env[:node_name] == 'a' - env[:node].remove_attribute('rel') - end - end - end - - def clean_spans - lambda do |env| - node = env[:node] - - return unless node.name == 'span' - return unless node.has_attribute?('class') - - unless has_ancestor?(node, 'pre') - node.remove_attribute('class') - end - - { node_whitelist: [node] } - end - end - end - end -end diff --git a/lib/gitlab/markdown/filter/snippet_reference_filter.rb b/lib/gitlab/markdown/filter/snippet_reference_filter.rb deleted file mode 100644 index f7bd07c2a34..00000000000 --- a/lib/gitlab/markdown/filter/snippet_reference_filter.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces snippet references with links. References to - # snippets that do not exist are ignored. - # - # This filter supports cross-project references. - class SnippetReferenceFilter < AbstractReferenceFilter - def self.object_class - Snippet - end - - def find_object(project, id) - project.snippets.find_by(id: id) - end - - def url_for_object(snippet, project) - h = Gitlab::Application.routes.url_helpers - h.namespace_project_snippet_url(project.namespace, project, snippet, - only_path: context[:only_path]) - end - end - end -end diff --git a/lib/gitlab/markdown/filter/syntax_highlight_filter.rb b/lib/gitlab/markdown/filter/syntax_highlight_filter.rb deleted file mode 100644 index 8597e02f0de..00000000000 --- a/lib/gitlab/markdown/filter/syntax_highlight_filter.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'rouge/plugins/redcarpet' - -module Gitlab - module Markdown - # HTML Filter to highlight fenced code blocks - # - class SyntaxHighlightFilter < HTML::Pipeline::Filter - include Rouge::Plugins::Redcarpet - - def call - doc.search('pre > code').each do |node| - highlight_node(node) - end - - doc - end - - def highlight_node(node) - language = node.attr('class') - code = node.text - - begin - highlighted = block_code(code, language) - rescue - # Gracefully handle syntax highlighter bugs/errors to ensure - # users can still access an issue/comment/etc. - highlighted = "<pre>#{code}</pre>" - end - - # Replace the parent `pre` element with the entire highlighted block - node.parent.replace(highlighted) - end - - private - - # Override Rouge::Plugins::Redcarpet#rouge_formatter - def rouge_formatter(lexer) - Rouge::Formatters::HTMLGitlab.new( - cssclass: "code highlight js-syntax-highlight #{lexer.tag}") - end - end - end -end diff --git a/lib/gitlab/markdown/filter/table_of_contents_filter.rb b/lib/gitlab/markdown/filter/table_of_contents_filter.rb deleted file mode 100644 index bbb3bf7fc8b..00000000000 --- a/lib/gitlab/markdown/filter/table_of_contents_filter.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' - -module Gitlab - module Markdown - # HTML filter that adds an anchor child element to all Headers in a - # document, so that they can be linked to. - # - # Generates the Table of Contents with links to each header. See Results. - # - # Based on HTML::Pipeline::TableOfContentsFilter. - # - # Context options: - # :no_header_anchors - Skips all processing done by this filter. - # - # Results: - # :toc - String containing Table of Contents data as a `ul` element with - # `li` child elements. - class TableOfContentsFilter < HTML::Pipeline::Filter - PUNCTUATION_REGEXP = /[^\p{Word}\- ]/u - - def call - return doc if context[:no_header_anchors] - - result[:toc] = "" - - headers = Hash.new(0) - - doc.css('h1, h2, h3, h4, h5, h6').each do |node| - text = node.text - - id = text.downcase - id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation - id.gsub!(' ', '-') # replace spaces with dash - id.squeeze!('-') # replace multiple dashes with one - - uniq = (headers[id] > 0) ? "-#{headers[id]}" : '' - headers[id] += 1 - - if header_content = node.children.first - href = "#{id}#{uniq}" - push_toc(href, text) - header_content.add_previous_sibling(anchor_tag(href)) - end - end - - result[:toc] = %Q{<ul class="section-nav">\n#{result[:toc]}</ul>} unless result[:toc].empty? - - doc - end - - private - - def anchor_tag(href) - %Q{<a id="#{href}" class="anchor" href="##{href}" aria-hidden="true"></a>} - end - - def push_toc(href, text) - result[:toc] << %Q{<li><a href="##{href}">#{text}</a></li>\n} - end - end - end -end diff --git a/lib/gitlab/markdown/filter/task_list_filter.rb b/lib/gitlab/markdown/filter/task_list_filter.rb deleted file mode 100644 index 2f133ae8500..00000000000 --- a/lib/gitlab/markdown/filter/task_list_filter.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'gitlab/markdown' -require 'task_list/filter' - -module Gitlab - module Markdown - # Work around a bug in the default TaskList::Filter that adds a `task-list` - # class to every list element, regardless of whether or not it contains a - # task list. - # - # This is a (hopefully) temporary fix, pending a new release of the - # task_list gem. - # - # See https://github.com/github/task_list/pull/60 - class TaskListFilter < TaskList::Filter - def add_css_class(node, *new_class_names) - if new_class_names.include?('task-list') - super if node.children.any? { |c| c['class'] == 'task-list-item' } - else - super - end - end - end - end -end diff --git a/lib/gitlab/markdown/filter/upload_link_filter.rb b/lib/gitlab/markdown/filter/upload_link_filter.rb deleted file mode 100644 index fbada73ab86..00000000000 --- a/lib/gitlab/markdown/filter/upload_link_filter.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'gitlab/markdown' -require 'html/pipeline/filter' -require 'uri' - -module Gitlab - module Markdown - # HTML filter that "fixes" relative upload links to files. - # Context options: - # :project (required) - Current project - # - class UploadLinkFilter < HTML::Pipeline::Filter - def call - doc.search('a').each do |el| - process_link_attr el.attribute('href') - end - - doc.search('img').each do |el| - process_link_attr el.attribute('src') - end - - doc - end - - protected - - def process_link_attr(html_attr) - return if html_attr.blank? - - uri = html_attr.value - if uri.starts_with?("/uploads/") - html_attr.value = build_url(uri).to_s - end - end - - def build_url(uri) - File.join(Gitlab.config.gitlab.url, context[:project].path_with_namespace, uri) - end - - # Ensure that a :project key exists in context - # - # Note that while the key might exist, its value could be nil! - def validate - needs :project - end - end - end -end diff --git a/lib/gitlab/markdown/filter/user_reference_filter.rb b/lib/gitlab/markdown/filter/user_reference_filter.rb deleted file mode 100644 index 0a20d9c0347..00000000000 --- a/lib/gitlab/markdown/filter/user_reference_filter.rb +++ /dev/null @@ -1,129 +0,0 @@ -require 'gitlab/markdown' - -module Gitlab - module Markdown - # HTML filter that replaces user or group references with links. - # - # A special `@all` reference is also supported. - class UserReferenceFilter < ReferenceFilter - # Public: Find `@user` user references in text - # - # UserReferenceFilter.references_in(text) do |match, username| - # "<a href=...>@#{user}</a>" - # end - # - # text - String text to search. - # - # Yields the String match, and the String user name. - # - # Returns a String replaced with the return of the block. - def self.references_in(text) - text.gsub(User.reference_pattern) do |match| - yield match, $~[:user] - end - end - - def self.referenced_by(node) - if node.has_attribute?('data-group') - group = Group.find(node.attr('data-group')) rescue nil - return unless group - - { user: group.users } - elsif node.has_attribute?('data-user') - { user: LazyReference.new(User, node.attr('data-user')) } - elsif node.has_attribute?('data-project') - project = Project.find(node.attr('data-project')) rescue nil - return unless project - - { user: project.team.members.flatten } - end - end - - def self.user_can_reference?(user, node, context) - if node.has_attribute?('data-group') - group = Group.find(node.attr('data-group')) rescue nil - Ability.abilities.allowed?(user, :read_group, group) - else - super - end - end - - def call - replace_text_nodes_matching(User.reference_pattern) do |content| - user_link_filter(content) - end - - replace_link_nodes_with_href(User.reference_pattern) do |link, text| - user_link_filter(link, link_text: text) - end - end - - # Replace `@user` user references in text with links to the referenced - # user's profile page. - # - # text - String text to replace references in. - # - # Returns a String with `@user` references replaced with links. All links - # have `gfm` and `gfm-project_member` class names attached for styling. - def user_link_filter(text, link_text: nil) - self.class.references_in(text) do |match, username| - if username == 'all' - link_to_all(link_text: link_text) - elsif namespace = Namespace.find_by(path: username) - link_to_namespace(namespace, link_text: link_text) || match - else - match - end - end - end - - private - - def urls - Gitlab::Application.routes.url_helpers - end - - def link_class - reference_class(:project_member) - end - - def link_to_all(link_text: nil) - project = context[:project] - url = urls.namespace_project_url(project.namespace, project, - only_path: context[:only_path]) - data = data_attribute(project: project.id) - text = link_text || User.reference_prefix + 'all' - - link_tag(url, data, text) - end - - def link_to_namespace(namespace, link_text: nil) - if namespace.is_a?(Group) - link_to_group(namespace.path, namespace, link_text: link_text) - else - link_to_user(namespace.path, namespace, link_text: link_text) - end - end - - def link_to_group(group, namespace, link_text: nil) - url = urls.group_url(group, only_path: context[:only_path]) - data = data_attribute(group: namespace.id) - text = link_text || Group.reference_prefix + group - - link_tag(url, data, text) - end - - def link_to_user(user, namespace, link_text: nil) - url = urls.user_url(user, only_path: context[:only_path]) - data = data_attribute(user: namespace.owner_id) - text = link_text || User.reference_prefix + user - - link_tag(url, data, text) - end - - def link_tag(url, data, text) - %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>) - end - end - end -end |