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

contact.rb « customer_relations « models « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f6455da890b72b5e0d8496da3f86e9dbf9da0f9f (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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# frozen_string_literal: true

class CustomerRelations::Contact < ApplicationRecord
  include Gitlab::SQL::Pattern
  include Sortable
  include StripAttribute

  self.table_name = "customer_relations_contacts"

  belongs_to :group, -> { where(type: Group.sti_name) }, foreign_key: 'group_id'
  belongs_to :organization, optional: true
  has_many :issue_contacts, inverse_of: :contact
  has_many :issues, through: :issue_contacts, inverse_of: :customer_relations_contacts

  strip_attributes! :phone, :first_name, :last_name

  enum state: {
    inactive: 0,
    active: 1
  }

  validates :group, presence: true
  validates :phone, length: { maximum: 32 }
  validates :first_name, presence: true, length: { maximum: 255 }
  validates :last_name, presence: true, length: { maximum: 255 }
  validates :email, length: { maximum: 255 }
  validates :description, length: { maximum: 1024 }
  validates :email, uniqueness: { case_sensitive: false, scope: :group_id }
  validate :validate_email_format
  validate :validate_root_group

  scope :order_scope_asc, ->(field) { order(arel_table[field].asc.nulls_last) }
  scope :order_scope_desc, ->(field) { order(arel_table[field].desc.nulls_last) }

  scope :order_by_organization_asc, -> { includes(:organization).order("customer_relations_organizations.name ASC NULLS LAST") }
  scope :order_by_organization_desc, -> { includes(:organization).order("customer_relations_organizations.name DESC NULLS LAST") }

  def self.reference_prefix
    '[contact:'
  end

  def self.reference_prefix_quoted
    '["contact:'
  end

  def self.reference_postfix
    ']'
  end

  # Searches for contacts with a matching first name, last name, email or description.
  #
  # This method uses ILIKE on PostgreSQL
  #
  # query - The search query as a String
  #
  # Returns an ActiveRecord::Relation.
  def self.search(query)
    fuzzy_search(query, [:first_name, :last_name, :email, :description], use_minimum_char_limit: false)
  end

  def self.search_by_state(state)
    where(state: state)
  end

  def self.sort_by_field(field, direction)
    if direction == :asc
      order_scope_asc(field)
    else
      order_scope_desc(field)
    end
  end

  def self.sort_by_organization(direction)
    if direction == :asc
      order_by_organization_asc
    else
      order_by_organization_desc
    end
  end

  def self.sort_by_name
    order(Gitlab::Pagination::Keyset::Order.build([
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'last_name',
        order_expression: arel_table[:last_name].asc,
        distinct: false
      ),
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'first_name',
        order_expression: arel_table[:first_name].asc,
        distinct: false
      ),
      Gitlab::Pagination::Keyset::ColumnOrderDefinition.new(
        attribute_name: 'id',
        order_expression: arel_table[:id].asc
      )
    ]))
  end

  def self.find_ids_by_emails(group, emails)
    raise ArgumentError, "Cannot lookup more than #{MAX_PLUCK} emails" if emails.length > MAX_PLUCK

    where(group: group).where('lower(email) in (?)', emails.map(&:downcase)).pluck(:id)
  end

  def self.exists_for_group?(group)
    return false unless group

    exists?(group: group)
  end

  def self.move_to_root_group(group)
    update_query = <<~SQL
      UPDATE #{CustomerRelations::IssueContact.table_name}
      SET contact_id = new_contacts.id
      FROM #{table_name} AS existing_contacts
      JOIN #{table_name} AS new_contacts ON new_contacts.group_id = :old_group_id AND LOWER(new_contacts.email) = LOWER(existing_contacts.email)
      WHERE existing_contacts.group_id = :new_group_id AND contact_id = existing_contacts.id
    SQL
    connection.execute(sanitize_sql([
      update_query,
      old_group_id: group.root_ancestor.id,
      new_group_id: group.id
      ]))

    dupes_query = <<~SQL
      DELETE FROM #{table_name} AS existing_contacts
      USING #{table_name} AS new_contacts
      WHERE existing_contacts.group_id = :new_group_id AND new_contacts.group_id = :old_group_id AND LOWER(new_contacts.email) = LOWER(existing_contacts.email)
    SQL
    connection.execute(sanitize_sql([
      dupes_query,
      old_group_id: group.root_ancestor.id,
      new_group_id: group.id
      ]))

    where(group: group).update_all(group_id: group.root_ancestor.id)
  end

  def self.counts_by_state
    group(:state).count
  end

  private

  def validate_email_format
    return unless email

    self.errors.add(:email, I18n.t(:invalid, scope: 'valid_email.validations.email')) unless ValidateEmail.valid?(self.email)
  end

  def validate_root_group
    return if group&.root?

    self.errors.add(:base, _('contacts can only be added to root groups'))
  end
end