maglevcms 1.8.0 → 2.0.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 (158) hide show
  1. checksums.yaml +4 -4
  2. data/.yarn/install-state.gz +0 -0
  3. data/README.md +4 -4
  4. data/Rakefile +2 -1
  5. data/app/components/maglev/block_component.rb +1 -1
  6. data/app/components/maglev/content/base.rb +1 -1
  7. data/app/components/maglev/content/link.rb +4 -0
  8. data/app/components/maglev/section_component.rb +2 -2
  9. data/app/controllers/concerns/maglev/fetchers_concern.rb +3 -1
  10. data/app/controllers/concerns/maglev/resource_id_concern.rb +10 -0
  11. data/app/controllers/concerns/maglev/standalone_sections_concern.rb +6 -0
  12. data/app/controllers/concerns/maglev/{ui_locale_concern.rb → user_interface_locale_concern.rb} +1 -1
  13. data/app/controllers/maglev/api/collection_items_controller.rb +7 -0
  14. data/app/controllers/maglev/api/page_clones_controller.rb +1 -2
  15. data/app/controllers/maglev/api_controller.rb +1 -1
  16. data/app/controllers/maglev/application_controller.rb +1 -5
  17. data/app/controllers/maglev/assets/active_storage_proxy_controller.rb +17 -0
  18. data/app/controllers/maglev/assets/proxy_controller.rb +21 -0
  19. data/app/controllers/maglev/editor_controller.rb +1 -1
  20. data/app/controllers/maglev/page_preview_controller.rb +3 -3
  21. data/app/frontend/admin/controllers/screenshot_controller.js +29 -1
  22. data/app/frontend/editor/assets/remixicons/clipboard-line.svg +1 -0
  23. data/app/frontend/editor/assets/remixicons/ri-draggable.svg +1 -0
  24. data/app/frontend/editor/assets/remixicons/ri-folders-line.svg +1 -0
  25. data/app/frontend/editor/assets/remixicons/ri-more-fill.svg +1 -0
  26. data/app/frontend/editor/assets/remixicons/ri-shadow-line.svg +1 -0
  27. data/app/frontend/editor/components/errors/forbidden.vue +24 -0
  28. data/app/frontend/editor/components/errors/stale-record.vue +2 -2
  29. data/app/frontend/editor/components/header-nav/device-toggler.vue +6 -3
  30. data/app/frontend/editor/components/header-nav/index.vue +13 -42
  31. data/app/frontend/editor/components/header-nav/locale-toggler/index.vue +1 -1
  32. data/app/frontend/editor/components/header-nav/page-info.vue +41 -0
  33. data/app/frontend/editor/components/header-nav/preview-button.vue +6 -1
  34. data/app/frontend/editor/components/header-nav/separator.vue +1 -1
  35. data/app/frontend/editor/components/image-library/index.vue +26 -29
  36. data/app/frontend/editor/components/kit/collection-item-input.vue +9 -0
  37. data/app/frontend/editor/components/kit/copy-paste-button.vue +26 -0
  38. data/app/frontend/editor/components/kit/dropdown.vue +5 -0
  39. data/app/frontend/editor/components/kit/icon-button.vue +24 -0
  40. data/app/frontend/editor/components/kit/icon.vue +2 -3
  41. data/app/frontend/editor/components/kit/index.js +4 -2
  42. data/app/frontend/editor/components/kit/link-input.vue +1 -1
  43. data/app/frontend/editor/components/kit/modal.vue +1 -1
  44. data/app/frontend/editor/components/kit/page-icon.vue +1 -1
  45. data/app/frontend/editor/components/kit/rich-text-input/link-buttons.vue +1 -1
  46. data/app/frontend/editor/components/kit/select-input.vue +4 -2
  47. data/app/frontend/editor/components/link-picker/actions.vue +1 -1
  48. data/app/frontend/editor/components/link-picker/index.vue +1 -1
  49. data/app/frontend/editor/components/link-picker/page.vue +2 -0
  50. data/app/frontend/editor/components/page/form/main.vue +1 -1
  51. data/app/frontend/editor/components/page/list/actions-button.vue +159 -0
  52. data/app/frontend/editor/components/page/list/index.vue +15 -24
  53. data/app/frontend/editor/components/page/list/list-item.vue +10 -61
  54. data/app/frontend/editor/components/section-highlighter/index.vue +47 -46
  55. data/app/frontend/editor/components/section-list/add-button.vue +39 -0
  56. data/app/frontend/editor/components/section-list/index.vue +30 -15
  57. data/app/frontend/editor/components/section-list/list-item.vue +23 -8
  58. data/app/frontend/editor/components/section-pane/block-list/index.vue +1 -0
  59. data/app/frontend/editor/components/section-pane/block-list/list-item.vue +9 -6
  60. data/app/frontend/editor/components/section-pane/block-tree/new-nested-block-button.vue +2 -2
  61. data/app/frontend/editor/components/section-pane/block-tree/tree-node.vue +2 -2
  62. data/app/frontend/editor/components/sidebar-nav/index.vue +102 -0
  63. data/app/frontend/editor/components/sidebar-nav/link.vue +60 -0
  64. data/app/frontend/editor/components/theme-section-list/category.vue +47 -0
  65. data/app/frontend/editor/components/theme-section-list/index.vue +7 -31
  66. data/app/frontend/editor/components/theme-section-list/list-item.vue +10 -3
  67. data/app/frontend/editor/design/components/modal.scss +1 -1
  68. data/app/frontend/editor/design/components/tooltip.scss +6 -0
  69. data/app/frontend/editor/design/transitions.scss +1 -1
  70. data/app/frontend/editor/layouts/app.vue +14 -2
  71. data/app/frontend/editor/layouts/default.vue +2 -2
  72. data/app/frontend/editor/layouts/slide-pane.vue +1 -1
  73. data/app/frontend/editor/locales/editor.ar.json +265 -0
  74. data/app/frontend/editor/locales/editor.en.json +23 -15
  75. data/app/frontend/editor/locales/editor.es.json +18 -10
  76. data/app/frontend/editor/locales/editor.fr.json +28 -13
  77. data/app/frontend/editor/locales/editor.pt-BR.json +18 -10
  78. data/app/frontend/editor/locales/index.js +3 -0
  79. data/app/frontend/editor/mixins/error-modal.js +7 -2
  80. data/app/frontend/editor/mixins/preview-transformation.js +0 -1
  81. data/app/frontend/editor/plugins/i18n.js +2 -17
  82. data/app/frontend/editor/services/__tests__/section.spec.js +6 -0
  83. data/app/frontend/editor/services/collection-item.js +8 -0
  84. data/app/frontend/editor/services/live-preview.js +11 -1
  85. data/app/frontend/editor/services/page.js +1 -1
  86. data/app/frontend/editor/services/section.js +30 -0
  87. data/app/frontend/editor/services/site.js +4 -4
  88. data/app/frontend/editor/services/theme.js +1 -0
  89. data/app/frontend/editor/spec/__mocks__/page.js +3 -1
  90. data/app/frontend/editor/spec/__mocks__/services.js +1 -0
  91. data/app/frontend/editor/store/__tests__/getters.spec.js +56 -1
  92. data/app/frontend/editor/store/actions/index.js +1 -0
  93. data/app/frontend/editor/store/actions/site.js +8 -4
  94. data/app/frontend/editor/store/getters.js +11 -0
  95. data/app/frontend/editor/views/page-preview.vue +2 -1
  96. data/app/frontend/images/favicon.svg +11 -0
  97. data/app/frontend/images/logo.svg +14 -0
  98. data/app/frontend/live-preview-client/iframe-decorator.js +20 -17
  99. data/app/frontend/live-preview-client/message.js +1 -0
  100. data/app/frontend/live-preview-client/rails.js +12 -2
  101. data/app/helpers/maglev/application_helper.rb +7 -2
  102. data/app/helpers/maglev/editor_helper.rb +12 -5
  103. data/app/models/concerns/maglev/sections_concern.rb +4 -0
  104. data/app/models/maglev/asset.rb +13 -14
  105. data/app/models/maglev/page/path_concern.rb +23 -8
  106. data/app/models/maglev/page.rb +17 -18
  107. data/app/models/maglev/page_path.rb +18 -18
  108. data/app/models/maglev/section/setting.rb +1 -1
  109. data/app/models/maglev/section.rb +18 -9
  110. data/app/models/maglev/setting_types/link.rb +1 -1
  111. data/app/models/maglev/site.rb +13 -24
  112. data/app/services/maglev/app_container.rb +2 -2
  113. data/app/services/maglev/extract_locale.rb +3 -2
  114. data/app/services/maglev/fetch_page.rb +2 -0
  115. data/app/services/maglev/fetch_theme_layout.rb +2 -0
  116. data/app/services/maglev/get_page_fullpath.rb +1 -2
  117. data/app/services/maglev/remove_section_type.rb +50 -0
  118. data/app/services/maglev/rename_section_type.rb +57 -0
  119. data/app/services/maglev/search_pages.rb +2 -1
  120. data/app/views/layouts/maglev/admin/application.html.erb +1 -1
  121. data/app/views/maglev/admin/sections/previews/show.html.erb +16 -1
  122. data/app/views/maglev/api/collection_items/show.json.jbuilder +7 -0
  123. data/app/views/maglev/api/page_clones/create.json.jbuilder +3 -0
  124. data/app/views/maglev/editor/show.html.erb +6 -5
  125. data/config/locales/activerecord.ar.yml +13 -0
  126. data/config/locales/activerecord.en.yml +8 -2
  127. data/config/locales/activerecord.es.yml +4 -0
  128. data/config/locales/activerecord.fr.yml +6 -2
  129. data/config/locales/activerecord.pt-BR.yml +13 -0
  130. data/config/routes.rb +3 -4
  131. data/db/migrate/20200831101942_create_maglev_section_content.rb +10 -2
  132. data/db/migrate/20210819092740_switch_to_localized_page_fields.rb +13 -3
  133. data/db/migrate/20211008064437_add_locales_to_sites.rb +7 -1
  134. data/db/migrate/20211013210954_translate_section_content.rb +16 -2
  135. data/db/migrate/20211203224112_add_open_graph_tags_to_pages.rb +9 -3
  136. data/db/migrate/20220612092235_add_style_to_sites.rb +5 -1
  137. data/lib/commands/maglev/change_site_locales_command.rb +61 -0
  138. data/lib/commands/maglev/create_site_command.rb +28 -0
  139. data/lib/commands/maglev/sections/remove_command.rb +48 -0
  140. data/lib/commands/maglev/sections/rename_command.rb +49 -0
  141. data/lib/generators/maglev/templates/install/config/initializers/maglev.rb +4 -1
  142. data/lib/generators/maglev/templates/theme/app/views/theme/layout.html.erb.tt +8 -2
  143. data/lib/maglev/errors.rb +1 -0
  144. data/lib/maglev/preview_constraint.rb +5 -1
  145. data/lib/maglev/theme_filesystem_loader.rb +7 -0
  146. data/lib/maglev/version.rb +1 -1
  147. data/lib/maglev.rb +13 -3
  148. data/lib/tasks/maglev/icons.rake +123 -0
  149. data/lib/tasks/maglev/vite.rake +62 -0
  150. data/lib/tasks/maglev_tasks.rake +9 -107
  151. data/tailwind.config.js +1 -0
  152. metadata +48 -33
  153. data/app/controllers/maglev/assets_controller.rb +0 -10
  154. data/app/frontend/editor/components/kit/list-item-button.vue +0 -16
  155. data/app/frontend/editor/components/sidebar-nav.vue +0 -125
  156. data/app/frontend/editor/plugins/maglev_dummy.js +0 -2
  157. data/app/frontend/images/favicon.png +0 -0
  158. data/app/frontend/images/logo.png +0 -0
