debian-mirror-gitlab/lib/gitlab/markdown/reference_filter.rb

134 lines
4 KiB
Ruby
Raw Normal View History

2015-09-11 14:41:01 +05:30
require 'active_support/core_ext/string/output_safety'
2015-09-25 12:07:36 +05:30
require 'gitlab/markdown'
2015-09-11 14:41:01 +05:30
require 'html/pipeline/filter'
module Gitlab
module Markdown
# Base class for GitLab Flavored Markdown reference filters.
#
# References within <pre>, <code>, <a>, and <style> elements are ignored.
#
# Context options:
# :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links.
class ReferenceFilter < HTML::Pipeline::Filter
2015-10-24 18:46:33 +05:30
LazyReference = Struct.new(:klass, :ids) do
def self.load(refs)
lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
2015-11-26 14:37:03 +05:30
2015-10-24 18:46:33 +05:30
lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
ids = refs.flat_map(&:ids)
klass.where(id: ids)
end
values + lazy_values
end
def load
self.klass.where(id: self.ids)
end
end
def self.user_can_reference?(user, node, context)
if node.has_attribute?('data-project')
project_id = node.attr('data-project').to_i
return true if project_id == context[:project].try(:id)
project = Project.find(project_id) rescue nil
Ability.abilities.allowed?(user, :read_project, project)
else
true
end
end
2015-09-11 14:41:01 +05:30
2015-10-24 18:46:33 +05:30
def self.referenced_by(node)
raise NotImplementedError, "#{self} does not implement #{__method__}"
2015-09-11 14:41:01 +05:30
end
# Returns a data attribute String to attach to a reference link
#
2015-10-24 18:46:33 +05:30
# attributes - Hash, where the key becomes the data attribute name and the
# value is the data attribute value
2015-09-11 14:41:01 +05:30
#
# Examples:
#
2015-10-24 18:46:33 +05:30
# data_attribute(project: 1, issue: 2)
# # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
#
# data_attribute(project: 3, merge_request: 4)
# # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
2015-09-11 14:41:01 +05:30
#
# Returns a String
2015-10-24 18:46:33 +05:30
def data_attribute(attributes = {})
attributes[:reference_filter] = self.class.name
attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ")
2015-09-11 14:41:01 +05:30
end
def escape_once(html)
ERB::Util.html_escape_once(html)
end
def ignore_parents
@ignore_parents ||= begin
# Don't look for references in text nodes that are children of these
# elements.
parents = %w(pre code a style)
parents << 'blockquote' if context[:ignore_blockquotes]
parents.to_set
end
end
def ignored_ancestry?(node)
has_ancestor?(node, ignore_parents)
end
def project
context[:project]
end
def reference_class(type)
2015-09-25 12:07:36 +05:30
"gfm gfm-#{type}"
2015-09-11 14:41:01 +05:30
end
# Iterate through the document's text nodes, yielding the current node's
# content if:
#
# * The `project` context value is present AND
# * The node's content matches `pattern` AND
# * The node is not an ancestor of an ignored node type
#
# pattern - Regex pattern against which to match the node's content
#
# Yields the current node's String contents. The result of the block will
# replace the node's existing content and update the current document.
#
2015-10-24 18:46:33 +05:30
# Returns the updated Nokogiri::HTML::DocumentFragment object.
2015-09-11 14:41:01 +05:30
def replace_text_nodes_matching(pattern)
return doc if project.nil?
search_text_nodes(doc).each do |node|
next if ignored_ancestry?(node)
2015-11-26 14:37:03 +05:30
next unless node.text =~ pattern
content = node.to_html
2015-09-11 14:41:01 +05:30
html = yield content
next if html == content
node.replace(html)
end
doc
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