spina 2.12.0 → 2.13.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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +17 -17
  3. data/app/assets/builds/spina/tailwind.css +79 -47
  4. data/app/components/spina/application_component.rb +1 -1
  5. data/app/components/spina/forms/group_component.rb +2 -3
  6. data/app/components/spina/forms/label_component.rb +2 -3
  7. data/app/components/spina/forms/password_field_component.rb +1 -1
  8. data/app/components/spina/forms/switch_component.rb +2 -3
  9. data/app/components/spina/forms/text_field_component.rb +8 -9
  10. data/app/components/spina/forms/trix_toolbar_component.rb +1 -3
  11. data/app/components/spina/hooks/hook_component.rb +3 -5
  12. data/app/components/spina/hooks/partial_component.rb +4 -5
  13. data/app/components/spina/main_navigation/link_component.rb +1 -3
  14. data/app/components/spina/main_navigation/sub_nav_component.rb +6 -7
  15. data/app/components/spina/media_picker/image_component.rb +1 -3
  16. data/app/components/spina/media_picker/modal_component.rb +4 -6
  17. data/app/components/spina/pages/actions_component.rb +1 -3
  18. data/app/components/spina/pages/list_component.rb +3 -4
  19. data/app/components/spina/pages/location_component.rb +10 -11
  20. data/app/components/spina/pages/new_page_button_component.rb +4 -5
  21. data/app/components/spina/pages/page_component.rb +8 -9
  22. data/app/components/spina/pages/page_select_component.rb +3 -4
  23. data/app/components/spina/pages/tab_button_component.rb +1 -3
  24. data/app/components/spina/pages/translations_component.rb +9 -11
  25. data/app/components/spina/user_interface/dropdown_button_component.rb +4 -4
  26. data/app/components/spina/user_interface/dropdown_component.rb +8 -9
  27. data/app/components/spina/user_interface/flash_message_component.rb +3 -5
  28. data/app/components/spina/user_interface/header_component.rb +2 -2
  29. data/app/components/spina/user_interface/modal_component.rb +1 -3
  30. data/app/components/spina/user_interface/tab_link_component.rb +1 -3
  31. data/app/components/spina/user_interface/translations_component.rb +8 -10
  32. data/app/controllers/concerns/spina/api/paginable.rb +36 -37
  33. data/app/controllers/concerns/spina/current_spina_account.rb +1 -1
  34. data/app/controllers/concerns/spina/current_theme.rb +6 -7
  35. data/app/controllers/concerns/spina/frontend.rb +58 -44
  36. data/app/controllers/spina/admin/accounts_controller.rb +9 -10
  37. data/app/controllers/spina/admin/admin_controller.rb +18 -18
  38. data/app/controllers/spina/admin/attachments_controller.rb +15 -10
  39. data/app/controllers/spina/admin/embeds_controller.rb +17 -19
  40. data/app/controllers/spina/admin/images_controller.rb +24 -25
  41. data/app/controllers/spina/admin/layout_controller.rb +19 -20
  42. data/app/controllers/spina/admin/media_folders_controller.rb +4 -5
  43. data/app/controllers/spina/admin/media_picker_controller.rb +2 -4
  44. data/app/controllers/spina/admin/move_pages_controller.rb +8 -10
  45. data/app/controllers/spina/admin/navigation_items_controller.rb +13 -14
  46. data/app/controllers/spina/admin/navigations_controller.rb +26 -26
  47. data/app/controllers/spina/admin/page_translations_controller.rb +1 -3
  48. data/app/controllers/spina/admin/pages_controller.rb +34 -34
  49. data/app/controllers/spina/admin/parent_pages_controller.rb +1 -3
  50. data/app/controllers/spina/admin/password_resets_controller.rb +8 -9
  51. data/app/controllers/spina/admin/resources_controller.rb +14 -15
  52. data/app/controllers/spina/admin/sessions_controller.rb +2 -2
  53. data/app/controllers/spina/admin/settings_controller.rb +28 -29
  54. data/app/controllers/spina/admin/theme_controller.rb +10 -11
  55. data/app/controllers/spina/admin/users_controller.rb +28 -29
  56. data/app/controllers/spina/api/api_controller.rb +17 -17
  57. data/app/controllers/spina/api/images_controller.rb +1 -3
  58. data/app/controllers/spina/api/navigations_controller.rb +2 -4
  59. data/app/controllers/spina/api/pages_controller.rb +8 -9
  60. data/app/controllers/spina/api/resources_controller.rb +2 -4
  61. data/app/controllers/spina/application_controller.rb +4 -3
  62. data/app/controllers/spina/pages_controller.rb +4 -5
  63. data/app/controllers/spina/sitemaps_controller.rb +0 -2
  64. data/app/helpers/spina/admin/icons_helper.rb +151 -153
  65. data/app/helpers/spina/admin/images_helper.rb +0 -2
  66. data/app/helpers/spina/admin/pages_helper.rb +3 -5
  67. data/app/helpers/spina/attachments_helper.rb +1 -3
  68. data/app/helpers/spina/images_helper.rb +5 -7
  69. data/app/helpers/spina/pages_helper.rb +0 -2
  70. data/app/helpers/spina/spina_helper.rb +1 -3
  71. data/app/jobs/spina/replace_signed_id_job.rb +10 -11
  72. data/app/jobs/spina/resource_pages_update_job.rb +1 -1
  73. data/app/mailers/spina/application_mailer.rb +2 -2
  74. data/app/mailers/spina/user_mailer.rb +4 -6
  75. data/app/models/concerns/spina/gravatar.rb +11 -12
  76. data/app/models/concerns/spina/partable.rb +4 -6
  77. data/app/models/concerns/spina/translated_content.rb +5 -5
  78. data/app/models/spina/account.rb +9 -10
  79. data/app/models/spina/application_record.rb +2 -3
  80. data/app/models/spina/attachment.rb +3 -4
  81. data/app/models/spina/current.rb +1 -1
  82. data/app/models/spina/embeds/base.rb +2 -2
  83. data/app/models/spina/embeds/button.rb +4 -4
  84. data/app/models/spina/embeds/vimeo.rb +17 -18
  85. data/app/models/spina/embeds/youtube.rb +18 -19
  86. data/app/models/spina/image.rb +4 -4
  87. data/app/models/spina/media_folder.rb +1 -1
  88. data/app/models/spina/navigation.rb +1 -2
  89. data/app/models/spina/navigation_item.rb +2 -2
  90. data/app/models/spina/page.rb +32 -38
  91. data/app/models/spina/parts/attachment.rb +1 -2
  92. data/app/models/spina/parts/base.rb +5 -4
  93. data/app/models/spina/parts/image.rb +3 -4
  94. data/app/models/spina/parts/image_collection.rb +2 -3
  95. data/app/models/spina/parts/image_variant.rb +3 -4
  96. data/app/models/spina/parts/repeater_content.rb +2 -2
  97. data/app/models/spina/resource.rb +3 -4
  98. data/app/models/spina/rewrite_rule.rb +1 -1
  99. data/app/models/spina/setting.rb +0 -2
  100. data/app/models/spina/user.rb +3 -4
  101. data/app/presenters/spina/content_presenter.rb +11 -12
  102. data/app/presenters/spina/menu_presenter.rb +55 -55
  103. data/app/presenters/spina/rich_text_presenter.rb +28 -29
  104. data/app/serializers/spina/api/base_serializer.rb +1 -1
  105. data/app/serializers/spina/api/image_serializer.rb +6 -6
  106. data/app/serializers/spina/api/navigation_serializer.rb +4 -7
  107. data/app/serializers/spina/api/page_serializer.rb +9 -12
  108. data/app/serializers/spina/api/resource_serializer.rb +5 -5
  109. data/app/views/layouts/spina/admin/application.html.erb +1 -0
  110. data/app/views/spina/admin/parts/attachments/_attachment.html.erb +3 -0
  111. data/app/views/spina/admin/parts/attachments/_form.html.erb +22 -1
  112. data/app/views/spina/admin/parts/repeaters/_fields.html.erb +2 -2
  113. data/app/views/spina/admin/parts/repeaters/_form.html.erb +4 -4
  114. data/app/views/spina/admin/shared/_navigation.html.erb +3 -1
  115. data/app/views/spina/admin/shared/_version.html.erb +18 -0
  116. data/app/views/spina/sitemaps/show.xml.builder +1 -1
  117. data/config/initializers/importmap.rb +3 -3
  118. data/config/initializers/yaml_column_permitted_classes.rb +3 -3
  119. data/config/locales/de.yml +1 -0
  120. data/config/locales/en.yml +3 -0
  121. data/config/locales/nl.yml +4 -1
  122. data/config/routes.rb +10 -17
  123. data/db/migrate/1_create_spina_tables.rb +57 -57
  124. data/db/migrate/3_create_spina_navigations.rb +1 -1
  125. data/db/seeds.rb +0 -1
  126. data/lib/generators/spina/embed_generator.rb +5 -6
  127. data/lib/generators/spina/install_generator.rb +48 -41
  128. data/lib/generators/spina/tailwind_config_generator.rb +3 -4
  129. data/lib/generators/spina/templates/app/models/spina/embeds/embed.rb.tt +20 -18
  130. data/lib/generators/spina/templates/config/initializers/spina.rb +11 -11
  131. data/lib/generators/spina/templates/config/initializers/themes/default.rb +17 -17
  132. data/lib/generators/spina/templates/config/initializers/themes/demo.rb +22 -22
  133. data/lib/spina/admin_sectionable.rb +5 -8
  134. data/lib/spina/attr_json_monkeypatch.rb +4 -7
  135. data/lib/spina/attr_json_spina_parts_model.rb +1 -3
  136. data/lib/spina/authentication/basic.rb +9 -10
  137. data/lib/spina/authentication/sessions.rb +10 -11
  138. data/lib/spina/embeddable.rb +14 -17
  139. data/lib/spina/embeds/trix_conversion.rb +25 -26
  140. data/lib/spina/embeds.rb +1 -3
  141. data/lib/spina/engine.rb +18 -19
  142. data/lib/spina/part.rb +1 -6
  143. data/lib/spina/plugin.rb +9 -13
  144. data/lib/spina/railtie.rb +5 -7
  145. data/lib/spina/theme.rb +13 -17
  146. data/lib/spina/theme_reloader.rb +12 -13
  147. data/lib/spina/version.rb +1 -1
  148. data/lib/spina.rb +39 -40
  149. data/lib/tasks/install.rake +8 -14
  150. data/lib/tasks/tailwind.rake +10 -8
  151. metadata +18 -2
