spina 1.1.4 → 2.0.1
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/assets/icons/spina/preview/icons_spina-preview.html +0 -2
- data/app/assets/icons/spina/preview/ics_spina-preview.html +0 -2
- data/app/assets/images/spina/favicon.png +0 -0
- data/app/assets/javascripts/spina/admin/application.js +1 -2
- data/app/assets/javascripts/spina/admin/controllers/attachment_picker_controller.js +15 -0
- data/app/assets/javascripts/spina/admin/controllers/image_form_controller.js +5 -4
- data/app/assets/javascripts/spina/admin/controllers/infinite_scroll_controller.js +20 -22
- data/app/assets/javascripts/spina/admin/controllers/media_picker_controller.js +171 -0
- data/app/assets/javascripts/spina/admin/controllers/modal_controller.js +18 -0
- data/app/assets/javascripts/spina/admin/controllers/repeater_form_controller.js +38 -0
- data/app/assets/javascripts/spina/admin/media_gallery.coffee +0 -3
- data/app/assets/javascripts/spina/admin/notifications.coffee +1 -1
- data/app/assets/javascripts/spina/admin/pages.coffee.erb +3 -20
- data/app/assets/javascripts/spina/admin/scaffold.coffee +1 -3
- data/app/assets/javascripts/spina/admin/trix.coffee.erb +14 -18
- data/app/assets/stylesheets/spina.sass +10 -8
- data/app/assets/stylesheets/spina/_buttons.sass +57 -31
- data/app/assets/stylesheets/spina/_forms.sass +66 -74
- data/app/assets/stylesheets/spina/_gallery.sass +18 -2
- data/app/assets/stylesheets/spina/_media_picker.sass +133 -0
- data/app/assets/stylesheets/spina/_modal.sass +35 -2
- data/app/assets/stylesheets/spina/_notifications.sass +20 -14
- data/app/assets/stylesheets/spina/_pages.sass +131 -0
- data/app/assets/stylesheets/spina/_sortable_lists.sass +14 -8
- data/app/assets/stylesheets/spina/_trix_custom.sass +15 -5
- data/app/controllers/concerns/spina/current_methods.rb +26 -0
- data/app/controllers/concerns/spina/frontend.rb +9 -0
- data/app/controllers/spina/admin/accounts_controller.rb +9 -13
- data/app/controllers/spina/admin/admin_controller.rb +5 -1
- data/app/controllers/spina/admin/attachments_controller.rb +0 -22
- data/app/controllers/spina/admin/images_controller.rb +1 -1
- data/app/controllers/spina/admin/media_picker_controller.rb +18 -19
- data/app/controllers/spina/admin/pages_controller.rb +2 -6
- data/app/controllers/spina/admin/resources_controller.rb +9 -4
- data/app/controllers/spina/application_controller.rb +6 -23
- data/app/controllers/spina/pages_controller.rb +11 -13
- data/app/controllers/spina/sitemaps_controller.rb +6 -6
- data/app/helpers/spina/admin/pages_helper.rb +22 -20
- data/app/helpers/spina/{files_helper.rb → attachments_helper.rb} +1 -1
- data/app/helpers/spina/images_helper.rb +9 -18
- data/app/helpers/spina/pages_helper.rb +23 -4
- data/app/models/concerns/spina/partable.rb +19 -7
- data/app/models/concerns/spina/translated_content.rb +19 -0
- data/app/models/spina/account.rb +3 -10
- data/app/models/spina/attachment.rb +0 -4
- data/app/models/spina/current.rb +1 -0
- data/app/models/spina/image.rb +0 -4
- data/app/models/spina/page.rb +29 -21
- data/app/models/spina/parts/attachment.rb +22 -0
- data/app/models/spina/parts/base.rb +12 -0
- data/app/models/spina/parts/image.rb +39 -0
- data/app/models/spina/parts/image_collection.rb +23 -0
- data/app/models/spina/parts/image_variant.rb +19 -0
- data/app/models/spina/parts/line.rb +7 -0
- data/app/models/spina/parts/multi_line.rb +7 -0
- data/app/models/spina/parts/option.rb +13 -0
- data/app/models/spina/parts/repeater.rb +10 -0
- data/app/models/spina/parts/repeater_content.rb +15 -0
- data/app/models/spina/parts/text.rb +7 -0
- data/app/models/spina/resource.rb +4 -12
- data/app/presenters/spina/content_presenter.rb +51 -0
- data/app/presenters/spina/menu_presenter.rb +2 -1
- data/app/views/layouts/spina/admin/admin.html.haml +3 -7
- data/app/views/layouts/spina/admin/pages.html.haml +5 -4
- data/app/views/layouts/spina/login.html.haml +0 -3
- data/app/views/spina/admin/accounts/_form.html.haml +13 -7
- data/app/views/spina/admin/accounts/analytics.html.haml +5 -3
- data/app/views/spina/admin/accounts/social.html.haml +13 -7
- data/app/views/spina/admin/accounts/style.html.haml +26 -21
- data/app/views/spina/admin/attachments/index.html.haml +1 -1
- data/app/views/spina/admin/images/_image.html.haml +1 -1
- data/app/views/spina/admin/images/index.html.haml +7 -5
- data/app/views/spina/admin/media_folders/_media_folder.html.haml +1 -1
- data/app/views/spina/admin/media_folders/show.html.haml +2 -2
- data/app/views/spina/admin/media_picker/_image.html.haml +3 -0
- data/app/views/spina/admin/media_picker/_media_picker.html.haml +32 -0
- data/app/views/spina/admin/media_picker/_media_picker_grid.html.haml +25 -0
- data/app/views/spina/admin/media_picker/_modal.html.haml +1 -1
- data/app/views/spina/admin/media_picker/infinite_scroll.js.erb +4 -4
- data/app/views/spina/admin/media_picker/select.js.erb +2 -0
- data/app/views/spina/admin/media_picker/show.html.haml +1 -0
- data/app/views/spina/admin/media_picker/show.js.erb +1 -1
- data/app/views/spina/admin/pages/_form.html.haml +9 -7
- data/app/views/spina/admin/pages/_form_advanced.html.haml +19 -17
- data/app/views/spina/admin/pages/_form_page_content.html.haml +14 -13
- data/app/views/spina/admin/pages/_form_page_seo.html.haml +15 -4
- data/app/views/spina/admin/pages/_page.html.haml +5 -3
- data/app/views/spina/admin/pages/_page_nested_list.html.haml +2 -3
- data/app/views/spina/admin/pages/index.html.haml +5 -4
- data/app/views/spina/admin/parts/attachments/_form.html.haml +6 -0
- data/app/views/spina/admin/parts/image_collections/_fields.html.haml +7 -0
- data/app/views/spina/admin/parts/image_collections/_form.html.haml +12 -0
- data/app/views/spina/admin/parts/images/_form.html.haml +18 -0
- data/app/views/spina/admin/parts/lines/_form.html.haml +2 -0
- data/app/views/spina/admin/parts/multi_lines/_form.html.haml +2 -0
- data/app/views/spina/admin/parts/options/_form.html.haml +3 -0
- data/app/views/spina/admin/parts/repeaters/_fields.html.haml +15 -0
- data/app/views/spina/admin/parts/repeaters/_form.html.haml +17 -0
- data/app/views/spina/admin/parts/texts/_form.html.haml +6 -0
- data/app/views/spina/admin/resources/edit.html.haml +28 -12
- data/app/views/spina/admin/resources/show.html.haml +6 -3
- data/app/views/spina/admin/shared/_notifications.html.haml +4 -4
- data/config/locales/TH.yml +9 -0
- data/config/locales/bg.yml +9 -0
- data/config/locales/de.yml +9 -0
- data/config/locales/en.yml +9 -0
- data/config/locales/es.yml +9 -0
- data/config/locales/fr.yml +9 -0
- data/config/locales/id.yml +9 -0
- data/config/locales/it.yml +9 -0
- data/config/locales/nl.yml +9 -0
- data/config/locales/pl.yml +23 -14
- data/config/locales/pt-BR.yml +9 -0
- data/config/locales/ro.yml +9 -0
- data/config/locales/ru.yml +9 -0
- data/config/locales/sv.yml +9 -0
- data/config/locales/tr.yml +9 -0
- data/config/locales/zh-CN.yml +9 -0
- data/config/routes.rb +13 -0
- data/db/migrate/11_create_spina_resources.rb +0 -2
- data/db/migrate/12_add_url_title_to_spina_page_translations.rb +5 -0
- data/db/migrate/13_add_json_attributes_to_spina_accounts.rb +5 -0
- data/db/migrate/14_add_json_attributes_to_spina_pages.rb +5 -0
- data/db/migrate/15_add_slug_to_spina_resources.rb +5 -0
- data/lib/generators/spina/templates/app/views/demo/pages/demo.html.haml +13 -21
- data/lib/generators/spina/templates/app/views/demo/pages/homepage.html.haml +1 -1
- data/lib/generators/spina/templates/config/initializers/mobility.rb +13 -4
- data/lib/generators/spina/templates/config/initializers/spina.rb +4 -0
- data/lib/generators/spina/templates/config/initializers/themes/default.rb +7 -7
- data/lib/generators/spina/templates/config/initializers/themes/demo.rb +32 -44
- data/lib/spina.rb +18 -3
- data/lib/spina/attr_json_spina_parts_model.rb +29 -0
- data/lib/spina/engine.rb +14 -6
- data/lib/spina/part.rb +19 -0
- data/lib/spina/theme.rb +1 -1
- data/lib/spina/version.rb +1 -1
- data/lib/tasks/spina_tasks.rake +1 -1
- data/vendor/assets/javascripts/spina/sortable.js +2 -2
- metadata +73 -51
- data/app/assets/javascripts/spina/admin/account.coffee +0 -16
- data/app/models/concerns/spina/image_collectable.rb +0 -23
- data/app/models/concerns/spina/optionable.rb +0 -12
- data/app/models/concerns/spina/part.rb +0 -38
- data/app/models/spina/attachment_collection.rb +0 -20
- data/app/models/spina/image_collection.rb +0 -23
- data/app/models/spina/image_collections_image.rb +0 -6
- data/app/models/spina/layout_part.rb +0 -22
- data/app/models/spina/line.rb +0 -10
- data/app/models/spina/option.rb +0 -17
- data/app/models/spina/page_part.rb +0 -22
- data/app/models/spina/structure.rb +0 -14
- data/app/models/spina/structure_item.rb +0 -22
- data/app/models/spina/structure_part.rb +0 -20
- data/app/models/spina/text.rb +0 -10
- data/app/views/dummy/show.html.haml +0 -1
- data/app/views/spina/admin/attachments/_attachment_collection.html.haml +0 -2
- data/app/views/spina/admin/attachments/_select.html.haml +0 -17
- data/app/views/spina/admin/attachments/_select_collection.html.haml +0 -18
- data/app/views/spina/admin/attachments/insert.js.erb +0 -5
- data/app/views/spina/admin/attachments/insert_collection.js.coffee +0 -7
- data/app/views/spina/admin/attachments/select.js.erb +0 -2
- data/app/views/spina/admin/attachments/select_collection.js.erb +0 -2
- data/app/views/spina/admin/image_collections/_image_collection.html.haml +0 -5
- data/app/views/spina/admin/layout_partables/lines/_form.html.haml +0 -2
- data/app/views/spina/admin/partables/attachment_collections/_form.html.haml +0 -14
- data/app/views/spina/admin/partables/attachments/_form.html.haml +0 -13
- data/app/views/spina/admin/partables/image_collections/_form.html.haml +0 -16
- data/app/views/spina/admin/partables/images/_form.html.haml +0 -21
- data/app/views/spina/admin/partables/lines/_form.html.haml +0 -5
- data/app/views/spina/admin/partables/options/_form.html.haml +0 -7
- data/app/views/spina/admin/partables/photo_collections/_form.html.haml +0 -4
- data/app/views/spina/admin/partables/photos/_form.html.haml +0 -4
- data/app/views/spina/admin/partables/structures/_form.html.haml +0 -21
- data/app/views/spina/admin/partables/texts/_form.html.haml +0 -8
- data/app/views/spina/admin/structure_items/_fields.html.haml +0 -15
- data/app/views/spina/admin/structure_partables/attachment_collections/_form.html.haml +0 -14
- data/app/views/spina/admin/structure_partables/attachments/_form.html.haml +0 -13
@@ -1,20 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
include Spina::Frontend
|
1
|
+
class Spina::PagesController < Spina::ApplicationController
|
2
|
+
include Spina::Frontend
|
4
3
|
|
5
|
-
|
4
|
+
before_action :current_spina_user_can_view_page?, except: [:robots]
|
6
5
|
|
7
|
-
|
6
|
+
helper_method :page
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def homepage
|
9
|
+
render_with_template(page)
|
10
|
+
end
|
12
11
|
|
13
|
-
|
12
|
+
private
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
def current_spina_user_can_view_page?
|
15
|
+
raise ActiveRecord::RecordNotFound unless current_spina_user.present? || page.live?
|
16
|
+
end
|
18
17
|
|
19
|
-
end
|
20
18
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
end
|
1
|
+
class Spina::SitemapsController < Spina::ApplicationController
|
2
|
+
|
3
|
+
def show
|
4
|
+
I18n.locale = I18n.default_locale
|
5
|
+
@pages = Spina::Page.live.sorted
|
7
6
|
end
|
7
|
+
|
8
8
|
end
|
@@ -1,34 +1,36 @@
|
|
1
1
|
module Spina
|
2
2
|
module Admin
|
3
3
|
module PagesHelper
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
render("spina/admin/
|
4
|
+
|
5
|
+
def link_to_add_repeater_fields(f)
|
6
|
+
repeater_content = Spina::Parts::RepeaterContent.new(name: f.object.name, title: f.object.title)
|
7
|
+
fields = f.fields_for(:content, [repeater_content], child_index: repeater_content.object_id) do |builder|
|
8
|
+
render("spina/admin/parts/repeaters/fields", f: builder)
|
9
9
|
end
|
10
|
-
link_to '#', class: "add_structure_item_fields button button-link", data: {id:
|
10
|
+
link_to '#', class: "add_structure_item_fields button button-link", data: {id: repeater_content.object_id, fields: fields.gsub("\n", "")} do
|
11
11
|
icon('plus')
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
part = item.parts.where(name: attributes[:name]).first_or_initialize(attributes)
|
20
|
-
part.partable = part.partable_type.constantize.new if part.partable.blank?
|
21
|
-
part.options = attributes[:options]
|
22
|
-
part
|
15
|
+
def data_attrs_for_image_collection(f)
|
16
|
+
image = Spina::Parts::Image.new
|
17
|
+
fields = f.fields_for(:images, [image], child_index: image.object_id) do |builder|
|
18
|
+
render("spina/admin/parts/image_collections/fields", f: builder)
|
23
19
|
end
|
20
|
+
{fields: fields.gsub("\n", ""), id: image.object_id}
|
24
21
|
end
|
25
22
|
|
26
|
-
def
|
27
|
-
|
23
|
+
def build_parts(partable, parts)
|
24
|
+
I18n.with_locale(@locale) do
|
25
|
+
parts.map do |part|
|
26
|
+
part_attributes = current_theme.parts.find{|p|p[:name].to_s == part.to_s}
|
27
|
+
partable.part(part_attributes)
|
28
|
+
end
|
29
|
+
end
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
31
|
-
|
32
|
+
def parts_partial_namespace(part_type)
|
33
|
+
part_type.tableize.sub(/\Aspina\/parts\//, '')
|
32
34
|
end
|
33
35
|
|
34
36
|
def flatten_nested_hash(hash)
|
@@ -36,14 +38,14 @@ module Spina
|
|
36
38
|
end
|
37
39
|
|
38
40
|
def page_ancestry_options(page)
|
39
|
-
pages = Spina::Page.active.regular_pages
|
41
|
+
pages = Spina::Page.active.regular_pages.includes(:translations)
|
40
42
|
pages = pages.where.not(id: page.subtree.ids) unless page.new_record? || !page.methods.include?(:subtree)
|
41
43
|
|
42
44
|
(flatten_nested_hash(pages.arrange(order: :position)).map do |page|
|
43
45
|
next if page.depth >= Spina.config.max_page_depth - 1
|
44
46
|
page_menu_title = page.depth.zero? ? page.menu_title : " #{page.menu_title}".indent(page.depth, '-')
|
45
47
|
[page_menu_title, page.id]
|
46
|
-
end << [page.parent&.menu_title, page&.parent_id].compact).uniq.compact
|
48
|
+
end << [page.parent&.menu_title, page&.parent_id].compact).map(&:presence).uniq.compact
|
47
49
|
end
|
48
50
|
|
49
51
|
def option_label(part, value)
|
@@ -1,24 +1,15 @@
|
|
1
1
|
module Spina
|
2
2
|
module ImagesHelper
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
def variant(image, options)
|
8
|
-
if image.attached? && image.variable?
|
9
|
-
variant = image.variant(options)
|
10
|
-
main_app.rails_blob_representation_path(variant.blob.signed_id, variant.variation.key, variant.blob.filename)
|
11
|
-
elsif image.image?
|
12
|
-
# Allows SVGs to be displayed as they are not variable. Requires:
|
13
|
-
# - https://github.com/Dreamersoul/administrate-field-active_storage/issues/36#issuecomment-608446819 to be applied
|
14
|
-
#
|
15
|
-
# Additionally, https://github.com/hopsoft/active_storage_svg_sanitizer/
|
16
|
-
# should be added if users are able to upload their own SVG content due to:
|
17
|
-
# https://github.com/rails/rails/issues/34665#issuecomment-445888009
|
18
|
-
main_app.url_for(image)
|
19
|
-
else
|
20
|
-
"https://placehold.it/100x100.png"
|
21
|
-
end
|
4
|
+
def thumbnail_url(image)
|
5
|
+
return "" if image.nil?
|
6
|
+
main_app.url_for(image.variant(resize: "400x300^", crop: "400x300+0+0"))
|
22
7
|
end
|
8
|
+
|
9
|
+
def embedded_image_url(image)
|
10
|
+
return "" if image.nil?
|
11
|
+
main_app.url_for(image.variant(resize: Spina.config.embedded_image_size))
|
12
|
+
end
|
13
|
+
|
23
14
|
end
|
24
15
|
end
|
@@ -1,17 +1,36 @@
|
|
1
1
|
module Spina
|
2
2
|
module PagesHelper
|
3
3
|
|
4
|
-
def content(part_name)
|
5
|
-
|
4
|
+
def content(part_name = nil)
|
5
|
+
Current.page.content(part_name)
|
6
6
|
end
|
7
7
|
|
8
8
|
def has_content?(part_name)
|
9
|
-
|
9
|
+
Current.page.has_content?(part_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def repeater(part)
|
13
|
+
part = Current.page.find_part(part)&.content unless part.is_a? Array
|
14
|
+
part&.each do |repeater_content|
|
15
|
+
repeater_content.view_context = self
|
16
|
+
yield(repeater_content)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def images(part)
|
21
|
+
part = Current.page.find_part(part)&.content unless part.is_a? Array
|
22
|
+
part&.each do |image|
|
23
|
+
yield(image)
|
24
|
+
end
|
10
25
|
end
|
11
26
|
|
12
27
|
def current_page
|
13
28
|
Current.page
|
14
29
|
end
|
15
30
|
|
31
|
+
def current_account
|
32
|
+
Current.account
|
33
|
+
end
|
34
|
+
|
16
35
|
end
|
17
|
-
end
|
36
|
+
end
|
@@ -2,23 +2,35 @@ module Spina
|
|
2
2
|
module Partable
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
attr_accessor :view_context
|
6
|
+
|
5
7
|
included do
|
8
|
+
|
6
9
|
def part(attributes)
|
7
|
-
part =
|
8
|
-
|
9
|
-
|
10
|
+
part = find_part(attributes[:name]) || attributes[:part_type].constantize.new
|
11
|
+
|
12
|
+
# Copy all attributes to part
|
13
|
+
%w(name title options).each do |attribute|
|
14
|
+
part.public_send("#{attribute}=", attributes[attribute.to_sym]) if part.respond_to?(attribute)
|
15
|
+
end
|
16
|
+
|
10
17
|
part
|
11
18
|
end
|
12
19
|
|
13
20
|
def has_content?(name)
|
14
|
-
|
21
|
+
find_part(name).present?
|
15
22
|
end
|
16
23
|
|
17
|
-
def content(name)
|
18
|
-
|
19
|
-
part.try(:content)
|
24
|
+
def content(name = nil)
|
25
|
+
name ? find_part(name)&.content : content_presenter
|
20
26
|
end
|
21
27
|
|
28
|
+
private
|
29
|
+
|
30
|
+
def content_presenter
|
31
|
+
@content_presenter ||= ContentPresenter.new(view_context, self)
|
32
|
+
end
|
33
|
+
|
22
34
|
end
|
23
35
|
end
|
24
36
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Spina
|
2
|
+
module TranslatedContent
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
|
7
|
+
# Store each locale's content in [locale]_content as an array of parts
|
8
|
+
Spina.config.locales.each do |locale|
|
9
|
+
attr_json "#{locale}_content".to_sym, AttrJson::Type::SpinaPartsModel.new, array: true, default: -> { [] }
|
10
|
+
attr_json_accepts_nested_attributes_for "#{locale}_content".to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_part(name)
|
14
|
+
send("#{I18n.locale}_content").find{|part| part.name.to_s == name.to_s}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/app/models/spina/account.rb
CHANGED
@@ -1,25 +1,18 @@
|
|
1
1
|
module Spina
|
2
2
|
class Account < ApplicationRecord
|
3
|
+
include AttrJson::Record
|
4
|
+
include AttrJson::NestedAttributes
|
3
5
|
include Partable
|
6
|
+
include TranslatedContent
|
4
7
|
|
5
8
|
serialize :preferences
|
6
9
|
|
7
|
-
has_many :layout_parts, dependent: :destroy
|
8
|
-
accepts_nested_attributes_for :layout_parts, allow_destroy: true
|
9
|
-
|
10
|
-
alias_attribute :layout_part, :part
|
11
|
-
alias_attribute :parts, :layout_parts
|
12
|
-
|
13
10
|
after_save :bootstrap_website
|
14
11
|
|
15
12
|
def to_s
|
16
13
|
name
|
17
14
|
end
|
18
15
|
|
19
|
-
def content(layout_part)
|
20
|
-
layout_parts.find_by(name: layout_part).try(:content)
|
21
|
-
end
|
22
|
-
|
23
16
|
def self.serialized_attr_accessor(*args)
|
24
17
|
args.each do |method_name|
|
25
18
|
define_method method_name do
|
@@ -2,10 +2,6 @@ module Spina
|
|
2
2
|
class Attachment < ApplicationRecord
|
3
3
|
has_one_attached :file
|
4
4
|
|
5
|
-
has_one :page_part, as: :page_partable
|
6
|
-
has_many :structure_parts, as: :structure_partable
|
7
|
-
has_and_belongs_to_many :attachment_collections, join_table: 'spina_attachment_collections_attachments'
|
8
|
-
|
9
5
|
attr_accessor :_destroy
|
10
6
|
|
11
7
|
scope :sorted, -> { order('file ASC') }
|
data/app/models/spina/current.rb
CHANGED
data/app/models/spina/image.rb
CHANGED
@@ -4,11 +4,7 @@ module Spina
|
|
4
4
|
|
5
5
|
has_one_attached :file
|
6
6
|
|
7
|
-
has_many :page_parts, as: :page_partable
|
8
|
-
has_many :structure_parts, as: :structure_partable
|
9
|
-
|
10
7
|
scope :sorted, -> { order('created_at DESC') }
|
11
|
-
scope :sorted_by_image_collection, -> { order('position') }
|
12
8
|
|
13
9
|
def name
|
14
10
|
file.try(:filename).to_s
|
data/app/models/spina/page.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
module Spina
|
2
2
|
class Page < ApplicationRecord
|
3
3
|
extend Mobility
|
4
|
+
include AttrJson::Record
|
5
|
+
include AttrJson::NestedAttributes
|
4
6
|
include Partable
|
7
|
+
include TranslatedContent
|
5
8
|
|
6
9
|
# Stores the old path when generating a new materialized_path
|
7
10
|
attr_accessor :old_path
|
8
11
|
|
9
|
-
# Page contains multiple parts called PageParts
|
10
|
-
has_many :page_parts, dependent: :destroy, inverse_of: :page
|
11
|
-
alias_attribute :parts, :page_parts
|
12
|
-
accepts_nested_attributes_for :page_parts, allow_destroy: true
|
13
|
-
|
14
12
|
# Orphaned pages are adopted by parent pages if available, otherwise become root
|
15
13
|
has_ancestry orphan_strategy: :adopt
|
16
14
|
|
@@ -28,10 +26,12 @@ module Spina
|
|
28
26
|
scope :live, -> { active.where(draft: false) }
|
29
27
|
scope :in_menu, -> { where(show_in_menu: true) }
|
30
28
|
|
29
|
+
# Copy resource from parent
|
30
|
+
before_save :set_resource_from_parent, if: -> { parent.present? }
|
31
|
+
|
31
32
|
# Save children to update all materialized_paths
|
32
33
|
after_save :save_children
|
33
34
|
after_save :touch_navigations
|
34
|
-
after_save -> { page_parts.each(&:save) }
|
35
35
|
|
36
36
|
# Create a 301 redirect if materialized_path changed
|
37
37
|
after_save :rewrite_rule
|
@@ -41,7 +41,7 @@ module Spina
|
|
41
41
|
|
42
42
|
translates :title, fallbacks: true
|
43
43
|
translates :description, :materialized_path
|
44
|
-
translates :menu_title, :seo_title, default: -> { title }
|
44
|
+
translates :menu_title, :seo_title, :url_title, default: -> { title }
|
45
45
|
|
46
46
|
def to_s
|
47
47
|
name
|
@@ -51,8 +51,8 @@ module Spina
|
|
51
51
|
id
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
55
|
-
|
54
|
+
def slug
|
55
|
+
url_title&.parameterize
|
56
56
|
end
|
57
57
|
|
58
58
|
def custom_page?
|
@@ -74,12 +74,17 @@ module Spina
|
|
74
74
|
def next_sibling
|
75
75
|
siblings.where('position > ?', position).sorted.first
|
76
76
|
end
|
77
|
-
|
77
|
+
|
78
78
|
def set_materialized_path
|
79
79
|
self.old_path = materialized_path
|
80
80
|
self.materialized_path = localized_materialized_path
|
81
|
-
|
82
|
-
materialized_path
|
81
|
+
|
82
|
+
# Append counter to duplicate materialized_path
|
83
|
+
i = 0
|
84
|
+
while duplicate_materialized_path?
|
85
|
+
i += 1
|
86
|
+
self.materialized_path = localized_materialized_path.concat("-#{i}")
|
87
|
+
end
|
83
88
|
end
|
84
89
|
|
85
90
|
def cache_key
|
@@ -91,12 +96,12 @@ module Spina
|
|
91
96
|
theme.view_templates.find { |template| template[:name] == view_template_name }
|
92
97
|
end
|
93
98
|
|
94
|
-
def view_template_page_parts(theme)
|
95
|
-
theme.page_parts.select { |page_part| page_part[:name].in? view_template_config(theme)[:page_parts] }
|
96
|
-
end
|
97
|
-
|
98
99
|
private
|
99
100
|
|
101
|
+
def set_resource_from_parent
|
102
|
+
self.resource_id = parent.resource_id
|
103
|
+
end
|
104
|
+
|
100
105
|
def touch_navigations
|
101
106
|
navigations.update_all(updated_at: Time.zone.now)
|
102
107
|
end
|
@@ -114,11 +119,14 @@ module Spina
|
|
114
119
|
end
|
115
120
|
|
116
121
|
def generate_materialized_path
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
+
path_fragments = [resource&.slug]
|
123
|
+
path_fragments.append *ancestors.collect(&:slug)
|
124
|
+
path_fragments.append(slug) unless name == 'homepage'
|
125
|
+
path_fragments.compact.map(&:parameterize).join('/')
|
126
|
+
end
|
127
|
+
|
128
|
+
def duplicate_materialized_path?
|
129
|
+
self.class.where.not(id: id).i18n.where(materialized_path: materialized_path).exists?
|
122
130
|
end
|
123
131
|
|
124
132
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Spina
|
2
|
+
module Parts
|
3
|
+
class Attachment < Base
|
4
|
+
attr_json :attachment_id, :integer, default: nil
|
5
|
+
attr_json :signed_blob_id, :string, default: nil
|
6
|
+
attr_json :filename, :string, default: ""
|
7
|
+
|
8
|
+
def content
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def present?
|
13
|
+
signed_blob_id.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def signed_id
|
17
|
+
signed_blob_id
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|