Class: Banzai::Filter::References::ReferenceFilter
- Inherits:
-
HTML::Pipeline::Filter
- Object
- HTML::Pipeline::Filter
- Banzai::Filter::References::ReferenceFilter
- Includes:
- Concerns::HtmlWriter, Concerns::OutputSafety, Concerns::PipelineTimingCheck, Concerns::TextReplacer, RequestStoreReferenceCache
- Defined in:
- lib/banzai/filter/references/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.
Direct Known Subclasses
AbstractReferenceFilter, ExternalIssueReferenceFilter, ProjectReferenceFilter, UserReferenceFilter
Constant Summary collapse
- REFERENCE_TYPE_ATTRIBUTE =
:reference_type- REFERENCE_TYPE_DATA_ATTRIBUTE_NAME =
"data-#{REFERENCE_TYPE_ATTRIBUTE.to_s.dasherize}".freeze
Constants included from Concerns::TextReplacer
Concerns::TextReplacer::REFERENCE_PLACEHOLDER, Concerns::TextReplacer::REFERENCE_PLACEHOLDER_PATTERN
Constants included from Concerns::PipelineTimingCheck
Concerns::PipelineTimingCheck::MAX_PIPELINE_SECONDS
Class Attribute Summary collapse
-
.object_class ⇒ Object
Implement in child class Example: self.object_class = MergeRequest.
-
.reference_type ⇒ Object
Implement in child class Example: self.reference_type = :merge_request.
Class Method Summary collapse
Instance Method Summary collapse
- #call ⇒ Object
- #call_and_update_nodes ⇒ Object
-
#each_node ⇒ Object
Iterates over all <a> and text() nodes in a document.
- #group ⇒ Object
-
#initialize(doc, context = nil, result = nil) ⇒ ReferenceFilter
constructor
A new instance of ReferenceFilter.
-
#nodes ⇒ Object
Returns an Array containing all HTML nodes.
- #nodes? ⇒ Boolean
- #object_class ⇒ Object
- #project ⇒ Object
-
#references_in(text, pattern = object_reference_pattern) ⇒ Object
Public: Find references in text (like ‘!123` for merge requests).
- #requires_unescaping? ⇒ Boolean
Methods included from Concerns::TextReplacer
#replace_references_in_text_with_html
Methods included from Concerns::HtmlWriter
Methods included from Concerns::OutputSafety
Methods included from RequestStoreReferenceCache
#cached_call, #get_or_set_cache
Methods included from Concerns::PipelineTimingCheck
Constructor Details
#initialize(doc, context = nil, result = nil) ⇒ ReferenceFilter
Returns a new instance of ReferenceFilter.
38 39 40 41 42 43 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 38 def initialize(doc, context = nil, result = nil) super @new_nodes = {} @nodes = self.result[:reference_filter_nodes] end |
Class Attribute Details
.object_class ⇒ Object
Implement in child class Example: self.object_class = MergeRequest
31 32 33 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 31 def object_class @object_class end |
.reference_type ⇒ Object
Implement in child class Example: self.reference_type = :merge_request
27 28 29 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 27 def reference_type @reference_type end |
Class Method Details
.call(doc, context = nil, result = nil) ⇒ Object
33 34 35 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 33 def call(doc, context = nil, result = nil) new(doc, context, result).call_and_update_nodes end |
Instance Method Details
#call ⇒ Object
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 49 def call ref_pattern_start = /\A#{object_reference_pattern}\z/ nodes.each_with_index do |node, index| if text_node?(node) replace_node_when_text_matches(node, index, object_reference_pattern) do |content| object_link_filter(content, object_reference_pattern) end elsif element_node?(node) yield_valid_link(node) do |link, _text, inner_html| if ref_pattern_start.match?(link) html = object_link_filter(link, ref_pattern_start, link_content_html: inner_html) replace_node_with_html(node, index, html) if html end end end end doc end |
#call_and_update_nodes ⇒ Object
45 46 47 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 45 def call_and_update_nodes with_update_nodes { call } end |
#each_node ⇒ Object
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.
121 122 123 124 125 126 127 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 121 def each_node return to_enum(__method__) unless block_given? doc.xpath(query).each do |node| yield node end end |
#group ⇒ Object
146 147 148 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 146 def group context[:group] end |
#nodes ⇒ Object
Returns an Array containing all HTML nodes.
130 131 132 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 130 def nodes @nodes ||= each_node.to_a end |
#nodes? ⇒ Boolean
134 135 136 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 134 def nodes? @nodes.present? end |
#object_class ⇒ Object
138 139 140 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 138 def object_class self.class.object_class end |
#project ⇒ Object
142 143 144 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 142 def project context[:project] end |
#references_in(text, pattern = object_reference_pattern) ⇒ Object
Public: Find references in text (like ‘!123` for merge requests).
Simplified usage (see AbstractReferenceFilter#object_link_filter for a full example):
references_in(text) do |match_text, id, project_ref, namespace_ref, matches|
object = find_object(namespace_ref, project_ref, id)
if object
write_opening_tag("a", {
"href" => "...",
# ... other attributes ...
}) << CGI.escapeHTML(object.to_reference) << "</a>"
else
CGI.escapeHTML(match_text)
end
end
text - String text to make replacements in. This is the content of a DOM text node, and cannot be returned without modification.
Yields each String text match as the first argument; the remaining arguments depend on the particular subclass of ReferenceFilter being used. The block must return HTML, and not text.
Returns a HTML String replaced with replacements made, or nil if no replacements were made.
Notes:
-
The input is text – we expect to match e.g. &1 or %“Git 2.5” without having to deal with HTML entities, as our object reference patterns are not compatible with entities.
-
Likewise, any strings yielded to the block are text, but the block must return HTML, whether it’s a successful resolution or not. Any text input used in the block must be escaped for return.
-
The return value is HTML, as the whole point is to create links (and possibly other elements). Care must be taken that any input text is escaped before reaching the output. This must be respected in all subclasses of ReferenceFilter.
See AbstractReferenceFilter#references_in for a reference implementation that safely handles user input.
112 113 114 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 112 def references_in(text, pattern = object_reference_pattern) raise NotImplementedError, "#{self.class} must implement method: #{__callee__}" end |
#requires_unescaping? ⇒ Boolean
150 151 152 |
# File 'lib/banzai/filter/references/reference_filter.rb', line 150 def requires_unescaping? false end |