@@ -8,13 +8,13 @@ module Spina
8
8
 
9
9
  # Configuration
10
10
  config_accessor :menu_tag, :menu_css,
11
- :list_tag, :list_css,
12
- :list_item_tag, :list_item_css,
13
- :link_tag_css,
14
- :active_list_item_css,
15
- :current_list_item_css,
16
- :include_drafts,
17
- :depth # root nodes are at depth 0
11
+ :list_tag, :list_css,
12
+ :list_item_tag, :list_item_css,
13
+ :link_tag_css,
14
+ :active_list_item_css,
15
+ :current_list_item_css,
16
+ :include_drafts,
17
+ :depth # root nodes are at depth 0
18
18
 
19
19
  # Default configuration
20
20
  self.menu_tag = :nav
@@ -32,67 +32,67 @@ module Spina
32
32
 
33
33
  private
34
34
 
35
- def roots
36
- return collection.navigation_items.roots if collection.is_a?(Navigation)
37
- collection.roots
38
- end
35
+ def roots
36
+ return collection.navigation_items.roots if collection.is_a?(Navigation)
37
+ collection.roots
38
+ end
39
39
 
40
- def render_menu(collection)
41
- content_tag(menu_tag, class: menu_css) do
42
- render_items(scoped_collection(collection))
43
- end
40
+ def render_menu(collection)
41
+ content_tag(menu_tag, class: menu_css) do
42
+ render_items(scoped_collection(collection))
44
43
  end
