Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/banzai')
-rw-r--r--lib/banzai/filter/base_relative_link_filter.rb18
-rw-r--r--lib/banzai/filter/markdown_pre_escape_filter.rb2
-rw-r--r--lib/banzai/filter/references/label_reference_filter.rb93
-rw-r--r--lib/banzai/filter/references/reference_cache.rb48
-rw-r--r--lib/banzai/filter/references/reference_filter.rb4
-rw-r--r--lib/banzai/filter/upload_link_filter.rb14
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb3
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb9
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb33
9 files changed, 129 insertions, 95 deletions
diff --git a/lib/banzai/filter/base_relative_link_filter.rb b/lib/banzai/filter/base_relative_link_filter.rb
index 3f775abb185..b2eaeb69f61 100644
--- a/lib/banzai/filter/base_relative_link_filter.rb
+++ b/lib/banzai/filter/base_relative_link_filter.rb
@@ -13,18 +13,12 @@ module Banzai
protected
def linkable_attributes
- if Feature.enabled?(:optimize_linkable_attributes, project, default_enabled: :yaml)
- # Nokorigi Nodeset#search performs badly for documents with many nodes
- #
- # Here we store fetched attributes in the shared variable "result"
- # This variable is passed through the chain of filters and can be
- # accessed by them
- result[:linkable_attributes] ||= fetch_linkable_attributes
- else
- strong_memoize(:linkable_attributes) do
- fetch_linkable_attributes
- end
- end
+ # Nokorigi Nodeset#search performs badly for documents with many nodes
+ #
+ # Here we store fetched attributes in the shared variable "result"
+ # This variable is passed through the chain of filters and can be
+ # accessed by them
+ result[:linkable_attributes] ||= fetch_linkable_attributes
end
def relative_url_root
diff --git a/lib/banzai/filter/markdown_pre_escape_filter.rb b/lib/banzai/filter/markdown_pre_escape_filter.rb
index 0c53444681d..8d54d140877 100644
--- a/lib/banzai/filter/markdown_pre_escape_filter.rb
+++ b/lib/banzai/filter/markdown_pre_escape_filter.rb
@@ -30,8 +30,6 @@ module Banzai
LITERAL_KEYWORD = 'cmliteral'
def call
- return @text unless Feature.enabled?(:honor_escaped_markdown, context[:group] || context[:project]&.group)
-
@text.gsub(ASCII_PUNCTUATION) do |match|
# The majority of markdown does not have literals. If none
# are found, we can bypass the post filter
diff --git a/lib/banzai/filter/references/label_reference_filter.rb b/lib/banzai/filter/references/label_reference_filter.rb
index bf6b3e47d3b..12afece6e53 100644
--- a/lib/banzai/filter/references/label_reference_filter.rb
+++ b/lib/banzai/filter/references/label_reference_filter.rb
@@ -8,21 +8,57 @@ module Banzai
self.reference_type = :label
self.object_class = Label
+ def parent_records(parent, ids)
+ return Label.none unless parent.is_a?(Project) || parent.is_a?(Group)
+
+ labels = find_labels(parent)
+ label_ids = ids.map {|y| y[:label_id]}.compact
+ label_names = ids.map {|y| y[:label_name]}.compact
+ id_relation = labels.where(id: label_ids)
+ label_relation = labels.where(title: label_names)
+
+ Label.from_union([id_relation, label_relation])
+ end
+
def find_object(parent_object, id)
- find_labels(parent_object).find(id)
+ key = reference_cache.records_per_parent[parent_object].keys.find do |k|
+ k[:label_id] == id[:label_id] || k[:label_name] == id[:label_name]
+ end
+
+ reference_cache.records_per_parent[parent_object][key] if key
+ end
+
+ # 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 label_id
+ # or the label_name, but not both. But below, we have both pieces of information.
+ # But it's accounted for in `find_object`
+ def parse_symbol(symbol, match_data)
+ { label_id: match_data[:label_id]&.to_i, label_name: match_data[:label_name]&.tr('"', '') }
+ end
+
+ # We assume that most classes are identifying records by ID.
+ #
+ # 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
+ def record_identifier(record)
+ { label_id: record.id, label_name: record.title }
end
def references_in(text, pattern = Label.reference_pattern)
labels = {}
- unescaped_html = unescape_html_entities(text).gsub(pattern) do |match|
- namespace = $~[:namespace]
- project = $~[:project]
- project_path = reference_cache.full_project_path(namespace, project)
- label = find_label_cached(project_path, $~[:label_id], $~[:label_name])
-
- if label
- labels[label.id] = yield match, label.id, project, namespace, $~
- "#{REFERENCE_PLACEHOLDER}#{label.id}"
+
+ unescaped_html = unescape_html_entities(text).gsub(pattern).with_index do |match, index|
+ ident = identifier($~)
+ label = yield match, ident, $~[:project], $~[:namespace], $~
+
+ if label != match
+ labels[index] = label
+ "#{REFERENCE_PLACEHOLDER}#{index}"
else
match
end
@@ -33,20 +69,6 @@ module Banzai
escape_with_placeholders(unescaped_html, labels)
end
- def find_label_cached(parent_ref, label_id, label_name)
- cached_call(:banzai_find_label_cached, label_name&.tr('"', '') || label_id, path: [object_class, parent_ref]) do
- find_label(parent_ref, label_id, label_name)
- end
- end
-
- def find_label(parent_ref, label_id, label_name)
- parent = parent_from_ref(parent_ref)
- return unless parent
-
- label_params = label_params(label_id, label_name)
- find_labels(parent).find_by(label_params)
- end
-
def find_labels(parent)
params = if parent.is_a?(Group)
{ group_id: parent.id,
@@ -60,21 +82,6 @@ module Banzai
LabelsFinder.new(nil, params).execute(skip_authorization: true)
end
- # Parameters to pass to `Label.find_by` based on the given arguments
- #
- # id - Integer ID to pass. If present, returns {id: id}
- # name - String name to pass. If `id` is absent, finds by name without
- # surrounding quotes.
- #
- # Returns a Hash.
- def label_params(id, name)
- if name
- { name: name.tr('"', '') }
- else
- { id: id.to_i }
- end
- end
-
def url_for_object(label, parent)
label_url_method =
if context[:label_url_method]
@@ -121,6 +128,14 @@ module Banzai
presenter = object.present(issuable_subject: project || group)
LabelsHelper.label_tooltip_title(presenter)
end
+
+ def parent
+ project || group
+ end
+
+ def requires_unescaping?
+ true
+ end
end
end
end
diff --git a/lib/banzai/filter/references/reference_cache.rb b/lib/banzai/filter/references/reference_cache.rb
index ab0c74e00d9..24b8b4984cd 100644
--- a/lib/banzai/filter/references/reference_cache.rb
+++ b/lib/banzai/filter/references/reference_cache.rb
@@ -29,15 +29,15 @@ module Banzai
refs = Hash.new { |hash, key| hash[key] = Set.new }
nodes.each do |node|
- node.to_html.scan(regex) do
- path = if parent_type == :project
- full_project_path($~[:namespace], $~[:project])
- else
- full_group_path($~[:group])
- end
+ prepare_node_for_scan(node).scan(regex) do
+ parent_path = if parent_type == :project
+ full_project_path($~[:namespace], $~[:project])
+ else
+ full_group_path($~[:group])
+ end
ident = filter.identifier($~)
- refs[path] << ident if ident
+ refs[parent_path] << ident if ident
end
end
@@ -55,9 +55,23 @@ module Banzai
@per_reference ||= {}
@per_reference[parent_type] ||= begin
- refs = references_per_parent.keys.to_set
+ refs = references_per_parent.keys
+ parent_ref = {}
- find_for_paths(refs.to_a).index_by(&:full_path)
+ # if we already have a parent, no need to query it again
+ refs.each do |ref|
+ next unless ref
+
+ if context[:project]&.full_path == ref
+ parent_ref[ref] = context[:project]
+ elsif context[:group]&.full_path == ref
+ parent_ref[ref] = context[:group]
+ end
+
+ refs -= [ref] if parent_ref[ref]
+ end
+
+ find_for_paths(refs).index_by(&:full_path).merge(parent_ref)
end
end
@@ -87,7 +101,7 @@ module Banzai
@_records_per_project[filter.object_class.to_s.underscore]
end
- def relation_for_paths(paths)
+ def objects_for_paths(paths)
klass = parent_type.to_s.camelize.constantize
result = klass.where_full_path_in(paths)
return result if parent_type == :group
@@ -102,7 +116,7 @@ module Banzai
to_query = paths - cache.keys
unless to_query.empty?
- records = relation_for_paths(to_query)
+ records = objects_for_paths(to_query)
found = []
records.each do |record|
@@ -119,7 +133,7 @@ module Banzai
cache.slice(*paths).values.compact
else
- relation_for_paths(paths)
+ objects_for_paths(paths)
end
end
@@ -170,6 +184,16 @@ module Banzai
def refs_cache
Gitlab::SafeRequestStore["banzai_#{parent_type}_refs".to_sym] ||= {}
end
+
+ def prepare_node_for_scan(node)
+ html = node.to_html
+
+ filter.requires_unescaping? ? unescape_html_entities(html) : html
+ end
+
+ def unescape_html_entities(text)
+ CGI.unescapeHTML(text.to_s)
+ end
end
end
end
diff --git a/lib/banzai/filter/references/reference_filter.rb b/lib/banzai/filter/references/reference_filter.rb
index 58436f4505e..6c2c993cc01 100644
--- a/lib/banzai/filter/references/reference_filter.rb
+++ b/lib/banzai/filter/references/reference_filter.rb
@@ -109,6 +109,10 @@ module Banzai
context[:group]
end
+ def requires_unescaping?
+ false
+ end
+
private
# Returns a data attribute String to attach to a reference link
diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index ceb7547a85d..2572481c8fc 100644
--- a/lib/banzai/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -15,16 +15,10 @@ module Banzai
def call
return doc if context[:system_note]
- if Feature.enabled?(:optimize_linkable_attributes, project, default_enabled: :yaml)
- # We exclude processed upload links from the linkable attributes to
- # prevent further modifications by RepositoryLinkFilter
- linkable_attributes.reject! do |attr|
- process_link_to_upload_attr(attr)
- end
- else
- linkable_attributes.each do |attr|
- process_link_to_upload_attr(attr)
- end
+ # We exclude processed upload links from the linkable attributes to
+ # prevent further modifications by RepositoryLinkFilter
+ linkable_attributes.reject! do |attr|
+ process_link_to_upload_attr(attr)
end
doc
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index c86d5f08ded..17a73f29afb 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -9,7 +9,8 @@ module Banzai
Filter::AssetProxyFilter,
Filter::ExternalLinkFilter,
Filter::PlantumlFilter,
- Filter::SyntaxHighlightFilter
+ Filter::SyntaxHighlightFilter,
+ Filter::KrokiFilter
]
end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 97c7173ac0f..6b1491cc56b 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -66,14 +66,7 @@ module Banzai
# These associations are primarily used for checking permissions.
# Eager loading these ensures we don't end up running dozens of
# queries in this process.
- project: [
- { namespace: :owner },
- { group: [:owners, :group_members] },
- :invited_groups,
- :project_members,
- :project_feature,
- :route
- ]
+ project: [:namespace, :project_feature, :route]
}
),
self.class.data_attribute
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 24bc1a24e09..1664fa1f9ff 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -7,6 +7,19 @@ module Banzai
self.reference_type = :merge_request
+ def nodes_visible_to_user(user, nodes)
+ return super if Feature.disabled?(:optimize_merge_request_parser, user, default_enabled: :yaml)
+
+ merge_request_nodes = nodes.select { |node| node.has_attribute?(self.class.data_attribute) }
+ records = projects_for_nodes(merge_request_nodes)
+
+ merge_request_nodes.select do |node|
+ project = records[node]
+
+ project && can_read_reference?(user, project)
+ end
+ end
+
def records_for_nodes(nodes)
@merge_requests_for_nodes ||= grouped_objects_for_nodes(
nodes,
@@ -17,27 +30,25 @@ module Banzai
# These associations are primarily used for checking permissions.
# Eager loading these ensures we don't end up running dozens of
# queries in this process.
- target_project: [
- { namespace: [:owner, :route] },
- { group: [:owners, :group_members] },
- :invited_groups,
- :project_members,
- :project_feature,
- :route
- ]
+ target_project: [{ namespace: :route }, :project_feature, :route]
}),
self.class.data_attribute
)
end
- def can_read_reference?(user, merge_request)
+ def can_read_reference?(user, object)
memo = strong_memoize(:can_read_reference) { {} }
- project_id = merge_request.project_id
+ project_id = object.project_id
return memo[project_id] if memo.key?(project_id)
- memo[project_id] = can?(user, :read_merge_request_iid, merge_request.project)
+ memo[project_id] = can?(user, :read_merge_request_iid, object)
+ end
+
+ def projects_for_nodes(nodes)
+ @projects_for_nodes ||=
+ grouped_objects_for_nodes(nodes, Project.includes(:project_feature, :group, :namespace), 'data-project')
end
end
end