@@ -5,9 +5,10 @@
5
5
 
6
6
  <%= csrf_meta_tags %>
7
7
  <%= csp_meta_tag %>
8
-
9
- <script>
8
+
9
+ <%= javascript_tag nonce: true do %>
10
10
  window.locale = <%= h content_locale.to_json.html_safe %>;
11
+ window.uiLocale = <%= h editor_ui_locale.to_json.html_safe %>;
11
12
  window.baseUrl = <%= h site_base_editor_path.to_json.html_safe %>;
12
13
  window.leaveUrl = <%= site_leave_editor_path.to_json.html_safe %>;
13
14
  window.apiBaseUrl = <%= h api_base_path.to_json.html_safe %>;
@@ -18,8 +19,8 @@
18
19
  window.primaryColor = <%= editor_primary_hex_color.to_json.html_safe %>;
19
20
  window.sitePublishable = <%= editor_site_publishable.to_json %>;
20
21
  window.customTranslations = <%= h editor_custom_translations.to_json.html_safe %>
21
- </script>
22
-
22
+ <% end %>
23
+
23
24
  <%= vite_client_tag %>
24
25
  <%= vite_javascript_tag 'editor' %>
25
26
 
@@ -32,7 +33,7 @@
32
33
  }
33
34
  </style>
