maglevcms 1.8.0 → 2.0.0.beta1

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 (156) 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 +1 -1
  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/page.js +1 -1
  85. data/app/frontend/editor/services/section.js +30 -0
  86. data/app/frontend/editor/services/site.js +4 -4
  87. data/app/frontend/editor/services/theme.js +1 -0
  88. data/app/frontend/editor/spec/__mocks__/page.js +3 -1
  89. data/app/frontend/editor/spec/__mocks__/services.js +1 -0
  90. data/app/frontend/editor/store/__tests__/getters.spec.js +56 -1
  91. data/app/frontend/editor/store/actions/index.js +1 -0
  92. data/app/frontend/editor/store/actions/site.js +8 -4
  93. data/app/frontend/editor/store/getters.js +11 -0
  94. data/app/frontend/editor/views/page-preview.vue +2 -1
  95. data/app/frontend/images/favicon.svg +11 -0
  96. data/app/frontend/images/logo.svg +14 -0
  97. data/app/frontend/live-preview-client/iframe-decorator.js +17 -15
  98. data/app/frontend/live-preview-client/rails.js +1 -1
  99. data/app/helpers/maglev/application_helper.rb +7 -2
  100. data/app/helpers/maglev/editor_helper.rb +12 -5
  101. data/app/models/concerns/maglev/sections_concern.rb +4 -0
  102. data/app/models/maglev/asset.rb +13 -14
  103. data/app/models/maglev/page/path_concern.rb +23 -8
  104. data/app/models/maglev/page.rb +17 -18
  105. data/app/models/maglev/page_path.rb +18 -18
  106. data/app/models/maglev/section/setting.rb +1 -1
  107. data/app/models/maglev/section.rb +18 -9
  108. data/app/models/maglev/setting_types/link.rb +1 -1
  109. data/app/models/maglev/site.rb +13 -24
  110. data/app/services/maglev/app_container.rb +2 -2
  111. data/app/services/maglev/extract_locale.rb +3 -2
  112. data/app/services/maglev/fetch_page.rb +2 -0
  113. data/app/services/maglev/fetch_theme_layout.rb +2 -0
  114. data/app/services/maglev/get_page_fullpath.rb +1 -2
  115. data/app/services/maglev/remove_section_type.rb +50 -0
  116. data/app/services/maglev/rename_section_type.rb +57 -0
  117. data/app/services/maglev/search_pages.rb +2 -1
  118. data/app/views/layouts/maglev/admin/application.html.erb +1 -1
  119. data/app/views/maglev/admin/sections/previews/show.html.erb +16 -1
  120. data/app/views/maglev/api/collection_items/show.json.jbuilder +7 -0
  121. data/app/views/maglev/api/page_clones/create.json.jbuilder +3 -0
  122. data/app/views/maglev/editor/show.html.erb +6 -5
  123. data/config/locales/activerecord.ar.yml +13 -0
  124. data/config/locales/activerecord.en.yml +8 -2
  125. data/config/locales/activerecord.es.yml +4 -0
  126. data/config/locales/activerecord.fr.yml +6 -2
  127. data/config/locales/activerecord.pt-BR.yml +13 -0
  128. data/config/routes.rb +3 -4
  129. data/db/migrate/20200831101942_create_maglev_section_content.rb +10 -2
  130. data/db/migrate/20210819092740_switch_to_localized_page_fields.rb +13 -3
  131. data/db/migrate/20211008064437_add_locales_to_sites.rb +7 -1
  132. data/db/migrate/20211013210954_translate_section_content.rb +16 -2
  133. data/db/migrate/20211203224112_add_open_graph_tags_to_pages.rb +9 -3
  134. data/db/migrate/20220612092235_add_style_to_sites.rb +5 -1
  135. data/lib/commands/maglev/change_site_locales_command.rb +61 -0
  136. data/lib/commands/maglev/create_site_command.rb +28 -0
  137. data/lib/commands/maglev/sections/remove_command.rb +48 -0
  138. data/lib/commands/maglev/sections/rename_command.rb +49 -0
  139. data/lib/generators/maglev/templates/install/config/initializers/maglev.rb +4 -1
  140. data/lib/generators/maglev/templates/theme/app/views/theme/layout.html.erb.tt +8 -2
  141. data/lib/maglev/errors.rb +1 -0
  142. data/lib/maglev/preview_constraint.rb +5 -1
  143. data/lib/maglev/theme_filesystem_loader.rb +7 -0
  144. data/lib/maglev/version.rb +1 -1
  145. data/lib/maglev.rb +13 -3
  146. data/lib/tasks/maglev/icons.rake +123 -0
  147. data/lib/tasks/maglev/vite.rake +62 -0
  148. data/lib/tasks/maglev_tasks.rake +9 -107
  149. data/tailwind.config.js +1 -0
  150. metadata +48 -33
  151. data/app/controllers/maglev/assets_controller.rb +0 -10
  152. data/app/frontend/editor/components/kit/list-item-button.vue +0 -16
  153. data/app/frontend/editor/components/sidebar-nav.vue +0 -125
  154. data/app/frontend/editor/plugins/maglev_dummy.js +0 -2
  155. data/app/frontend/images/favicon.png +0 -0
  156. data/app/frontend/images/logo.png +0 -0
