maglevcms 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/maglev/content/builder.rb +3 -1
  3. data/app/components/maglev/content/checkbox.rb +7 -1
  4. data/app/components/maglev/content/void.rb +11 -0
  5. data/app/controllers/concerns/maglev/fetchers_concern.rb +2 -1
  6. data/app/controllers/concerns/maglev/rendering_concern.rb +0 -2
  7. data/app/controllers/maglev/editor_controller.rb +4 -0
  8. data/app/frontend/admin/controllers/screenshot_controller.js +1 -1
  9. data/app/frontend/editor/components/dynamic-form/dynamic-input.vue +30 -10
  10. data/app/frontend/editor/components/dynamic-form/index.vue +2 -0
  11. data/app/frontend/editor/components/kit/color-input/core-input.vue +1 -1
  12. data/app/frontend/editor/components/kit/color-input.vue +1 -1
  13. data/app/frontend/editor/components/kit/divider.vue +22 -0
  14. data/app/frontend/editor/components/kit/hint.vue +12 -0
  15. data/app/frontend/editor/components/kit/index.js +4 -0
  16. data/app/frontend/editor/components/page/form/seo.vue +1 -1
  17. data/app/frontend/editor/components/section-block-pane/setting-list.vue +4 -0
  18. data/app/frontend/editor/components/section-highlighter/top-left-actions.vue +7 -1
  19. data/app/frontend/editor/components/section-list/list-item.vue +6 -1
  20. data/app/frontend/editor/components/section-pane/index.vue +2 -1
  21. data/app/frontend/editor/components/section-pane/setting-list.vue +4 -0
  22. data/app/frontend/editor/components/style-pane/index.vue +4 -0
  23. data/app/frontend/editor/locales/editor.fr.json +8 -2
  24. data/app/frontend/editor/locales/editor.pt-BR.json +257 -0
  25. data/app/frontend/editor/locales/index.js +3 -0
  26. data/app/frontend/editor/mixins/global.js +16 -0
  27. data/app/frontend/editor/router/index.js +1 -2
  28. data/app/frontend/editor/views/content-pane.vue +2 -2
  29. data/app/models/concerns/maglev/sections_concern.rb +17 -13
  30. data/app/models/concerns/maglev/translatable.rb +4 -0
  31. data/app/models/maglev/page.rb +6 -0
  32. data/app/models/maglev/section/setting.rb +22 -25
  33. data/app/models/maglev/setting_types/base.rb +10 -0
  34. data/app/models/maglev/setting_types/checkbox.rb +9 -0
  35. data/app/models/maglev/setting_types/collection_item.rb +13 -0
  36. data/app/models/maglev/setting_types/color.rb +6 -0
  37. data/app/models/maglev/setting_types/divider.rb +9 -0
  38. data/app/models/maglev/setting_types/hint.rb +9 -0
  39. data/app/models/maglev/setting_types/icon.rb +9 -0
  40. data/app/models/maglev/setting_types/image.rb +13 -0
  41. data/app/models/maglev/setting_types/link.rb +13 -0
  42. data/app/models/maglev/setting_types/select.rb +6 -0
  43. data/app/models/maglev/setting_types/text.rb +6 -0
  44. data/app/models/maglev/site/locales_concern.rb +12 -1
  45. data/app/models/maglev/site.rb +4 -0
  46. data/app/models/maglev/theme/style_setting.rb +1 -1
  47. data/app/models/maglev/theme.rb +35 -0
  48. data/app/services/maglev/add_site_locale.rb +64 -0
  49. data/app/services/maglev/app_container.rb +1 -0
  50. data/app/services/maglev/change_site_locales.rb +1 -1
  51. data/app/services/maglev/fetch_page.rb +7 -1
  52. data/app/services/maglev/fetch_section_screenshot_path.rb +1 -1
  53. data/app/services/maglev/get_page_fullpath.rb +5 -1
  54. data/app/services/maglev/persist_page.rb +15 -11
  55. data/lib/maglev/active_storage.rb +2 -2
  56. data/lib/maglev/engine.rb +6 -2
  57. data/lib/maglev/errors.rb +7 -0
  58. data/lib/maglev/preview_constraint.rb +15 -1
  59. data/lib/maglev/version.rb +1 -1
  60. metadata +23 -7
