From 9f884cdaab03b4f248762e494c10c6a8290a4c0b Mon Sep 17 00:00:00 2001 From: Alan Guo Xiang Tan Date: Thu, 8 Feb 2024 10:20:59 +0800 Subject: [PATCH] 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. --- app/models/theme_setting.rb | 21 +++++++++++++-- config/locales/server.ar.yml | 2 +- config/locales/server.da.yml | 2 +- config/locales/server.de.yml | 2 +- config/locales/server.en.yml | 2 +- config/locales/server.es.yml | 2 +- config/locales/server.fi.yml | 2 +- config/locales/server.fr.yml | 2 +- config/locales/server.gl.yml | 2 +- config/locales/server.he.yml | 2 +- config/locales/server.hr.yml | 2 +- config/locales/server.hu.yml | 2 +- config/locales/server.id.yml | 2 +- config/locales/server.it.yml | 2 +- config/locales/server.ja.yml | 2 +- config/locales/server.ko.yml | 2 +- config/locales/server.lt.yml | 2 +- config/locales/server.nl.yml | 2 +- config/locales/server.pl_PL.yml | 2 +- config/locales/server.pt.yml | 2 +- config/locales/server.pt_BR.yml | 2 +- config/locales/server.ru.yml | 2 +- config/locales/server.sv.yml | 2 +- config/locales/server.tr_TR.yml | 2 +- config/locales/server.uk.yml | 2 +- config/locales/server.ur.yml | 2 +- config/locales/server.vi.yml | 2 +- config/locales/server.zh_CN.yml | 2 +- config/site_settings.yml | 3 +++ ...032242_add_json_value_to_theme_settings.rb | 7 +++++ lib/theme_settings_manager.rb | 15 +++++++++++ lib/theme_settings_parser.rb | 1 + .../theme_settings/valid_settings.yaml | 13 ++++++++++ spec/lib/theme_settings_manager_spec.rb | 19 ++++++++++++++ spec/models/theme_field_spec.rb | 3 ++- spec/models/theme_setting_spec.rb | 26 +++++++++++++++++++ 36 files changed, 132 insertions(+), 30 deletions(-) create mode 100644 db/migrate/20240202032242_add_json_value_to_theme_settings.rb create mode 100644 spec/models/theme_setting_spec.rb diff --git a/app/models/theme_setting.rb b/app/models/theme_setting.rb index c8d273471bd..52588ee7e62 100644 --- a/app/models/theme_setting.rb +++ b/app/models/theme_setting.rb @@ -5,8 +5,12 @@ class ThemeSetting < ActiveRecord::Base has_many :upload_references, as: :target, dependent: :destroy + TYPES_ENUM = + Enum.new(integer: 0, float: 1, string: 2, bool: 3, list: 4, enum: 5, upload: 6, objects: 7) + validates_presence_of :name, :theme - validates :data_type, numericality: { only_integer: true } + before_validation :objects_type_enabled + validates :data_type, inclusion: { in: TYPES_ENUM.values } validates :name, length: { maximum: 255 } after_save :clear_settings_cache @@ -24,7 +28,7 @@ class ThemeSetting < ActiveRecord::Base end def self.types - @types ||= Enum.new(integer: 0, float: 1, string: 2, bool: 3, list: 4, enum: 5, upload: 6) + TYPES_ENUM end def self.acceptable_value_for_type?(value, type) @@ -37,6 +41,9 @@ class ThemeSetting < ActiveRecord::Base value.is_a?(TrueClass) || value.is_a?(FalseClass) when self.types[:list] value.is_a?(String) + when self.types[:objects] + # TODO: This is a simple check now but we want to validate the default objects agianst the schema as well. + value.is_a?(Array) else true end @@ -62,6 +69,15 @@ class ThemeSetting < ActiveRecord::Base types[:bool] end end + + private + + def objects_type_enabled + if self.data_type == ThemeSetting.types[:objects] && + !SiteSetting.experimental_objects_type_for_theme_settings + self.data_type = nil + end + end end # == Schema Information @@ -75,4 +91,5 @@ end # theme_id :integer not null # created_at :datetime not null # updated_at :datetime not null +# json_value :jsonb # diff --git a/config/locales/server.ar.yml b/config/locales/server.ar.yml index 2bd5a3257eb..0b5c241b9cf 100644 --- a/config/locales/server.ar.yml +++ b/config/locales/server.ar.yml @@ -114,7 +114,7 @@ ar: optimized_link: روابط الصور المحسَّنة سريعة الزوال ولا يجب تضمينها في الرمز البرمجي المصدري للسمة. settings_errors: invalid_yaml: "ملف YAML الذي أدخلته غير صالح." - data_type_not_a_number: "نوع الإعداد `%{name}` غير مدعوم. الأنواع المدعومة هي: `integer`، و`bool`، و`list`، و`enum` و`upload`" + data_type_inclusion: "نوع الإعداد `%{name}` غير مدعوم. الأنواع المدعومة هي: `integer`، و`bool`، و`list`، و`enum` و`upload`" name_too_long: "يوجد إعداد باسم طويل جدًا. الحد الأقصى للطول هو 255" default_value_missing: "لا توجد قيمة افتراضية للإعداد `%{name}`" default_not_match_type: "لا تتطابق القيمة الافتراضية للإعداد `%{name}` مع نوعه." diff --git a/config/locales/server.da.yml b/config/locales/server.da.yml index 4eb1d6e8913..464cb998d0e 100644 --- a/config/locales/server.da.yml +++ b/config/locales/server.da.yml @@ -78,7 +78,7 @@ da: optimized_link: Optimerede billedlinks er midlertidige og bør ikke indgå i tema kildekode. settings_errors: invalid_yaml: "Leveret YAML er ugyldig." - data_type_not_a_number: "Indstilling`%{name}` typen understøttes ikke. Understøttede typer er `integer`, `bool`, `list`, `enum` og `upload`" + data_type_inclusion: "Indstilling`%{name}` typen understøttes ikke. Understøttede typer er `integer`, `bool`, `list`, `enum` og `upload`" name_too_long: "Der er en indstilling med et for langt navn. Maksimal længde er 255" default_value_missing: "Indstilling af `%{name}` har ingen standard værdi" default_not_match_type: "Indstilling af `%{name}`s standard værdi's type stemmer ikke overens med den type fundet i indstillingerne." diff --git a/config/locales/server.de.yml b/config/locales/server.de.yml index db76770a21f..c4e40ef93c2 100644 --- a/config/locales/server.de.yml +++ b/config/locales/server.de.yml @@ -110,7 +110,7 @@ de: optimized_link: Optimierte Bildlinks sind flüchtig und sollten nicht in den Theme-Quellcode eingebunden werden. settings_errors: invalid_yaml: "Der YAML-Code ist ungültig." - data_type_not_a_number: "Typ der Einstellung `%{name}` wird nicht unterstützt. Unterstützte Typen sind `integer`, `bool`, `list`, `enum` und `upload`" + data_type_inclusion: "Typ der Einstellung `%{name}` wird nicht unterstützt. Unterstützte Typen sind `integer`, `bool`, `list`, `enum` und `upload`" name_too_long: "Es gibt eine Einstellung mit einem zu langen Namen. Die maximale Länge beträgt 255 Zeichen." default_value_missing: "Einstellung `%{name}` hat keinen Standardwert." default_not_match_type: "Bei der Einstellung `%{name}` stimmt der Datentyp des Standardwerts nicht mit dem Datentyp der Einstellung überein." diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 754acf230c2..13c944df02b 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -120,7 +120,7 @@ en: optimized_link: Optimized image links are ephemeral and should not be included in theme source code. settings_errors: invalid_yaml: "Provided YAML is invalid." - data_type_not_a_number: "Setting `%{name}` type is unsupported. Supported types are `integer`, `bool`, `list`, `enum` and `upload`" + data_type_inclusion: "Setting `%{name}` type is unsupported. Supported types are `integer`, `bool`, `list`, `enum` and `upload`" name_too_long: "There is a setting with a too long name. Maximum length is 255" default_value_missing: "Setting `%{name}` has no default value" default_not_match_type: "Setting `%{name}` default value's type doesn't match with the setting type." diff --git a/config/locales/server.es.yml b/config/locales/server.es.yml index 7a9e32936d9..b619102d622 100644 --- a/config/locales/server.es.yml +++ b/config/locales/server.es.yml @@ -110,7 +110,7 @@ es: optimized_link: Los enlaces de imagen optimizados son efímeros y no deben incluirse en el código fuente del tema. settings_errors: invalid_yaml: "El YAML provisto no es válido." - data_type_not_a_number: "Tipo de ajuste «%{name}» no soportado. Los tipos soportados son «integer», «bool», «list», «enum» y «upload»" + data_type_inclusion: "Tipo de ajuste «%{name}» no soportado. Los tipos soportados son «integer», «bool», «list», «enum» y «upload»" name_too_long: "Hay un ajuste con un nombre muy largo. La cantidad máxima de caracteres es 255" default_value_missing: "El ajuste «%{name}» no tiene valor por defecto" default_not_match_type: "El tipo de valor por defecto para el ajuste «%{name}» no coincide con el tipo de ajuste." diff --git a/config/locales/server.fi.yml b/config/locales/server.fi.yml index 407a5859bef..51103fea5d5 100644 --- a/config/locales/server.fi.yml +++ b/config/locales/server.fi.yml @@ -110,7 +110,7 @@ fi: optimized_link: Optimoidut kuvalinkit ovat lyhytikäisiä, eikä niitä tulisi sisällyttää teeman lähdekoodiin. settings_errors: invalid_yaml: "Annettu YAML ei kelpaa" - data_type_not_a_number: "Tietotyypiksi ei ole mahdollista asettaa `%{name}`. Tuettuja tietotyyppejä ovat `integer`, `bool`, `list`, `enum` ja `upload`" + data_type_inclusion: "Tietotyypiksi ei ole mahdollista asettaa `%{name}`. Tuettuja tietotyyppejä ovat `integer`, `bool`, `list`, `enum` ja `upload`" name_too_long: "Jollain asetuksella on liian pitkä nimi. Enimmäispituus on 255." default_value_missing: "Asetuksella \"%{name}\" ei ole oletusarvoa" default_not_match_type: "Asetuksen \"%{name}\" oletusarvo ei sovi asetuksen tyyppiin." diff --git a/config/locales/server.fr.yml b/config/locales/server.fr.yml index 5ac1276483e..6017c21537c 100644 --- a/config/locales/server.fr.yml +++ b/config/locales/server.fr.yml @@ -110,7 +110,7 @@ fr: optimized_link: Les liens optimisés d'images sont éphémères et ne devraient pas être inclus dans le code source d'un thème. settings_errors: invalid_yaml: "Le YAML est invalide" - data_type_not_a_number: "Le type « %{name} » n'est pas supporté. Les types supportés sont « integer », « bool », « list », « enum » et « upload »" + data_type_inclusion: "Le type « %{name} » n'est pas supporté. Les types supportés sont « integer », « bool », « list », « enum » et « upload »" name_too_long: "Il y a un paramètre avec un nom trop long. Longueur maximum 255" default_value_missing: "Le paramètre « %{name} » n'a pas de valeur par défaut" default_not_match_type: "Le type de la valeur par défaut du paramètre « %{name} » ne correspond pas au type du paramètre." diff --git a/config/locales/server.gl.yml b/config/locales/server.gl.yml index c10f981581d..d66fd10140c 100644 --- a/config/locales/server.gl.yml +++ b/config/locales/server.gl.yml @@ -78,7 +78,7 @@ gl: optimized_link: As ligazóns de imaxe optimizadas son efémeras e non deberían ser incluídas no código fonte do tema. settings_errors: invalid_yaml: "O YAML proporcionado non é válido." - data_type_not_a_number: "Tipo de axuste `%{name}` non compatible. Os tipos compatibles son `integer`, `bool`, `list`, `enum` e `upload`" + data_type_inclusion: "Tipo de axuste `%{name}` non compatible. Os tipos compatibles son `integer`, `bool`, `list`, `enum` e `upload`" name_too_long: "Hai un axuste cun nome demasiado longo. A lonxitude máxima é de 255 caracteres" default_value_missing: "O axuste '%{name}' non ten un valor predefinido" default_not_match_type: "O tipo de valor predefinido para o axuste `%{name}` non coincide co tipo de axuste." diff --git a/config/locales/server.he.yml b/config/locales/server.he.yml index 50ee8980166..10cedb0eb85 100644 --- a/config/locales/server.he.yml +++ b/config/locales/server.he.yml @@ -112,7 +112,7 @@ he: optimized_link: קישורים משופרים לתמונות הם בני חלוף ואין לכלול אותם בקוד המקור של ערכת העיצוב. settings_errors: invalid_yaml: "ה־YAML שסופק שגוי." - data_type_not_a_number: "סוג ההגדרה `%{name}` אינו נתמך. הסוגים הנתמכים הם: `integer`,‏ `bool`,‏ `list`,‏ `enum` ו־`upload`" + data_type_inclusion: "סוג ההגדרה `%{name}` אינו נתמך. הסוגים הנתמכים הם: `integer`,‏ `bool`,‏ `list`,‏ `enum` ו־`upload`" name_too_long: "קיימת הגדרה עם שם ארוך מדי. האורך המרבי הוא 255" default_value_missing: "להגדרה `%{name}` אין ערך בררת מחדל" default_not_match_type: "סוג ערך בררת המחדל של ההגדרה `%{name}` אינו תואם לסוג ההגדרה." diff --git a/config/locales/server.hr.yml b/config/locales/server.hr.yml index af86ff666cb..83512d29106 100644 --- a/config/locales/server.hr.yml +++ b/config/locales/server.hr.yml @@ -84,7 +84,7 @@ hr: optimized_link: Optimizirane veze na slike su prolazne i ne bi trebale biti uključene u izvorni kod teme. settings_errors: invalid_yaml: "Zadani YAML je nevažeći" - data_type_not_a_number: "Vrsta postavke `%{name}` nije podržana. Podržane vrste su `integer`, `bool`, `list`, `enum` i `upload`" + data_type_inclusion: "Vrsta postavke `%{name}` nije podržana. Podržane vrste su `integer`, `bool`, `list`, `enum` i `upload`" name_too_long: "Postoji postavka s predugim imenom. Najveća dopuštena duljina je 255" default_value_missing: "Postavka `%{name}` nema podrazumijevanu vrijednost." default_not_match_type: "Tip zadane vrijednosti postavke `%{name}` ne podudara se s postavljenom vrijednošću postavke." diff --git a/config/locales/server.hu.yml b/config/locales/server.hu.yml index de17e216f44..31b4c99ee66 100644 --- a/config/locales/server.hu.yml +++ b/config/locales/server.hu.yml @@ -81,7 +81,7 @@ hu: no_multilevels_components: "A gyermektémákat tartalmazó témák nem lehet maguk is gyermektémák" settings_errors: invalid_yaml: "A megadott YAML érvénytelen." - data_type_not_a_number: "A `%{name}` típus beállítása nem támogatott. Támogatott típusok: `integer`, `bool`, `list`, `enum` és `upload`." + data_type_inclusion: "A `%{name}` típus beállítása nem támogatott. Támogatott típusok: `integer`, `bool`, `list`, `enum` és `upload`." name_too_long: "Az egyik beállítás neve túl hosszú. A legnagyobb hossz 255" default_value_missing: "A(z) „%{name}” beállításnak nincs alapértelmezett értéke." default_not_match_type: "A(z) „%{name}” beállítás alapértelmezett értékének típusa nem egyezik a beállítás típusával." diff --git a/config/locales/server.id.yml b/config/locales/server.id.yml index d193ad90509..ae2a62990ef 100644 --- a/config/locales/server.id.yml +++ b/config/locales/server.id.yml @@ -93,7 +93,7 @@ id: optimized_link: Tautan gambar yang dioptimalkan bersifat sementara dan tidak boleh disertakan dalam kode sumber tema. settings_errors: invalid_yaml: "YAML yang diberikan tidak valid." - data_type_not_a_number: "Pengaturan jenis `%{name}` tidak didukung. Jenis yang didukung adalah `integer`, `bool`, `list`, `enum` dan `upload`" + data_type_inclusion: "Pengaturan jenis `%{name}` tidak didukung. Jenis yang didukung adalah `integer`, `bool`, `list`, `enum` dan `upload`" name_too_long: "Ada pengaturan dengan nama yang terlalu panjang. Panjang maksimum adalah 255" default_value_missing: "Pengaturan `%{name}` tidak memiliki nilai bawaan" default_not_match_type: "Pengaturan jenis nilai bawaan `%{name}` tidak cocok dengan jenis pengaturan." diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml index 7d59d61cb41..30d59b55dd6 100644 --- a/config/locales/server.it.yml +++ b/config/locales/server.it.yml @@ -110,7 +110,7 @@ it: optimized_link: I collegamenti ad immagini ottimizzate sono effimeri e non devono essere inclusi nel codice sorgente del tema. settings_errors: invalid_yaml: "Il codice YAML fornito non è valido." - data_type_not_a_number: "L'impostazione del tipo `%{name}` non è supportata. I tipi supportati sono `integer`,` bool`, `list`,` enum` e `upload`" + data_type_inclusion: "L'impostazione del tipo `%{name}` non è supportata. I tipi supportati sono `integer`,` bool`, `list`,` enum` e `upload`" name_too_long: "Una delle impostazioni ha un nome troppo lungo. La lunghezza massima è 255 caratteri" default_value_missing: "L'impostazione `%{name}` non ha alcun valore predefinito" default_not_match_type: "L'impostazione del valore predefinito `%{name}` non corrisponde al tipo di impostazione." diff --git a/config/locales/server.ja.yml b/config/locales/server.ja.yml index 8139d85a9cf..0d56ed7a447 100644 --- a/config/locales/server.ja.yml +++ b/config/locales/server.ja.yml @@ -109,7 +109,7 @@ ja: optimized_link: 最適化された画像のリンクは一時的なリンクであるため、テーマのソースコードに含めるべきではありません。 settings_errors: invalid_yaml: "入力された YAML は無効です。" - data_type_not_a_number: "設定 `%{name}` の型はサポートされていません。サポートされている型は `integer`、`bool`、`list`、`enum`、`upload` です" + data_type_inclusion: "設定 `%{name}` の型はサポートされていません。サポートされている型は `integer`、`bool`、`list`、`enum`、`upload` です" name_too_long: "名前が長すぎる設定があります。最大長は 255 です" default_value_missing: "設定 `%{name}` にデフォルト値がありません" default_not_match_type: "設定 `%{name}` のデフォルト値の型が設定の型に一致していません。" diff --git a/config/locales/server.ko.yml b/config/locales/server.ko.yml index 90843bb70a9..f2abb02f9a5 100644 --- a/config/locales/server.ko.yml +++ b/config/locales/server.ko.yml @@ -81,7 +81,7 @@ ko: optimized_link: 최적화된 이미지 링크는 일시적이므로 테마 소스 코드에 포함하지 않아야 합니다. settings_errors: invalid_yaml: "제공된 YAML이 유효하지 않습니다." - data_type_not_a_number: "`%{name}` 유형 설정은 지원되지 않습니다. 지원되는 유형은 `integer` , `bool` , `list` , `enum` 및 `upload` 입니다." + data_type_inclusion: "`%{name}` 유형 설정은 지원되지 않습니다. 지원되는 유형은 `integer` , `bool` , `list` , `enum` 및 `upload` 입니다." name_too_long: "이름이 너무 긴 설정이 있습니다. 최대 길이는 255입니다" default_value_missing: "`%{name}` 설정에 디폴트값이 없습니다" default_not_match_type: "`%{name}` 디폴트값 유형 설정이 설정 유형과 일치하지 않습니다." diff --git a/config/locales/server.lt.yml b/config/locales/server.lt.yml index 6f001561481..ab3c97bf1ba 100644 --- a/config/locales/server.lt.yml +++ b/config/locales/server.lt.yml @@ -81,7 +81,7 @@ lt: optimized_link: Optimizuotos vaizdo nuorodos yra trumpalaikės ir neturėtų būti įtrauktos į temos šaltinio kodą. settings_errors: invalid_yaml: "Nurodytas YAML negalimas." - data_type_not_a_number: "“%{name}” tipo nustatymas nepalaikomas. Palaikomi tipai yra `integer`, `bool`, `list`, `enum` ir `upload`" + data_type_inclusion: "“%{name}” tipo nustatymas nepalaikomas. Palaikomi tipai yra `integer`, `bool`, `list`, `enum` ir `upload`" name_too_long: "Yra nustatymas su per ilgu pavadinimu. Maksimalus ilgis yra 255" default_value_missing: "Nustatymas „%{name}“ neturi numatytosios vertės" default_not_match_type: "Nustatymas „%{name}“ numatytosios vertės tipas neatitinka nustatymo tipo." diff --git a/config/locales/server.nl.yml b/config/locales/server.nl.yml index afb9b2cc706..305970733e4 100644 --- a/config/locales/server.nl.yml +++ b/config/locales/server.nl.yml @@ -110,7 +110,7 @@ nl: optimized_link: Geoptimaliseerde afbeeldingskoppelingen zijn kortstondig en dienen niet in broncode van thema's te worden opgenomen. settings_errors: invalid_yaml: "Opgegeven YAML is ongeldig." - data_type_not_a_number: "Type van instelling `%{name}` wordt niet ondersteund. Ondersteunde typen zijn `integer`, `bool`, `list`, `enum` en `upload`." + data_type_inclusion: "Type van instelling `%{name}` wordt niet ondersteund. Ondersteunde typen zijn `integer`, `bool`, `list`, `enum` en `upload`." name_too_long: "Er is een instelling met een te lange naam. Maximale lengte is 255." default_value_missing: "Instelling `%{name}` heeft geen standaardwaarde." default_not_match_type: "Type van standaardwaarde van instelling `%{name}` komt niet overeen met het type van de instelling." diff --git a/config/locales/server.pl_PL.yml b/config/locales/server.pl_PL.yml index 0c433a4baaf..9b2129bfded 100644 --- a/config/locales/server.pl_PL.yml +++ b/config/locales/server.pl_PL.yml @@ -112,7 +112,7 @@ pl_PL: optimized_link: Zoptymalizowane łącza do obrazów są efemeryczne i nie powinny być zawarte w kodzie źródłowym motywu. settings_errors: invalid_yaml: "Wprowadzony YAML jest nieprawidłowy" - data_type_not_a_number: "Ustawienie typu `%{name}` nie jest obsługiwane. Poprawne typy to `integer`, `bool`, `list`, `enum` oraz `upload`" + data_type_inclusion: "Ustawienie typu `%{name}` nie jest obsługiwane. Poprawne typy to `integer`, `bool`, `list`, `enum` oraz `upload`" name_too_long: "Jedno z ustawień ma zbyt długą nazwę. Maksymalna długość to 255 znaków" default_value_missing: "Ustawienie `%{name}` nie ma domyślnej wartości" default_not_match_type: "Typ domyślnej wartości ustawienia \"%{name}\" nie odpowiada typowi ustawienia." diff --git a/config/locales/server.pt.yml b/config/locales/server.pt.yml index ce877ddfefb..201fc356ef8 100644 --- a/config/locales/server.pt.yml +++ b/config/locales/server.pt.yml @@ -80,7 +80,7 @@ pt: optimized_link: Os links de imagens otimizadas são efémeros e não devem ser incluídos no código-fonte do tema. settings_errors: invalid_yaml: "O YAML fornecido é inválido" - data_type_not_a_number: "A opção `%{name}` tipo não é suportada. Os tipos suportados são `integer`, `bool`, `list`, `enum` e `upload`" + data_type_inclusion: "A opção `%{name}` tipo não é suportada. Os tipos suportados são `integer`, `bool`, `list`, `enum` e `upload`" name_too_long: "Existe uma configuração com um nome demasiado longo. O tamanho máximo é 255" default_value_missing: "A configuração `%{name}` não tem um valor predefinido" default_not_match_type: "O tipo do valor predefinido para a configuração `%{name}` não é igual ao esperado pela configuração." diff --git a/config/locales/server.pt_BR.yml b/config/locales/server.pt_BR.yml index e71027a595a..1bd69eba93c 100644 --- a/config/locales/server.pt_BR.yml +++ b/config/locales/server.pt_BR.yml @@ -110,7 +110,7 @@ pt_BR: optimized_link: Links otimizados de imagem são temporários e não devem ser incluídos no código-fonte do tema. settings_errors: invalid_yaml: "O YAML informado é inválido." - data_type_not_a_number: "A configuração do tipo \"%{name}\" não é compatível. Os tipos compatíveis são \"integer\", \"bool\", \"list\", \"enum\" e \"upload\"" + data_type_inclusion: "A configuração do tipo \"%{name}\" não é compatível. Os tipos compatíveis são \"integer\", \"bool\", \"list\", \"enum\" e \"upload\"" name_too_long: "Esta é uma configuração com um nome muito longo. O comprimento máximo é de 255" default_value_missing: "A configuração \"%{name}\" não tem valor padrão" default_not_match_type: "O tipo de valor padrão da configuração \"%{name}\" não corresponde ao tipo de configuração." diff --git a/config/locales/server.ru.yml b/config/locales/server.ru.yml index 62c136bf535..c9e3f332ce9 100644 --- a/config/locales/server.ru.yml +++ b/config/locales/server.ru.yml @@ -112,7 +112,7 @@ ru: optimized_link: Оптимизированные ссылки на изображения являются эфемерными и не должны включаться в исходный код темы. settings_errors: invalid_yaml: "Неверный формат YAML-файла." - data_type_not_a_number: "Тип `%{name}` не поддерживается. Поддерживаются следующие типы: `integer`, `bool`, `list`, `enum` и `upload`." + data_type_inclusion: "Тип `%{name}` не поддерживается. Поддерживаются следующие типы: `integer`, `bool`, `list`, `enum` и `upload`." name_too_long: "Есть настройка со слишком длинным названием. Максимальная длина — 255 символов." default_value_missing: "Настройка `%{name}` не имеет значения по умолчанию" default_not_match_type: "Стандартные значения настройки `%{name}` не соответствуют допустимым типам." diff --git a/config/locales/server.sv.yml b/config/locales/server.sv.yml index 774bf28a929..21eec9f2f98 100644 --- a/config/locales/server.sv.yml +++ b/config/locales/server.sv.yml @@ -110,7 +110,7 @@ sv: optimized_link: Optimerade bildlänkar är flyktiga och bör inte inkluderas i temakällkoden. settings_errors: invalid_yaml: "Angivet YAML är ogiltigt." - data_type_not_a_number: "Det går inte att ställa in typen `%{name}`. Typer som stöds är `integer`, `bool`, `list` , `enum` och `upload`" + data_type_inclusion: "Det går inte att ställa in typen `%{name}`. Typer som stöds är `integer`, `bool`, `list` , `enum` och `upload`" name_too_long: "En inställning har ett namn som är för långt. Maximal längd är 255" default_value_missing: "Inställningen `%{name}` har inget standardvärde" default_not_match_type: "Standardvärdestypen i inställningen `%{name}` överensstämmer inte med inställningstypen." diff --git a/config/locales/server.tr_TR.yml b/config/locales/server.tr_TR.yml index 50e492fb2a3..a7a54ec1f90 100644 --- a/config/locales/server.tr_TR.yml +++ b/config/locales/server.tr_TR.yml @@ -110,7 +110,7 @@ tr_TR: optimized_link: Optimize edilmiş resim bağlantıları geçicidir ve tema kaynak koduna dahil edilmemelidir. settings_errors: invalid_yaml: "Sağlanan YAML geçersiz." - data_type_not_a_number: "\"%{name}\" türünün ayarlanması desteklenmiyor. Desteklenen türler şunlardır: \"integer\", \"bool\", \"list\", \"enum\" ve \"upload\"" + data_type_inclusion: "\"%{name}\" türünün ayarlanması desteklenmiyor. Desteklenen türler şunlardır: \"integer\", \"bool\", \"list\", \"enum\" ve \"upload\"" name_too_long: "Adı çok uzun olan bir ayar var. Maksimum uzunluk 255'tir" default_value_missing: "\"%{name}\" ayarının varsayılan değeri yok" default_not_match_type: "\"%{name}\" ayarının varsayılan değerinin türü ayar türüyle eşleşmiyor." diff --git a/config/locales/server.uk.yml b/config/locales/server.uk.yml index d4aaafa54e5..894f28a021f 100644 --- a/config/locales/server.uk.yml +++ b/config/locales/server.uk.yml @@ -112,7 +112,7 @@ uk: optimized_link: Посилання на оптимізовані зображення є тимчасовими і не повинні включатись у сирці теми. settings_errors: invalid_yaml: "Наданий YAML пошкоджений." - data_type_not_a_number: "Налаштування типу \"%{name}\" не підтримується. Підтримувані типи: `integer`, `bool`, `list`, `enum` і `upload`" + data_type_inclusion: "Налаштування типу \"%{name}\" не підтримується. Підтримувані типи: `integer`, `bool`, `list`, `enum` і `upload`" name_too_long: "У вас є параметр із занадто довгим імʼям. Максимальна довжина 255" default_value_missing: "Параметр `%{name}` не має стандартного значення" default_not_match_type: "Стандартне значення параметра `%{name}`, не збігається з типом параметра." diff --git a/config/locales/server.ur.yml b/config/locales/server.ur.yml index a873c0fadbe..a209f8e3d33 100644 --- a/config/locales/server.ur.yml +++ b/config/locales/server.ur.yml @@ -80,7 +80,7 @@ ur: optimized_link: آپٹمائزڈ تصویری لنکس عارضی ہیں اور تھیم سورس کوڈ میں شامل نہیں ہونے چاہئیں۔ settings_errors: invalid_yaml: "فراہم کردہ YAML غلط ہے۔" - data_type_not_a_number: "`%{name}` قسم کی ترتیب غیر تعاون یافتہ ہے۔ تائید شدہ اقسام ہیں `انٹیجر`، `بول`، `لسٹ`، `اینم` اور `اپ لوڈ`" + data_type_inclusion: "`%{name}` قسم کی ترتیب غیر تعاون یافتہ ہے۔ تائید شدہ اقسام ہیں `انٹیجر`، `بول`، `لسٹ`، `اینم` اور `اپ لوڈ`" name_too_long: "بہت طویل نام کے ساتھ ایک ترتیب موجود ہے۔ زیادہ سے زیادہ لمبائی 255 ہے" default_value_missing: "ترتیب `%{name}` کی کوئی ڈِیفالٹ قدر نہیں ہے" default_not_match_type: "ترتیب `%{name}` کی ڈِیفالٹ قدر کی قِسم، ترتیب قِسم سے مَیچ نہیں کرتی۔" diff --git a/config/locales/server.vi.yml b/config/locales/server.vi.yml index 101035d6daf..a25b16417b4 100644 --- a/config/locales/server.vi.yml +++ b/config/locales/server.vi.yml @@ -81,7 +81,7 @@ vi: optimized_link: Các liên kết hình ảnh được tối ưu hóa là không lâu và không nên được bao gồm trong mã nguồn theme. settings_errors: invalid_yaml: "YAML được cung cấp không hợp lệ." - data_type_not_a_number: "Thiết lập kiểu `%{name}` không được hỗ trợ. Các kiểu được hỗ trợ là `integer`, `bool`, `list`, `enum` và `upload`" + data_type_inclusion: "Thiết lập kiểu `%{name}` không được hỗ trợ. Các kiểu được hỗ trợ là `integer`, `bool`, `list`, `enum` và `upload`" name_too_long: "Có một cài đặt có tên quá dài. Chiều dài tối đa là 255" default_value_missing: "Cài đặt `%{name}'không có giá trị mặc định" default_not_match_type: "Đặt kiểu giá trị mặc định `%{name}` không khớp với kiểu cài đặt." diff --git a/config/locales/server.zh_CN.yml b/config/locales/server.zh_CN.yml index 400a5e56d95..c11df8ddb5f 100644 --- a/config/locales/server.zh_CN.yml +++ b/config/locales/server.zh_CN.yml @@ -109,7 +109,7 @@ zh_CN: optimized_link: 优化的图片链接是暂时的,不应包含在主题源代码中。 settings_errors: invalid_yaml: "提供的 YAML 无效。" - data_type_not_a_number: "设置 `%{name}` 的类型不受支持。支持的类型为 `integer`、`bool`、`list` 、`enum` 和 `upload`" + data_type_inclusion: "设置 `%{name}` 的类型不受支持。支持的类型为 `integer`、`bool`、`list` 、`enum` 和 `upload`" name_too_long: "有一个设置的名称过长。最大长度为 255" default_value_missing: "设置 `%{name}` 无默认值" default_not_match_type: "设置 `%{name}` 默认值的类型与设置类型不匹配。" diff --git a/config/site_settings.yml b/config/site_settings.yml index d41ddfb3352..dfec1cbca2f 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2357,6 +2357,9 @@ developer: list_type: compact allow_any: false refresh: true + experimental_objects_type_for_theme_settings: + default: false + hidden: true navigation: navigation_menu: diff --git a/db/migrate/20240202032242_add_json_value_to_theme_settings.rb b/db/migrate/20240202032242_add_json_value_to_theme_settings.rb new file mode 100644 index 00000000000..4ef87c13b96 --- /dev/null +++ b/db/migrate/20240202032242_add_json_value_to_theme_settings.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddJsonValueToThemeSettings < ActiveRecord::Migration[7.0] + def change + add_column :theme_settings, :json_value, :jsonb + end +end diff --git a/lib/theme_settings_manager.rb b/lib/theme_settings_manager.rb index 88e2ea32831..997ae92a538 100644 --- a/lib/theme_settings_manager.rb +++ b/lib/theme_settings_manager.rb @@ -113,6 +113,21 @@ class ThemeSettingsManager (max.is_a?(::Integer) || max.is_a?(::Float)) && max != ::Float::INFINITY end + class Objects < self + def value + has_record? ? db_record.json_value : default.map!(&:deep_stringify_keys) + end + + def value=(objects) + # TODO: Validate the objects against the schema + + record = has_record? ? db_record : create_record! + record.json_value = objects + record.save! + record.json_value + end + end + class List < self def list_type @opts[:list_type] diff --git a/lib/theme_settings_parser.rb b/lib/theme_settings_parser.rb index 52f95a2a41e..2a95e9aad05 100644 --- a/lib/theme_settings_parser.rb +++ b/lib/theme_settings_parser.rb @@ -41,6 +41,7 @@ class ThemeSettingsParser opts[:textarea] = !!raw_opts[:textarea] opts[:json_schema] = raw_opts[:json_schema] + opts[:schema] = raw_opts[:schema] opts[:refresh] = !!raw_opts[:refresh] diff --git a/spec/fixtures/theme_settings/valid_settings.yaml b/spec/fixtures/theme_settings/valid_settings.yaml index 4c3e3b480d4..e212f6e74b3 100644 --- a/spec/fixtures/theme_settings/valid_settings.yaml +++ b/spec/fixtures/theme_settings/valid_settings.yaml @@ -84,3 +84,16 @@ valid_json_schema_setting: causes_refresh: default: "" refresh: true + +valid_objects_setting: + type: objects + default: + - title: "Some title" + description: "Some description" + schema: + name: "Some Object" + fields: + title: + type: string + description: + type: string diff --git a/spec/lib/theme_settings_manager_spec.rb b/spec/lib/theme_settings_manager_spec.rb index 52b2fc7b0b2..55b5ca307e4 100644 --- a/spec/lib/theme_settings_manager_spec.rb +++ b/spec/lib/theme_settings_manager_spec.rb @@ -12,6 +12,8 @@ RSpec.describe ThemeSettingsManager do theme.settings end + before { SiteSetting.experimental_objects_type_for_theme_settings = true } + describe "Enum" do it "only accepts values from its choices" do enum_setting = theme_settings[:enum_setting] @@ -184,4 +186,21 @@ RSpec.describe ThemeSettingsManager do end end end + + describe ThemeSettingsManager::Objects do + it "can store a list of objects" do + objects_setting = theme_settings[:valid_objects_setting] + + expect(objects_setting.value).to eq( + [{ "title" => "Some title", "description" => "Some description" }], + ) + + objects_setting.value = [{ title: "title 1", description: "description 1" }] + objects_setting = theme.reload.settings[:valid_objects_setting] + + expect(objects_setting.value).to eq( + [{ "title" => "title 1", "description" => "description 1" }], + ) + end + end end diff --git a/spec/models/theme_field_spec.rb b/spec/models/theme_field_spec.rb index 3ea94529972..deb81723f9d 100644 --- a/spec/models/theme_field_spec.rb +++ b/spec/models/theme_field_spec.rb @@ -7,6 +7,7 @@ RSpec.describe ThemeField do before do SvgSprite.clear_plugin_svg_sprite_cache! ThemeJavascriptCompiler.disable_terser! + SiteSetting.experimental_objects_type_for_theme_settings = true end after { ThemeJavascriptCompiler.enable_terser! } @@ -385,7 +386,7 @@ HTML it "generates errors when invalid type is passed" do field = create_yaml_field(get_fixture("invalid")) expect(field.error).to include( - I18n.t("#{key}.data_type_not_a_number", name: "invalid_type_setting"), + I18n.t("#{key}.data_type_inclusion", name: "invalid_type_setting"), ) end diff --git a/spec/models/theme_setting_spec.rb b/spec/models/theme_setting_spec.rb new file mode 100644 index 00000000000..1bb52a558e9 --- /dev/null +++ b/spec/models/theme_setting_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +RSpec.describe ThemeSetting do + fab!(:theme) + + context "for validations" do + it "should be invalid when setting data_type to objects and `experimental_objects_type_for_theme_settings` is disabled" do + SiteSetting.experimental_objects_type_for_theme_settings = false + + theme_setting = + ThemeSetting.new(name: "test", data_type: ThemeSetting.types[:objects], theme:) + + expect(theme_setting.valid?).to eq(false) + expect(theme_setting.errors[:data_type]).to contain_exactly("is not included in the list") + end + + it "should be valid when setting data_type to objects and `experimental_objects_type_for_theme_settings` is enabled" do + SiteSetting.experimental_objects_type_for_theme_settings = true + + theme_setting = + ThemeSetting.new(name: "test", data_type: ThemeSetting.types[:objects], theme:) + + expect(theme_setting.valid?).to eq(true) + end + end +end