discourse/lib/theme_settings_parser.rb
Alan Guo Xiang Tan 9f884cdaab
DEV: Introduce experimental type: objects theme setting (#25538)
Why this change?

This commit introduces an experimental `type: objects` theme setting
which will allow theme developers to store a collection of objects as
JSON in the database. Currently, the feature is still in development and
this commit is simply setting up the ground work for us to introduce the
feature in smaller pieces.

What does this change do?

1. Adds a `json_value` column as `jsonb` data type to the `theme_settings` table.
2. Adds a `experimental_objects_type_for_theme_settings` site setting to
   determine whether `ThemeSetting` records of with the `objects` data
   type can be created.
3. Updates `ThemeSettingsManager` to support read/write access from the
   `ThemeSettings#json_value` column.
2024-02-08 10:20:59 +08:00

79 lines
2.3 KiB
Ruby

# frozen_string_literal: true
class ThemeSettingsParser
class InvalidYaml < StandardError
end
def initialize(setting_field)
@setting_field = setting_field
@types = ThemeSetting.types
end
def extract_description(desc)
return desc if desc.is_a?(String)
if desc.is_a?(Hash)
default_locale = SiteSetting.default_locale.to_sym
fallback_locale = desc.keys.find { |key| I18n.locale_available?(key) }
locale = desc[I18n.locale] || desc[default_locale] || desc[:en] || desc[fallback_locale]
locale if locale.is_a?(String)
end
end
def create_opts(default, type, raw_opts = {})
opts = {}
opts[:description] = extract_description(raw_opts[:description])
if type == @types[:enum]
choices = raw_opts[:choices]
choices = [] unless choices.is_a?(Array)
choices << default unless choices.include?(default)
opts[:choices] = choices
end
if [@types[:integer], @types[:string], @types[:float]].include?(type)
opts[:max] = raw_opts[:max].is_a?(Numeric) ? raw_opts[:max] : Float::INFINITY
opts[:min] = raw_opts[:min].is_a?(Numeric) ? raw_opts[:min] : -Float::INFINITY
end
opts[:list_type] = raw_opts[:list_type] if raw_opts[:list_type]
opts[:textarea] = !!raw_opts[:textarea]
opts[:json_schema] = raw_opts[:json_schema]
opts[:schema] = raw_opts[:schema]
opts[:refresh] = !!raw_opts[:refresh]
opts
end
def load
return if @setting_field.value.blank?
begin
parsed = YAML.safe_load(@setting_field.value)
rescue Psych::SyntaxError, Psych::DisallowedClass => e
raise InvalidYaml.new(e.message)
end
raise InvalidYaml.new(I18n.t("themes.settings_errors.invalid_yaml")) unless parsed.is_a?(Hash)
parsed.deep_symbolize_keys!
parsed.each_pair do |setting, value|
if (type = ThemeSetting.guess_type(value)).present?
result = [setting, value, type, create_opts(value, type)]
elsif (hash = value).is_a?(Hash)
default = hash[:default]
type = hash.key?(:type) ? @types[hash[:type]&.to_sym] : ThemeSetting.guess_type(default)
result = [setting, default, type, create_opts(default, type, hash)]
else
result = [setting, value, nil, {}]
end
yield(*result)
end
end
end