44
+ end
45
45
 
46
- def render_items(collection)
47
- content_tag(list_tag, class: list_css) do
48
- collection.inject(ActiveSupport::SafeBuffer.new) do |buffer, item|
49
- buffer << render_item(item)
50
- end
46
+ def render_items(collection)
47
+ content_tag(list_tag, class: list_css) do
48
+ collection.inject(ActiveSupport::SafeBuffer.new) do |buffer, item|
49
+ buffer << render_item(item)
51
50
  end
52
51
  end
52
+ end
53
53
 
54
- def render_item(item)
55
- return nil unless item.materialized_path
54
+ def render_item(item)
55
+ return nil unless item.materialized_path
56
56
 
57
- children = scoped_collection(item.children)
57
+ children = scoped_collection(item.children)
58
58
 
59
- content_tag(list_item_tag, class: item_css(item), data: { page_id: item.page_id, draft: (true if item.draft?) }) do
60
- buffer = ActiveSupport::SafeBuffer.new
61
- buffer << link_to(item.menu_title, item.materialized_path, class: link_tag_css)
62
- buffer << render_items(children) if render_children?(item) && children.any?
63
- buffer
64
- end
59
+ content_tag(list_item_tag, class: item_css(item), data: {page_id: item.page_id, draft: (true if item.draft?)}) do
60
+ buffer = ActiveSupport::SafeBuffer.new
61
+ buffer << link_to(item.menu_title, item.materialized_path, class: link_tag_css)
62
+ buffer << render_items(children) if render_children?(item) && children.any?
63
+ buffer
65
64
  end
65
+ end
66
66
 
67
- def scoped_collection(collection)
68
- scoped = collection.regular_pages.active.in_menu.sorted
69
- include_drafts ? scoped : scoped.live
70
- end
67
+ def scoped_collection(collection)
68
+ scoped = collection.regular_pages.active.in_menu.sorted
69
+ include_drafts ? scoped : scoped.live
70
+ end
71
71
 
