spree_page_builder 5.3.0.rc1
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.
- checksums.yaml +7 -0
- data/README.md +43 -0
- data/Rakefile +18 -0
- data/app/controllers/concerns/spree/admin/page_builder_concern.rb +45 -0
- data/app/controllers/concerns/spree/admin/storefront_breadcrumb_concern.rb +12 -0
- data/app/controllers/spree/admin/page_blocks_controller.rb +58 -0
- data/app/controllers/spree/admin/page_links_controller.rb +60 -0
- data/app/controllers/spree/admin/page_sections_controller.rb +82 -0
- data/app/controllers/spree/admin/pages_controller.rb +39 -0
- data/app/controllers/spree/admin/storefront_controller.rb +41 -0
- data/app/controllers/spree/admin/themes_controller.rb +87 -0
- data/app/helpers/spree/admin/page_builder_helper.rb +125 -0
- data/app/helpers/spree/admin/themes_helper.rb +9 -0
- data/app/jobs/spree/page_builder/products/touch_taxons_job_decorator.rb +15 -0
- data/app/jobs/spree/themes/duplicate_components_job.rb +59 -0
- data/app/jobs/spree/themes/screenshot_job.rb +81 -0
- data/app/models/concerns/spree/has_page_links.rb +53 -0
- data/app/models/spree/page.rb +188 -0
- data/app/models/spree/page_block.rb +73 -0
- data/app/models/spree/page_blocks/buttons.rb +29 -0
- data/app/models/spree/page_blocks/heading.rb +18 -0
- data/app/models/spree/page_blocks/image.rb +20 -0
- data/app/models/spree/page_blocks/link.rb +21 -0
- data/app/models/spree/page_blocks/mega_nav.rb +33 -0
- data/app/models/spree/page_blocks/mega_nav_with_subcategories.rb +32 -0
- data/app/models/spree/page_blocks/metafields.rb +18 -0
- data/app/models/spree/page_blocks/nav.rb +15 -0
- data/app/models/spree/page_blocks/newsletter_form.rb +18 -0
- data/app/models/spree/page_blocks/products/brand.rb +15 -0
- data/app/models/spree/page_blocks/products/buy_buttons.rb +24 -0
- data/app/models/spree/page_blocks/products/description.rb +18 -0
- data/app/models/spree/page_blocks/products/price.rb +18 -0
- data/app/models/spree/page_blocks/products/quantity_selector.rb +20 -0
- data/app/models/spree/page_blocks/products/share.rb +8 -0
- data/app/models/spree/page_blocks/products/title.rb +19 -0
- data/app/models/spree/page_blocks/products/variant_picker.rb +13 -0
- data/app/models/spree/page_blocks/subheading.rb +17 -0
- data/app/models/spree/page_blocks/text.rb +16 -0
- data/app/models/spree/page_builder/policy_decorator.rb +17 -0
- data/app/models/spree/page_builder/post_decorator.rb +17 -0
- data/app/models/spree/page_builder/product_decorator.rb +17 -0
- data/app/models/spree/page_builder/store_decorator.rb +46 -0
- data/app/models/spree/page_builder/taxon_decorator.rb +45 -0
- data/app/models/spree/page_link.rb +60 -0
- data/app/models/spree/page_section.rb +222 -0
- data/app/models/spree/page_sections/announcement_bar.rb +28 -0
- data/app/models/spree/page_sections/breadcrumbs.rb +12 -0
- data/app/models/spree/page_sections/collection_banner.rb +18 -0
- data/app/models/spree/page_sections/custom_code.rb +11 -0
- data/app/models/spree/page_sections/featured_posts.rb +45 -0
- data/app/models/spree/page_sections/featured_product.rb +50 -0
- data/app/models/spree/page_sections/featured_taxon.rb +90 -0
- data/app/models/spree/page_sections/featured_taxons.rb +45 -0
- data/app/models/spree/page_sections/footer.rb +101 -0
- data/app/models/spree/page_sections/header.rb +62 -0
- data/app/models/spree/page_sections/image_banner.rb +55 -0
- data/app/models/spree/page_sections/image_with_text.rb +65 -0
- data/app/models/spree/page_sections/main_password_footer.rb +18 -0
- data/app/models/spree/page_sections/main_password_header.rb +20 -0
- data/app/models/spree/page_sections/newsletter.rb +54 -0
- data/app/models/spree/page_sections/page_title.rb +19 -0
- data/app/models/spree/page_sections/post_details.rb +19 -0
- data/app/models/spree/page_sections/post_grid.rb +19 -0
- data/app/models/spree/page_sections/product_details.rb +53 -0
- data/app/models/spree/page_sections/product_grid.rb +13 -0
- data/app/models/spree/page_sections/related_products.rb +58 -0
- data/app/models/spree/page_sections/rich_text.rb +31 -0
- data/app/models/spree/page_sections/taxon_banner.rb +18 -0
- data/app/models/spree/page_sections/taxon_grid.rb +17 -0
- data/app/models/spree/page_sections/video.rb +107 -0
- data/app/models/spree/pages/account.rb +19 -0
- data/app/models/spree/pages/cart.rb +19 -0
- data/app/models/spree/pages/checkout.rb +15 -0
- data/app/models/spree/pages/custom.rb +38 -0
- data/app/models/spree/pages/homepage.rb +72 -0
- data/app/models/spree/pages/login.rb +19 -0
- data/app/models/spree/pages/password.rb +59 -0
- data/app/models/spree/pages/post.rb +27 -0
- data/app/models/spree/pages/post_list.rb +36 -0
- data/app/models/spree/pages/product_details.rb +30 -0
- data/app/models/spree/pages/search_results.rb +43 -0
- data/app/models/spree/pages/shop_all.rb +40 -0
- data/app/models/spree/pages/taxon.rb +29 -0
- data/app/models/spree/pages/taxon_list.rb +41 -0
- data/app/models/spree/pages/wishlist.rb +15 -0
- data/app/models/spree/theme.rb +233 -0
- data/app/models/spree/themes/default.rb +97 -0
- data/app/services/spree/taxons/touch_featured_sections.rb +21 -0
- data/app/views/layouts/spree/page_builder.html.erb +46 -0
- data/app/views/spree/admin/dashboard/_store_preview.html.erb +38 -0
- data/app/views/spree/admin/page_blocks/_form_tab_buttons.html.erb +5 -0
- data/app/views/spree/admin/page_blocks/create.turbo_stream.erb +4 -0
- data/app/views/spree/admin/page_blocks/destroy.turbo_stream.erb +9 -0
- data/app/views/spree/admin/page_blocks/edit.html.erb +43 -0
- data/app/views/spree/admin/page_blocks/forms/_brand.html.erb +15 -0
- data/app/views/spree/admin/page_blocks/forms/_brand_logo.html.erb +4 -0
- data/app/views/spree/admin/page_blocks/forms/_buttons.html.erb +13 -0
- data/app/views/spree/admin/page_blocks/forms/_heading.html.erb +28 -0
- data/app/views/spree/admin/page_blocks/forms/_image.html.erb +10 -0
- data/app/views/spree/admin/page_blocks/forms/_link.html.erb +19 -0
- data/app/views/spree/admin/page_blocks/forms/_mega_nav.html.erb +14 -0
- data/app/views/spree/admin/page_blocks/forms/_mega_nav_with_subcategories.html.erb +9 -0
- data/app/views/spree/admin/page_blocks/forms/_metafields.html.erb +35 -0
- data/app/views/spree/admin/page_blocks/forms/_nav.html.erb +6 -0
- data/app/views/spree/admin/page_blocks/forms/_newsletter_form.html.erb +20 -0
- data/app/views/spree/admin/page_blocks/forms/_price.html.erb +15 -0
- data/app/views/spree/admin/page_blocks/forms/_share.html.erb +5 -0
- data/app/views/spree/admin/page_blocks/forms/_subheading.html.erb +26 -0
- data/app/views/spree/admin/page_blocks/forms/_text.html.erb +21 -0
- data/app/views/spree/admin/page_blocks/forms/_title.html.erb +20 -0
- data/app/views/spree/admin/page_blocks/forms/_variant_picker.html.erb +10 -0
- data/app/views/spree/admin/page_blocks/forms/products/_buy_buttons.html.erb +10 -0
- data/app/views/spree/admin/page_blocks/forms/products/_description.html.erb +0 -0
- data/app/views/spree/admin/page_blocks/forms/products/_quantity_selector.html.erb +10 -0
- data/app/views/spree/admin/page_blocks/move_higher.turbo_stream.erb +4 -0
- data/app/views/spree/admin/page_blocks/move_lower.turbo_stream.erb +4 -0
- data/app/views/spree/admin/page_blocks/show.html.erb +1 -0
- data/app/views/spree/admin/page_blocks/update.turbo_stream.erb +6 -0
- data/app/views/spree/admin/page_builder/_add_block.html.erb +22 -0
- data/app/views/spree/admin/page_builder/_color_palette.html.erb +17 -0
- data/app/views/spree/admin/page_builder/_color_picker.html.erb +26 -0
- data/app/views/spree/admin/page_builder/_header.html.erb +113 -0
- data/app/views/spree/admin/page_builder/_labeled_range_input.html.erb +10 -0
- data/app/views/spree/admin/page_builder/_pages_dropdown.html.erb +17 -0
- data/app/views/spree/admin/page_builder/_range_input.html.erb +12 -0
- data/app/views/spree/admin/page_builder/_sidebar.html.erb +12 -0
- data/app/views/spree/admin/page_builder/_sidebar_block.html.erb +30 -0
- data/app/views/spree/admin/page_builder/_sidebar_colors.html.erb +86 -0
- data/app/views/spree/admin/page_builder/_sidebar_fonts.html.erb +85 -0
- data/app/views/spree/admin/page_builder/_sidebar_section.html.erb +44 -0
- data/app/views/spree/admin/page_builder/_sidebar_sections.html.erb +38 -0
- data/app/views/spree/admin/page_builder/_sidebar_sections_toolbar.html.erb +180 -0
- data/app/views/spree/admin/page_links/_form.html.erb +10 -0
- data/app/views/spree/admin/page_links/_linkable_type_dropdown.html.erb +15 -0
- data/app/views/spree/admin/page_links/_list.html.erb +36 -0
- data/app/views/spree/admin/page_links/_refresh_theme_preview.turbo_stream.erb +5 -0
- data/app/views/spree/admin/page_links/create.turbo_stream.erb +10 -0
- data/app/views/spree/admin/page_links/destroy.turbo_stream.erb +15 -0
- data/app/views/spree/admin/page_links/edit.html.erb +27 -0
- data/app/views/spree/admin/page_links/update.turbo_stream.erb +14 -0
- data/app/views/spree/admin/page_sections/_form_tab_buttons.html.erb +8 -0
- data/app/views/spree/admin/page_sections/create.turbo_stream.erb +11 -0
- data/app/views/spree/admin/page_sections/destroy.turbo_stream.erb +16 -0
- data/app/views/spree/admin/page_sections/edit.html.erb +39 -0
- data/app/views/spree/admin/page_sections/fields/_background_color.html.erb +6 -0
- data/app/views/spree/admin/page_sections/fields/_border_bottom_width.html.erb +5 -0
- data/app/views/spree/admin/page_sections/fields/_border_color.html.erb +7 -0
- data/app/views/spree/admin/page_sections/fields/_border_top_width.html.erb +6 -0
- data/app/views/spree/admin/page_sections/fields/_bottom_padding.html.erb +5 -0
- data/app/views/spree/admin/page_sections/fields/_button.html.erb +15 -0
- data/app/views/spree/admin/page_sections/fields/_header_layout.html.erb +26 -0
- data/app/views/spree/admin/page_sections/fields/_height.html.erb +6 -0
- data/app/views/spree/admin/page_sections/fields/_text_color.html.erb +7 -0
- data/app/views/spree/admin/page_sections/fields/_top_padding.html.erb +6 -0
- data/app/views/spree/admin/page_sections/forms/_announcement_bar.html.erb +5 -0
- data/app/views/spree/admin/page_sections/forms/_brand_story.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_breadcrumbs.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_collection_banner.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_custom_code.html.erb +4 -0
- data/app/views/spree/admin/page_sections/forms/_featured_posts.html.erb +33 -0
- data/app/views/spree/admin/page_sections/forms/_featured_product.html.erb +4 -0
- data/app/views/spree/admin/page_sections/forms/_featured_taxon.html.erb +59 -0
- data/app/views/spree/admin/page_sections/forms/_featured_taxons.html.erb +13 -0
- data/app/views/spree/admin/page_sections/forms/_footer.html.erb +20 -0
- data/app/views/spree/admin/page_sections/forms/_header.html.erb +12 -0
- data/app/views/spree/admin/page_sections/forms/_image_banner.html.erb +13 -0
- data/app/views/spree/admin/page_sections/forms/_image_with_text.html.erb +28 -0
- data/app/views/spree/admin/page_sections/forms/_main_password_footer.html.erb +9 -0
- data/app/views/spree/admin/page_sections/forms/_main_password_header.erb +13 -0
- data/app/views/spree/admin/page_sections/forms/_newsletter.html.erb +15 -0
- data/app/views/spree/admin/page_sections/forms/_page_title.html.erb +4 -0
- data/app/views/spree/admin/page_sections/forms/_post_details.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_post_grid.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_product_details.html.erb +1 -0
- data/app/views/spree/admin/page_sections/forms/_product_grid.html.erb +1 -0
- data/app/views/spree/admin/page_sections/forms/_related_products.html.erb +32 -0
- data/app/views/spree/admin/page_sections/forms/_rich_text.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_taxon_banner.html.erb +0 -0
- data/app/views/spree/admin/page_sections/forms/_taxon_grid.html.erb +5 -0
- data/app/views/spree/admin/page_sections/forms/_video.html.erb +15 -0
- data/app/views/spree/admin/page_sections/move_higher.turbo_stream.erb +7 -0
- data/app/views/spree/admin/page_sections/move_lower.turbo_stream.erb +7 -0
- data/app/views/spree/admin/page_sections/new.html.erb +26 -0
- data/app/views/spree/admin/page_sections/remove_attachment.turbo_stream.erb +4 -0
- data/app/views/spree/admin/page_sections/restore_design_settings_to_defaults.turbo_stream.erb +4 -0
- data/app/views/spree/admin/page_sections/show.html.erb +1 -0
- data/app/views/spree/admin/page_sections/update.turbo_stream.erb +10 -0
- data/app/views/spree/admin/pages/_form.html.erb +20 -0
- data/app/views/spree/admin/pages/_table_header.html.erb +6 -0
- data/app/views/spree/admin/pages/_table_row.html.erb +28 -0
- data/app/views/spree/admin/pages/edit.html.erb +13 -0
- data/app/views/spree/admin/pages/filters.html.erb +7 -0
- data/app/views/spree/admin/pages/index.html.erb +11 -0
- data/app/views/spree/admin/pages/new.html.erb +1 -0
- data/app/views/spree/admin/storefront/edit.html.erb +89 -0
- data/app/views/spree/admin/themes/_theme.html.erb +56 -0
- data/app/views/spree/admin/themes/_theme_preview_image.html.erb +17 -0
- data/app/views/spree/admin/themes/edit.html.erb +14 -0
- data/app/views/spree/admin/themes/index.html.erb +80 -0
- data/app/views/spree/admin/themes/update.turbo_stream.erb +1 -0
- data/config/initializers/spree_admin_navigation.rb +46 -0
- data/config/initializers/spree_admin_partials.rb +3 -0
- data/config/locales/en.yml +42 -0
- data/config/routes.rb +43 -0
- data/db/migrate/20250120094216_create_page_builder_models.rb +78 -0
- data/db/migrate/20250305121352_remove_page_builder_indices.rb +11 -0
- data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +7 -0
- data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +10 -0
- data/lib/generators/spree/page_builder/install/install_generator.rb +23 -0
- data/lib/spree/page_builder/engine.rb +185 -0
- data/lib/spree/page_builder/testing_support/factories/page_block_factory.rb +22 -0
- data/lib/spree/page_builder/testing_support/factories/page_factory.rb +33 -0
- data/lib/spree/page_builder/testing_support/factories/page_link_factory.rb +7 -0
- data/lib/spree/page_builder/testing_support/factories/page_section_factory.rb +27 -0
- data/lib/spree/page_builder/testing_support/factories/theme_factory.rb +14 -0
- data/lib/spree/page_builder/testing_support/factories.rb +3 -0
- data/lib/spree/page_builder.rb +4 -0
- data/lib/spree_page_builder.rb +1 -0
- metadata +288 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
module Products
|
|
4
|
+
class BuyButtons < Spree::PageBlock
|
|
5
|
+
TOP_PADDING_DEFAULT = 0
|
|
6
|
+
BOTTOM_PADDING_DEFAULT = 20
|
|
7
|
+
|
|
8
|
+
preference :text_color, :string
|
|
9
|
+
|
|
10
|
+
def icon_name
|
|
11
|
+
'shopping-cart-plus'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def display_name
|
|
15
|
+
Spree.t('page_blocks.products.buy_buttons.display_name')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def form_partial_name
|
|
19
|
+
'products/buy_buttons'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
module Products
|
|
4
|
+
class Description < Spree::PageBlocks::Text
|
|
5
|
+
TOP_PADDING_DEFAULT = 0
|
|
6
|
+
BOTTOM_PADDING_DEFAULT = 20
|
|
7
|
+
|
|
8
|
+
def form_partial_name
|
|
9
|
+
'products/description'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def display_name
|
|
13
|
+
I18n.t('activerecord.attributes.spree/product.description')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
module Products
|
|
4
|
+
class Price < Spree::PageBlocks::Text
|
|
5
|
+
TOP_PADDING_DEFAULT = 16
|
|
6
|
+
BOTTOM_PADDING_DEFAULT = 16
|
|
7
|
+
|
|
8
|
+
def icon_name
|
|
9
|
+
'cash'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def display_name
|
|
13
|
+
Spree.t(:price)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
module Products
|
|
4
|
+
class QuantitySelector < Spree::PageBlock
|
|
5
|
+
TOP_PADDING_DEFAULT = 0
|
|
6
|
+
BOTTOM_PADDING_DEFAULT = 20
|
|
7
|
+
|
|
8
|
+
preference :text_color, :string
|
|
9
|
+
|
|
10
|
+
def icon_name
|
|
11
|
+
'plus-minus'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def form_partial_name
|
|
15
|
+
'products/quantity_selector'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
module Products
|
|
4
|
+
class Title < Spree::PageBlocks::Heading
|
|
5
|
+
TOP_PADDING_DEFAULT = 16
|
|
6
|
+
|
|
7
|
+
preference :text_color, :string
|
|
8
|
+
|
|
9
|
+
def display_name
|
|
10
|
+
I18n.t('activerecord.attributes.spree/product.name')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def icon_name
|
|
14
|
+
'h-1'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
class Subheading < Spree::PageBlock
|
|
4
|
+
SIZE_DEFAULT = 'small'
|
|
5
|
+
|
|
6
|
+
preference :text_color, :string
|
|
7
|
+
|
|
8
|
+
def icon_name
|
|
9
|
+
'h-2'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def display_name
|
|
13
|
+
text.to_plain_text.truncate(30)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBlocks
|
|
3
|
+
class Text < Spree::PageBlock
|
|
4
|
+
preference :text_color, :string
|
|
5
|
+
preference :background_color, :string
|
|
6
|
+
|
|
7
|
+
def icon_name
|
|
8
|
+
'align-justified'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def display_name
|
|
12
|
+
text.to_plain_text.truncate(30)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBuilder
|
|
3
|
+
module PolicyDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.include Spree::Linkable
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def page_builder_url
|
|
9
|
+
return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:policy_path)
|
|
10
|
+
|
|
11
|
+
Spree::Core::Engine.routes.url_helpers.policy_path(self)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Spree::Policy.prepend(Spree::PageBuilder::PolicyDecorator)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBuilder
|
|
3
|
+
module PostDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.include Spree::Linkable
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def page_builder_url
|
|
9
|
+
return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:post_path)
|
|
10
|
+
|
|
11
|
+
Spree::Core::Engine.routes.url_helpers.post_path(self)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Spree::Post.prepend(Spree::PageBuilder::PostDecorator)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBuilder
|
|
3
|
+
module ProductDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.include Spree::Linkable
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def page_builder_url
|
|
9
|
+
return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:product_path)
|
|
10
|
+
|
|
11
|
+
Spree::Core::Engine.routes.url_helpers.product_path(self)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Spree::Product.prepend(Spree::PageBuilder::ProductDecorator)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBuilder
|
|
3
|
+
module StoreDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.include Spree::HasPageLinks
|
|
6
|
+
|
|
7
|
+
# Page Builder associations
|
|
8
|
+
base.has_many :themes, -> { without_previews }, class_name: 'Spree::Theme', dependent: :destroy, inverse_of: :store
|
|
9
|
+
base.has_many :theme_previews,
|
|
10
|
+
-> { only_previews },
|
|
11
|
+
class_name: 'Spree::Theme',
|
|
12
|
+
through: :themes,
|
|
13
|
+
source: :previews,
|
|
14
|
+
inverse_of: :store,
|
|
15
|
+
dependent: :destroy
|
|
16
|
+
base.has_one :default_theme, -> { without_previews.where(default: true) }, class_name: 'Spree::Theme', inverse_of: :store
|
|
17
|
+
base.alias_method :theme, :default_theme
|
|
18
|
+
base.has_many :theme_pages, class_name: 'Spree::Page', through: :themes, source: :pages
|
|
19
|
+
base.has_many :theme_page_previews, class_name: 'Spree::Page', through: :theme_pages, source: :previews
|
|
20
|
+
base.has_many :pages, -> { without_previews.custom }, class_name: 'Spree::Pages::Custom', dependent: :destroy, as: :pageable
|
|
21
|
+
base.has_many :page_previews, class_name: 'Spree::Pages::Custom', through: :pages, as: :pageable, source: :previews
|
|
22
|
+
|
|
23
|
+
base.after_create :create_default_theme
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def create_default_theme
|
|
29
|
+
themes.find_or_initialize_by(default: true) do |theme|
|
|
30
|
+
theme.name = Spree.t(:default_theme_name)
|
|
31
|
+
theme.save!
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def create_default_policies
|
|
36
|
+
super
|
|
37
|
+
|
|
38
|
+
policies.each do |policy|
|
|
39
|
+
links.find_or_create_by(linkable: policy)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
Spree::Store.prepend(Spree::PageBuilder::StoreDecorator)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageBuilder
|
|
3
|
+
module TaxonDecorator
|
|
4
|
+
def self.prepended(base)
|
|
5
|
+
base.include Spree::Linkable
|
|
6
|
+
|
|
7
|
+
base.class_eval do
|
|
8
|
+
after_commit :touch_featured_sections, on: [:update]
|
|
9
|
+
after_touch :touch_featured_sections
|
|
10
|
+
after_destroy :remove_featured_sections, if: -> { featured? }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def page_builder_url
|
|
15
|
+
return unless Spree::Core::Engine.routes.url_helpers.respond_to?(:nested_taxons_path)
|
|
16
|
+
|
|
17
|
+
Spree::Core::Engine.routes.url_helpers.nested_taxons_path(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def page_builder_image
|
|
21
|
+
square_image.presence || image
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def featured?
|
|
25
|
+
featured_sections.any?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def featured_sections
|
|
29
|
+
@featured_sections ||= Spree::PageSections::FeaturedTaxon.published.by_taxon_id(id)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def touch_featured_sections
|
|
35
|
+
Spree::Taxons::TouchFeaturedSections.call(taxon_ids: [id])
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def remove_featured_sections
|
|
39
|
+
featured_sections.destroy_all
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Spree::Taxon.prepend(Spree::PageBuilder::TaxonDecorator)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PageLink < Spree.base_class
|
|
3
|
+
#
|
|
4
|
+
# Magic methods
|
|
5
|
+
#
|
|
6
|
+
acts_as_list scope: :parent
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Associations
|
|
10
|
+
#
|
|
11
|
+
belongs_to :parent, polymorphic: true, touch: true, counter_cache: true # Block or Section
|
|
12
|
+
belongs_to :linkable, polymorphic: true, optional: true # Page, Product, etc.
|
|
13
|
+
has_one :theme, through: :parent
|
|
14
|
+
|
|
15
|
+
#
|
|
16
|
+
# Validations
|
|
17
|
+
#
|
|
18
|
+
validates :label, :parent, presence: true
|
|
19
|
+
validates :url, presence: true, unless: :linkable
|
|
20
|
+
|
|
21
|
+
#
|
|
22
|
+
# Callbacks
|
|
23
|
+
#
|
|
24
|
+
after_destroy :notify_parent
|
|
25
|
+
before_validation :set_label, on: [:create, :update]
|
|
26
|
+
|
|
27
|
+
def linkable_url
|
|
28
|
+
@linkable_url ||= linkable&.page_builder_url || formatted_url
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def formatted_url
|
|
32
|
+
return if url.blank?
|
|
33
|
+
return url if %w[mailto: tel:].any? { |protocol| url.start_with?(protocol) }
|
|
34
|
+
|
|
35
|
+
@formatted_url ||= url.match(/http:\/\/|https:\/\//) ? url : "http://#{url}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def notify_parent
|
|
41
|
+
return unless parent.present?
|
|
42
|
+
return unless parent.respond_to?(:link_destroyed)
|
|
43
|
+
|
|
44
|
+
parent.link_destroyed(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def set_label
|
|
48
|
+
return if label.present? && new_record?
|
|
49
|
+
return unless linkable_id_changed?
|
|
50
|
+
|
|
51
|
+
if linkable.respond_to?(:title)
|
|
52
|
+
self.label = linkable.title
|
|
53
|
+
elsif linkable.respond_to?(:display_name)
|
|
54
|
+
self.label = linkable.display_name
|
|
55
|
+
elsif linkable.respond_to?(:name)
|
|
56
|
+
self.label = linkable.name
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PageSection < Spree.base_class
|
|
3
|
+
include Spree::HasPageLinks
|
|
4
|
+
#
|
|
5
|
+
# Magic methods
|
|
6
|
+
#
|
|
7
|
+
acts_as_paranoid
|
|
8
|
+
acts_as_list scope: [:pageable_type, :pageable_id, { deleted_at: nil }]
|
|
9
|
+
|
|
10
|
+
#
|
|
11
|
+
# Associations
|
|
12
|
+
#
|
|
13
|
+
belongs_to :pageable, polymorphic: true, touch: true # this can be either Page or Theme
|
|
14
|
+
has_many :blocks, lambda {
|
|
15
|
+
order(position: :asc)
|
|
16
|
+
}, class_name: 'Spree::PageBlock', dependent: :destroy, inverse_of: :section, foreign_key: :section_id
|
|
17
|
+
alias page_blocks blocks
|
|
18
|
+
delegate :store, to: :pageable
|
|
19
|
+
|
|
20
|
+
#
|
|
21
|
+
# Attachments
|
|
22
|
+
#
|
|
23
|
+
has_one_attached :asset, service: Spree.public_storage_service_name
|
|
24
|
+
alias image asset
|
|
25
|
+
|
|
26
|
+
#
|
|
27
|
+
# Rich Text
|
|
28
|
+
#
|
|
29
|
+
has_rich_text :text
|
|
30
|
+
has_rich_text :description
|
|
31
|
+
|
|
32
|
+
#
|
|
33
|
+
# Validations
|
|
34
|
+
#
|
|
35
|
+
validates :name, :pageable, presence: true
|
|
36
|
+
validates :asset, content_type: Rails.application.config.active_storage.web_image_content_types
|
|
37
|
+
|
|
38
|
+
#
|
|
39
|
+
# Callbacks
|
|
40
|
+
#
|
|
41
|
+
before_validation :set_name, on: :create
|
|
42
|
+
after_create :create_blocks, unless: :do_not_create_blocks
|
|
43
|
+
|
|
44
|
+
#
|
|
45
|
+
# Scopes
|
|
46
|
+
#
|
|
47
|
+
scope :published, -> { where(pageable_type: 'Spree::Page') }
|
|
48
|
+
scope :related_products, -> { where(type: 'Spree::PageSections::RelatedProducts') }
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# Preferences
|
|
52
|
+
#
|
|
53
|
+
TEXT_COLOR_DEFAULT = nil
|
|
54
|
+
BACKGROUND_COLOR_DEFAULT = nil
|
|
55
|
+
BORDER_COLOR_DEFAULT = nil
|
|
56
|
+
TOP_PADDING_DEFAULT = 40
|
|
57
|
+
BOTTOM_PADDING_DEFAULT = 40
|
|
58
|
+
TOP_BORDER_WIDTH_DEFAULT = 1
|
|
59
|
+
BOTTOM_BORDER_WIDTH_DEFAULT = 0
|
|
60
|
+
|
|
61
|
+
preference :text_color, :string, default: -> { self.class::TEXT_COLOR_DEFAULT if defined?(self.class::TEXT_COLOR_DEFAULT) }
|
|
62
|
+
preference :background_color, :string, default: -> { self.class::BACKGROUND_COLOR_DEFAULT if defined?(self.class::BACKGROUND_COLOR_DEFAULT) }
|
|
63
|
+
preference :border_color, :string, default: -> { self.class::BORDER_COLOR_DEFAULT if defined?(self.class::BORDER_COLOR_DEFAULT) }
|
|
64
|
+
preference :top_padding, :integer, default: -> { self.class::TOP_PADDING_DEFAULT if defined?(self.class::TOP_PADDING_DEFAULT) }
|
|
65
|
+
preference :bottom_padding, :integer, default: -> { self.class::BOTTOM_PADDING_DEFAULT if defined?(self.class::BOTTOM_PADDING_DEFAULT) }
|
|
66
|
+
preference :top_border_width, :integer, default: -> { self.class::TOP_BORDER_WIDTH_DEFAULT if defined?(self.class::TOP_BORDER_WIDTH_DEFAULT) }
|
|
67
|
+
preference :bottom_border_width, :integer, default: -> { self.class::BOTTOM_BORDER_WIDTH_DEFAULT if defined?(self.class::BOTTOM_BORDER_WIDTH_DEFAULT) }
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
# Virtual attributes
|
|
71
|
+
#
|
|
72
|
+
attribute :do_not_create_blocks, :boolean, default: false
|
|
73
|
+
attr_accessor :default_blocks
|
|
74
|
+
|
|
75
|
+
def to_partial_path
|
|
76
|
+
"spree/page_sections/#{type.to_s.demodulize.underscore}"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# this should be overridden in subclasses
|
|
80
|
+
def default_blocks
|
|
81
|
+
@default_blocks.presence || []
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def blocks_available?
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def available_blocks_to_add
|
|
89
|
+
[]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def can_sort_blocks?
|
|
93
|
+
false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# this should be overridden in subclasses
|
|
97
|
+
# content - can be managed by the user
|
|
98
|
+
# system - main parts of the page eg. product details, login form
|
|
99
|
+
# header - header section
|
|
100
|
+
# footer - footer section
|
|
101
|
+
def self.role
|
|
102
|
+
'content'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def role
|
|
106
|
+
self.class.role
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def theme
|
|
110
|
+
@theme ||= if pageable.is_a?(Spree::Theme)
|
|
111
|
+
pageable
|
|
112
|
+
else
|
|
113
|
+
pageable.theme
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def icon_name
|
|
118
|
+
'text-caption'
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def can_be_deleted?
|
|
122
|
+
role == 'content'
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def can_be_sorted?
|
|
126
|
+
true
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def lazy?
|
|
130
|
+
false
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def lazy_path(variables)
|
|
134
|
+
url_options = variables[:url_options] || {}
|
|
135
|
+
|
|
136
|
+
Spree::Core::Engine.routes.url_helpers.page_section_path(self, **url_options)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def rich_text_fields
|
|
140
|
+
self.class.rich_text_association_names.map { |rt| rt.to_s.sub('rich_text_', '') }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def copy_rich_text_fields_from(other)
|
|
144
|
+
rich_text_fields.each do |field|
|
|
145
|
+
send(field).body = other.send(field).body
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def deep_clone(target)
|
|
150
|
+
new_section = type.constantize.new(
|
|
151
|
+
preferences: preferences,
|
|
152
|
+
position: position,
|
|
153
|
+
pageable: target
|
|
154
|
+
)
|
|
155
|
+
new_section.copy_rich_text_fields_from(self)
|
|
156
|
+
new_section.do_not_create_blocks = true
|
|
157
|
+
new_section.do_not_create_links = true
|
|
158
|
+
new_section.save!
|
|
159
|
+
|
|
160
|
+
new_section.asset.attach(asset.blob) if asset.attached?
|
|
161
|
+
|
|
162
|
+
blocks.includes(:links, asset_attachment: :blob).each do |block|
|
|
163
|
+
new_block = block.type.constantize.new(
|
|
164
|
+
preferences: block.preferences,
|
|
165
|
+
position: block.position,
|
|
166
|
+
section: new_section
|
|
167
|
+
)
|
|
168
|
+
new_block.do_not_create_links = true
|
|
169
|
+
new_block.text = block.text if block.respond_to?(:text) # we need to copy ActionText content
|
|
170
|
+
new_block.save!
|
|
171
|
+
|
|
172
|
+
new_block.asset.attach(block.asset.blob) if block.asset.attached?
|
|
173
|
+
|
|
174
|
+
deep_clone_links(block, new_block)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
deep_clone_links(self, new_section)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def deep_clone_links(section_or_block, new_section_or_block)
|
|
181
|
+
section_or_block.links.each do |link|
|
|
182
|
+
new_link = link.dup
|
|
183
|
+
new_link.parent = new_section_or_block
|
|
184
|
+
new_link.save!
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Turns out #dup doesn't not copy rich text content to the new record.
|
|
189
|
+
# https://github.com/rails/rails/issues/36683
|
|
190
|
+
def dup
|
|
191
|
+
super.tap do |object|
|
|
192
|
+
object.copy_rich_text_fields_from(self)
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def restore_design_settings_to_defaults
|
|
197
|
+
restore_preferences_for(design_settings_to_restore)
|
|
198
|
+
save
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def display_name
|
|
202
|
+
name
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
private
|
|
206
|
+
|
|
207
|
+
def design_settings_to_restore
|
|
208
|
+
[:text_color, :background_color, :border_color, :top_padding, :bottom_padding, :top_border_width, :bottom_border_width]
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def create_blocks
|
|
212
|
+
default_blocks.each do |block|
|
|
213
|
+
block.section = self
|
|
214
|
+
block.save!
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def set_name
|
|
219
|
+
self.name ||= type.to_s.demodulize.titleize
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageSections
|
|
3
|
+
class AnnouncementBar < Spree::PageSection
|
|
4
|
+
before_validation :set_default_text, on: :create
|
|
5
|
+
|
|
6
|
+
BACKGROUND_COLOR_DEFAULT = '#F5F5F4'
|
|
7
|
+
TOP_PADDING_DEFAULT = 8
|
|
8
|
+
BOTTOM_PADDING_DEFAULT = 8
|
|
9
|
+
TOP_BORDER_WIDTH_DEFAULT = 0
|
|
10
|
+
|
|
11
|
+
def self.role
|
|
12
|
+
'header'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def icon_name
|
|
16
|
+
'speakerphone'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def set_default_text
|
|
22
|
+
return unless text.blank?
|
|
23
|
+
|
|
24
|
+
self.text = Spree.t('page_sections.announcement_bar.default_text')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module PageSections
|
|
3
|
+
class CollectionBanner < Spree::PageSection
|
|
4
|
+
TOP_PADDING_DEFAULT = 20
|
|
5
|
+
BOTTOM_PADDING_DEFAULT = 20
|
|
6
|
+
TOP_BORDER_WIDTH_DEFAULT = 0
|
|
7
|
+
BOTTOM_BORDER_WIDTH_DEFAULT = 1
|
|
8
|
+
|
|
9
|
+
def icon_name
|
|
10
|
+
'heading'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.role
|
|
14
|
+
'system'
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|