Class: Banzai::Filter::ReferenceFilter

Inherits:
HTML::Pipeline::Filter
  • Object
show all
Includes:
OutputSafety, RequestStoreReferenceCache
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 Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from OutputSafety

#escape_once

Methods included from RequestStoreReferenceCache

#cached_call, #get_or_set_cache

Constructor Details

#initialize(doc, context = nil, result = nil) ⇒ ReferenceFilter

Returns a new instance of ReferenceFilter.


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

def initialize(doc, context = nil, result = nil)
  super

  @new_nodes = {}
  @nodes = self.result[:reference_filter_nodes]
end

Class Attribute Details

.reference_typeObject

Returns the value of attribute reference_type


18
19
20
# File 'lib/banzai/filter/reference_filter.rb', line 18

def reference_type
  @reference_type
end

Class Method Details

.call(doc, context = nil, result = nil) ⇒ Object


20
21
22
# File 'lib/banzai/filter/reference_filter.rb', line 20

def call(doc, context = nil, result = nil)
  new(doc, context, result).call_and_update_nodes
end

Instance Method Details

#call_and_update_nodesObject


32
33
34
# File 'lib/banzai/filter/reference_filter.rb', line 32

def call_and_update_nodes
  with_update_nodes { call }
end

#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-type=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""

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

Returns a String


50
51
52
53
54
55
56
57
58
59
60
# File 'lib/banzai/filter/reference_filter.rb', line 50

def data_attribute(attributes = {})
  attributes = attributes.reject { |_, v| v.nil? }

  attributes[:reference_type] ||= self.class.reference_type
  attributes[:container] ||= 'body'
  attributes[:placement] ||= 'top'
  attributes.delete(:original) if context[:no_original_data]
  attributes.map do |key, value|
    %Q(data-#{key.to_s.dasherize}="#{escape_once(value)}")
  end.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.


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

def each_node
  return to_enum(__method__) unless block_given?

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

#element_node?(node) ⇒ Boolean

Returns:

  • (Boolean)

155
156
157
# File 'lib/banzai/filter/reference_filter.rb', line 155

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

#groupObject


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

def group
  context[:group]
end

#ignore_ancestor_queryObject


62
63
64
65
66
67
68
69
# File 'lib/banzai/filter/reference_filter.rb', line 62

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

#nodesObject

Returns an Array containing all HTML nodes.


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

def nodes
  @nodes ||= each_node.to_a
end

#projectObject


71
72
73
# File 'lib/banzai/filter/reference_filter.rb', line 71

def project
  context[:project]
end

#reference_class(type, tooltip: true) ⇒ Object


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

def reference_class(type, tooltip: true)
  gfm_klass = "gfm gfm-#{type}"

  return gfm_klass unless tooltip

  "#{gfm_klass} has-tooltip"
end

145
146
147
148
149
# File 'lib/banzai/filter/reference_filter.rb', line 145

def replace_link_node_with_href(node, index, link)
  html = yield

  replace_text_with_html(node, index, html) unless html == link
end

139
140
141
142
143
# File 'lib/banzai/filter/reference_filter.rb', line 139

def replace_link_node_with_text(node, index)
  html = yield

  replace_text_with_html(node, index, html) unless html == node.text
end

#replace_text_when_pattern_matches(node, index, pattern) ⇒ Object


130
131
132
133
134
135
136
137
# File 'lib/banzai/filter/reference_filter.rb', line 130

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

  content = node.to_html
  html = yield content

  replace_text_with_html(node, index, html) unless html == content
end

#skip_project_check?Boolean

Returns:

  • (Boolean)

83
84
85
# File 'lib/banzai/filter/reference_filter.rb', line 83

def skip_project_check?
  context[:skip_project_check]
end

#text_node?(node) ⇒ Boolean

Returns:

  • (Boolean)

151
152
153
# File 'lib/banzai/filter/reference_filter.rb', line 151

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

#userObject


79
80
81
# File 'lib/banzai/filter/reference_filter.rb', line 79

def user
  context[:user]
end

#validateObject

Ensure that a :project key exists in context

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


98
99
100
# File 'lib/banzai/filter/reference_filter.rb', line 98

def validate
  needs :project unless skip_project_check?
end

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

Yields:

  • (link, inner_html)

121
122
123
124
125
126
127
128
# File 'lib/banzai/filter/reference_filter.rb', line 121

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

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

  yield link, inner_html
end