summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/user_mentions/models/concerns/isolated_mentionable.rb
blob: be9c0ad2b3a2b88c9b0ba8cd08bb3d64ecb9d751 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    module UserMentions
      module Models
        module Concerns
          # == IsolatedMentionable concern
          #
          # Shortcutted for isolation version of Mentionable to be used in mentions migrations
          #
          module IsolatedMentionable
            extend ::ActiveSupport::Concern

            class_methods do
              # Indicate which attributes of the Mentionable to search for GFM references.
              def attr_mentionable(attr, options = {})
                attr = attr.to_s
                mentionable_attrs << [attr, options]
              end
            end

            included do
              # Accessor for attributes marked mentionable.
              cattr_accessor :mentionable_attrs, instance_accessor: false do
                []
              end

              if self < Participable
                participant -> (user, ext) { all_references(user, extractor: ext) }
              end
            end

            def all_references(current_user = nil, extractor: nil)
              # Use custom extractor if it's passed in the function parameters.
              if extractor
                extractors[current_user] = extractor
              else
                extractor = extractors[current_user] ||=
                  Gitlab::BackgroundMigration::UserMentions::Lib::Gitlab::IsolatedReferenceExtractor.new(project, current_user)

                extractor.reset_memoized_values
              end

              self.class.mentionable_attrs.each do |attr, options|
                text    = __send__(attr) # rubocop:disable GitlabSecurity/PublicSend
                options = options.merge(
                  cache_key: [self, attr],
                  author: author,
                  skip_project_check: skip_project_check?
                ).merge(mentionable_params)

                cached_html = self.try(:updated_cached_html_for, attr.to_sym)
                options[:rendered] = cached_html if cached_html

                extractor.analyze(text, options)
              end

              extractor
            end

            def extractors
              @extractors ||= {}
            end

            def skip_project_check?
              false
            end

            def build_mention_values(resource_foreign_key)
              refs = all_references(author)

              mentioned_users_ids = array_to_sql(refs.mentioned_users.pluck(:id))
              mentioned_projects_ids = array_to_sql(refs.mentioned_projects.pluck(:id))
              mentioned_groups_ids = array_to_sql(refs.isolated_mentioned_groups.pluck(:id))

              return if mentioned_users_ids.blank? && mentioned_projects_ids.blank? && mentioned_groups_ids.blank?

              {
                "#{resource_foreign_key}": user_mention_resource_id,
                note_id: user_mention_note_id,
                mentioned_users_ids: mentioned_users_ids,
                mentioned_projects_ids: mentioned_projects_ids,
                mentioned_groups_ids: mentioned_groups_ids
              }
            end

            def array_to_sql(ids_array)
              return unless ids_array.present?

              '{' + ids_array.join(", ") + '}'
            end

            private

            def mentionable_params
              {}
            end
          end
        end
      end
    end
  end
end