@@ -4,22 +4,32 @@
4
4
  module Maglev::Page::PathConcern
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ COMMON_ASSOCIATION_PATH_OPTIONS = {
8
+ class_name: '::Maglev::PagePath',
9
+ foreign_key: 'maglev_page_id',
10
+ inverse_of: :page
11
+ }.freeze
12
+
7
13
  included do
8
14
  ## associations ##
9
15
  has_many :paths,
10
- class_name: '::Maglev::PagePath',
16
+ **COMMON_ASSOCIATION_PATH_OPTIONS,
11
17
  dependent: :delete_all,
12
- foreign_key: 'maglev_page_id',
13
- inverse_of: :page,
14
18
  autosave: true
15
19
 
20
+ # rubocop:disable Rails/HasManyOrHasOneDependent, Rails/InverseOf
21
+ has_many :canonical_paths,
22
+ -> { where(canonical: true) },
23
+ **COMMON_ASSOCIATION_PATH_OPTIONS
24
+ # rubocop:enable Rails/HasManyOrHasOneDependent, Rails/InverseOf
25
+
16
26
  ## callbacks ##
17
27
  before_validation { path } # force the initialization of a new path if it doesn't exist
18
28
  before_save :spawn_redirection, if: :spawn_redirection?
19
29
  end
20
30
 
21
31
  def default_path
22
- @default_path ||= paths.find_by(locale: Maglev::I18n.default_locale)&.value
32
+ @default_path ||= path_hash[Maglev::I18n.default_locale]
23
33
  end
24
34
 
25
35
  def path
@@ -38,13 +48,18 @@ module Maglev::Page::PathConcern
38
48
  end
39
49
 
40
50
  def current_path
41
- locale = Maglev::I18n.current_locale.to_sym
42
- @memoized_paths ||= {}
43
- @memoized_paths[locale] ||= paths.canonical.find_or_initialize_by(locale: locale, canonical: true)
51
+ locale = Maglev::I18n.current_locale
52
+ canonical_path_hash[locale] ||= paths.build(locale: locale, canonical: true)
53
+ end
54
+
55
+ def canonical_path_hash
56
+ @canonical_path_hash ||= canonical_paths.to_a.each_with_object({}.with_indifferent_access) do |path, memo|
57
+ memo[path.locale] ||= path
58
+ end
44
59
  end
45
60
 
46
61
  def path_hash
47
- paths.build_hash
62
+ canonical_path_hash.transform_values(&:value)
48
63
  end
49
64
 
50
65
  def disable_spawn_redirection
@@ -1,5 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Schema Information
4
+ #
5
+ # Table name: maglev_pages
6
+ #
7
+ # id :bigint not null, primary key
8
+ # lock_version :integer
9
+ # meta_description_translations :jsonb
10
+ # og_description_translations :jsonb
11
+ # og_image_url_translations :jsonb
12
+ # og_title_translations :jsonb
13
+ # sections_translations :jsonb
14
+ # seo_title_translations :jsonb
15
+ # title_translations :jsonb
16
+ # visible :boolean default(TRUE)
17
+ # created_at :datetime not null
18
+ # updated_at :datetime not null
19
+ #
3
20
  module Maglev
4
21
  class Page < ApplicationRecord
5
22
  ## concerns ##
@@ -42,21 +59,3 @@ module Maglev
42
59
  end
43
60
  end
44
61
  end
