spina 2.9.0 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of spina might be problematic. Click here for more details.

Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/app/components/spina/pages/actions_component.html.erb +7 -0
  3. data/app/components/spina/pages/translations_component.rb +1 -1
  4. data/app/components/spina/user_interface/translations_component.rb +1 -1
  5. data/app/controllers/spina/admin/attachments_controller.rb +13 -3
  6. data/app/controllers/spina/admin/images_controller.rb +9 -1
  7. data/app/controllers/spina/admin/page_translations_controller.rb +14 -0
  8. data/app/controllers/spina/admin/pages_controller.rb +3 -4
  9. data/app/jobs/spina/replace_signed_id_job.rb +24 -0
  10. data/app/models/concerns/spina/translated_content.rb +1 -1
  11. data/app/models/spina/account.rb +11 -1
  12. data/app/models/spina/page.rb +9 -9
  13. data/app/models/spina/resource.rb +7 -0
  14. data/app/presenters/spina/rich_text_presenter.rb +2 -2
  15. data/app/views/spina/admin/attachments/_attachment.html.erb +9 -0
  16. data/app/views/spina/admin/images/_image.html.erb +10 -0
  17. data/app/views/spina/admin/resources/edit.html.erb +1 -1
  18. data/app/views/spina/sitemaps/show.xml.builder +1 -1
  19. data/config/initializers/yaml_column_permitted_classes.rb +16 -0
  20. data/config/locales/de.yml +8 -0
  21. data/config/locales/en.yml +12 -0
  22. data/config/locales/nl.yml +5 -0
  23. data/config/routes.rb +3 -2
  24. data/lib/generators/spina/templates/app/assets/config/spina/tailwind.config.js.tt +2 -4
  25. data/lib/generators/spina/templates/app/views/demo/shared/_languages.html.erb +2 -2
  26. data/lib/generators/spina/templates/config/initializers/spina.rb +2 -1
  27. data/lib/spina/version.rb +1 -1
  28. data/lib/spina.rb +18 -15
  29. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3d8217bb30f0c85ecf1680a8f3d951cb2e958d993e51ad467b078ab4fda9670
4
- data.tar.gz: 523554889c7c32b1e966131b377ad6004f8093248a25459fe2ae09810edc2d4b
3
+ metadata.gz: 68c17b8a615a45c86dce79d2e4b32a5f7bc4fdbf8f71d94fbdc2f530a68baa48
4
+ data.tar.gz: a85f8e9b3a922c1b3de5866d37bab8bdae583c264e35894baa7871b95aa2eaf5
5
5
  SHA512:
6
- metadata.gz: 6b2786876a21c1056a9f7d0dff1e3cf8eb4243b9310cbfa36fde3608a623943e199de1f1505be5bb8c61f53ad663a124dc2a914b8c1172e59d266a6747c906b7
7
- data.tar.gz: ec28bbdf8802db18df4f99fab9343a309e079792fd2c1b99866bcb76db9b034c2f3cbc70aaf0827e0ced5350693aad3be12ea7a330ddcaf82f0537ae59059217
6
+ metadata.gz: be079dbe9d681c2d2912946c01127d40ffe84fd28c600be31be1a0a3d56aa0e7591f4ab8423943490aa52f5f70adc04d8eac73df8bd827397ea536e5fb53fce1
7
+ data.tar.gz: fdbb328aba58cbdc660869b2745d6469cbb10a9e40576a3b09205862e4deef8e6ca5f717bb7171039915010c4227e2998a2c1bb86907b194f5844e68a4925dd4
@@ -32,6 +32,13 @@
32
32
  <% if !@page.custom_page? %>
33
33
  <%= link_to t('spina.pages.change_view_template') + "...", helpers.spina.edit_template_admin_page_path(@page), class: "block px-4 py-2 text-sm font-medium leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:bg-gray-100 focus:text-gray-900", data: {turbo_frame: "modal", action: "reveal#hide"} %>
34
34
  <% end %>