34
35
 
35
- <%= render 'header' %>
36
+ <%= render 'header' %>
36
37
  </head>
37
38
  <body class="h-full overflow-y-hidden font-nunito">
38
39
  <div id="maglev-app"></div>
@@ -0,0 +1,13 @@
1
+ ar:
2
+ activerecord:
3
+ attributes:
4
+ 'maglev/page':
5
+ cloned_title: "%{title} نسخة"
6
+ 'maglev/page/paths':
7
+ value: المسار
8
+ errors:
9
+ models:
10
+ maglev/page_path:
11
+ attributes:
12
+ value:
13
+ invalid_path: "ليس مسارًا صالحًا"
@@ -1,7 +1,13 @@
1
1
  en:
2
2
  activerecord:
3
3
  attributes:
4
- 'maglev/page':
4
+ 'maglev/page':
5
5
  cloned_title: "%{title} COPY"
6
6
  'maglev/page/paths':
7
- value: Path
7
+ value: Path
8
+ errors:
9
+ models:
10
+ maglev/page_path:
11
+ attributes:
12
+ value:
13
+ invalid_path: "is not a valid path"
@@ -24,3 +24,7 @@ es:
24
24
  path:
25
25
  taken: ya está en uso por otra página
26
26
  exclusion: ya está en uso por otra página
