Class: Banzai::Filter::ReferenceFilter

Inherits:
HTML::Pipeline::Filter
  • Object
show all
Defined in:
lib/banzai/filter/reference_filter.rb

Overview

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 Method Summary collapse

Instance Method Summary collapse

Class Method Details

.referenced_by(node) ⇒ Object

Raises:

  • (NotImplementedError)

27
28
29
# File 'lib/banzai/filter/reference_filter.rb', line 27

def self.referenced_by(node)
  raise NotImplementedError, "#{self} does not implement #{__method__}"
end

.user_can_reference?(user, node, context) ⇒ Boolean

Returns:

  • (Boolean)

23
24
25
# File 'lib/banzai/filter/reference_filter.rb', line 23

def self.user_can_reference?(user, node, context)
  true
end

.user_can_see_reference?(user, node, context) ⇒ Boolean

Returns:

  • (Boolean)

11
12
13
14
15
16
17
18
19
20
21
# File 'lib/banzai/filter/reference_filter.rb', line 11

def self.user_can_see_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

Instance Method Details

#data_attribute(attributes = {}) ⇒ Object

Returns a data attribute String to attach to a reference link

attributes - Hash, where the key becomes the data attribute name and the

value is the data attribute value

Examples:

data_attribute(project: 1, issue: 2)
# => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""

data_attribute(project: 3, merge_request: 4)
# => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""

Returns a String


45
46
47
48
49
# File 'lib/banzai/filter/reference_filter.rb', line 45

def data_attribute(attributes = {})
  attributes[:reference_filter] = self.class.name.demodulize
  attributes.delete(:original) if context[:no_original_data]
  attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}") }.join(" ")
end

#each_nodeObject

Iterates over all <a> and text() nodes in a document.

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.


84
85
86
87
88
89
90
91
92
93
# File 'lib/banzai/filter/reference_filter.rb', line 84

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 = "")
  ]}

  doc.xpath(query).each do |node|
    yield node
  end
end

#element_node?(node) ⇒ Boolean

Returns:

  • (Boolean)

130
131
132
# File 'lib/banzai/filter/reference_filter.rb', line 130

def element_node?(node)
  node.is_a?(Nokogiri::XML::Element)
end

#escape_once(html) ⇒ Object


51
52
53
# File 'lib/banzai/filter/reference_filter.rb', line 51

def escape_once(html)
  html.html_safe? ? html : ERB::Util.html_escape_once(html)
end

#ignore_ancestor_queryObject


55
56
57
58
59
60
61
62
# File 'lib/banzai/filter/reference_filter.rb', line 55

def ignore_ancestor_query
  @ignore_ancestor_query ||= begin
    parents = %w(pre code a style)
    parents << 'blockquote' if context[:ignore_blockquotes]

    parents.map { |n| "ancestor::#{n}" }.join(' or ')
  end
end

#projectObject


64
65
66
# File 'lib/banzai/filter/reference_filter.rb', line 64

def project
  context[:project]
end

#reference_class(type) ⇒ Object


68
69
70
# File 'lib/banzai/filter/reference_filter.rb', line 68

def reference_class(type)
  "gfm gfm-#{type}"
end

120
121
122
123
124
# File 'lib/banzai/filter/reference_filter.rb', line 120

def replace_link_node_with_href(node, link)
  html = yield

  node.replace(html) unless html == link
end

114
115
116
117
118
# File 'lib/banzai/filter/reference_filter.rb', line 114

def replace_link_node_with_text(node, link)
  html = yield

  node.replace(html) unless html == node.text
end

#replace_text_when_pattern_matches(node, pattern) ⇒ Object


105
106
107
108
109
110
111
112
# File 'lib/banzai/filter/reference_filter.rb', line 105

def replace_text_when_pattern_matches(node, pattern)
  return unless node.text =~ pattern

  content = node.to_html
  html = yield content

  node.replace(html) unless content == html
end

#text_node?(node) ⇒ Boolean

Returns:

  • (Boolean)

126
127
128
# File 'lib/banzai/filter/reference_filter.rb', line 126

def text_node?(node)
  node.is_a?(Nokogiri::XML::Text)
end

#validateObject

Ensure that a :project key exists in context

Note that while the key might exist, its value could be nil!


75
76
77
# File 'lib/banzai/filter/reference_filter.rb', line 75

def validate
  needs :project
end

Yields the link's URL and text whenever the node is a valid <a> tag.

Yields:

  • (link, text)

96
97
98
99
100
101
102
103
# File 'lib/banzai/filter/reference_filter.rb', line 96

def yield_valid_link(node)
  link = CGI.unescape(node.attr('href').to_s)
  text = node.text

  return unless link.force_encoding('UTF-8').valid_encoding?

  yield link, text
end