35
+
36
+ <% if @locale.to_sym != I18n.locale.to_sym %>
37
+ <% @page.translations.where(locale: @locale).each do |translation| %>
38
+ <%= button_to t("spina.page_translations.delete", translation: @locale.upcase), helpers.spina.admin_page_translation_path(translation), method: :delete, class: "block w-full text-left px-4 py-2 text-sm leading-5 font-medium text-red-500 cursor-pointer bg-white hover:bg-red-100 hover:bg-opacity-50 hover:text-red-500 focus:outline-none focus:bg-gray-100 focus:text-gray-900", form: {data: {controller: "confirm", confirm_message: t('spina.page_translations.delete_confirmation', subject: @locale.upcase)}} %>
39
+ <% end %>
40
+ <% end %>
41
+
35
42
  <% if @page.deletable? %>
36
43
  <%= button_to t('spina.pages.delete'), helpers.spina.admin_page_path(@page), method: :delete, class: "block w-full text-left px-4 py-2 text-sm leading-5 font-medium text-red-500 cursor-pointer bg-white hover:bg-red-100 hover:bg-opacity-50 hover:text-red-500 focus:outline-none focus:bg-gray-100 focus:text-gray-900", form: {data: {controller: "confirm", confirm_message: t('spina.pages.delete_confirmation', subject: @page.title)}} %>
37
44
  <% else %>
@@ -24,7 +24,7 @@ module Spina
24
24
  private
25
25
 
26
26
  def spina_locales
27
- Spina.config.locales.map(&:to_sym)
27
+ Spina.locales.map(&:to_sym)
28
28
  end
29
29
 
30
30
  end
@@ -18,7 +18,7 @@ module Spina
18
18
  private
19
19
 
20
20
  def spina_locales
21
- Spina.config.locales.map(&:to_sym)
21
+ Spina.locales.map(&:to_sym)
22
22
  end
23
23
 
24
24
  end
@@ -17,10 +17,12 @@ module Spina
17
17
 
18
18
  def create
19
19
  @attachments = params[:attachment][:files].map do |file|
20
+ next if file.blank? # Skip the blank string posted by the hidden files[] field
21
+
20
22
  attachment = Attachment.create(attachment_params)
21
23
  attachment.file.attach(file)
22
24
  attachment
23
- end
25
+ end.compact
24
26
 
25
27
  respond_to do |format|
26
28
  format.turbo_stream { render turbo_stream: turbo_stream.prepend("attachments", partial: "attachment", collection: @attachments)}
@@ -30,13 +32,21 @@ module Spina
30
32
 
31
33
  def update
32
34
  @attachment = Attachment.find(params[:id])
35
+ old_signed_id = @attachment.file&.blob&.signed_id
36
+ @attachment.update(attachment_params) if params[:attachment].present?
33
37
  if params[:filename].present?
34
38
  extension = @attachment.file.filename.extension
35
39
  filename = "#{params[:filename]}.#{extension}"
36
40
  @attachment.file.blob.update(filename: filename)
37
41
  end
38
-
39
- redirect_to [:admin, @attachment]
42
+
43
+ # Replace all occurrences of the old signed blob ID
44
+ # with the new ID in a background job
45
+ if @attachment.reload.file&.blob&.signed_id != old_signed_id
46
+ Spina::ReplaceSignedIdJob.perform_later(old_signed_id, @attachment.file&.blob&.signed_id)
47
+ end
48
+
49
+ render @attachment
40
50
  end
41
51
 
42
52
  def destroy
@@ -44,15 +44,23 @@ module Spina
44
44
 
45
45
  def update
46
46
  @image = Image.find(params[:id])
47
+ old_signed_id = @image.file&.blob&.signed_id
47
48
  @image.update(image_params) if params[:image].present?
48
49
  if params[:filename].present?
49
50
  extension = @image.file.filename.extension
50
51
  filename = "#{params[:filename]}.#{extension}"
51
52
  @image.file.blob.update(filename: filename)
52
53
  end
54
+
53
55
  if @image.saved_change_to_media_folder_id?
54
56
  render :update
55
57
  else
58
+ # Replace all occurrences of the old signed blob ID
59
+ # with the new ID in a background job
60
+ if @image.reload.file&.blob&.signed_id != old_signed_id
61
+ Spina::ReplaceSignedIdJob.perform_later(old_signed_id, @image.file&.blob&.signed_id)
62
+ end
63
+
56
64
  @media_folders = MediaFolder.order(:name)