72
- def render_children?(item)
73
- return true unless depth
74
- item.depth < depth
75
- end
76
-
77
- def item_css(item)
78
- return current_list_item_css if apply_current_css?(item)
79
- return active_list_item_css if apply_active_css?(item)
80
- list_item_css
81
- end
72
+ def render_children?(item)
73
+ return true unless depth
74
+ item.depth < depth
75
+ end
82
76
 
83
- def apply_current_css?(item)
84
- return false if current_list_item_css.nil?
85
- Spina::Current.page == item
86
- end
77
+ def item_css(item)
78
+ return current_list_item_css if apply_current_css?(item)
79
+ return active_list_item_css if apply_active_css?(item)
80
+ list_item_css
81
+ end
87
82
 
88
- def apply_active_css?(item)
89
- return false if apply_current_css?(item)
90
- parent_of_current?(item)
91
- end
83
+ def apply_current_css?(item)
84
+ return false if current_list_item_css.nil?
85
+ Spina::Current.page == item
86
+ end
92
87
 
93
- def parent_of_current?(item)
94
- return false if item.homepage?
95
- Spina::Current.page.materialized_path.starts_with? item.materialized_path
96
- end
88
+ def apply_active_css?(item)
89
+ return false if apply_current_css?(item)
90
+ parent_of_current?(item)
91
+ end
92
+
93
+ def parent_of_current?(item)
94
+ return false if item.homepage?
95
+ Spina::Current.page.materialized_path.starts_with? item.materialized_path
96
+ end
97
97
  end
98
98
  end
@@ -1,45 +1,44 @@
1
1
  module Spina
2
2
  class RichTextPresenter
3
3
  attr_reader :html, :view_context
4
-
4
+
5
5
  EMBED_CONTENT_TYPE = "application/vnd+spina.embed+html"
6
-
6
+
7
7
  def initialize(view_context, html)
8
8
  @view_context = view_context || Spina::Current.page&.view_context
9
9
  @html = html
10
10
  end
11
-
11
+
12
12
  def to_s
13
13
  ActiveSupport::SafeBuffer.new(render_embeds(html))
14
14
  end
15
-
15
+
16
16
  private
17
-
18
- def embed_selector
19
- "figure[data-trix-content-type=\"#{EMBED_CONTENT_TYPE}\"]"
20
- end
21
-
22
- def render_embeds(html)
23
- doc = Nokogiri::HTML.fragment(html)
24
- doc.css(embed_selector).each do |node|
25
- node.replace render_embed(node.first_element_child)
26
- end
27
- doc.to_s
28
- end
29
-
30
- def render_embed(element)
31
- embeddable = element_to_embeddable(element)
32
- view_context.render(embeddable)
33
- end
34
-
35
- def element_to_embeddable(element)
36
- embeddable = Spina::Embeds.constantize(element["data-embed-type"])
37
- embeddable&.from_json(element["data-embed-attributes"]) || null_object
38
- end
39
-
40
- def null_object
41
- {inline: ""}
17
+
18
+ def embed_selector
19
+ "figure[data-trix-content-type=\"#{EMBED_CONTENT_TYPE}\"]"
20
+ end
21
+
22
+ def render_embeds(html)
23
+ doc = Nokogiri::HTML.fragment(html)
24
+ doc.css(embed_selector).each do |node|
25
+ node.replace render_embed(node.first_element_child)
42
26
  end
27
+ doc.to_s
28
+ end
29
+
30
+ def render_embed(element)
31
+ embeddable = element_to_embeddable(element)
32
+ view_context.render(embeddable)
33
+ end
34
+
35
+ def element_to_embeddable(element)
36
+ embeddable = Spina::Embeds.constantize(element["data-embed-type"])
37
+ embeddable&.from_json(element["data-embed-attributes"]) || null_object
38
+ end
43
39
 
40
+ def null_object
41
+ {inline: ""}
42
+ end
44
43
  end
45
44
  end
@@ -3,4 +3,4 @@ module Spina::Api
3
3
  include JSONAPI::Serializer
4
4
  singleton_class.include Spina::Engine.routes.url_helpers
5
5
  end
6
- end
6
+ end
@@ -1,20 +1,20 @@
1
1
  module Spina::Api
2
- class ImageSerializer < BaseSerializer
2
+ class ImageSerializer < BaseSerializer
3
3
  set_type :image
4
-
4
+
5
5
  attributes :id
6
-
6
+
7
7
  attribute :original_url do |image, params|
8
8
  params[:view_context].main_app.url_for(image.file)
9
9
  end
10
-
10
+
11
11
  attribute :thumbnail_url do |image, params|
12
12
  params[:view_context].main_app.url_for(image.variant(resize_to_fill: Spina.config.thumbnail_image_size))
13
13
  end
