Class: Banzai::Filter::References::MilestoneReferenceFilter

Inherits:
AbstractReferenceFilter show all
Defined in:
lib/banzai/filter/references/milestone_reference_filter.rb

Overview

HTML filter that replaces milestone references with links.

Constant Summary

Constants inherited from AbstractReferenceFilter

AbstractReferenceFilter::REFERENCE_PLACEHOLDER, AbstractReferenceFilter::REFERENCE_PLACEHOLDER_PATTERN

Instance Method Summary collapse

Methods inherited from AbstractReferenceFilter

#call, #data_attributes_for, #find_object_cached, #find_object_from_link, #find_object_from_link_cached, #from_ref_cached, #identifier, #initialize, #object_link_filter, #object_link_text_extras, #parent_type, #symbol_from_match, #url_for_object_cached, #wrap_link

Methods included from CrossProjectReference

#parent_from_ref

Methods inherited from ReferenceFilter

call, #call, #call_and_update_nodes, #each_node, #group, #initialize, #nodes, #object_class, #project

Methods included from OutputSafety

#escape_once

Methods included from RequestStoreReferenceCache

#cached_call, #get_or_set_cache

Constructor Details

This class inherits a constructor from Banzai::Filter::References::AbstractReferenceFilter

Instance Method Details

#find_milestones(parent, find_by_iid = false) ⇒ Object


100
101
102
103
104
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 100

def find_milestones(parent, find_by_iid = false)
  finder_params = milestone_finder_params(parent, find_by_iid)

  MilestonesFinder.new(finder_params).execute
end

#find_object(parent_object, id) ⇒ Object


30
31
32
33
34
35
36
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 30

def find_object(parent_object, id)
  key = reference_cache.records_per_parent[parent_object].keys.find do |k|
    k[:milestone_iid] == id[:milestone_iid] || k[:milestone_name] == id[:milestone_name]
  end

  reference_cache.records_per_parent[parent_object][key] if key
end

#group_and_ancestors_ids(parent) ⇒ Object


116
117
118
119
120
121
122
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 116

def group_and_ancestors_ids(parent)
  if group_context?(parent)
    parent.self_and_ancestors.select(:id)
  elsif project_context?(parent)
    parent.group&.self_and_ancestors&.select(:id)
  end
end

#group_context?(parent) ⇒ Boolean

Returns:

  • (Boolean)

67
68
69
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 67

def group_context?(parent)
  parent.is_a?(Group)
end

#milestone_finder_params(parent, find_by_iid) ⇒ Object


106
107
108
109
110
111
112
113
114
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 106

def milestone_finder_params(parent, find_by_iid)
  { order: nil, state: 'all' }.tap do |params|
    params[:project_ids] = parent.id if project_context?(parent)

    # We don't support IID lookups because IIDs can clash between
    # group/project milestones and group/subgroup milestones.
    params[:group_ids] = group_and_ancestors_ids(parent) unless find_by_iid
  end
end

130
131
132
133
134
135
136
137
138
139
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 130

def object_link_text(object, matches)
  milestone_link = escape_once(super)
  reference = object.project&.to_reference_base(project)

  if reference.present?
    "#{milestone_link} <i>in #{reference}</i>".html_safe
  else
    milestone_link
  end
end

141
142
143
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 141

def object_link_title(object, matches)
  nil
end

#parentObject


145
146
147
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 145

def parent
  project || group
end

#parent_records(parent, ids) ⇒ Object


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 11

def parent_records(parent, ids)
  return Milestone.none unless valid_context?(parent)

  milestone_iids = ids.map {|y| y[:milestone_iid]}.compact
  unless milestone_iids.empty?
    iid_relation = find_milestones(parent, true).where(iid: milestone_iids)
  end

  milestone_names = ids.map {|y| y[:milestone_name]}.compact
  unless milestone_names.empty?
    milestone_relation = find_milestones(parent, false).where(name: milestone_names)
  end

  relation = [iid_relation, milestone_relation].compact
  return Milestone.none if relation.all?(Milestone.none)

  Milestone.from_union(relation).includes(:project, :group)
end

#parse_symbol(symbol, match_data) ⇒ Object

Transform a symbol extracted from the text to a meaningful value

This method has the contract that if a string `ref` refers to a record `record`, then `parse_symbol(ref) == record_identifier(record)`.

This contract is slightly broken here, as we only have either the milestone_iid or the milestone_name, but not both. But below, we have both pieces of information. But it's accounted for in `find_object`


46
47
48
49
50
51
52
53
54
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 46

def parse_symbol(symbol, match_data)
  if symbol
    # when parsing links, there is no `match_data[:milestone_iid]`, but `symbol`
    # holds the iid
    { milestone_iid: symbol.to_i, milestone_name: nil }
  else
    { milestone_iid: match_data[:milestone_iid]&.to_i, milestone_name: match_data[:milestone_name]&.tr('"', '') }
  end
end

#project_context?(parent) ⇒ Boolean

Returns:

  • (Boolean)

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

def project_context?(parent)
  parent.is_a?(Project)
end

#record_identifier(record) ⇒ Object

This method has the contract that if a string `ref` refers to a record `record`, then `class.parse_symbol(ref) == record_identifier(record)`. See note in `parse_symbol` above


59
60
61
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 59

def record_identifier(record)
  { milestone_iid: record.iid, milestone_name: record.name }
end

#references_in(text, pattern = Milestone.reference_pattern) ⇒ Object


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 75

def references_in(text, pattern = Milestone.reference_pattern)
  # We'll handle here the references that follow the `reference_pattern`.
  # Other patterns (for example, the link pattern) are handled by the
  # default implementation.
  return super(text, pattern) if pattern != Milestone.reference_pattern

  milestones = {}

  unescaped_html = unescape_html_entities(text).gsub(pattern).with_index do |match, index|
    ident = identifier($~)
    milestone = yield match, ident, $~[:project], $~[:namespace], $~

    if milestone != match
      milestones[index] = milestone
      "#{REFERENCE_PLACEHOLDER}#{index}"
    else
      match
    end
  end

  return text if milestones.empty?

  escape_with_placeholders(unescaped_html, milestones)
end

#requires_unescaping?Boolean

Returns:

  • (Boolean)

149
150
151
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 149

def requires_unescaping?
  true
end

#url_for_object(milestone, project) ⇒ Object


124
125
126
127
128
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 124

def url_for_object(milestone, project)
  Gitlab::Routing
    .url_helpers
    .milestone_url(milestone, only_path: context[:only_path])
end

#valid_context?(parent) ⇒ Boolean

Returns:

  • (Boolean)

63
64
65
# File 'lib/banzai/filter/references/milestone_reference_filter.rb', line 63

def valid_context?(parent)
  group_context?(parent) || project_context?(parent)
end