57
65
  render @image
58
66
  end
@@ -82,7 +90,7 @@ module Spina
82
90
  end
83
91
 
84
92
  def image_params
85
- params.require(:image).permit(:media_folder_id)
93
+ params.require(:image).permit(:media_folder_id, :file)
86
94
  end
87
95
 
88
96
  end
@@ -0,0 +1,14 @@
1
+ module Spina
2
+ module Admin
3
+ class PageTranslationsController < AdminController
4
+
5
+ def destroy
6
+ @translation = Spina::Page::Translation.find(params[:id])
7
+ @translation.destroy
8
+ flash[:info] = t("spina.page_translations.deleted")
9
+ redirect_to spina.edit_admin_page_path(@translation.translated_model), status: :see_other
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -93,12 +93,11 @@ module Spina
93
93
  end
94
94
 
95
95
  def add_index_breadcrumb
96
- path = spina.admin_pages_path
97
96
  if @page.resource
98
- path = spina.admin_pages_path(resource_id: @page.resource_id)
97
+ add_breadcrumb @page.resource.label, spina.admin_pages_path(resource_id: @page.resource_id), class: 'text-gray-400'
98
+ else
99
+ add_breadcrumb t('spina.website.pages'), spina.admin_pages_path, class: 'text-gray-400'
99
100
  end
100
-
101
- add_breadcrumb t('spina.website.pages'), path, class: 'text-gray-400'
102
101
  end
103
102
 
104
103
  def page_params
@@ -0,0 +1,24 @@
1
+ module Spina
2
+ class ReplaceSignedIdJob < ApplicationJob
3
+ queue_as { Spina.config.queues[:page_updates] }
4
+
5
+ def perform(old_signed_id, new_signed_id)
6
+ return if old_signed_id.blank? || new_signed_id.blank?
7
+
8
+ pages = get_pages(old_signed_id)
9
+ accounts = Spina::Account.all
10
+
11
+ [pages, accounts].each do |records|
12
+ records.update_all("json_attributes = REGEXP_REPLACE(json_attributes::text, '#{old_signed_id}', '#{new_signed_id}', 'g')::jsonb")
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def get_pages(signed_id)
19
+ return Spina::Page.none unless signed_id.present?
20
+ Spina::Page.where("json_attributes::text LIKE ?", "%#{signed_id}%")
21
+ end
22
+
23
+ end
24
+ end
@@ -6,7 +6,7 @@ module Spina
6
6
 
7
7
  included do
8
8
  # Store each locale's content in [locale]_content as an array of parts
9
- Spina.config.locales.each do |locale|
9
+ Spina.locales.each do |locale|
10
10
  attr_json "#{locale}_content".to_sym, AttrJson::Type::SpinaPartsModel.new, array: true, default: -> { [] }
11
11
  attr_json_setter_monkeypatch "#{locale}_content".to_sym
12
12
  attr_json_accepts_nested_attributes_for "#{locale}_content".to_sym
@@ -15,15 +15,25 @@ module Spina
15
15
  name
16
16
  end
17
17
 
18
+ # Spina previously stored account preferences with symbols as keys.
19
+ # Because of CVE-2022-32224 we're changing that to strings instead.
20
+ # This fallback ensures backwards compatibility, but in the long run this
21
+ # should be refactored to use a simple JSONB-column with Postgres.
18
22
  def self.serialized_attr_accessor(*args)
19
23
  args.each do |method_name|
20
24
  define_method method_name do
25
+ if self.preferences.try(:[], method_name.to_sym).present?
26
+ ActiveSupport::Deprecation.warn("#{method_name} is stored as a symbol. Please set and save it again using #{method_name}= on your Spina::Account model to store it as a string. You can do this from the UI by saving your account preferences.")
27
+ end
28
+
29
+ self.preferences.try(:[], method_name.to_s) ||
21
30
  self.preferences.try(:[], method_name.to_sym)
22
31
  end
23
32
 
24
33
  define_method "#{method_name}=" do |value|