14
-
14
+
15
15
  attribute :embedded_image_size_url do |image, params|
16
16
  resize_key = Spina.config.embedded_image_size.is_a?(Array) ? :resize_to_limit : :resize
17
- params[:view_context].main_app.url_for(image.variant(Hash[resize_key, Spina.config.embedded_image_size]))
17
+ params[:view_context].main_app.url_for(image.variant({resize_key => Spina.config.embedded_image_size}))
18
18
  end
19
19
  end
20
20
  end
@@ -1,15 +1,14 @@
1
1
  module Spina::Api
2
- class NavigationSerializer < BaseSerializer
2
+ class NavigationSerializer < BaseSerializer
3
3
  set_type :navigation
4
-
4
+
5
5
  attributes :name, :label
6
-
6
+
7
7
  attribute :tree do |navigation|
8
8
  items_to_tree(navigation.navigation_items.sorted.roots.in_menu.live.joins(:page))
9
9
  end
10
-
10
+
11
11
  class << self
12
-
13
12
  def items_to_tree(collection)
14
13
  collection.map do |item|
15
14
  {
@@ -23,8 +22,6 @@ module Spina::Api
23
22
  }
24
23
  end
25
24
  end
26
-
27
25
  end
28
-
29
26
  end
30
27
  end
@@ -1,28 +1,25 @@
1
1
  module Spina::Api
2
- class PageSerializer < BaseSerializer
2
+ class PageSerializer < BaseSerializer
3
3
  set_type :page
4
-
4
+
5
5
  attributes :title, :seo_title, :menu_title, :materialized_path, :name, :description, :view_template
6
-
6
+
7
7
  attribute(:content) { |page| page_content(page) }
8
-
8
+
9
9
  belongs_to :resource
10
10
 
11
11
  class << self
12
-
13
12
  def page_content(page)
14
13
  return [] unless view_template(page)
15
-
16
- view_template(page)[:parts].map do |part|
17
- { part => page.content(part) }
14
+
15
+ view_template(page)[:parts].map do |part|
16
+ {part => page.content(part)}
18
17
  end
19
18
  end
20
-
19
+
21
20
  def view_template(page)
22
- Spina::Current.theme.view_templates.find{|view_template| view_template[:name] == page.view_template}
21
+ Spina::Current.theme.view_templates.find { |view_template| view_template[:name] == page.view_template }
23
22
  end
24
-
25
23
  end
26
-
27
24
  end
28
25
  end
@@ -1,12 +1,12 @@
1
1
  module Spina::Api
2
2
  class ResourceSerializer < BaseSerializer
3
3
  set_type :resource
4
-
4
+
5
5
  attributes :name, :label, :view_template, :order_by, :slug
6
-
6
+
7
7
  has_many :pages, lazy_load_data: true, links: {
8
- self: -> (object) { api_resource_path(object.id) },
9
- related: -> (object) { api_resource_pages_path(object.id) }
10
- }, meta: -> (resource) { {count: resource.pages.live.length} }
8
+ self: ->(object) { api_resource_path(object.id) },
9
+ related: ->(object) { api_resource_pages_path(object.id) }
10
+ }, meta: ->(resource) { {count: resource.pages.live.length} }
11
11
  end
12
12
  end
@@ -22,6 +22,7 @@
22
22
  </head>
23
23
 
24
24
  <body>
25
+ <%= render "spina/admin/shared/version" %>
25
26
  <%= content_for?(:body) ? yield(:body) : yield %>
26
27
  </body>
27
28
  </html>
@@ -0,0 +1,3 @@
1
+ <option value="<%= attachment.id %>" signed-blob-id="<%= attachment.file&.blob&.signed_id %>" filename="<%= attachment.file&.filename %>" selected>
2
+ <%= attachment.file&.filename %>
3
+ </option>
@@ -5,5 +5,26 @@
5
5
  <%= f.hidden_field :signed_blob_id, data: {attachment_picker_target: 'signedBlobId'} %>
6
6
  <%= f.hidden_field :filename, data: {attachment_picker_target: 'filename'} %>
7
7
 