27
+ maglev/page_path:
28
+ attributes:
29
+ value:
30
+ invalid_path: "no es una ruta válida"
@@ -4,7 +4,7 @@ fr:
4
4
  'maglev/page': Page
5
5
 
6
6
  attributes:
7
- 'maglev/page':
7
+ 'maglev/page':
8
8
  title: Titre
9
9
  cloned_title: "%{title} COPIE"
10
10
  seo_title: Titre SEO
@@ -23,4 +23,8 @@ fr:
23
23
  blank: doît être rempli
24
24
  path:
25
25
  taken: est déjà utilisé par une autre page
26
- exclusion: est déjà utilisé par une autre page
26
+ exclusion: est déjà utilisé par une autre page
27
+ maglev/page_path:
28
+ attributes:
29
+ value:
30
+ invalid_path: "n'est pas un chemin valide"
@@ -0,0 +1,13 @@
1
+ 'pt-BR':
2
+ activerecord:
3
+ attributes:
4
+ 'maglev/page':
5
+ cloned_title: "%{title} CÓPIA"
6
+ 'maglev/page/paths':
7
+ value: Caminho
8
+ errors:
9
+ models:
10
+ maglev/page_path:
11
+ attributes:
12
+ value:
13
+ invalid_path: "não é um caminho válido"
data/config/routes.rb CHANGED
@@ -12,9 +12,7 @@ Maglev::Engine.routes.draw do
12
12
  end
13
13
  resources :assets
14
14
  resource :publication, only: %i[show create]
15
- scope 'collections/:collection_id' do
16
- get '/', to: 'collection_items#index', as: :collection_items
17
- end
15
+ resources :collection_items, path: 'collections/:collection_id', only: %i[index show]
18
16
  end
19
17
  end
20
18
 
@@ -52,6 +50,7 @@ Maglev::Engine.routes.draw do
52
50
  post 'preview/(*path)', to: 'page_preview#create', defaults: { path: 'index', rendering_mode: :editor }