45
-
46
- # == Schema Information
47
- #
48
- # Table name: maglev_pages
49
- #
50
- # id :bigint not null, primary key
51
- # lock_version :integer
52
- # meta_description_translations :jsonb
53
- # og_description_translations :jsonb
54
- # og_image_url_translations :jsonb
55
- # og_title_translations :jsonb
56
- # sections_translations :jsonb
57
- # seo_title_translations :jsonb
58
- # title_translations :jsonb
59
- # visible :boolean default(TRUE)
60
- # created_at :datetime not null
61
- # updated_at :datetime not null
62
- #
@@ -1,5 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Schema Information
4
+ #
5
+ # Table name: maglev_page_paths
6
+ #
7
+ # id :bigint not null, primary key
8
+ # canonical :boolean default(TRUE)
9
+ # locale :string not null
10
+ # value :string not null
11
+ # maglev_page_id :bigint
12
+ #
13
+ # Indexes
14
+ #
15
+ # canonical_speed (canonical,locale,value)
16
+ # index_maglev_page_paths_on_maglev_page_id (maglev_page_id)
17
+ # scoped_canonical_speed (canonical,maglev_page_id,locale)
18
+ #
3
19
  module Maglev
4
20
  class PagePath < ApplicationRecord
5
21
  ## associations ##
@@ -13,6 +29,7 @@ module Maglev
13
29
  ## validations ##
14
30
  validates :value, presence: true, exclusion: { in: Maglev.config.reserved_paths }
15
31
  validates :value, uniqueness: { scope: %i[locale canonical] }, if: :canonical?
32
+ validates :value, format: { without: /\s/, message: :invalid_path }
16
33
  validates :canonical, uniqueness: { scope: %i[locale maglev_page_id] }, if: :canonical?
17
34
 
18
35
  ## callbacks ##
@@ -23,7 +40,7 @@ module Maglev
23
40
 
24
41
  ## class methods ##
25
42
 
26
- def self.build_hash(page_id = nil)
43
+ def self.canonical_value_hash(page_id = nil)
27
44
  query = page_id ? where(maglev_page_id: page_id) : all
28
45
  query.canonical.pluck(:locale, :value).to_h
29
46
  end
@@ -44,20 +61,3 @@ module Maglev
44
61
  end
45
62
  end
46
63
  end
47
-
48
- # == Schema Information
49
- #
50
- # Table name: maglev_page_paths
51
- #
52
- # id :bigint not null, primary key
53
- # canonical :boolean default(TRUE)
54
- # locale :string not null
55
- # value :string not null
56
- # maglev_page_id :bigint
57
- #
58
- # Indexes
59
- #
60
- # canonical_speed (canonical,locale,value)
61
- # index_maglev_page_paths_on_maglev_page_id (maglev_page_id)
62
- # scoped_canonical_speed (canonical,maglev_page_id,locale)
63
- #
@@ -12,7 +12,7 @@ class Maglev::Section::Setting
12
12
  attr_accessor :id, :label, :type, :default, :options
13
13
 
14
14
  ## validations ##
15
- validates :id, :label, :type, :default, 'maglev/presence': true
15
+ validates :id, :label, :type, 'maglev/presence': true
16
16
  validates :type, inclusion: { in: REGISTERED_TYPES }
17
17
 
18
18
  ## methods ##
@@ -35,19 +35,28 @@ module Maglev
35
35
  !!viewport_fixed_position?
36
36
  end
37
37
 
38
+ def assign_attributes_from_yaml(hash)
39
+ attributes = prepare_default_attributes(hash).merge(
40
+ settings: ::Maglev::Section::Setting.build_many(hash['settings']),
41
+ blocks: ::Maglev::Section::Block.build_many(hash['blocks'])
42
+ )
43
+
44
+ assign_attributes(attributes)
45
+ end
46
+
38
47
  ## class methods ##
39
- def self.build(hash)
40
- attributes = prepare_attributes(hash)
41
48
 
42
- new(
43
- attributes.merge(
44
- settings: ::Maglev::Section::Setting.build_many(hash['settings']),
45
- blocks: ::Maglev::Section::Block.build_many(hash['blocks'])
46
- )
47
- )
49
+ def self.build(hash)
50
+ new.tap do |section|
51
+ section.assign_attributes_from_yaml(hash)
52
+ end
48
53
  end
49
54
 
50
- def self.prepare_attributes(hash)
55
+ ## private methods ##
56
+
57
+ private
58
+
59
+ def prepare_default_attributes(hash)
51
60
  attributes = hash.slice(*HASH_ATTRIBUTES)
52
61
 
53
62
  %w[site_scoped singleton viewport_fixed_position max_width_pane].each do |name|
@@ -5,7 +5,7 @@ class Maglev::SettingTypes::Link < Maglev::SettingTypes::Base
5
5
  def cast_value(value)
6
6
  if value.is_a?(String)
7
7
  { text: 'Link', link_type: 'url', href: value }
8
- else
8
+ elsif value
9
9
  { text: 'Link', link_type: 'url', href: '#' }.merge(value.symbolize_keys)
10
10
  end