25
34
  self.preferences ||= {}
26
- self.preferences[method_name.to_sym] = value
35
+ self.preferences.except!(method_name.to_sym)
36
+ self.preferences[method_name.to_s] = value
27
37
  end
28
38
  end
29
39
  end
@@ -17,16 +17,16 @@ module Spina
17
17
  has_many :navigations, through: :navigation_items
18
18
 
19
19
  # Pages can belong to a resource
20
- belongs_to :resource, optional: true
20
+ belongs_to :resource, optional: true, touch: true
21
21
 
22
- scope :main, -> { where(resource_id: nil) }
22
+ scope :main, -> { where(resource_id: nil) }
23
23
  scope :regular_pages, -> { main }
24
24
  scope :resource_pages, -> { where.not(resource: nil) }
25
25
  scope :active, -> { where(active: true) }
26
26
  scope :sorted, -> { order(:position) }
27
27
  scope :live, -> { active.where(draft: false) }
28
28
  scope :in_menu, -> { where(show_in_menu: true) }
29
-
29
+
30
30
  before_create :set_default_position
31
31
 
32
32
  # Copy resource from parent
@@ -47,7 +47,7 @@ module Spina
47
47
  translates :menu_title, :seo_title, :url_title, default: -> { title }
48
48
 
49
49
  def to_s
50
- name
50
+ title
51
51
  end
52
52
 
53
53
  def page_id
@@ -57,7 +57,7 @@ module Spina
57
57
  def slug
58
58
  url_title.to_s.to_slug.transliterate(*Spina.config.transliterations).normalize.to_s
59
59
  end
60
-
60
+
61
61
  def homepage?
62
62
  name == 'homepage'
63
63
  end
@@ -81,11 +81,11 @@ module Spina
81
81
  def next_sibling
82
82
  siblings.where('position > ?', position).sorted.first
83
83
  end
84
-
84
+
85
85
  def set_materialized_path
86
86
  self.old_path = materialized_path
87
87
  self.materialized_path = localized_materialized_path
88
-
88
+
89
89
  # Append counter to duplicate materialized_path
90
90
  i = 0
91
91
  while duplicate_materialized_path?
@@ -104,7 +104,7 @@ module Spina
104
104
  end
105
105
 
106
106
  private
107
-
107
+
108
108
  def set_default_position
109
109
  self.position ||= self.class.maximum(:position).to_i.next
110
110
  end
@@ -136,7 +136,7 @@ module Spina
136
136
  path_fragments.append(slug) unless homepage?
137
137
  path_fragments.compact.map(&:parameterize).join('/')
138
138
  end
139
-
139
+
140
140
  def duplicate_materialized_path?
141
141
  self.class.where.not(id: id).i18n.where(materialized_path: materialized_path).exists?
142
142
  end
@@ -24,6 +24,13 @@ module Spina
24
24
  ResourcePagesUpdateJob.perform_later(id)
25
25
  end
26
26
  end
27
+
28
+ def order_by_options
29
+ [
30
+ [Spina::Page.human_attribute_name(:title), "title"],
31
+ [Spina::Page.human_attribute_name(:created_at), "created_at"]
32
+ ]
33
+ end
27
34
 
28
35
  end
29
36
  end
@@ -20,7 +20,7 @@ module Spina
20
20
  end
21
21
 
22
22
  def render_embeds(html)
23
- doc = Nokogiri::HTML(html)
23
+ doc = Nokogiri::HTML.fragment(html)
24
24
  doc.css(embed_selector).each do |node|
25
25
  node.replace render_embed(node.first_element_child)
26
26
  end
@@ -42,4 +42,4 @@ module Spina
42
42
  end
43
43
 
44
44
  end
45
- end
45
+ end
@@ -5,6 +5,15 @@
5
5
  <span class="truncate"><%= attachment.file.filename %></span>
6
6
 
7
7
  <div class="absolute h-full opacity-0 group-hover:opacity-100 flex items-center right-0 pr-3 top-0">