53
51
 
54
52
  # Public Assets
55
- get 'assets/:id(/:filename)', to: 'assets#show', as: :public_asset
53
+ get 'assets/:id(/:filename)', to: "#{Maglev.uploader_proxy_controller_name}#show",
54
+ as: :public_asset
56
55
  end
57
56
  # rubocop:enable Metrics/BlockLength
@@ -1,11 +1,19 @@
1
1
  class CreateMaglevSectionContent < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  change_table :maglev_sites do |t|
4
- t.jsonb :sections, default: []
4
+ if t.respond_to? :jsonb
5
+ t.jsonb :sections, default: []
6
+ else
7
+ t.json :sections, default: []
8
+ end
5
9
  end
6
10
 
7
11
  change_table :maglev_pages do |t|
8
- t.jsonb :sections, default: []
12
+ if t.respond_to? :jsonb
13
+ t.jsonb :sections, default: []
14
+ else
15
+ t.json :sections, default: []
16
+ end
9
17
  end
10
18
  end
11
19
  end
@@ -1,15 +1,25 @@
1
1
  class SwitchToLocalizedPageFields < ActiveRecord::Migration[6.1]
2
2
  def up
3
3
  remove_columns :maglev_pages, :title, :seo_title, :meta_description
4
- add_column :maglev_pages, :title_translations, :jsonb, default: {}
5
- add_column :maglev_pages, :seo_title_translations, :jsonb, default: {}
6
- add_column :maglev_pages, :meta_description_translations, :jsonb, default: {}
4
+
5
+ change_table :maglev_pages do |t|
6
+ if t.respond_to? :jsonb
7
+ t.jsonb :title_translations, default: {}
8
+ t.jsonb :seo_title_translations, default: {}
9
+ t.jsonb :meta_description_translations, default: {}
10
+ else
11
+ t.json :title_translations, default: {}
12
+ t.json :seo_title_translations, default: {}
13
+ t.json :meta_description_translations, default: {}
14
+ end
15
+ end
7
16
  end
8
17
 
9
18
  def down
10
19
  add_column :maglev_pages, :title, :string
11
20
  add_column :maglev_pages, :seo_title, :string
12
21
  add_column :maglev_pages, :meta_description, :string
22
+
13
23
  remove_column :maglev_pages, :title_translations
14
24
  remove_column :maglev_pages, :seo_title_translations
15
25
  remove_column :maglev_pages, :meta_description_translations
@@ -1,5 +1,11 @@
1
1
  class AddLocalesToSites < ActiveRecord::Migration[6.0]
2
2
  def change
3
- add_column :maglev_sites, :locales, :jsonb, default: []
3
+ change_table :maglev_sites do |t|
4
+ if t.respond_to? :jsonb
5
+ t.jsonb :locales, default: []
6
+ else
7
+ t.json :locales, default: []
8
+ end
9
+ end
4
10
  end
5
11
  end
@@ -1,8 +1,22 @@
1
1
  class TranslateSectionContent < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  remove_column :maglev_sites, :sections, :jsonb, default: []
4
- add_column :maglev_sites, :sections_translations, :jsonb, default: {}
5
4
  remove_column :maglev_pages, :sections, :jsonb, default: []
6
- add_column :maglev_pages, :sections_translations, :jsonb, default: {}
5
+
6
+ change_table :maglev_sites do |t|
7
+ if t.respond_to? :jsonb
8
+ t.jsonb :sections_translations, default: {}
9
+ else
10
+ t.json :sections_translations, default: {}
11
+ end
12
+ end
13
+
14
+ change_table :maglev_pages do |t|
15
+ if t.respond_to? :jsonb
16
+ t.jsonb :sections_translations, default: {}
17
+ else
18
+ t.json :sections_translations, default: {}
19
+ end
20
+ end
7
21
  end
8
22
  end
@@ -1,9 +1,15 @@
1
1
  class AddOpenGraphTagsToPages < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  change_table :maglev_pages do |t|
