File: user_preference.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 (145 lines) | stat: -rw-r--r-- 5,045 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# frozen_string_literal: true

class UserPreference < ApplicationRecord
  # We could use enums, but Rails 4 doesn't support multiple
  # enum options with same name for multiple fields, also it creates
  # extra methods that aren't really needed here.
  NOTES_FILTERS = { all_notes: 0, only_comments: 1, only_activity: 2 }.freeze
  TIME_DISPLAY_FORMATS = { system: 0, non_iso_format: 1, iso_format: 2 }.freeze

  belongs_to :user
  belongs_to :home_organization, class_name: "Organizations::Organization", optional: true

  scope :with_user, -> { joins(:user) }
  scope :gitpod_enabled, -> { where(gitpod_enabled: true) }

  validates :issue_notes_filter, :merge_request_notes_filter, inclusion: { in: NOTES_FILTERS.values }, presence: true
  validates :tab_width, numericality: {
    only_integer: true,
    greater_than_or_equal_to: Gitlab::TabWidth::MIN,
    less_than_or_equal_to: Gitlab::TabWidth::MAX
  }
  validates :diffs_deletion_color, :diffs_addition_color,
    format: { with: ColorsHelper::HEX_COLOR_PATTERN },
    allow_blank: true

  validates :time_display_relative, allow_nil: false, inclusion: { in: [true, false] }
  validates :render_whitespace_in_code, allow_nil: false, inclusion: { in: [true, false] }
  validates :pass_user_identities_to_ci_jwt, allow_nil: false, inclusion: { in: [true, false] }

  validates :pinned_nav_items, json_schema: { filename: 'pinned_nav_items' }

  validates :time_display_format, inclusion: { in: TIME_DISPLAY_FORMATS.values }, presence: true

  validate :user_belongs_to_home_organization, if: :home_organization_changed?

  attribute :tab_width, default: -> { Gitlab::TabWidth::DEFAULT }
  attribute :time_display_relative, default: true
  attribute :time_display_format, default: 0
  attribute :render_whitespace_in_code, default: false
  attribute :project_shortcut_buttons, default: true
  attribute :keyboard_shortcuts_enabled, default: true
  attribute :dpop_enabled, default: false

  enum :visibility_pipeline_id_type, { id: 0, iid: 1 }, scopes: false

  enum text_editor_type: { plain_text_editor: 1, rich_text_editor: 2 }
  enum extensions_marketplace_opt_in_status: Enums::WebIde::ExtensionsMarketplaceOptInStatus.statuses
  enum organization_groups_projects_display: { projects: 0, groups: 1 }

  class << self
    def notes_filters
      {
        s_('Notes|Show all activity') => NOTES_FILTERS[:all_notes],
        s_('Notes|Show comments only') => NOTES_FILTERS[:only_comments],
        s_('Notes|Show history only') => NOTES_FILTERS[:only_activity]
      }
    end
  end

  def set_notes_filter(filter_id, issuable)
    # No need to update the column if the value is already set.
    if filter_id && NOTES_FILTERS.value?(filter_id)
      field = notes_filter_field_for(issuable)
      self[field] = filter_id

      save if attribute_changed?(field)
    end

    notes_filter_for(issuable)
  end

  # Returns the current discussion filter for a given issuable
  # or issuable type.
  def notes_filter_for(resource)
    self[notes_filter_field_for(resource)]
  end

  class << self
    def time_display_formats
      {
        s_('Time Display|System') => TIME_DISPLAY_FORMATS[:system],
        s_('Time Display|12-hour: 2:34 PM') => TIME_DISPLAY_FORMATS[:non_iso_format],
        s_('Time Display|24-hour: 14:34') => TIME_DISPLAY_FORMATS[:iso_format]
      }
    end
  end

  def early_access_event_tracking?
    early_access_program_participant? && early_access_program_tracking?
  end

  # NOTE: Despite this returning a boolean, it does not end in `?` out of
  #       symmetry with the other integration fields like `gitpod_enabled`
  def extensions_marketplace_enabled
    extensions_marketplace_opt_in_status == "enabled"
  end

  def extensions_marketplace_enabled=(value)
    status = ActiveRecord::Type::Boolean.new.cast(value) ? 'enabled' : 'disabled'

    self.extensions_marketplace_opt_in_status = status
  end

  def dpop_enabled=(value)
    if value.nil?
      default = self.class.column_defaults['dpop_enabled']
      super(default)
    else
      super(value)
    end
  end

  def text_editor
    text_editor_type || (Feature.enabled?(:rich_text_editor_as_default, user) ? :rich_text_editor : :plain_text_editor)
  end

  def text_editor=(value)
    self.text_editor_type = value
  end

  private

  def user_belongs_to_home_organization
    # If we don't ignore the default organization id below then all users need to have their corresponding entry
    # with default organization id as organization id in the `organization_users` table.
    # Otherwise, the user won't be able to set the default organization as the home organization.
    return if home_organization.default?
    return if home_organization.user?(user)

    errors.add(:user, _("is not part of the given organization"))
  end

  def notes_filter_field_for(resource)
    field_key =
      if resource.is_a?(Issuable)
        resource.model_name.param_key
      else
        resource
      end

    "#{field_key}_notes_filter"
  end
end

UserPreference.prepend_mod_with('UserPreference')