8
- <%= f.select :attachment_id, Spina::Attachment.sorted.map{|attachment| [attachment.file&.filename, attachment.id, data: {signed_blob_id: attachment.file&.blob&.signed_id, filename: attachment.file&.filename}]}, {include_blank: t("spina.attachments.choose_attachment")}, {class: "form-select mt-1", data: {action: "attachment-picker#pick"}} %>
8
+ <div class="flex items-center space-x-3 mt-1">
9
+ <% select_id = dom_id(f.object, f.object.object_id) %>
10
+
11
+ <%= f.select :attachment_id, Spina::Attachment.sorted.map{|attachment| [attachment.file&.filename, attachment.id, data: {signed_blob_id: attachment.file&.blob&.signed_id, filename: attachment.file&.filename}]}, {include_blank: t("spina.attachments.choose_attachment")}, {id: select_id, class: "form-select", data: {action: "attachment-picker#pick"}} %>
12
+
13
+ <div class="text-gray-400 font-medium text-sm">
14
+ <%=t 'spina.ui.or' %>
15
+ </div>
16
+
17
+ <%= form_with model: [:admin, Spina::Attachment.new], url: spina.inline_upload_admin_attachments_path, data: {controller: "form loading-button", loading_message: t('spina.media_library.uploading'), action: "turbo:submit-end->loading-button#doneLoading"} do |f| %>
18
+ <%= hidden_field_tag :select_id, select_id %>
19
+ <% file_field_id = "file_#{f.object.object_id}" %>
20
+
21
+ <%= f.file_field :file, id: file_field_id, class: 'hidden', data: {action: "loading-button#loading form#requestSubmit"} %>
22
+
23
+ <button type="button" class="btn btn-default" data-controller="delegate-click" data-action="delegate-click#click" data-loading-button-target="button" data-delegate-click-target="#<%= file_field_id %>">
24
+ <%= heroicon("upload", style: :solid, class: "w-4 h-4 -ml-1 mr-1") %>
25
+ <%=t 'spina.attachments.upload_one' %>
26
+ </button>
27
+ <% end %>
28
+
29
+ </div>
9
30
  </div>
@@ -13,6 +13,6 @@
13
13
  <% end %>
14
14
 
15
15
  <div class="text-right">
16
- <%= button_tag t('spina.ui.delete'), type: :button, class: 'btn btn-default bg-transparent hover:bg-white hover:text-red-500 h-8 px-3 inline-block mt-3', data: {action: "repeater#removeFields", id: "pane_#{f.object.object_id}"} %>
16
+ <%= button_tag t('spina.ui.delete_item', item: item_name), type: :button, class: 'btn btn-default bg-transparent hover:bg-white hover:text-red-500 h-8 px-3 inline-block mt-3', data: {action: "repeater#removeFields", id: "pane_#{f.object.object_id}"} %>
17
17
  </div>
18
- </div>
18
+ </div>
@@ -4,11 +4,11 @@
4
4
 
5
5
  <div class="-mt-4 flex flex-col md:flex-row" data-controller="tabs" data-tabs-active="bg-spina-dark bg-opacity-10 text-gray-900" data-tabs-inactive="text-gray-500">
6
6
  <div class="md:w-64 md:pr-6">
7
- <%
7
+ <%
8
8
  # Fields
9
9
  repeater_content = Spina::Parts::RepeaterContent.new(name: f.object.name, title: f.object.title)
10
10
  fields = f.fields_for(:content, [repeater_content], child_index: repeater_content.object_id) do |builder|
11
- render("spina/admin/parts/repeaters/fields", f: builder)
11
+ render("spina/admin/parts/repeaters/fields", f: builder, item_name: f.object.item_name)
12
12
  end.gsub("\n", "")
13
13
  %>
14
14
 
@@ -31,8 +31,8 @@
31
31
 
32
32
  <div class="flex-1 pl-6 md:pl-0" data-repeater-target="content">
33
33
  <%= f.fields_for :content do |ff| %>
34
- <%= render 'spina/admin/parts/repeaters/fields', f: ff %>
34
+ <%= render 'spina/admin/parts/repeaters/fields', f: ff, item_name: f.object.item_name %>
35
35
  <% end %>
36
36
  </div>
37
37
  </div>
38
- </div>
38
+ </div>
@@ -61,7 +61,9 @@
61
61
  <ul class="md:block">
62
62
  <li>
63
63
  <%= link_to logout_path, class: 'p-3 w-14 block flex justify-center opacity-50 hover:opacity-100 transition duration-200 ease', data: {turbo: false} do %>
64
- <%= heroicon('logout', style: :solid, class: 'h-8 w-8 text-white') %>
64
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor" class="h-7 w-7 text-white">
65
+ <path fill-rule="evenodd" d="M128,256 C93.81,256 61.666,242.686 37.49,218.509 C13.315,194.334 0,162.19 0,128 C0,93.81 13.315,61.668 37.49,37.491 C43.739,31.242 53.869,31.242 60.118,37.491 C66.367,43.739 66.367,53.87 60.118,60.118 C41.985,78.25 32,102.357 32,128 C32,153.643 41.985,177.75 60.118,195.882 C78.25,214.014 102.357,224 128,224 C153.643,224 177.75,214.014 195.882,195.882 C214.015,177.75 224,153.643 224,128 C224,102.357 214.015,78.25 195.882,60.118 C189.633,53.87 189.633,43.739 195.882,37.491 C202.13,31.243 212.261,31.243 218.51,37.491 C242.686,61.668 256,93.809 256,128 C256,162.191 242.685,194.334 218.51,218.509 C194.334,242.686 162.19,256 128,256 L128,256 L128,256 Z M128,144 C136.837,144 144,136.837 144,128 L144,16 C144,7.163 136.837,0 128,0 C119.163,0 112,7.163 112,16 L112,128 C112,136.837 119.163,144 128,144 L128,144 L128,144 Z" clip-rule="evenodd" />
66
+ </svg>
65
67
  <% end %>