4
- t.jsonb :og_title_translations, default: {}
5
- t.jsonb :og_description_translations, default: {}
6
- t.jsonb :og_image_url_translations, default: {}
4
+ if t.respond_to? :jsonb
5
+ t.jsonb :og_title_translations, default: {}
6
+ t.jsonb :og_description_translations, default: {}
7
+ t.jsonb :og_image_url_translations, default: {}
8
+ else
9
+ t.json :og_title_translations, default: {}
10
+ t.json :og_description_translations, default: {}
11
+ t.json :og_image_url_translations, default: {}
12
+ end
7
13
  end
8
14
  end
9
15
  end
@@ -1,7 +1,11 @@
1
1
  class AddStyleToSites < ActiveRecord::Migration[6.0]
2
2
  def change
3
3
  change_table :maglev_sites do |t|
4
- t.jsonb :style, default: []
4
+ if t.respond_to? :jsonb
5
+ t.jsonb :style, default: []
6
+ else
7
+ t.json :style, default: []
8
+ end
5
9
  end
6
10
  end
7
11
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module Maglev
6
+ class ChangeSiteLocalesCommand < Rails::Command::Base
7
+ desc 'change_site_locales [LOCALES]', 'Change site locales (format: label:prefix)'
8
+
9
+ def self.banner
10
+ 'bin/rails maglev:change_site_locales [label:prefix label2:prefix2 ...]'
11
+ end
12
+
13
+ def perform(*locale_args)
14
+ require File.expand_path('config/environment', Rails.root)
15
+
16
+ site = fetch_site
17
+ return if site.blank?
18
+
19
+ locales = build_locales(locale_args)
20
+ return if locales.empty?
21
+
22
+ update_site_locales(site, locales)
23
+ end
24
+
25
+ private
26
+
27
+ def fetch_site
28
+ Maglev::Site.first.tap do |site|
29
+ say("[Error] You don't seem to have an existing site. 🤔", :red) unless site
30
+ end
31
+ end
32
+
33
+ def build_locales(locale_args)
34
+ build_custom_locales(locale_args).tap do |locales|
35
+ validate_locales(locales)
36
+ end
37
+ end
38
+
39
+ def build_custom_locales(locale_args)
40
+ (locale_args || []).map do |arg|
41
+ label, prefix = arg.split(':')
42
+ Maglev::Site::Locale.new(label: label, prefix: prefix)
43
+ end
44
+ end
45
+
46
+ def validate_locales(locales)
47
+ return true if !locales.empty? && locales.all?(&:valid?)
48
+
49
+ say("[Error] make sure your locales follow the 'label:prefix' pattern. 🤓", :red)
50
+ false
51
+ end
52
+
53
+ def update_site_locales(site, locales)
54
+ Maglev::ChangeSiteLocales.call(
55
+ site: site,
56
+ locales: locales
57
+ )
58
+ say('Success! 🎉🎉🎉', :green)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module Maglev
6
+ class CreateSiteCommand < Rails::Command::Base
7
+ desc 'create_site', 'Create your site'
8
+
9
+ def self.banner
10
+ 'bin/rails maglev:create_site'
11
+ end
12
+
13
+ def perform
14
+ require File.expand_path('config/environment')
15
+
16
+ if Maglev::Site.exists?
17
+ say '🤔 You already have a site. 🤔', :yellow
18
+ return
19
+ end
20
+
21
+ Maglev::GenerateSite.call(
22
+ theme: Maglev.local_themes.first
23
+ )
24
+
25
+ say '🎉 Your site has been created with success!'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+
5
+ module Maglev
6
+ module Sections
7
+ class RemoveCommand < Rails::Command::Base
8
+ desc 'remove TYPE', 'Remove a section type across the site and its pages'
9
+
10
+ def self.banner
11
+ 'bin/rails maglev:sections:remove TYPE'
12
+ end
13
+
14
+ def perform(type)
15
+ require File.expand_path('config/environment', Rails.root)
16
+
17
+ site = fetch_site
18
+
19
+ return if site.blank?
20
+
21
+ removed_count = Maglev::RemoveSectionType.call(
22
+ site: site,
23
+ type: type
24
+ )
25
+
26
+ display_final_message(removed_count, type)
27
+ end
28
+
29
+ private
30
+
31
+ def fetch_site
32
+ Maglev::Site.first.tap do |site|
33
+ say("[Error] You don't seem to have an existing site. 🤔", :red) unless site
34
+ end
35
+ end
36
+
37
+ def display_final_message(removed_count, type)
38
+ if removed_count.zero?
39
+ say "No section of type '#{type}' found 🤔", :yellow
40
+ return
41
+ end
42
+
43
+ say "Successfully removed #{removed_count} #{'section'.pluralize(removed_count)} of type '#{type}' 🎉",
44
+ :green
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ module Sections
5
+ class RenameCommand < Rails::Command::Base
6
+ desc 'rename OLD_TYPE NEW_TYPE', 'Rename a section type across the site and its pages'
7
+
8
+ def self.banner
9
+ 'bin/rails maglev:sections:rename OLD_TYPE NEW_TYPE'
10
+ end
11
+
12
+ def perform(old_type, new_type)
13
+ require File.expand_path('config/environment', Rails.root)
14
+
15
+ site = fetch_site
16
+ theme = fetch_theme
17
+
18
+ return if site.blank? || theme.blank?
19
+
20
+ rename_sections(site, theme, old_type, new_type)
21
+
22
+ say "Successfully renamed all '#{old_type}' sections to '#{new_type}' 🎉", :green
23
+ end
24
+
25
+ private
26
+
27
+ def fetch_site
28
+ Maglev::Site.first.tap do |site|
29
+ say("[Error] You don't seem to have an existing site. 🤔", :red) unless site
30
+ end
31
+ end
32
+
33
+ def fetch_theme
34
+ Maglev.local_themes&.first.tap do |theme|
35
+ say('[Error] No theme found. 🤔', :red) unless theme
36
+ end
37
+ end
38
+
39
+ def rename_sections(site, theme, old_type, new_type)
40
+ Maglev::RenameSectionType.call(
41
+ site: site,
42
+ theme: theme,
43
+ old_type: old_type,
44
+ new_type: new_type
45
+ )
46
+ end
47
+ end
48
+ end
49
+ end
@@ -3,6 +3,7 @@
3
3
  Maglev.configure do |config|