8
+ <%= form_with model: attachment, url: spina.admin_attachment_path(attachment), data: {turbo_frame: dom_id(attachment), controller: "form loading-button", loading_message: t('spina.media_library.uploading'), action: "turbo:submit-end->loading-button#doneLoading"} do |f| %>
9
+ <%= f.file_field :file, id: "attachment_#{attachment.id}_file_field", class: 'hidden', data: {action: "loading-button#loading form#requestSubmit"} %>
10
+
11
+ <button type="button" class="btn btn-default h-7 px-2 mr-2 text-xs" data-controller="delegate-click" data-action="delegate-click#click" data-loading-button-target="button" data-delegate-click-target="#attachment_<%= attachment.id %>_file_field">
12
+ <%= heroicon("upload", style: :solid, class: "w-4 h-4 mr-1 text-gray-600") %>
13
+ <%=t 'spina.ui.replace' %>
14
+ </button>
15
+ <% end %>
16
+
8
17
  <%= link_to spina.edit_admin_attachment_path(attachment), class: "btn btn-default h-7 px-2 mr-2 text-xs" do %>
9
18
  <%= heroicon('pencil', style: :solid, class: 'w-4 h-4 mr-1 text-gray-600') %>
10
19
  <%=t 'spina.ui.rename' %>
@@ -10,6 +10,16 @@
10
10
  <span class="truncate"><%= image.file.filename %></span>
11
11
 
12
12
  <div class="absolute h-full opacity-0 group-hover:opacity-100 flex items-center right-0 pr-3 top-0">
13
+
14
+ <%= form_with model: image, url: spina.admin_image_path(image), data: {turbo_frame: dom_id(image), controller: "form loading-button", loading_message: t('spina.media_library.uploading'), action: "turbo:submit-end->loading-button#doneLoading"} do |f| %>
15
+ <%= f.file_field :file, accept: "image/*", id: "image_#{image.id}_file_field", class: 'hidden', data: {action: "loading-button#loading form#requestSubmit"} %>
16
+
17
+ <button type="button" class="btn btn-default h-7 px-2 mr-2 text-xs" data-controller="delegate-click" data-action="delegate-click#click" data-loading-button-target="button" data-delegate-click-target="#image_<%= image.id %>_file_field">
18
+ <%= heroicon("upload", style: :solid, class: "w-4 h-4 mr-1 text-gray-600") %>
19
+ <%=t 'spina.ui.replace' %>
20
+ </button>
21
+ <% end %>
22
+
13
23
  <%= link_to spina.edit_admin_image_path(image), class: "btn btn-default h-7 px-2 mr-2 text-xs" do %>
14
24
  <%= heroicon('pencil', style: :solid, class: 'w-4 h-4 mr-1 text-gray-600') %>
15
25
  <%=t 'spina.ui.rename' %>
@@ -52,7 +52,7 @@
52
52
 
53
53
  <div class="mt-5">
54
54
  <%= f.label :order_by, class: 'font-medium text-sm text-gray-700 block' %>
55
- <%= f.select :order_by, [[Spina::Page.human_attribute_name(:title), "title"], [Spina::Page.human_attribute_name(:created_at), "created_at"]], {include_blank: t('spina.resources.manual_sorting')}, class: 'form-select' %>
55
+ <%= f.select :order_by, f.object.order_by_options, {include_blank: t('spina.resources.manual_sorting')}, class: 'form-select' %>
56
56
  </div>
57
57
  </div>
58
58
  </div>
@@ -7,7 +7,7 @@ xml.urlset "xmlns" => "http://www.google.com/schemas/sitemap/0.9", "xmlns:xhtml"
7
7
 
8
8
  # Translations
9
9
  page.translations.each do |translation|
10
- if translation.locale.in? Spina.config.locales.map(&:to_s)
10
+ if translation.locale.in? Spina.locales.map(&:to_s)
11
11
  Mobility.with_locale(translation.locale) do
12
12
  xml.xhtml(:link, rel: "alternate", hreflang: translation.locale, href: "#{request.protocol}#{request.host}#{page.materialized_path}")
13
13
  end
