debian-mirror-gitlab/lib/banzai/filter/reference_filter.rb

136 lines
4 KiB
Ruby
Raw Normal View History

2015-12-23 02:04:40 +05:30
module Banzai
module Filter
2015-09-11 14:41:01 +05:30
# 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
def self.user_can_see_reference?(user, node, context)
2015-10-24 18:46:33 +05:30
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
def self.user_can_reference?(user, node, context)
true
end
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)
2015-12-23 02:04:40 +05:30
# # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
2015-10-24 18:46:33 +05:30
#
# data_attribute(project: 3, merge_request: 4)
2015-12-23 02:04:40 +05:30
# # => "data-reference-filter=\"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 = {})
2015-12-23 02:04:40 +05:30
attributes[:reference_filter] = self.class.name.demodulize
2016-06-02 11:05:42 +05:30
attributes.delete(:original) if context[:no_original_data]
attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
2015-09-11 14:41:01 +05:30
end
def escape_once(html)
html.html_safe? ? html : ERB::Util.html_escape_once(html)
2015-09-11 14:41:01 +05:30
end
2016-06-02 11:05:42 +05:30
def ignore_ancestor_query
@ignore_ancestor_query ||= begin
2015-09-11 14:41:01 +05:30
parents = %w(pre code a style)
parents << 'blockquote' if context[:ignore_blockquotes]
2016-06-02 11:05:42 +05:30
parents.map { |n| "ancestor::#{n}" }.join(' or ')
end
2015-09-11 14:41:01 +05:30
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
2016-06-02 11:05:42 +05:30
# Ensure that a :project key exists in context
2015-09-11 14:41:01 +05:30
#
2016-06-02 11:05:42 +05:30
# Note that while the key might exist, its value could be nil!
def validate
needs :project
2015-09-11 14:41:01 +05:30
end
2016-06-02 11:05:42 +05:30
# Iterates over all <a> and text() nodes in a document.
2015-12-23 02:04:40 +05:30
#
2016-06-02 11:05:42 +05:30
# Nodes are skipped whenever their ancestor is one of the nodes returned
# by `ignore_ancestor_query`. Link tags are not processed if they have a
# "gfm" class or the "href" attribute is empty.
def each_node
query = %Q{descendant-or-self::text()[not(#{ignore_ancestor_query})]
| descendant-or-self::a[
not(contains(concat(" ", @class, " "), " gfm ")) and not(@href = "")
]}
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
doc.xpath(query).each do |node|
yield node
end
end
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
# Yields the link's URL and text whenever the node is a valid <a> tag.
def yield_valid_link(node)
link = CGI.unescape(node.attr('href').to_s)
text = node.text
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
return unless link.force_encoding('UTF-8').valid_encoding?
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
yield link, text
2015-12-23 02:04:40 +05:30
end
2016-06-02 11:05:42 +05:30
def replace_text_when_pattern_matches(node, pattern)
return unless node.text =~ pattern
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
content = node.to_html
html = yield content
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
node.replace(html) unless content == html
end
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
def replace_link_node_with_text(node, link)
html = yield
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
node.replace(html) unless html == node.text
end
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
def replace_link_node_with_href(node, link)
html = yield
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
node.replace(html) unless html == link
end
2015-12-23 02:04:40 +05:30
2016-06-02 11:05:42 +05:30
def text_node?(node)
node.is_a?(Nokogiri::XML::Text)
2015-12-23 02:04:40 +05:30
end
2016-06-02 11:05:42 +05:30
def element_node?(node)
node.is_a?(Nokogiri::XML::Element)
2015-09-11 14:41:01 +05:30
end
end
end
end