File: notification_setting.rb

package info (click to toggle)
gitlab 17.6.5-19
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 629,368 kB
  • sloc: ruby: 1,915,304; javascript: 557,307; sql: 60,639; xml: 6,509; sh: 4,567; makefile: 1,239; python: 406
file content (130 lines) | stat: -rw-r--r-- 3,602 bytes parent folder | download
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
# frozen_string_literal: true

class NotificationSetting < ApplicationRecord
  include EachBatch
  include FromUnion

  enum level: { global: 3, watch: 2, participating: 1, mention: 4, disabled: 0, custom: 5 }, _default: :global

  belongs_to :user
  belongs_to :source, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
  belongs_to :project, foreign_key: 'source_id'

  validates :user, presence: true
  validates :level, presence: true
  validates :user_id, uniqueness: { scope: [:source_type, :source_id],
                                    message: "already exists in source",
                                    allow_nil: true }
  validate :notification_email_verified, if: :notification_email_changed?

  scope :for_groups, -> { where(source_type: 'Namespace') }

  # Exclude projects not included by the Project model's default scope (those that are
  # pending delete).
  #
  scope :for_projects, -> do
    includes(:project).references(:projects)
      .where(source_type: 'Project')
      .where.not(projects: { id: nil })
      .where.not(projects: { pending_delete: true })
  end

  scope :preload_source_route, -> { preload(source: [:route]) }

  scope :order_by_id_asc, -> { order(id: :asc) }

  # NOTE: Applicable unfound_translations.rb also needs to be updated when below events are changed.
  EMAIL_EVENTS = [
    :new_release,
    :new_note,
    :new_issue,
    :reopen_issue,
    :close_issue,
    :reassign_issue,
    :issue_due,
    :new_merge_request,
    :push_to_merge_request,
    :reopen_merge_request,
    :close_merge_request,
    :reassign_merge_request,
    :change_reviewer_merge_request,
    :merge_merge_request,
    :failed_pipeline,
    :fixed_pipeline,
    :success_pipeline,
    :moved_project,
    :merge_when_pipeline_succeeds
  ].freeze

  def self.email_events(source = nil)
    EMAIL_EVENTS
  end

  def self.allowed_fields(source = nil)
    NotificationSetting.email_events(source).dup + %i[level notification_email]
  end

  def email_events
    self.class.email_events(source)
  end

  EXCLUDED_WATCHER_EVENTS = [
    :push_to_merge_request,
    :issue_due,
    :success_pipeline,
    :approver
  ].freeze

  def self.find_or_create_for(source)
    setting = find_or_initialize_by(source: source)

    unless setting.persisted?
      setting.save
    end

    setting
  end

  def self.reset_email_for_user!(email)
    where(
      user_id: email.user_id,
      notification_email: email.email
    ).each_batch(of: 500) do |relation|
      relation.update_all(notification_email: nil)
    end
  end

  # Allow people to receive both failed pipeline/fixed pipeline notifications
  # if they already have custom notifications enabled,
  # as these are more like mentions than the other custom settings.
  def failed_pipeline
    bool = super

    bool.nil? || bool
  end
  alias_method :failed_pipeline?, :failed_pipeline

  def fixed_pipeline
    bool = super

    bool.nil? || bool
  end
  alias_method :fixed_pipeline?, :fixed_pipeline

  def event_enabled?(event)
    # We override these two attributes, so we can't use read_attribute
    return failed_pipeline if event.to_sym == :failed_pipeline
    return fixed_pipeline if event.to_sym == :fixed_pipeline

    has_attribute?(event) && !!read_attribute(event)
  end

  def notification_email_verified
    return if user.temp_oauth_email?
    return if notification_email.blank?

    errors.add(:notification_email, _("must be an email you have verified")) unless user.verified_emails.include?(notification_email)
  end
end

NotificationSetting.prepend_mod_with('NotificationSetting')