@@ -0,0 +1,16 @@
1
+ # CVE-2022-32224 RAILS
2
+ #
3
+ # There was a bug in Rails allowing YAML-serialized data to be vulnerable to RCE.
4
+ # Spina uses the serialize method to store various preferences stored using symbols.
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
7
+ # list of permitted classes.
8
+ # This can be removed in the future.
9
+ #
10
+ # More information:
11
+ # https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
12
+
13
+ if ActiveRecord.respond_to?(:yaml_column_permitted_classes)
14
+ Rails.application.config.active_record.yaml_column_permitted_classes ||= []
15
+ Rails.application.config.active_record.yaml_column_permitted_classes += [Symbol]
16
+ end
@@ -13,6 +13,12 @@ de:
13
13
  spina/embeds/vimeo:
14
14
  url: vimeo.com/...
15
15
  activerecord:
16
+ errors:
17
+ models:
18
+ spina/page:
19
+ attributes:
20
+ title:
21
+ blank: Titel erforderlich
16
22
  attributes:
17
23
  spina/account:
18
24
  address: Adresse
@@ -277,6 +283,7 @@ de:
277
283
  save_draft: Entwurf speichern
278
284
  saved: Seite gespeichert
279
285
  saving: wird gespeichert...
286
+ couldnt_be_saved: Seite konnte nicht gespeichert werden
280
287
  search_engines: Suchmaschinen
281
288
  show_in_menu: unsichtbar
282
289
  skip_to_first_child: Zur ersten Unterseite weiterleiten
@@ -345,6 +352,7 @@ de:
345
352
  new_entry: Neuer Eintrag
346
353
  optional: Optional
347
354
  rename: Umbenennen
355
+ replace: Ersetzen
348
356
  save_changes: Änderungen speichern
349
357
  saving: Speichern...
350
358
  user_mailer:
@@ -6,6 +6,12 @@ en:
6
6
  spina/embeds/vimeo:
7
7
  url: vimeo.com/...
8
8
  activerecord:
9
+ errors:
10
+ models:
11
+ spina/page:
12
+ attributes:
13
+ title:
14
+ blank: Title required
9
15
  attributes:
10
16
  spina/account:
11
17
  address: Address
@@ -270,10 +276,15 @@ en:
270
276
  save_draft: Save draft
271
277
  saved: Page saved
272
278
  saving: Saving...
279
+ couldnt_be_saved: Page couldn't be saved
273
280
  search_engines: Search engines
274
281
  show_in_menu: invisible
275
282
  skip_to_first_child: forward to first child page
276
283
  sorting_saved: Sorting saved
284
+ page_translations:
285
+ delete: Delete translation (%{translation})
286
+ delete_confirmation: Are you sure you want to delete this translation <strong>(%{subject})</strong>?
287
+ deleted: Translation deleted
277
288
  permanently_delete: Permanently delete
278
289
  photos:
279
290
  cannot_be_created: 'Image could not be processed:'
@@ -338,6 +349,7 @@ en:
338
349
  new_entry: New entry
339
350
  optional: Optional
340
351
  rename: Rename
352
+ replace: Replace
341
353
  save_changes: Save changes
342
354
  saving: Saving...
343
355
  user_mailer:
@@ -254,6 +254,10 @@ nl:
254
254
  show_in_menu: onzichtbaar
255
255
  skip_to_first_child: doorsturen naar 1<sup>e</sup> subpagina
256
256
  sorting_saved: Volgorde opgeslagen
257
+ page_translations:
258
+ delete: Verwijder vertaling (%{translation})
259
+ delete_confirmation: Weet je zeker dat je deze vertaling <strong>(%{subject})</strong> wilt verwijderen?
260
+ deleted: Vertaling verwijderd
257
261
  permanently_delete: Definitief verwijderen
258
262
  photos:
259
263
  cannot_be_created: 'Foto kon niet worden verwerkt:'
@@ -316,6 +320,7 @@ nl:
316
320
  new_entry: Nieuw item
317
321
  optional: Optioneel
318
322
  rename: Hernoemen
323
+ replace: Vervangen
319
324
  save_changes: Wijzigingen opslaan
320
325
  saving: Opslaan...
321
326
  users:
data/config/routes.rb CHANGED
@@ -53,6 +53,7 @@ Spina::Engine.routes.draw do
53
53
 