4
4
  # Title of the Editor window
5
5
  # config.title = 'Maglev - Editor'
6
+ # config.title = ->(site) { "#{site.name} 👋" }
6
7
 
7
8
  # Logo of the Editor (top left corner).
8
9
  # Put your custom logo in the app/assets/images folder of your Rails application.
@@ -50,7 +51,9 @@ Maglev.configure do |config|
50
51
  config.admin_username = Rails.env.production? ? ENV.fetch('MAGLEV_ADMIN_USERNAME') : nil
51
52
  config.admin_password = Rails.env.production? ? ENV.fetch('MAGLEV_ADMIN_PASSWORD') : nil
52
53
 
53
- # Uploader engine (:active_storage is only supported for now)
54
+ # Uploader engine (:active_storage is the default one)
55
+ # Checkout https://github.com/MarsBased/maglevcms-shrine for an example of a custom uploader
56
+ # config.uploader = 'Maglev::Shrine::Uploader'
54
57
  config.uploader = :active_storage
55
58
 
56
59
  # Collections mapping (https://docs.maglev.dev/guides/setup-collections)
@@ -4,7 +4,7 @@
4
4
  <title><%%= maglev_page.seo_title.presence || maglev_site.name %></title>
5
5
  <meta content="width=device-width, initial-scale=1" name="viewport">
6
6
 
7
- <meta name="description" content="<%%= maglev_page.meta_description %>" />
7
+ <meta name="description" content="<%%= maglev_page.meta_description %>" />
8
8
  <meta property="og:title" content="<%%= maglev_page.og_title %>" />
9
9
  <meta property="og:description" content="<%%= maglev_page.og_description %>" />
10
10
  <meta property="og:image" content="<%%= maglev_page.og_image_url %>" />
@@ -13,7 +13,13 @@
13
13
 
14
14
  <%%= maglev_live_preview_client_javascript_tag %>
15
15
 