66
68
  </li>
67
69
  </ul>
@@ -0,0 +1,18 @@
1
+ <% unless defined?(Spina::Pro) %>
2
+ <%= link_to "https://spinacms.com/pro", class: "fixed bottom-0 right-0 px-5 py-4 group text-gray-400 text-xs font-medium", target: :blank do %>
3
+ <div class="flex items-center justify-end">
4
+ <svg viewBox="0 0 100 100" class="h-3 w-3 mr-1 text-gray-400 group-hover:text-spina" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
5
+ <path d="M48.6582924,96.1623174 L0.472662785,39.1647593 C-0.121452015,38.4619959 -0.159345587,37.4445295 0.380849751,36.699521 L20.3780861,9.12040239 C20.6765111,8.70883071 21.1204689,8.42634978 21.6197077,8.33038495 L49.1629705,3.03595601 C49.3934881,2.99164538 49.6300066,2.9882617 49.8616974,3.02595987 L78.3181435,7.65607741 C78.8410521,7.74115931 79.30884,8.0302376 79.6188319,8.45986224 L99.9955684,36.7004267 C100.533013,37.4452826 100.494021,38.460456 99.9010274,39.1618937 L51.7129756,96.1623174 C50.999859,97.0058449 49.7379497,97.1115638 48.8944223,96.3984473 C48.8092674,96.3264575 48.7302822,96.2474723 48.6582924,96.1623174 Z"></path>
6
+ </svg>
7
+
8
+ Spina CMS v<%= Spina::VERSION %>
9
+ </div>
10
+
11
+ <div class="mt-1">
12
+ Upgrade to Spina
13
+ <span class="bg-gray-400 group-hover:bg-pink-500 text-white rounded px-1 py-0.5 uppercase tracking-wider" style="font-size: 10px">
14
+ Pro
15
+ </span>
16
+ </div>
17
+ <% end %>
18
+ <% end %>
@@ -19,4 +19,4 @@ xml.urlset "xmlns" => "http://www.google.com/schemas/sitemap/0.9", "xmlns:xhtml"
19
19
  xml.priority 0.9
20
20
  end
21
21
  end
22
- end
22
+ end
@@ -3,10 +3,10 @@ Spina.config.importmap.draw do
3
3
  pin "@hotwired/stimulus", to: "stimulus.js"
4
4
  pin "@hotwired/stimulus-loading", to: "stimulus-loading.js"
5
5
  pin "@hotwired/turbo-rails", to: "turbo.js"
6
-
6
+
7
7
  # Spina entrypoint
8
8
  pin "application", to: "spina/application.js"
9
-
9
+
10
10
  pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/controllers"), under: "controllers", to: "spina/controllers"
11
11
  pin_all_from Spina::Engine.root.join("app/assets/javascripts/spina/libraries"), under: "libraries", to: "spina/libraries"
12
- end
12
+ end
@@ -1,12 +1,12 @@
1
1
  # CVE-2022-32224 RAILS
2
- #
2
+ #
3
3
  # There was a bug in Rails allowing YAML-serialized data to be vulnerable to RCE.
4
4
  # Spina uses the serialize method to store various preferences stored using symbols.
5
5
  # We've now changed this so preferences are stored with strings as keys instead of
6
- # symbols, but in order to not break existing projects we're adding 'Symbol' to the
6
+ # symbols, but in order to not break existing projects we're adding 'Symbol' to the
7
7
  # list of permitted classes.
8
8
  # This can be removed in the future.
9
- #
9
+ #
10
10
  # More information:
11
11
  # https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
12
12
 
@@ -347,6 +347,7 @@ de:
347
347
  ui:
348
348
  cancel: Abbrechen
349
349
  delete: Löschen
350
+ delete_item: "%{item} löschen"
350
351
  load_more: Weitere laden
351
352
  move_to: Verschieben nach
352
353
  new_entry: Neuer Eintrag
@@ -131,6 +131,7 @@ en:
131
131
  insert: Insert document
132
132
  insert_multiple: Insert documents
133
133
  upload: Upload files
134
+ upload_one: Upload file
134
135
  cancel: Cancel
135
136
  choose_from_library: Choose from library
136
137
  close: Close