54
54
  post :sort, on: :collection
55
55
  end
56
+ resources :page_translations, only: [:destroy]
56
57
  resources :parent_pages
57
58
  resource :layout, controller: :layout, only: [:edit, :update]
58
59
 
@@ -92,8 +93,8 @@ Spina::Engine.routes.draw do
92
93
  root to: "pages#homepage"
93
94
 
94
95
  # Pages
95
- get '/:locale/*id' => 'pages#show', constraints: {locale: /#{Spina.config.locales.join('|')}/ }
96
- get '/:locale/' => 'pages#homepage', constraints: {locale: /#{Spina.config.locales.join('|')}/ }
96
+ get '/:locale/*id' => 'pages#show', constraints: {locale: /#{Spina.locales.join('|')}/ }
97
+ get '/:locale/' => 'pages#homepage', constraints: {locale: /#{Spina.locales.join('|')}/ }
97
98
  get '/*id' => 'pages#show', as: "page", controller: 'pages', constraints: -> (request) {
98
99
  request.path.exclude?(ActiveStorage.routes_prefix) &&
99
100
  !(Rails.env.development? && request.path.starts_with?('/rails/'))
@@ -1,7 +1,7 @@
1
1
  module.exports = {
2
2
  content: [
3
3
  <%= Spina.config.tailwind_content.map{|path|"'#{path}'"}.join(",\n") %>
4
- ],
4
+ ],
5
5
  theme: {
6
6
  fontFamily: {
7
7
  body: ['Metropolis'],
@@ -18,8 +18,6 @@ module.exports = {
18
18
  }
19
19
  },
20
20
  plugins: [
21
- require('@tailwindcss/forms'),
22
- require('@tailwindcss/aspect-ratio'),
23
- require('@tailwindcss/typography'),
21
+ <%= Spina.config.tailwind_plugins.map {|plugin|"require('#{plugin}')"}.join(",\n\t") %>
24
22
  ]
25
23
  }
@@ -1,5 +1,5 @@
1
- <% if Spina.config.locales.size > 1 %>
1
+ <% if Spina.locales.size > 1 %>
2
2
  <div id="languages">
3
- <%= Spina.config.locales.map{|l| link_to_unless(l == I18n.locale, l.upcase, root_url(params: {locale: l}))}.join(' / ').html_safe %>
3
+ <%= Spina.locales.map{|l| link_to_unless(l == I18n.locale, l.upcase, root_url(params: {locale: l}))}.join(' / ').html_safe %>
4
4
  </div>
5
5
  <% end %>
@@ -2,7 +2,8 @@ Spina.configure do |config|
2
2
  # Locales
3
3
  # ===============
4
4
  # All locales your content should be available in.
5
- # config.locales = [I18n.default_locale]
5
+ # Defaults to I18n.default_locale
6
+ # config.locales = [:en, :nl]
6
7
 
7
8
  # Backend path
8
9
  # ===============
data/lib/spina/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Spina
2
- VERSION = "2.9.0"
2
+ VERSION = "2.11.0"
3
3
  end
data/lib/spina.rb CHANGED
@@ -22,17 +22,18 @@ module Spina
22
22
  config_accessor :api_key,
23
23
  :api_path,
24
24
  :authentication,
25
- :backend_path,
25
+ :backend_path,
26
26
  :importmap,
27
27
  :frontend_parent_controller,
28
28
  :disable_frontend_routes,
29
29
  :disable_decorator_load,
30
- :locales,
30
+ :locales,
31
31
  :embedded_image_size,
32
32
  :mailer_defaults,
33
33
  :thumbnail_image_size,
34
34
  :party_pooper,
35
35
  :tailwind_content,
36
+ :tailwind_plugins,
36
37
  :queues,
37
38
  :transliterations
38
39
 
@@ -50,17 +51,17 @@ module Spina
50
51
  self.locales = [I18n.default_locale]
51
52
  self.party_pooper = false
52
53
  self.transliterations = %i(latin)
53
-
54
+
54
55
  # Queues for background jobs
55
56
  # - config.queues.page_updates
56
57
  self.queues = ActiveSupport::InheritableOptions.new
57
-
58
+
58
59
  # An importmap specifically meant for Spina
59
60
  self.importmap = Importmap::Map.new
60
-
61
+
61
62
  # Tailwind content
62
- # In order for Tailwind to generate all of the CSS Spina needs,
63
- # it needs to know about every single file in your project
63
+ # In order for Tailwind to generate all of the CSS Spina needs,
64
+ # it needs to know about every single file in your project
64
65
  # that contains any Tailwind class names.
65
66
  # Make sure to add your own glob patterns if you're extending
66
67
  # Spina's UI.
@@ -70,39 +71,41 @@ module Spina
70
71
  "#{Spina::Engine.root}/app/assets/javascripts/**/*.js",
71
72
  "#{Spina::Engine.root}/app/**/application.tailwind.css"]
72
73
 
74
+ self.tailwind_plugins = %w[@tailwindcss/forms @tailwindcss/aspect-ratio @tailwindcss/typography]
75
+
73
76
  # Images that are embedded in the Trix editor are resized to fit
74
77
  # You can optimize this for your website and go for a smaller (or larger) size
75
78
  # Default: 2000x2000px
76
79
  class << self
77
80
  alias_method :config_original, :config
78
-
81
+
79
82
  def config
80
83
  config_obj = self.config_original
81
-
84
+
82
85
  def config_obj.tailwind_purge_content
83
86
  ActiveSupport::Deprecation.warn("config.tailwind_purge_content has been renamed to config.tailwind_content")
84
87
  tailwind_content
85
88
  end
86
-
89
+
87
90
  def config_obj.tailwind_purge_content=(paths)
88
91
  ActiveSupport::Deprecation.warn("config.tailwind_purge_content has been renamed to config.tailwind_content")
89
92
  tailwind_content = paths
90
93
  end
91
-
94
+
92
95
  def config_obj.embedded_image_size=(image_size)
93
96
  if image_size.is_a? String
94
97
  ActiveSupport::Deprecation.warn("Spina embedded_image_size should be set to an array of arguments to be passed to the :resize_to_limit ImageProcessing macro. https://github.com/janko/image_processing/blob/master/doc/minimagick.md#resize_to_limit")
95
98
  end
96
-
99
+
97
100
  self[:embedded_image_size] = image_size
98
101
  end
99
-
102
+
100
103
  config_obj
101
104
  end
102
-
105
+
103
106
  def mounted_at
104
107
  Spina::Engine.routes.find_script_name({})
105
108
  end
106
-
109
+
107
110
  end
108
111
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spina
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.0
4
+ version: 2.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bram Jetten
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-15 00:00:00.000000000 Z
11
+ date: 2022-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -948,6 +948,7 @@ files:
948
948
  - app/controllers/spina/admin/move_pages_controller.rb
949
949
  - app/controllers/spina/admin/navigation_items_controller.rb
950
950
  - app/controllers/spina/admin/navigations_controller.rb
951
+ - app/controllers/spina/admin/page_translations_controller.rb
951
952
  - app/controllers/spina/admin/pages_controller.rb
952
953
  - app/controllers/spina/admin/parent_pages_controller.rb
953
954
  - app/controllers/spina/admin/password_resets_controller.rb
@@ -972,6 +973,7 @@ files:
972
973
  - app/helpers/spina/pages_helper.rb
973
974
  - app/helpers/spina/spina_helper.rb
974
975
  - app/jobs/spina/application_job.rb
976
+ - app/jobs/spina/replace_signed_id_job.rb
975
977
  - app/jobs/spina/resource_pages_update_job.rb
976
978
  - app/mailers/spina/application_mailer.rb
977
979
  - app/mailers/spina/user_mailer.rb
@@ -1095,6 +1097,7 @@ files:
1095
1097
  - app/views/spina/user_mailer/forgot_password.html.erb
1096
1098
  - app/views/spina/user_mailer/forgot_password.text.erb
1097
1099
  - config/initializers/importmap.rb
1100
+ - config/initializers/yaml_column_permitted_classes.rb
1098
1101
  - config/locales/TH.yml
1099
1102
  - config/locales/bg.yml
1100
1103
  - config/locales/de.yml