16
- <script src="https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio,line-clamp"></script>
16
+ <%%=
17
+ javascript_include_tag(
18
+ "https://cdn.tailwindcss.com?plugins=forms,typography,aspect-ratio",
19
+ nonce: true,
20
+ id: "tailwind-cdn"
21
+ )
22
+ %>
17
23
  </head>
18
24
  <body class="bg-white">
19
25
  <main data-maglev-dropzone>
data/lib/maglev/errors.rb CHANGED
@@ -4,6 +4,7 @@ module Maglev
4
4
  module Errors
5
5
  class NotAuthorized < StandardError; end
6
6
  class UnknownSection < StandardError; end
7
+ class DuplicateSectionDefinition < StandardError; end
7
8
 
8
9
  class UnknownSetting < StandardError
9
10
  def initialize(section_id, block_id, setting_id)
@@ -13,7 +13,7 @@ module Maglev
13
13
  end
14
14
 
15
15
  def matches?(request)
16
- (accepted_format?(request) || crawler?(request)) && match_host?(request)
16
+ !websocket?(request) && (accepted_format?(request) || crawler?(request)) && match_host?(request)
17
17
  end
18
18
 
19
19
  protected
@@ -32,6 +32,10 @@ module Maglev
32
32
  request.format.symbol.nil? && CRAWLER_USER_AGENTS.match?(request.user_agent)
33
33
  end
34
34
 
35
+ def websocket?(request)
36
+ request.headers['HTTP_UPGRADE'] == 'websocket'
37
+ end
38
+
35
39
  def match_host?(request)
36
40
  !preview_host || preview_host == request.host
37
41
  end
@@ -11,6 +11,7 @@ module Maglev
11
11
  def call(path)
12
12
  theme = add(YAML.safe_load(File.read(path.join('theme.yml')), aliases: true))
13
13
  sections = load_sections(theme, Pathname.new(path).join('sections/**/*.yml'))
14
+ detect_duplicate_sections(sections)
14
15
  theme.sections = Maglev::Section::Store.new(sections)
15
16
  theme
16
17
  rescue Errno::ENOENT
@@ -52,6 +53,12 @@ module Maglev
52
53
  File.exist?(path) ? File.mtime(path).to_i : nil
53
54
  end
54
55
 
56
+ def detect_duplicate_sections(sections)
57
+ sections.group_by(&:id).each do |id, list|
58
+ raise Maglev::Errors::DuplicateSectionDefinition, "Duplicate section definition: #{id}" if list.size > 1
59
+ end
60
+ end
61
+
55
62
  def log_missing_theme_file(path)
56
63
  # don't log the error if the ruby code is not executed inside
57
64
  # the Rails console or when the Rails server is running
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Maglev
4
- VERSION = '1.8.0'
4
+ VERSION = '2.0.0'
5
5
  end
data/lib/maglev.rb CHANGED
@@ -26,7 +26,7 @@ module Maglev
26
26
  c.favicon = nil
27
27
  c.logo = nil
28
28
  c.primary_color = '#040712'
29
- c.uploader = 'active_storage'
29
+ c.uploader = :active_storage
30
30
  c.site_publishable = false
31
31
  c.preview_host = nil
32
32
  c.asset_host = Rails.application.config.action_controller.asset_host
@@ -47,12 +47,22 @@ module Maglev
47
47
  config.tap do
48
48
  yield(config)
49
49
  config.reserved_paths = Maglev::ReservedPaths.new(config.reserved_paths)
50
+ require_relative 'maglev/active_storage' if config.uploader == :active_storage
50
51
  end
51
52
  end
52
53
 
53
54
  def uploader
54
- require_relative "maglev/#{config.uploader}"
55
- const_get("::Maglev::#{config.uploader.to_s.classify}")
55
+ case config.uploader
56
+ when Symbol then const_get("::Maglev::#{config.uploader.to_s.classify}")
57
+ when String then const_get(config.uploader)
58
+ when Class then config.uploader
59
+ else
60
+ raise "Invalid or not set uploader: #{config.uploader}"
61
+ end
62
+ end
63
+
64
+ def uploader_proxy_controller_name
65
+ config.uploader == :active_storage ? 'assets/active_storage_proxy' : 'assets/proxy'
56
66
  end
57
67
 
58
68
  def services(overrides = {})