mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-06 14:53:13 +08:00
This allows themes to set a modifier in their `about.json` that
restricts a theme's color palettes to only what's defined within the
theme.
```json
"modifiers": {
"only_theme_color_schemes": true
}
```
Once set, a theme's color palette settings will only list the palettes
included in about.json. A banner is shown indicating that the theme
restricts palettes.
<img width="700" alt="image"
src="https://github.com/user-attachments/assets/cfb4433c-c7c9-4923-a121-fddd572c29ea"
/>
<img width="300" alt="image"
src="https://github.com/user-attachments/assets/67ec1f72-d408-4e3b-911f-a2d0e4db9a37"
/>
If there's only 1 palette defined by the theme, it will be set to both
light and dark settings.
If there are more than 1 palettes defined, we check to see if one is
dark and will use the first dark palette for the dark setting.
If there are no color palettes defined by the theme, the color selection
will be unrestricted.
---------
Co-authored-by: Gabriel Grubba <70247653+Grubba27@users.noreply.github.com>
163 lines
4.8 KiB
Ruby
163 lines
4.8 KiB
Ruby
# frozen_string_literal: true
|
|
class ThemeModifierSet < ActiveRecord::Base
|
|
class ThemeModifierSetError < StandardError
|
|
end
|
|
|
|
belongs_to :theme
|
|
|
|
def self.modifiers
|
|
@modifiers ||= self.load_modifiers
|
|
end
|
|
|
|
validate :type_validator
|
|
|
|
def type_validator
|
|
ThemeModifierSet.modifiers.each do |k, config|
|
|
value = self[k]
|
|
next if value.nil?
|
|
|
|
case config[:type]
|
|
when :boolean
|
|
next if [true, false].include?(value)
|
|
when :string_array
|
|
next if value.is_a?(Array) && value.all? { |v| v.is_a?(String) }
|
|
end
|
|
errors.add(k, :invalid)
|
|
end
|
|
end
|
|
|
|
after_save do
|
|
SvgSprite.expire_cache if saved_change_to_svg_icons?
|
|
CSP::Extension.clear_theme_extensions_cache! if saved_change_to_csp_extensions?
|
|
if saved_change_to_only_theme_color_schemes?
|
|
ApplicationSerializer.expire_cache_fragment!("user_color_schemes")
|
|
ApplicationSerializer.expire_cache_fragment!("user_themes")
|
|
end
|
|
end
|
|
|
|
# Given the ids of multiple active themes / theme components, this function
|
|
# will combine them into a 'resolved' behavior
|
|
def self.resolve_modifier_for_themes(theme_ids, modifier_name)
|
|
return nil if !(config = self.modifiers[modifier_name])
|
|
|
|
all_values =
|
|
self
|
|
.where(theme_id: theme_ids)
|
|
.where.not(modifier_name => nil)
|
|
.map { |s| s.public_send(modifier_name) }
|
|
case config[:type]
|
|
when :boolean
|
|
all_values.any?
|
|
when :string_array
|
|
all_values.flatten(1)
|
|
else
|
|
raise ThemeModifierSetError, "Invalid theme modifier combine_mode"
|
|
end
|
|
end
|
|
|
|
def topic_thumbnail_sizes
|
|
array = read_attribute(:topic_thumbnail_sizes)
|
|
|
|
return if array.nil?
|
|
|
|
array
|
|
.map do |dimension|
|
|
parts = dimension.split("x")
|
|
next if parts.length != 2
|
|
[parts[0].to_i, parts[1].to_i]
|
|
end
|
|
.filter(&:present?)
|
|
end
|
|
|
|
def topic_thumbnail_sizes=(val)
|
|
return write_attribute(:topic_thumbnail_sizes, val) if val.nil?
|
|
return write_attribute(:topic_thumbnail_sizes, val) if !val.is_a?(Array)
|
|
if !val.all? { |v| v.is_a?(Array) && v.length == 2 }
|
|
return write_attribute(:topic_thumbnail_sizes, val)
|
|
end
|
|
|
|
super(val.map { |dim| "#{dim[0]}x#{dim[1]}" })
|
|
end
|
|
|
|
def add_theme_setting_modifier(modifier_name, setting_name)
|
|
self.theme_setting_modifiers ||= {}
|
|
self.theme_setting_modifiers[modifier_name] = setting_name
|
|
end
|
|
|
|
def refresh_theme_setting_modifiers(target_setting_name: nil, target_setting_value: nil)
|
|
changed = false
|
|
if self.theme_setting_modifiers.present?
|
|
self.theme_setting_modifiers.each do |modifier_name, setting_name|
|
|
modifier_name = modifier_name.to_sym
|
|
setting_name = setting_name.to_sym
|
|
|
|
next if target_setting_name.present? && target_setting_name.to_sym != setting_name
|
|
|
|
value =
|
|
target_setting_name.present? ? target_setting_value : theme.settings[setting_name]&.value
|
|
value = coerce_setting_value(modifier_name, value)
|
|
if self[modifier_name] != value
|
|
self[modifier_name] = value
|
|
changed = true
|
|
end
|
|
end
|
|
end
|
|
changed
|
|
end
|
|
|
|
private
|
|
|
|
def coerce_setting_value(modifier_name, value)
|
|
type = ThemeModifierSet.modifiers.dig(modifier_name, :type)
|
|
if type == :boolean
|
|
value.to_s != "false"
|
|
elsif type == :string_array
|
|
value.is_a?(Array) ? value : value.to_s.split("|")
|
|
end
|
|
end
|
|
|
|
# Build the list of modifiers from the DB schema.
|
|
# This allows plugins to introduce new modifiers by adding columns to the table
|
|
def self.load_modifiers
|
|
hash = {}
|
|
columns_hash.each do |column_name, info|
|
|
next if %w[id theme_id theme_setting_modifiers].include?(column_name)
|
|
|
|
type = nil
|
|
if info.type == :string && info.array?
|
|
type = :string_array
|
|
elsif info.type == :boolean && !info.array?
|
|
type = :boolean
|
|
else
|
|
if !%i[boolean string].include?(info.type)
|
|
raise ThemeModifierSetError, "Invalid theme modifier column type"
|
|
end
|
|
end
|
|
|
|
hash[column_name.to_sym] = { type: type }
|
|
end
|
|
hash
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: theme_modifier_sets
|
|
#
|
|
# id :bigint not null, primary key
|
|
# csp_extensions :string is an Array
|
|
# custom_homepage :boolean
|
|
# only_theme_color_schemes :boolean
|
|
# serialize_post_user_badges :string is an Array
|
|
# serialize_topic_excerpts :boolean
|
|
# serialize_topic_is_hot :boolean
|
|
# serialize_topic_op_likes_data :boolean
|
|
# svg_icons :string is an Array
|
|
# theme_setting_modifiers :jsonb
|
|
# topic_thumbnail_sizes :string is an Array
|
|
# theme_id :bigint not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_theme_modifier_sets_on_theme_id (theme_id) UNIQUE
|
|
#
|