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.
- checksums.yaml +4 -4
- data/app/components/spina/pages/actions_component.html.erb +7 -0
- data/app/components/spina/pages/translations_component.rb +1 -1
- data/app/components/spina/user_interface/translations_component.rb +1 -1
- data/app/controllers/spina/admin/attachments_controller.rb +13 -3
- data/app/controllers/spina/admin/images_controller.rb +9 -1
- data/app/controllers/spina/admin/page_translations_controller.rb +14 -0
- data/app/controllers/spina/admin/pages_controller.rb +3 -4
- data/app/jobs/spina/replace_signed_id_job.rb +24 -0
- data/app/models/concerns/spina/translated_content.rb +1 -1
- data/app/models/spina/account.rb +11 -1
- data/app/models/spina/page.rb +9 -9
- data/app/models/spina/resource.rb +7 -0
- data/app/presenters/spina/rich_text_presenter.rb +2 -2
- data/app/views/spina/admin/attachments/_attachment.html.erb +9 -0
- data/app/views/spina/admin/images/_image.html.erb +10 -0
- data/app/views/spina/admin/resources/edit.html.erb +1 -1
- data/app/views/spina/sitemaps/show.xml.builder +1 -1
- data/config/initializers/yaml_column_permitted_classes.rb +16 -0
- data/config/locales/de.yml +8 -0
- data/config/locales/en.yml +12 -0
- data/config/locales/nl.yml +5 -0
- data/config/routes.rb +3 -2
- data/lib/generators/spina/templates/app/assets/config/spina/tailwind.config.js.tt +2 -4
- data/lib/generators/spina/templates/app/views/demo/shared/_languages.html.erb +2 -2
- data/lib/generators/spina/templates/config/initializers/spina.rb +2 -1
- data/lib/spina/version.rb +1 -1
- data/lib/spina.rb +18 -15
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 68c17b8a615a45c86dce79d2e4b32a5f7bc4fdbf8f71d94fbdc2f530a68baa48
|
4
|
+
data.tar.gz: a85f8e9b3a922c1b3de5866d37bab8bdae583c264e35894baa7871b95aa2eaf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 %>
|
@@ -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
|
-
|
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
|
-
|
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.
|
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
|
data/app/models/spina/account.rb
CHANGED
@@ -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
|
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
|
data/app/models/spina/page.rb
CHANGED
@@ -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
|
-
|
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,
|
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.
|
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
|
data/config/locales/de.yml
CHANGED
@@ -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:
|
data/config/locales/en.yml
CHANGED
@@ -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:
|
data/config/locales/nl.yml
CHANGED
@@ -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.
|
96
|
-
get '/:locale/' => 'pages#homepage', constraints: {locale: /#{Spina.
|
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('
|
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.
|
1
|
+
<% if Spina.locales.size > 1 %>
|
2
2
|
<div id="languages">
|
3
|
-
<%= Spina.
|
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 %>
|
data/lib/spina/version.rb
CHANGED
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.
|
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-
|
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
|