summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb81
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb34
2 files changed, 74 insertions, 41 deletions
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 685b43605ae..ef4578aabd6 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -54,42 +54,42 @@ module Banzai
self.class.references_in(*args, &block)
end
+ # Implement in child class
+ # Example: project.merge_requests.find
def find_object(project, id)
- # Implement in child class
- # Example: project.merge_requests.find
end
- def find_object_cached(project, id)
- if RequestStore.active?
- cache = find_objects_cache[object_class][project.id]
+ # Override if the link reference pattern produces a different ID (global
+ # ID vs internal ID, for instance) to the regular reference pattern.
+ def find_object_from_link(project, id)
+ find_object(project, id)
+ end
- get_or_set_cache(cache, id) { find_object(project, id) }
- else
+ # Implement in child class
+ # Example: project_merge_request_url
+ def url_for_object(object, project)
+ end
+
+ def find_object_cached(project, id)
+ cached_call(:banzai_find_object, id, path: [object_class, project.id]) do
find_object(project, id)
end
end
- def project_from_ref_cached(ref)
- if RequestStore.active?
- cache = project_refs_cache
-
- get_or_set_cache(cache, ref) { project_from_ref(ref) }
- else
- project_from_ref(ref)
+ def find_object_from_link_cached(project, id)
+ cached_call(:banzai_find_object_from_link, id, path: [object_class, project.id]) do
+ find_object_from_link(project, id)
end
end
- def url_for_object(object, project)
- # Implement in child class
- # Example: project_merge_request_url
+ def project_from_ref_cached(ref)
+ cached_call(:banzai_project_refs, ref) do
+ project_from_ref(ref)
+ end
end
def url_for_object_cached(object, project)
- if RequestStore.active?
- cache = url_for_object_cache[object_class][project.id]
-
- get_or_set_cache(cache, object) { url_for_object(object, project) }
- else
+ cached_call(:banzai_url_for_object, object, path: [object_class, project.id]) do
url_for_object(object, project)
end
end
@@ -120,7 +120,7 @@ module Banzai
if link == inner_html && inner_html =~ /\A#{link_pattern}/
replace_link_node_with_text(node, link) do
- object_link_filter(inner_html, link_pattern)
+ object_link_filter(inner_html, link_pattern, link_reference: true)
end
next
@@ -128,7 +128,7 @@ module Banzai
if link =~ /\A#{link_pattern}\z/
replace_link_node_with_href(node, link) do
- object_link_filter(link, link_pattern, link_content: inner_html)
+ object_link_filter(link, link_pattern, link_content: inner_html, link_reference: true)
end
next
@@ -146,15 +146,26 @@ module Banzai
# text - String text to replace references in.
# pattern - Reference pattern to match against.
# link_content - Original content of the link being replaced.
+ # link_reference - True if this was using the link reference pattern,
+ # false otherwise.
#
# Returns a String with references replaced with links. All links
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
- def object_link_filter(text, pattern, link_content: nil)
+ def object_link_filter(text, pattern, link_content: nil, link_reference: false)
references_in(text, pattern) do |match, id, project_ref, namespace_ref, matches|
project_path = full_project_path(namespace_ref, project_ref)
project = project_from_ref_cached(project_path)
- if project && object = find_object_cached(project, id)
+ if project
+ object =
+ if link_reference
+ find_object_from_link_cached(project, id)
+ else
+ find_object_cached(project, id)
+ end
+ end
+
+ if object
title = object_link_title(object)
klass = reference_class(object_sym)
@@ -297,15 +308,17 @@ module Banzai
RequestStore[:banzai_project_refs] ||= {}
end
- def find_objects_cache
- RequestStore[:banzai_find_objects_cache] ||= Hash.new do |hash, key|
- hash[key] = Hash.new { |h, k| h[k] = {} }
- end
- end
+ def cached_call(request_store_key, cache_key, path: [])
+ if RequestStore.active?
+ cache = RequestStore[request_store_key] ||= Hash.new do |hash, key|
+ hash[key] = Hash.new { |h, k| h[k] = {} }
+ end
- def url_for_object_cache
- RequestStore[:banzai_url_for_object] ||= Hash.new do |hash, key|
- hash[key] = Hash.new { |h, k| h[k] = {} }
+ cache = cache.dig(*path) if path.any?
+
+ get_or_set_cache(cache, cache_key) { yield }
+ else
+ yield
end
end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index 45c033d32a8..4fc5f211e84 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -8,8 +8,15 @@ module Banzai
Milestone
end
+ # Links to project milestones contain the IID, but when we're handling
+ # 'regular' references, we need to use the global ID to disambiguate
+ # between group and project milestones.
def find_object(project, id)
- project.milestones.find_by(iid: id)
+ find_milestone_with_finder(project, id: id)
+ end
+
+ def find_object_from_link(project, iid)
+ find_milestone_with_finder(project, iid: iid)
end
def references_in(text, pattern = Milestone.reference_pattern)
@@ -22,7 +29,7 @@ module Banzai
milestone = find_milestone($~[:project], $~[:namespace], $~[:milestone_iid], $~[:milestone_name])
if milestone
- yield match, milestone.iid, $~[:project], $~[:namespace], $~
+ yield match, milestone.id, $~[:project], $~[:namespace], $~
else
match
end
@@ -36,7 +43,8 @@ module Banzai
return unless project
milestone_params = milestone_params(milestone_id, milestone_name)
- project.milestones.find_by(milestone_params)
+
+ find_milestone_with_finder(project, milestone_params)
end
def milestone_params(iid, name)
@@ -47,15 +55,27 @@ module Banzai
end
end
+ def find_milestone_with_finder(project, params)
+ finder_params = { project_ids: [project.id], order: nil }
+
+ # We don't support IID lookups for group milestones, because IIDs can
+ # clash between group and project milestones.
+ if project.group && !params[:iid]
+ finder_params[:group_ids] = [project.group.id]
+ end
+
+ MilestonesFinder.new(finder_params).execute.find_by(params)
+ end
+
def url_for_object(milestone, project)
- h = Gitlab::Routing.url_helpers
- h.project_milestone_url(project, milestone,
- only_path: context[:only_path])
+ Gitlab::Routing
+ .url_helpers
+ .milestone_url(milestone, only_path: context[:only_path])
end
def object_link_text(object, matches)
milestone_link = escape_once(super)
- reference = object.project.to_reference(project)
+ reference = object.project&.to_reference(project)
if reference.present?
"#{milestone_link} <i>in #{reference}</i>".html_safe