0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-04 10:39:43 +08:00
discourse/lib/theme_settings_manager.rb
Martin Brennan fc8df41adb
DEV: Add disallowed_groups to theme/component settings (#41792)
Further following on from bee1be8599,
this brings greater parity between theme settings and site settings.

This commit adds a new `disallowed_groups` field to theme and component
settings, which allows theme authors to specify groups that cannot be
selected
by admins for a particular list type or object group type setting.

This is useful for cases where the group-based setting isn't usable
for a group like e.g. anonymous_users, no point allowing them for
a setting that requires a user to be logged in to take effect.

c.f.
https://meta.discourse.org/t/granular-group-based-permissions-for-anonymous-and-logged-in-users/402273/18?u=martin

### List groups

With `disallowed_groups: "4|5"` specified (`logged_in_users` &
`anonymous_users`)

<img width="734" height="450" alt="image"
src="https://github.com/user-attachments/assets/ed8d7338-65fa-44e6-8dce-7073a87076e6"
/>

### Object groups

With `disallowed_groups: "4|5"` specified (`logged_in_users` &
`anonymous_users`)

<img width="696" height="725" alt="image"
src="https://github.com/user-attachments/assets/1a9103d4-cf44-4038-9d62-3838cb779b49"
/>
2026-07-20 09:28:52 +10:00

109 lines
2.5 KiB
Ruby
Vendored

# frozen_string_literal: true
class ThemeSettingsManager
attr_reader :name, :theme, :default, :opts
def self.types
ThemeSetting.types
end
def self.cast_row_value(row)
type_name = types.invert[row.data_type].downcase.capitalize
klass = "ThemeSettingsManager::#{type_name}".constantize
klass.cast(klass.extract_value_from_row(row))
end
def self.create(name, default, type, theme, opts = {})
type_name = types.invert[type].downcase.capitalize
klass = "ThemeSettingsManager::#{type_name}".constantize
klass.new(name, default, theme, opts)
end
def self.cast(value)
value
end
def self.extract_value_from_row(row)
row.value
end
def initialize(name, default, theme, opts = {})
@name = name.to_sym
@default = default
@theme = theme
@opts = opts
@types = self.class.types
end
def value
has_record? ? db_record.value : default
end
def type_name
self.class.name.demodulize.downcase.to_sym
end
def type
@types[type_name]
end
def description
@opts[:description] # Old method of specifying description. Is now overridden by locale file
end
def requests_refresh?
@opts[:refresh]
end
def disallowed_groups
@opts[:disallowed_groups]
end
def value=(new_value)
ensure_is_valid_value!(new_value)
value = new_value.to_s
record = has_record? ? update_record!(value:) : create_record!(value:)
record.value
end
def db_record
# theme.theme_settings will already be preloaded, so it is better to use
# `find` on an array, rather than make a round trip to the database
theme.theme_settings.to_a.find do |i|
i.name.to_s == @name.to_s && i.data_type.to_s == type.to_s
end
end
def update_record!(args)
db_record.tap { |instance| instance.update!(args) }
end
def create_record!(args)
record = ThemeSetting.new(name: @name, data_type: type, theme: @theme, **args)
record.save!
record
end
def has_record?
db_record.present?
end
def ensure_is_valid_value!(new_value)
return if new_value.nil?
error_messages = ThemeSettingsValidator.validate_value(new_value, type, @opts)
raise Discourse::InvalidParameters.new error_messages.join(" ") if error_messages.present?
end
def has_min?
min = @opts[:min]
(min.is_a?(::Integer) || min.is_a?(::Float)) && min != -::Float::INFINITY
end
def has_max?
max = @opts[:max]
(max.is_a?(::Integer) || max.is_a?(::Float)) && max != ::Float::INFINITY
end
end