@@ -344,10 +345,12 @@ en:
344
345
  ui:
345
346
  cancel: Cancel
346
347
  delete: Delete
348
+ delete_item: "Delete %{item}"
347
349
  load_more: Load more
348
350
  move_to: Move to
349
351
  new_entry: New entry
350
352
  optional: Optional
353
+ or: or
351
354
  rename: Rename
352
355
  replace: Replace
353
356
  save_changes: Save changes
@@ -108,7 +108,8 @@ nl:
108
108
  delete_confirmation_html: Weet je het zeker? Het kan zijn dat dit document in gebruik is op een pagina.
109
109
  insert: Document toevoegen
110
110
  insert_multiple: Documenten toevoegen
111
- upload:
111
+ upload: Upload documenten
112
+ upload_one: Upload document
112
113
  cancel: Annuleren
113
114
  choose_from_library: Kies uit bibliotheek
114
115
  close: Sluiten
@@ -315,10 +316,12 @@ nl:
315
316
  ui:
316
317
  cancel: Annuleer
317
318
  delete: Verwijder
319
+ delete_item: "Verwijder %{item}"
318
320
  load_more: Meer laden
319
321
  move_to: Verplaats naar
320
322
  new_entry: Nieuw item
321
323
  optional: Optioneel
324
+ or: of
322
325
  rename: Hernoemen
323
326
  replace: Vervangen
324
327
  save_changes: Wijzigingen opslaan
data/config/routes.rb CHANGED
@@ -11,7 +11,6 @@ Rails.application.routes.draw do
11
11
  end
12
12
 
13
13
  Spina::Engine.routes.draw do
14
-
15
14
  # API
16
15
  namespace :api, path: Spina.config.api_path do
17
16
  resources :pages, only: [:index, :show]
@@ -33,7 +32,7 @@ Spina::Engine.routes.draw do
33
32
  patch "/settings/:plugin", to: "settings#update", as: :settings
34
33
 
35
34
  resources :users
36
-
35
+
37
36
  # Sessions
38
37
  resources :sessions
39
38
  get "login" => "sessions#new"
@@ -48,7 +47,7 @@ Spina::Engine.routes.draw do
48
47
  get :edit_template
49
48
  get :children
50
49
  end
51
-
50
+
52
51
  resource :move, controller: "move_pages"
53
52
 
54
53
  post :sort, on: :collection
@@ -60,18 +59,13 @@ Spina::Engine.routes.draw do
60
59
 
61
60
  resources :resources, only: [:show, :edit, :update]
62
61
 
63
- resources :navigations do
62
+ resources :navigations, only: [:index, :edit, :update] do
64
63
  post :sort, on: :member
65
64
  resources :navigation_items
66
65
  end
67
66
 
68
67
  resources :attachments do
69
- collection do
70
- get 'select/:page_part_id' => 'attachments#select', as: :select
71
- post 'insert/:page_part_id' => 'attachments#insert', as: :insert
72
- get 'select_collection/:page_part_id' => 'attachments#select_collection', as: :select_collection
73
- post 'insert_collection/:page_part_id' => 'attachments#insert_collection', as: :insert_collection
74
- end
68
+ post :inline_upload, on: :collection
75
69
  end
76
70
  resources :rename_files
77
71
 
@@ -80,9 +74,9 @@ Spina::Engine.routes.draw do
80
74
  end
81
75
 
82
76
  resources :images
83
-
77
+
84
78
  resource :media_picker, controller: "media_picker", only: [:show]
85
-
79
+
86
80
  resources :embeds, only: [:new, :create]
87
81
  end
88
82
 
@@ -94,12 +88,11 @@ Spina::Engine.routes.draw do
94
88
  root to: "pages#homepage"
95
89
 
96
90
  # Pages
97
- get '/:locale/*id' => 'pages#show', constraints: {locale: /#{Spina.locales.join('|')}/ }
98
- get '/:locale/' => 'pages#homepage', constraints: {locale: /#{Spina.locales.join('|')}/ }
99
- get '/*id' => 'pages#show', as: "page", controller: 'pages', constraints: -> (request) {
91
+ get "/:locale/*id" => "pages#show", :constraints => {locale: /#{Spina.locales.join('|')}/}
92
+ get "/:locale/" => "pages#homepage", :constraints => {locale: /#{Spina.locales.join('|')}/}
93
+ get "/*id" => "pages#show", :as => "page", :controller => "pages", :constraints => ->(request) {
100
94
  request.path.exclude?(ActiveStorage.routes_prefix) &&
101
- !(Rails.env.development? && request.path.starts_with?('/rails/'))
95
+ !(Rails.env.development? && request.path.starts_with?("/rails/"))
102
96
  }
103
97
  end
104
-
105
98
  end