11
11
  end
@@ -1,5 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Schema Information
4
+ #
5
+ # Table name: maglev_sites
6
+ #
7
+ # id :bigint not null, primary key
8
+ # locales :jsonb
9
+ # lock_version :integer
10
+ # name :string
11
+ # sections_translations :jsonb
12
+ # style :jsonb
13
+ # created_at :datetime not null
14
+ # updated_at :datetime not null
15
+ #
3
16
  module Maglev
4
17
  class Site < ApplicationRecord
5
18
  ## concerns ##
@@ -27,27 +40,3 @@ module Maglev
27
40
  end
28
41
  end
29
42
  end
30
-
31
- # == Schema Information
32
- #
33
- # Table name: maglev_sites
34
- #
35
- # id :bigint not null, primary key
36
- # domain :string
37
- # handle :string
38
- # locales :jsonb
39
- # lock_version :integer
40
- # name :string
41
- # navigation :jsonb
42
- # sections_translations :jsonb
43
- # siteable_type :string
44
- # style :jsonb
45
- # created_at :datetime not null
46
- # updated_at :datetime not null
47
- # siteable_id :bigint
48
- # theme_id :string
49
- #
50
- # Indexes
51
- #
52
- # index_maglev_sites_on_siteable (siteable_type,siteable_id)
53
- #
@@ -23,7 +23,7 @@ module Maglev
23
23
 
24
24
  dependency :fetch_collection_items, class: Maglev::FetchCollectionItems, depends_on: %i[fetch_site config]
25
25
  dependency :fetch_static_pages, class: Maglev::FetchStaticPages, depends_on: %i[config]
26
- dependency :search_pages, class: Maglev::SearchPages, depends_on: %i[fetch_site fetch_static_pages]
26
+ dependency :search_pages, class: Maglev::SearchPages, depends_on: %i[fetch_site fetch_static_pages context]
27
27
 
28
28
  dependency :get_base_url, class: Maglev::GetBaseUrl, depends_on: %i[context fetch_site]
29
29
  dependency :extract_locale, class: Maglev::ExtractLocale
@@ -34,7 +34,7 @@ module Maglev
34
34
  dependency :add_site_locale, class: Maglev::AddSiteLocale
35
35
  dependency :change_site_locales, class: Maglev::ChangeSiteLocales
36
36
 
37
- dependency :fetch_page, class: Maglev::FetchPage, depends_on: :fetch_site
37
+ dependency :fetch_page, class: Maglev::FetchPage, depends_on: %i[context fetch_site]
38
38
  dependency :get_page_fullpath, class: Maglev::GetPageFullpath, depends_on: %i[fetch_site get_base_url]
39
39
  dependency :get_page_sections, class: Maglev::GetPageSections,