@@ -0,0 +1,257 @@
1
+ {
2
+ "headerNav": {
3
+ "pages": "Páginas:",
4
+ "pageSettings": "Configurações da página",
5
+ "previewSite": "Visualizar site",
6
+ "previewToggler": {
7
+ "button": "Visualizar site",
8
+ "draftLink": "Versão de rascunho",
9
+ "liveLink": "Versão publicada"
10
+ },
11
+ "publishButton": {
12
+ "default": "Publicar site",
13
+ "inProgress": "Publicando...",
14
+ "success": "Publicado!",
15
+ "fail": "Falhou!"
16
+ },
17
+ "saveButton": {
18
+ "default": "Salvar",
19
+ "inProgress": "Salvando...",
20
+ "success": "Salvo!",
21
+ "fail": "Falhou!"
22
+ }
23
+ },
24
+ "sidebarNav": {
25
+ "addNewSectionTooltip": "Adicionar uma nova seção na parte inferior da página",
26
+ "managePageSectionsTooltip": "Reordenar / excluir as seções da página",
27
+ "editStyleTooltip": "Alterar o estilo do seu site",
28
+ "openImageLibraryTooltip": "Abrir a galeria de imagens",
29
+ "leaveEditorTooltip": "Voltar para o aplicativo principal"
30
+ },
31
+ "pagePreview": {
32
+ "loading": "Carregando...",
33
+ "empty": {
34
+ "title": {
35
+ "withoutLocale": "Sua página está em branco",
36
+ "withLocale": "Sua página em %{localeName} está em branco"
37
+ },
38
+ "message": "Por favor, adicione a primeira seção clicando no botão {icon} na barra lateral esquerda."
39
+ }
40
+ },
41
+ "page": {
42
+ "list": {
43
+ "title": "Lista de páginas",
44
+ "subTitle": "Gerenciar as páginas do seu site",
45
+ "newButton": "Nova página",
46
+ "searchPlaceholder": "Digite sua pesquisa aqui...",
47
+ "item": {
48
+ "edit": "Editar configurações da página",
49
+ "clone": "Clonar página",
50
+ "hide": "Ocultar página",
51
+ "show": "Mostrar página",
52
+ "delete": "Excluir página"
53
+ }
54
+ },
55
+ "new": {
56
+ "title": "Criar uma nova página",
57
+ "submitButton": {
58
+ "default": "Criar página",
59
+ "inProgress": "Criando...",
60
+ "success": "Criado!",
61
+ "fail": "Falhou!"
62
+ },
63
+ "cancelButton": "Cancelar"
64
+ },
65
+ "edit": {
66
+ "title": "Editando uma página",
67
+ "currentPage": {
68
+ "title": "Editando a página atual"
69
+ },
70
+ "submitButton": {
71
+ "default": "Atualizar página",
72
+ "inProgress": "Atualizando...",
73
+ "success": "Atualizado!",
74
+ "fail": "Falhou!"
75
+ },
76
+ "cancelButton": "Cancelar"
77
+ },
78
+ "form": {
79
+ "tabs": {
80
+ "main": "Principal",
81
+ "seo": "SEO"
82
+ },
83
+ "title": "Título",
84
+ "path": "Caminho",
85
+ "visible": "Visível",
86
+ "visiblePlaceholder": "Mostrar/Ocultar a página",
87
+ "seoTitle": "Título meta",
88
+ "metaDescription": "Descrição meta",
89
+ "ogTitle": "og:title",
90
+ "ogDescription": "og:description",
91
+ "ogImageUrl": "og:image"
92
+ }
93
+ },
94
+ "sections": {
95
+ "addPane": {
96
+ "title": "Adicionar uma seção"
97
+ },
98
+ "listPane": {
99
+ "title": "Organizar seções",
100
+ "empty": "Não há seções."
101
+ }
102
+ },
103
+ "sectionPane": {
104
+ "tabs": {
105
+ "settings": "Conteúdo",
106
+ "blocks": "Blocos",
107
+ "advanced": "Avançado"
108
+ },
109
+ "blockList": {
110
+ "add": "Adicionar novo item"
111
+ }
112
+ },
113
+ "sectionBlockPane": {
114
+ "tabs": {
115
+ "settings": "Conteúdo",
116
+ "blocks": "Blocos",
117
+ "advanced": "Avançado"
118
+ }
119
+ },
120
+ "themeSectionList": {
121
+ "emptyCategory": "Nenhuma seção"
122
+ },
123
+ "style": {
124
+ "edit": {
125
+ "title": "Estilizar seu site",
126
+ "submitButton": {
127
+ "default": "Atualizar estilo",
128
+ "inProgress": "Atualizando...",
129
+ "success": "Atualizado!",
130
+ "fail": "Falhou!"
131
+ }
132
+ }
133
+ },
134
+ "imageInput": {
135
+ "addButton": "Adicionar imagem",
136
+ "replaceButton": "Substituir imagem",
137
+ "clearButton": "Limpar",
138
+ "altTextPlaceholder": "Texto alternativo para ajudar os mecanismos de busca"
139
+ },
140
+ "imageLibrary": {
141
+ "title": "Lista de imagens",
142
+ "pickerTitle": "Selecionar uma imagem",
143
+ "none": "Nenhuma imagem enviada ainda",
144
+ "searchPlaceholder": "Buscar por um nome de arquivo",
145
+ "pagination": {
146
+ "label": "%{start} - %{end} de %{totalItems} imagens",
147
+ "noItems": "Nenhuma imagem"
148
+ },
149
+ "destroy": {
150
+ "text": "Tem certeza?",
151
+ "ok": "Sim, exclua",
152
+ "cancel": "Cancelar"
153
+ },
154
+ "uploader": {
155
+ "wrongFiles": "Certifique-se de que o tamanho de cada imagem não seja maior que %{limit}.",
156
+ "uploadButton": {
157
+ "default": "Enviar imagens",
158
+ "inProgress": "Enviando...",
159
+ "success": "Enviado!",
160
+ "fail": "Falha!"
161
+ }
162
+ }
163
+ },
164
+ "iconInput": {
165
+ "addButton": "Adicionar ícone",
166
+ "replaceButton": "Substituir ícone",
167
+ "clearButton": "Limpar"
168
+ },
169
+ "iconLibrary": {
170
+ "title": "Ícones",
171
+ "pickerTitle": "Escolher um ícone"
172
+ },
173
+ "linkInput": {
174
+ "placeholder": "Clique aqui para selecionar um link",
175
+ "withNestedTextLabel": "Ativar texto",
176
+ "nestedTextPlaceholder": "Texto"
177
+ },
178
+ "linkPicker": {
179
+ "title": "Selecionar um link",
180
+ "insertTitle": "Inserir um link",
181
+ "page": {
182
+ "name": "Página",
183
+ "input": {
184
+ "label": "Página",
185
+ "placeholder": "Selecione uma página",
186
+ "searchPlaceholder": "Pesquisar...",
187
+ "emptyLabel": "Nenhuma página encontrada"
188
+ },
189
+ "sectionInput": {
190
+ "label": "Seção",
191
+ "placeholder": "Selecione uma seção",
192
+ "emptyLabel": "Nenhuma seção selecionada"
193
+ }
194
+ },
195
+ "url": {
196
+ "name": "URL",
197
+ "input": {
198
+ "label": "URL"
199
+ }
200
+ },
201
+ "email": {
202
+ "name": "Email",
203
+ "input": {
204
+ "label": "Email"
205
+ }
206
+ },
207
+ "shared": {
208
+ "mainButton": {
209
+ "select": "Selecionar",
210
+ "insert": "Adicionar"
211
+ },
212
+ "cancelButton": "Cancelar",
213
+ "newWindowInput": {
214
+ "label": "Abrir em nova janela"
215
+ }
216
+ }
217
+ },
218
+ "collectionItemInput": {
219
+ "select": {
220
+ "placeholder": "Selecione um item",
221
+ "searchPlaceholder": "Pesquisar...",
222
+ "emptyLabel": "Nenhum item encontrado"
223
+ }
224
+ },
225
+ "pagination": {
226
+ "defaultLabel": "%{start} - %{end} de %{totalItems} itens",
227
+ "defaultNoItems": "Nenhum"
228
+ },
229
+ "confirmationButton": {
230
+ "text": "Você tem certeza de que deseja realizar esta ação?",
231
+ "confirmButtonLabel": "Sim",
232
+ "cancelButtonLabel": "Não"
233
+ },
234
+ "errorModals": {
235
+ "staleRecord": {
236
+ "title": "Pedimos desculpas 🙇",
237
+ "message": "Não conseguimos salvar suas alterações porque outra pessoa atualizou o conteúdo no intervalo de tempo.",
238
+ "button": "Por favor, recarregue a página"
239
+ }
240
+ },
241
+ "support": {
242
+ "human": {
243
+ "storageUnits": {
244
+ "format": "%{number} %{unit}",
245
+ "units": {
246
+ "byte": {
247
+ "one": "Byte",
248
+ "other": "Bytes"
249
+ },
250
+ "kb": "KB",
251
+ "mb": "MB",
252
+ "gb": "GB"
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }
@@ -1,14 +1,17 @@
1
1
  import EditorEN from './editor.en.json'
2
2
  import EditorES from './editor.es.json'
3
3
  import EditorFR from './editor.fr.json'
4
+ import EditorPTBR from './editor.pt-BR.json'
4
5
  import { deepMerge } from '@/misc/utils'
5
6
 
6
7
  const overriddenEN = window.customTranslations?.en ?? {}
7
8
  const overriddenES = window.customTranslations?.es ?? {}
8
9
  const overriddenFR = window.customTranslations?.fr ?? {}
10
+ const overriddenPTBR = (window.customTranslations && window.customTranslations['pt-BR']) ? window.customTranslations['pt-BR'] : {}
9
11
 
10
12
  export default {
11
13
  en: deepMerge(EditorEN, overriddenEN),
12
14
  es: deepMerge(EditorES, overriddenES),
13
15
  fr: deepMerge(EditorFR, overriddenFR),
16
+ "pt-BR": deepMerge(EditorPTBR, overriddenPTBR),
14
17
  }
@@ -16,9 +16,15 @@ Vue.mixin({
16
16
  currentSite() {
17
17
  return this.$store.state.site
18
18
  },
19
+ currentI18nScope() {
20
+ return `themes.${this.currentTheme.id}`
21
+ },
19
22
  currentStyle() {
20
23
  return this.$store.state.style
21
24
  },
25
+ currentStyleI18nScope() {
26
+ return `${this.currentI18nScope}.style`
27
+ },
22
28
  currentLocale() {
23
29
  return this.$store.state.locale
24
30
  },
@@ -34,6 +40,9 @@ Vue.mixin({
34
40
  currentSection() {
35
41
  return this.$store.state.section
36
42
  },
43
+ currentSectionI18nScope() {
44
+ return `${this.currentI18nScope}.sections.${this.currentSection.type}`
45
+ },
37
46
  currentSectionList() {
38
47
  return this.$store.getters.sectionList
39
48
  },
@@ -55,6 +64,9 @@ Vue.mixin({
55
64
  currentSectionBlock() {
56
65
  return this.$store.state.sectionBlock
57
66
  },
67
+ currentSectionBlockI18nScope() {
68
+ return `${this.currentSectionI18nScope}.blocks.${this.currentSectionBlock.type}`
69
+ },
58
70
  currentSectionBlockIndex() {
59
71
  return this.$store.getters.sectionBlockIndex
60
72
  },
@@ -120,5 +132,9 @@ Vue.mixin({
120
132
  closeModal() {
121
133
  ModalBus.$emit('close')
122
134
  },
135
+ $st(key) {
136
+ // console.log('$st', key, this.$te(key) ? this.$t(key) : null)
137
+ return this.$te(key) ? this.$t(key) : null
138
+ }
123
139
  },
124
140
  })
@@ -15,7 +15,7 @@ const router = new VueRouter({
15
15
  router.beforeEach((to, from, next) => {
16
16
  // The router hasn't found a component to display so get back
17
17
  // to the screen without any UI drawer opened.
18
- if (to.matched.length === 0) {
18
+ if (to.matched.length === 0)
19
19
  return next({
20
20
  name: 'editPage',
21
21
  params: {
@@ -23,7 +23,6 @@ router.beforeEach((to, from, next) => {
23
23
  locale: store.state.locale,
24
24
  },
25
25
  })
26
- }
27
26
 
28
27
  // When an user wants to edit another page or to edit the current page
29
28
  // in a different locale, the router detects it and dispatch the new
@@ -53,10 +53,10 @@ export default {
53
53
  else return null
54
54
  },
55
55
  sectionTitle() {
56
- return this.currentSectionDefinition?.name
56
+ return this.$st(`${this.currentSectionI18nScope}.name`) || this.currentSectionDefinition?.name
57
57
  },
58
58
  sectionBlockTitle() {
59
- return (
59
+ return this.$st(`${this.currentSectionI18nScope}.blocks.label`) || (
60
60
  this.currentSectionBlockDefinition?.name +
61
61
  ' ' +
62
62
  `#${this.currentSectionBlockIndex}`
@@ -2,54 +2,58 @@
2
2
 
3
3
  # rubocop:disable Style/ClassAndModuleChildren
4
4
  module Maglev::SectionsConcern
5
- def prepare_sections
5
+ def prepare_sections(theme)
6
6
  # NOTE: pages defined in the theme definition
7
7
  # don't include the ids for sections/blocks
8
8
  self.sections ||= [] # NOTE: the self is mandatory here
9
9
  sections.each do |section|
10
- prepare_section(section)
10
+ prepare_section(theme, section)
11
11
  end
12
12
  end
13
13
 
14
- def prepare_sections_translations
14
+ def prepare_sections_translations(theme)
15
15
  return if sections_translations.blank?
16
16
 
17
17
  sections_translations.each_key do |locale|
18
18
  Maglev::I18n.with_locale(locale) do
19
- prepare_sections
19
+ prepare_sections(theme)
20
20
  end
21
21
  end
22
22
  end
23
23
 
24
24
  private
25
25
 
26
- def prepare_section(section)
26
+ def prepare_section(theme, section)
27
27
  section['id'] ||= SecureRandom.urlsafe_base64(8)
28
- section['settings'] = prepare_settings(section['settings'])
28
+ section['settings'] = prepare_settings(theme, section['type'], nil, section['settings'])
29
29
  section['blocks'] = (section['blocks'] || []).map do |block|
30
- prepare_block(block)
30
+ prepare_block(theme, section['type'], block)
31
31
  end.flatten
32
32
  section
33
33
  end
34
34
 
35
- def prepare_block(block)
35
+ def prepare_block(theme, section_type, block)
36
36
  block['id'] ||= SecureRandom.urlsafe_base64(8)
37
- block['settings'] = prepare_settings(block['settings'])
37
+ block['settings'] = prepare_settings(theme, section_type, block['type'], block['settings'])
38
38
 
39
39
  # the children key is accepted when the sections come from a theme preset
40
40
  children = (block.delete('children') || []).map do |nested_block|
41
41
  nested_block['parent_id'] = block['id']
42
- prepare_block(nested_block)
42
+ prepare_block(theme, section_type, nested_block)
43
43
  end
44
44
  [block, children].flatten
45
45
  end
46
46
 
47
- def prepare_settings(settings)
47
+ def prepare_settings(theme, section_type, block_type, settings)
48
48
  # NOTE: in the theme definition file, we allow developers to declare
49
49
  # default content like this: { <setting_id_1>: <setting_value_1>, ..., <setting_id_n>: <setting_value_n> }
50
- return settings if settings.is_a?(Array)
50
+ settings = settings.map { |key, value| { id: key, value: value } } unless settings.is_a?(Array)
51
51
 
52
- settings.map { |key, value| { id: key, value: value } }
52
+ settings.map do |setting|
53
+ setting = setting.with_indifferent_access
54
+ type = theme.find_setting!(section_type, block_type, setting['id'])
55
+ setting.merge({ value: type.cast_value(setting['value']) })
56
+ end
53
57
  end
54
58
  end
55
59
  # rubocop:enable Style/ClassAndModuleChildren
@@ -11,6 +11,10 @@ module Maglev
11
11
  public_send("#{attr}_translations")
12
12
  end
13
13
 
14
+ def translate_attr_in(attr, locale, source_locale)
15
+ translations_for(attr)[locale.to_s] ||= translations_for(attr)[source_locale.to_s]
16
+ end
17
+
14
18
  class_methods do
15
19
  def order_by_translated(attr, direction)
16
20
  order(Arel.sql("#{attr}_translations->>'#{Maglev::I18n.current_locale}'") => direction)
@@ -34,5 +34,11 @@ module Maglev
34
34
  def static?
35
35
  false
36
36
  end
37
+
38
+ def translate_in(locale, source_locale)
39
+ %i[title sections seo_title meta_description og_title og_description og_image_url].each do |attr|
40
+ translate_attr_in(attr, locale, source_locale)
41
+ end
42
+ end
37
43
  end
38
44
  end
@@ -5,46 +5,37 @@ class Maglev::Section::Setting
5
5
  ## concerns ##
6
6
  include ActiveModel::Model
7
7
 
8
+ ## constants ##
9
+ REGISTERED_TYPES = %w[text image checkbox link color select collection_item icon divider hint].freeze
10
+
8
11
  ## attributes ##
9
12
  attr_accessor :id, :label, :type, :default, :options
10
13
 
11
14
  ## validations ##
12
15
  validates :id, :label, :type, :default, 'maglev/presence': true
13
- validates :type, inclusion: { in: %w[text image checkbox link color select collection_item icon] }
16
+ validates :type, inclusion: { in: REGISTERED_TYPES }
14
17
 
15
18
  ## methods ##
16
19
 
17
- # NOTE: any modification to that method must be reflected to the JS editor
18
- def build_default_content(custom_default = nil)
19
- default = custom_default.nil? ? self.default : custom_default
20
- case type.to_sym
21
- when :image then build_default_image_content(default)
22
- when :link then build_default_link_content(default)
23
- when :checkbox then build_default_checkbox_content(default)
24
- when :collection_item then build_default_collection_item_content(default)
25
- else
26
- default || label
20
+ # shortcuts
21
+ REGISTERED_TYPES.each do |type|
22
+ define_method(:"#{type}_type?") do
23
+ self.type.to_s == type
27
24
  end
28
25
  end
29
26
 
30
- def build_default_image_content(default)
31
- default.is_a?(String) ? { url: default } : default || {}
27
+ def cast_value(value)
28
+ self.class.registered_types[type.to_s].cast_value(value)
32
29
  end
33
30
 
34
- def build_default_link_content(default)
35
- if default.is_a?(String)
36
- { text: 'Link', link_type: 'url', href: default }
37
- else
38
- { text: 'Link', link_type: 'url', href: '#' }.merge(default)
39
- end
40
- end
31
+ # NOTE: any modification to that method must be reflected in the JS editor
32
+ def build_default_content(custom_default = nil)
33
+ default = custom_default.nil? ? self.default : custom_default
41
34
 
42
- def build_default_checkbox_content(default)
43
- default.presence || false
44
- end
35
+ # special case: text type
36
+ default ||= label if text_type?
45
37
 
46
- def build_default_collection_item_content(default)
47
- { id: default }
38
+ cast_value(default)
48
39
  end
49
40
 
50
41
  ## class methods ##
@@ -58,5 +49,11 @@ class Maglev::Section::Setting
58
49
  def self.build_many(list)
59
50
  list.map { |hash| build(hash) }
60
51
  end
52
+
53
+ def self.registered_types
54
+ @registered_types ||= REGISTERED_TYPES.index_with do |type|
55
+ "Maglev::SettingTypes::#{type.camelize}".constantize.new
56
+ end
57
+ end
61
58
  end
62
59
  # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ value
7
+ end
8
+ end
9
+
10
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Checkbox < Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ ActiveModel::Type::Boolean.new.cast(value) || false
7
+ end
8
+ end
9
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::CollectionItem < Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ if value.is_a?(String)
7
+ { id: default }
8
+ else
9
+ value
10
+ end
11
+ end
12
+ end
13
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Color < Maglev::SettingTypes::Base
5
+ end
6
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Divider < Maglev::SettingTypes::Base
5
+ def cast_value(_value)
6
+ nil
7
+ end
8
+ end
9
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Hint < Maglev::SettingTypes::Base
5
+ def cast_value(_value)
6
+ nil
7
+ end
8
+ end
9
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Icon < Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ value
7
+ end
8
+ end
9
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Image < Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ if value.is_a?(String)
7
+ { url: value }
8
+ else
9
+ value || {}
10
+ end
11
+ end
12
+ end
13
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Link < Maglev::SettingTypes::Base
5
+ def cast_value(value)
6
+ if value.is_a?(String)
7
+ { text: 'Link', link_type: 'url', href: value }
8
+ else
9
+ { text: 'Link', link_type: 'url', href: '#' }.merge(value)
10
+ end
11
+ end
12
+ end
13
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Select < Maglev::SettingTypes::Base
5
+ end
6
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/ClassAndModuleChildren
4
+ class Maglev::SettingTypes::Text < Maglev::SettingTypes::Base
5
+ end
6
+ # rubocop:enable Style/ClassAndModuleChildren
@@ -6,12 +6,23 @@ module Maglev::Site::LocalesConcern
6
6
 
7
7
  included do
8
8
  ## serializers ##
9
- serialize :locales, LocalesSerializer
9
+ if Rails::VERSION::MAJOR > 6
10
+ serialize :locales, coder: LocalesSerializer
11
+ else
12
+ serialize :locales, LocalesSerializer
13
+ end
10
14
 
11
15
  ## validation ##
12
16
  validates :locales, 'maglev/collection': true, length: { minimum: 1 }
13
17
  end
14
18
 
19
+ def add_locale(locale)
20
+ return nil if locale_prefixes.include?(locale.prefix.to_sym)
21
+
22
+ locales << locale
23
+ locales
24
+ end
25
+
15
26
  def default_locale
16
27
  locales.first
17
28
  end