diff options
Diffstat (limited to 'lib/banzai')
-rw-r--r-- | lib/banzai/filter/references/label_reference_filter.rb | 20 | ||||
-rw-r--r-- | lib/banzai/filter/references/milestone_reference_filter.rb | 101 | ||||
-rw-r--r-- | lib/banzai/filter/upload_link_filter.rb | 19 | ||||
-rw-r--r-- | lib/banzai/filter/wiki_link_filter.rb | 19 | ||||
-rw-r--r-- | lib/banzai/reference_extractor.rb | 4 | ||||
-rw-r--r-- | lib/banzai/reference_parser/base_parser.rb | 12 | ||||
-rw-r--r-- | lib/banzai/reference_parser/commit_parser.rb | 2 | ||||
-rw-r--r-- | lib/banzai/reference_parser/commit_range_parser.rb | 2 | ||||
-rw-r--r-- | lib/banzai/reference_parser/external_issue_parser.rb | 2 | ||||
-rw-r--r-- | lib/banzai/reference_parser/issuable_parser.rb | 2 | ||||
-rw-r--r-- | lib/banzai/reference_parser/user_parser.rb | 2 |
11 files changed, 125 insertions, 60 deletions
diff --git a/lib/banzai/filter/references/label_reference_filter.rb b/lib/banzai/filter/references/label_reference_filter.rb index 12afece6e53..3ae9c5f8d90 100644 --- a/lib/banzai/filter/references/label_reference_filter.rb +++ b/lib/banzai/filter/references/label_reference_filter.rb @@ -11,13 +11,21 @@ module Banzai 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) + labels = find_labels(parent) + label_ids = ids.map {|y| y[:label_id]}.compact - Label.from_union([id_relation, label_relation]) + unless label_ids.empty? + id_relation = labels.where(id: label_ids) + end + + label_names = ids.map {|y| y[:label_name]}.compact + unless label_names.empty? + label_relation = labels.where(title: label_names) + end + + return Label.none if (relation = [id_relation, label_relation].compact).empty? + + Label.from_union(relation) end def find_object(parent_object, id) diff --git a/lib/banzai/filter/references/milestone_reference_filter.rb b/lib/banzai/filter/references/milestone_reference_filter.rb index 31a961f3e73..d992e667056 100644 --- a/lib/banzai/filter/references/milestone_reference_filter.rb +++ b/lib/banzai/filter/references/milestone_reference_filter.rb @@ -10,19 +10,55 @@ module Banzai self.reference_type = :milestone self.object_class = Milestone - # 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(parent, id) - return unless valid_context?(parent) + def parent_records(parent, ids) + return Milestone.none unless valid_context?(parent) - find_milestone_with_finder(parent, id: id) + 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 + + return Milestone.none if (relation = [iid_relation, milestone_relation].compact).empty? + + Milestone.from_union(relation).includes(:project, :group) + end + + 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 - def find_object_from_link(parent, iid) - return unless valid_context?(parent) + # 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` + 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 - find_milestone_with_finder(parent, iid: iid) + # 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) + { milestone_iid: record.iid, milestone_name: record.name } end def valid_context?(parent) @@ -50,12 +86,14 @@ module Banzai return super(text, pattern) if pattern != Milestone.reference_pattern milestones = {} - unescaped_html = unescape_html_entities(text).gsub(pattern) do |match| - milestone = find_milestone($~[:project], $~[:namespace], $~[:milestone_iid], $~[:milestone_name]) - if milestone - milestones[milestone.id] = yield match, milestone.id, $~[:project], $~[:namespace], $~ - "#{REFERENCE_PLACEHOLDER}#{milestone.id}" + 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 @@ -66,31 +104,10 @@ module Banzai escape_with_placeholders(unescaped_html, milestones) end - def find_milestone(project_ref, namespace_ref, milestone_id, milestone_name) - project_path = reference_cache.full_project_path(namespace_ref, project_ref) - - # Returns group if project is not found by path - parent = parent_from_ref(project_path) + def find_milestones(parent, find_by_iid = false) + finder_params = milestone_finder_params(parent, find_by_iid) - return unless parent - - milestone_params = milestone_params(milestone_id, milestone_name) - - find_milestone_with_finder(parent, milestone_params) - end - - def milestone_params(iid, name) - if name - { name: name.tr('"', '') } - else - { iid: iid.to_i } - end - end - - def find_milestone_with_finder(parent, params) - finder_params = milestone_finder_params(parent, params[:iid].present?) - - MilestonesFinder.new(finder_params).find_by(params) + MilestonesFinder.new(finder_params).execute end def milestone_finder_params(parent, find_by_iid) @@ -131,6 +148,14 @@ module Banzai def object_link_title(object, matches) nil end + + def parent + project || group + end + + def requires_unescaping? + true + end end end end diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb index 2572481c8fc..b110c59a4f1 100644 --- a/lib/banzai/filter/upload_link_filter.rb +++ b/lib/banzai/filter/upload_link_filter.rb @@ -45,13 +45,15 @@ module Banzai return end - html_attr.value = + path = if context[:only_path] path else Addressable::URI.join(Gitlab.config.gitlab.base_url, path).to_s end + replace_html_attr_value(html_attr, path) + if html_attr.name == 'href' html_attr.parent.set_attribute('data-link', 'true') end @@ -59,6 +61,21 @@ module Banzai html_attr.parent.add_class('gfm') end + def replace_html_attr_value(html_attr, path) + if path != html_attr.value + preserve_original_link(html_attr, html_attr.parent) + end + + html_attr.value = path + end + + def preserve_original_link(html_attr, node) + return if html_attr.blank? + return if node.value?('data-canonical-src') + + node.set_attribute('data-canonical-src', html_attr.value) + end + def group context[:group] end diff --git a/lib/banzai/filter/wiki_link_filter.rb b/lib/banzai/filter/wiki_link_filter.rb index 2b95d87ff8e..0736181d940 100644 --- a/lib/banzai/filter/wiki_link_filter.rb +++ b/lib/banzai/filter/wiki_link_filter.rb @@ -36,7 +36,7 @@ module Banzai protected def process_link(link_attr, node) - process_link_attr(link_attr) + process_link_attr(link_attr, node) remove_unsafe_links({ node: node }, remove_invalid_links: false) end @@ -44,14 +44,27 @@ module Banzai !context[:wiki].nil? end - def process_link_attr(html_attr) + def process_link_attr(html_attr, node) return if html_attr.blank? - html_attr.value = apply_rewrite_rules(html_attr.value) + rewritten_value = apply_rewrite_rules(html_attr.value) + + if html_attr.value != rewritten_value + preserve_original_link(html_attr, node) + end + + html_attr.value = rewritten_value rescue URI::Error, Addressable::URI::InvalidURIError # noop end + def preserve_original_link(html_attr, node) + return if html_attr.blank? + return if node.value?('data-canonical-src') + + node.set_attribute('data-canonical-src', html_attr.value) + end + def apply_rewrite_rules(link_string) Rewriter.new(link_string, wiki: context[:wiki], slug: context[:page_slug]).apply_rules end diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb index 3fc3ae02088..af0dcad107e 100644 --- a/lib/banzai/reference_extractor.rb +++ b/lib/banzai/reference_extractor.rb @@ -11,11 +11,11 @@ module Banzai @texts_and_contexts << { text: text, context: context } end - def references(type, project, current_user = nil) + def references(type, project, current_user, ids_only: false) context = RenderContext.new(project, current_user) processor = Banzai::ReferenceParser[type].new(context) - processor.process(html_documents) + processor.process(html_documents, ids_only: ids_only) end def reset_memoized_values diff --git a/lib/banzai/reference_parser/base_parser.rb b/lib/banzai/reference_parser/base_parser.rb index 3dfea8ee895..0c015ba00c7 100644 --- a/lib/banzai/reference_parser/base_parser.rb +++ b/lib/banzai/reference_parser/base_parser.rb @@ -76,9 +76,11 @@ module Banzai end # Returns an Array of objects referenced by any of the given HTML nodes. - def referenced_by(nodes) + def referenced_by(nodes, options = {}) ids = unique_attribute_values(nodes, self.class.data_attribute) + return ids if options.fetch(:ids_only, false) + if ids.empty? references_relation.none else @@ -194,7 +196,7 @@ module Banzai # Processes the list of HTML documents and returns an Array containing all # the references. - def process(documents) + def process(documents, ids_only: false) type = self.class.reference_type reference_options = self.class.reference_options @@ -202,17 +204,17 @@ module Banzai Querying.css(document, "a[data-reference-type='#{type}'].gfm", reference_options).to_a end - gather_references(nodes) + gather_references(nodes, ids_only: ids_only) end # Gathers the references for the given HTML nodes. Returns visible # references and a list of nodes which are not visible to the user - def gather_references(nodes) + def gather_references(nodes, ids_only: false) nodes = nodes_user_can_reference(current_user, nodes) visible = nodes_visible_to_user(current_user, nodes) not_visible = nodes - visible - { visible: referenced_by(visible), not_visible: not_visible } + { visible: referenced_by(visible, ids_only: ids_only), not_visible: not_visible } end # Returns a Hash containing the projects for a given list of HTML nodes. diff --git a/lib/banzai/reference_parser/commit_parser.rb b/lib/banzai/reference_parser/commit_parser.rb index 0bfb6a92020..88896970bc6 100644 --- a/lib/banzai/reference_parser/commit_parser.rb +++ b/lib/banzai/reference_parser/commit_parser.rb @@ -5,7 +5,7 @@ module Banzai class CommitParser < BaseParser self.reference_type = :commit - def referenced_by(nodes) + def referenced_by(nodes, options = {}) commit_ids = commit_ids_per_project(nodes) projects = find_projects_for_hash_keys(commit_ids) diff --git a/lib/banzai/reference_parser/commit_range_parser.rb b/lib/banzai/reference_parser/commit_range_parser.rb index 480eefd5c4d..fb4a392105f 100644 --- a/lib/banzai/reference_parser/commit_range_parser.rb +++ b/lib/banzai/reference_parser/commit_range_parser.rb @@ -5,7 +5,7 @@ module Banzai class CommitRangeParser < BaseParser self.reference_type = :commit_range - def referenced_by(nodes) + def referenced_by(nodes, options = {}) range_ids = commit_range_ids_per_project(nodes) projects = find_projects_for_hash_keys(range_ids) diff --git a/lib/banzai/reference_parser/external_issue_parser.rb b/lib/banzai/reference_parser/external_issue_parser.rb index 029b09dcd25..e8ee337064a 100644 --- a/lib/banzai/reference_parser/external_issue_parser.rb +++ b/lib/banzai/reference_parser/external_issue_parser.rb @@ -5,7 +5,7 @@ module Banzai class ExternalIssueParser < BaseParser self.reference_type = :external_issue - def referenced_by(nodes) + def referenced_by(nodes, options = {}) issue_ids = issue_ids_per_project(nodes) projects = find_projects_for_hash_keys(issue_ids) issues = [] diff --git a/lib/banzai/reference_parser/issuable_parser.rb b/lib/banzai/reference_parser/issuable_parser.rb index f8c26288017..efcaa6664b6 100644 --- a/lib/banzai/reference_parser/issuable_parser.rb +++ b/lib/banzai/reference_parser/issuable_parser.rb @@ -13,7 +13,7 @@ module Banzai end end - def referenced_by(nodes) + def referenced_by(nodes, options = {}) records = records_for_nodes(nodes) nodes.map { |node| records[node] }.compact.uniq diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb index 36c41c6615f..c40ca9dc7cd 100644 --- a/lib/banzai/reference_parser/user_parser.rb +++ b/lib/banzai/reference_parser/user_parser.rb @@ -5,7 +5,7 @@ module Banzai class UserParser < BaseParser self.reference_type = :user - def referenced_by(nodes) + def referenced_by(nodes, options = {}) group_ids = [] user_ids = [] project_ids = [] |