40
40
  depends_on: %i[fetch_site fetch_theme
@@ -22,9 +22,10 @@ module Maglev
22
22
  protected
23
23
 
24
24
  def extract_locale
25
- segments = params[:path].split('/')
25
+ path = params[:path] || 'index'
26
+ segments = path.split('/')
26
27
 
27
- return [default_locale, params[:path]] unless locales.include?(segments[0]&.to_sym)
28
+ return [default_locale, path] unless locales.include?(segments[0]&.to_sym)
28
29
 
29
30
  [segments.shift, segments.empty? ? 'index' : segments.join('/')]
30
31
  end
@@ -6,6 +6,8 @@ module Maglev
6
6
  class FetchPage
7
7
  include Injectable
8
8
 
9
+ dependency :context
10
+
9
11
  argument :path
10
12
  argument :locale
11
13
  argument :default_locale
@@ -7,6 +7,8 @@ module Maglev
7
7
 
8
8
  dependency :fetch_theme
9
9
 
10
+ argument :page
11
+
10
12
  def call
11
13
  'theme/layout'
12
14
  end
@@ -38,8 +38,7 @@ module Maglev
38
38
  end
39
39
 
40
40
  def fetch_paths
41
- page_id = page.respond_to?(:path) ? page.id : page
42
- Maglev::PagePath.build_hash(page_id)
41
+ page.respond_to?(:path_hash) ? page.path_hash : Maglev::PagePath.canonical_value_hash(page)
43
42
  end
44
43
 
45
44
  def build_fullpath(base_url, path)
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ # Remove all sections of a specific type across a site and its pages
5
+ class RemoveSectionType
6
+ include Injectable
7
+
8
+ argument :site
9
+ argument :type
10
+
11
+ def call
12
+ @removed_count = 0
13
+
14
+ remove_sections
15
+
16
+ @removed_count
17
+ end
18
+
19
+ private
20
+
21
+ def remove_sections
22
+ ActiveRecord::Base.transaction do
23
+ remove_resource_sections(site)
24
+ site_pages.find_each do |page|
25
+ remove_resource_sections(page)
26
+ end
27
+ end
28
+ end
29
+
30
+ def remove_resource_sections(resource)
31
+ site.each_locale do
32
+ @removed_count += remove_sections_of_type(resource.sections, type)
33
+ end
34
+
35
+ resource.save!
36
+ end
37
+
38
+ def remove_sections_of_type(sections, type)
39
+ return 0 if sections.blank?
40
+
41
+ original_size = sections.size
42
+ sections.reject! { |section| section['type'] == type }
43
+ original_size - sections.size
44
+ end
45
+
46
+ def site_pages
47
+ Maglev::Page
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Maglev
4
+ # Rename all sections of a specific type to a new type across a site and its pages
5
+ class RenameSectionType
6
+ include Injectable
7
+
8
+ argument :site
9
+ argument :theme
10
+ argument :old_type
11
+ argument :new_type
12
+
13
+ def call
14
+ validate_section_types!
15
+
16
+ ActiveRecord::Base.transaction do
17
+ rename_resource_sections(site)
18
+ site_pages.find_each do |page|
19
+ rename_resource_sections(page)
20
+ end
21
+ end
22
+
23
+ true
24
+ end
25
+
26
+ private
27
+
28
+ def validate_section_types!
29
+ return if theme.sections.find(new_type)
30
+
31
+ raise Maglev::Errors::UnknownSection,
32
+ "New section type '#{new_type}' doesn't exist in the theme"
33
+ end
34
+
35
+ def rename_resource_sections(resource)
36
+ site.each_locale do |locale|
37
+ Maglev::I18n.with_locale(locale) do
38
+ change_section_type(resource.sections, old_type, new_type)
39
+ end
40
+ end
41
+
42
+ resource.save!
43
+ end
44
+
45
+ def change_section_type(sections, old_type, new_type)
46
+ return if sections.blank?
47
+
48
+ sections.each do |section|
49
+ section['type'] = new_type if section['type'] == old_type
50
+ end
51
+ end
52
+
53
+ def site_pages
54
+ Maglev::Page
55
+ end
56
+ end
57
+ end
@@ -9,6 +9,7 @@ module Maglev
9
9
 
10
10
  dependency :fetch_site
11
11
  dependency :fetch_static_pages
12
+ dependency :context
12
13
 
13
14
  argument :id, default: nil
14
15
  argument :q, default: nil
@@ -54,7 +55,7 @@ module Maglev
54
55
  end
55
56
 
56
57
  def resources
57
- ::Maglev::Page
58
+ ::Maglev::Page.includes(:canonical_paths)
58
59
  end
59
60
  end
60
61
  end
@@ -9,7 +9,7 @@
9
9
  <%= vite_javascript_tag 'admin' %>
10
10
  <%= vite_stylesheet_tag 'admin.scss' %>
11
11
 
12
- <%= favicon_link_tag vite_asset_path 'images/favicon.png' %>
12
+ <%= favicon_link_tag vite_asset_path 'images/favicon.svg' %>
13
13
  </head>
14
14
  <body class="bg-gray-100">
15
15
  <main role="main" class="container mx-auto">
@@ -12,11 +12,26 @@
12
12
  Open in a new tab
13
13
  <% end %>
14
14
  <button
15
- class="bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-opacity-50 rounded text-white py-2 px-3"
15
+ class="bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-opacity-50 rounded text-white py-2 px-3 mr-3"
16
16
  data-action="click->screenshot#take"
17
17
  >
18
18
  Take screenshot
19
19
  </button>
20
+ <div class="relative">
21
+ <button
22
+ class="bg-purple-600 hover:bg-purple-700 focus:outline-none focus:ring-2 focus:ring-purple-600 focus:ring-opacity-50 rounded text-white py-2 px-3"
23
+ data-action="click->screenshot#triggerFileInput"
24
+ >
25
+ Upload screenshot
26
+ </button>
27
+ <input
28
+ type="file"
29
+ accept="image/*"
30
+ class="hidden"
31
+ data-screenshot-target="fileInput"
32
+ data-action="change->screenshot#handleFileUpload"
33
+ />
34
+ </div>
20
35
  </div>
21
36
  </div>
22
37
  <iframe
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if @item
4
+ json.partial!('show', item: @item)
5
+ else
6
+ json.nil!
7
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ json.partial!('/maglev/api/pages/show', page: @page)
@@ -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