spina-blocks 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -12
- data/Rakefile +20 -2
- data/app/assets/javascripts/spina/controllers/block_collection_controller.js +10 -3
- data/app/controllers/spina/blocks/admin/blocks_controller.rb +29 -19
- data/app/controllers/spina/blocks/admin/categories_controller.rb +6 -6
- data/app/controllers/spina/blocks/admin/page_blocks_controller.rb +9 -9
- data/app/helpers/spina/blocks/blocks_helper.rb +3 -3
- data/app/models/spina/blocks/account_extension.rb +27 -0
- data/app/models/spina/blocks/block.rb +5 -5
- data/app/models/spina/blocks/category.rb +1 -1
- data/app/models/spina/blocks/page_block.rb +2 -2
- data/app/models/spina/blocks/page_extension.rb +14 -0
- data/app/models/spina/blocks/search_extension.rb +15 -0
- data/app/views/spina/admin/parts/block_collections/_form.html.erb +8 -1
- data/app/views/spina/admin/parts/block_references/_form.html.erb +1 -1
- data/app/views/spina/admin/pro/search/results/_block.html.erb +23 -0
- data/app/views/spina/blocks/admin/blocks/_form_block_content.html.erb +2 -2
- data/app/views/spina/blocks/admin/blocks/_modal_form.html.erb +61 -0
- data/app/views/spina/blocks/admin/blocks/_new_block_form.html.erb +1 -1
- data/app/views/spina/blocks/admin/blocks/edit_modal.html.erb +3 -0
- data/app/views/spina/blocks/admin/blocks/index.html.erb +1 -1
- data/app/views/spina/blocks/admin/page_blocks/index.html.erb +3 -3
- data/config/locales/en.yml +1 -0
- data/config/routes.rb +4 -3
- data/db/migrate/1_create_spina_blocks_categories.rb +36 -0
- data/db/migrate/2_create_spina_blocks_blocks.rb +39 -0
- data/db/migrate/3_create_spina_blocks_page_blocks.rb +36 -0
- data/db/migrate/5_rename_title_to_name_in_spina_blocks_blocks.rb +18 -0
- data/lib/generators/spina/blocks/install_generator.rb +17 -0
- data/lib/spina/blocks/engine.rb +25 -25
- data/lib/spina/blocks/version.rb +1 -1
- data/lib/spina-blocks.rb +3 -3
- metadata +71 -13
- data/app/overrides/spina/account_override.rb +0 -19
- data/app/overrides/spina/page_override.rb +0 -6
- data/db/migrate/20250101000001_create_spina_blocks_categories.rb +0 -31
- data/db/migrate/20250101000002_create_spina_blocks_blocks.rb +0 -35
- data/db/migrate/20250101000003_create_spina_blocks_page_blocks.rb +0 -31
- data/db/migrate/20250101000004_remove_name_from_spina_blocks_blocks.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 76633f69018375e8d330eee4b062677dd4dc10bd380ec2e7b08f3900fd448540
|
|
4
|
+
data.tar.gz: dd386042f84a6554db3eb15200011e4a953f3559054a44f05caa0e052b10fa3b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 19b5d1d156f66e85fb34aa6d2240d0f23cd0ffdb973601e2a4a1f3eca8c9058d39332c33bc986b52a69519b7eee22c3e39c6b713c6aadc9ccb5bdb93abaf5e3e
|
|
7
|
+
data.tar.gz: 7442845e4527e7dcfc8e429771b957638b89c053ae6b20767393756429000908af24337853d5899b9af5d06298efd492f53316a1db91712e85d9321f690647f2
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Spina Blocks
|
|
2
2
|
|
|
3
|
+
> ⚠️ **Warning**: This plugin is not production-ready and may cause issues. Use at your own risk.
|
|
4
|
+
|
|
3
5
|
A plugin for [Spina CMS](https://www.spinacms.com) that adds reusable block components. Blocks are independent content units with their own templates and fields that can be assembled into pages.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
@@ -14,9 +16,15 @@ Run:
|
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
18
|
bundle install
|
|
17
|
-
rails
|
|
19
|
+
rails generate spina:blocks:install
|
|
18
20
|
```
|
|
19
21
|
|
|
22
|
+
This copies the plugin's migrations into your app and runs `db:migrate`.
|
|
23
|
+
|
|
24
|
+
## Upgrading
|
|
25
|
+
|
|
26
|
+
If you are upgrading from a version that used timestamp migrations (`20250101000001`–`20250101000004`), the new migrations will detect the old version numbers in your `schema_migrations` table, clean them up automatically, and skip any tables that already exist. No manual intervention is needed.
|
|
27
|
+
|
|
20
28
|
## Configuration
|
|
21
29
|
|
|
22
30
|
### Theme setup
|
|
@@ -125,10 +133,10 @@ Then in your template:
|
|
|
125
133
|
|
|
126
134
|
## Models
|
|
127
135
|
|
|
128
|
-
| Model
|
|
129
|
-
|
|
130
|
-
| `Spina::Blocks::Block`
|
|
131
|
-
| `Spina::Blocks::Category`
|
|
136
|
+
| Model | Description |
|
|
137
|
+
| -------------------------- | -------------------------------------------------- |
|
|
138
|
+
| `Spina::Blocks::Block` | Reusable content block with template and parts |
|
|
139
|
+
| `Spina::Blocks::Category` | Block category for organizing the library |
|
|
132
140
|
| `Spina::Blocks::PageBlock` | Join model linking blocks to pages (with position) |
|
|
133
141
|
|
|
134
142
|
## Admin interface
|
|
@@ -142,13 +150,13 @@ The plugin adds:
|
|
|
142
150
|
|
|
143
151
|
## Helper methods
|
|
144
152
|
|
|
145
|
-
| Helper
|
|
146
|
-
|
|
147
|
-
| `render_blocks(page)`
|
|
148
|
-
| `render_block(block)`
|
|
149
|
-
| `block_content(block, :part_name)`
|
|
150
|
-
| `block_has_content?(block, :part_name)` | Check if a block has content
|
|
153
|
+
| Helper | Description |
|
|
154
|
+
| --------------------------------------- | --------------------------------------------------- |
|
|
155
|
+
| `render_blocks(page)` | Render all blocks attached to a page via PageBlocks |
|
|
156
|
+
| `render_block(block)` | Render a single block using its template partial |
|
|
157
|
+
| `block_content(block, :part_name)` | Access a block's content |
|
|
158
|
+
| `block_has_content?(block, :part_name)` | Check if a block has content |
|
|
151
159
|
|
|
152
160
|
## License
|
|
153
161
|
|
|
154
|
-
MIT
|
|
162
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
|
|
5
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
|
6
|
+
load "rails/tasks/engine.rake"
|
|
7
|
+
|
|
8
|
+
# The engine.rake mechanism auto-appends db/migrate/ from ENGINE_ROOT to the
|
|
9
|
+
# migration paths. This causes ordering issues because engine migrations use
|
|
10
|
+
# sequential versions (1, 2, 3, 4) that sort before the timestamped copies in
|
|
11
|
+
# spec/dummy/db/migrate/. Since we copy engine migrations to the dummy app via
|
|
12
|
+
# railties:install:migrations, we remove the engine source path to avoid
|
|
13
|
+
# duplicates and ordering problems.
|
|
14
|
+
Rake::Task["app:db:load_config"].enhance do
|
|
15
|
+
engine_migrate = File.expand_path("db/migrate", ENGINE_ROOT)
|
|
16
|
+
ActiveRecord::Tasks::DatabaseTasks.migrations_paths.delete(engine_migrate)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
require "rspec/core/rake_task"
|
|
20
|
+
RSpec::Core::RakeTask.new(spec: "app:db:test:prepare")
|
|
21
|
+
|
|
22
|
+
task default: :spec
|
|
@@ -18,8 +18,9 @@ export default class extends Controller {
|
|
|
18
18
|
|
|
19
19
|
static get values() {
|
|
20
20
|
return {
|
|
21
|
-
blocks: Array, // [{id,
|
|
21
|
+
blocks: Array, // [{id, name, templateName, templateTitle}]
|
|
22
22
|
selectedIds: Array, // [id, id, ...]
|
|
23
|
+
editUrl: String, // base edit_modal URL with __ID__ placeholder
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -150,7 +151,7 @@ export default class extends Controller {
|
|
|
150
151
|
this.dropdownOptionTemplateTarget.content.cloneNode(true);
|
|
151
152
|
const button = option.querySelector("button");
|
|
152
153
|
button.dataset.blockId = block.id;
|
|
153
|
-
option.querySelector("[data-role='title']").textContent = block.
|
|
154
|
+
option.querySelector("[data-role='title']").textContent = block.name;
|
|
154
155
|
this.dropdownTarget.appendChild(option);
|
|
155
156
|
});
|
|
156
157
|
});
|
|
@@ -169,12 +170,18 @@ export default class extends Controller {
|
|
|
169
170
|
const fragment = this.listItemTemplateTarget.content.cloneNode(true);
|
|
170
171
|
const root = fragment.querySelector("[data-block-id]");
|
|
171
172
|
root.dataset.blockId = block.id;
|
|
172
|
-
fragment.querySelector("[data-role='title']").textContent = block.
|
|
173
|
+
fragment.querySelector("[data-role='title']").textContent = block.name;
|
|
173
174
|
fragment.querySelector("[data-role='template-label']").textContent =
|
|
174
175
|
"(" + (block.templateTitle || block.templateName) + ")";
|
|
175
176
|
fragment.querySelector(
|
|
176
177
|
"[data-action='block-collection#remove']",
|
|
177
178
|
).dataset.blockId = block.id;
|
|
179
|
+
|
|
180
|
+
const editLink = fragment.querySelector("[data-role='edit-link']");
|
|
181
|
+
if (editLink && this.hasEditUrlValue && this.editUrlValue) {
|
|
182
|
+
editLink.href = this.editUrlValue.replace("__ID__", block.id);
|
|
183
|
+
}
|
|
184
|
+
|
|
178
185
|
return fragment;
|
|
179
186
|
}
|
|
180
187
|
|
|
@@ -7,13 +7,13 @@ module Spina
|
|
|
7
7
|
admin_section :content
|
|
8
8
|
|
|
9
9
|
before_action :set_locale
|
|
10
|
-
before_action :set_block, only:
|
|
11
|
-
before_action :set_tabs, only:
|
|
10
|
+
before_action :set_block, only: [:edit, :edit_content, :edit_modal, :update, :destroy]
|
|
11
|
+
before_action :set_tabs, only: [:edit, :edit_modal, :update]
|
|
12
12
|
|
|
13
13
|
helper ::Spina::Admin::PagesHelper
|
|
14
14
|
|
|
15
15
|
def index
|
|
16
|
-
add_breadcrumb
|
|
16
|
+
add_breadcrumb(I18n.t("spina.blocks.title"), spina.blocks_admin_blocks_path)
|
|
17
17
|
|
|
18
18
|
@block_templates = current_theme.try(:block_templates) || []
|
|
19
19
|
|
|
@@ -34,19 +34,19 @@ module Spina
|
|
|
34
34
|
def create
|
|
35
35
|
@block = Spina::Blocks::Block.new(block_params)
|
|
36
36
|
if @block.save
|
|
37
|
-
redirect_to
|
|
37
|
+
redirect_to(spina.edit_blocks_admin_block_url(@block))
|
|
38
38
|
else
|
|
39
39
|
@block_templates = current_theme.try(:block_templates) || []
|
|
40
|
-
render
|
|
40
|
+
render(turbo_stream: turbo_stream.update(
|
|
41
41
|
helpers.dom_id(@block, :new_block_form),
|
|
42
|
-
partial:
|
|
43
|
-
)
|
|
42
|
+
partial: "new_block_form",
|
|
43
|
+
))
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
def edit
|
|
48
|
-
add_breadcrumb
|
|
49
|
-
add_breadcrumb
|
|
48
|
+
add_breadcrumb(I18n.t("spina.blocks.title"), spina.blocks_admin_blocks_path, class: "text-gray-400")
|
|
49
|
+
add_breadcrumb(@block.name)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def edit_content
|
|
@@ -55,17 +55,27 @@ module Spina
|
|
|
55
55
|
end&.dig(:parts) || []
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
+
def edit_modal
|
|
59
|
+
end
|
|
60
|
+
|
|
58
61
|
def update
|
|
59
62
|
Mobility.locale = @locale
|
|
60
63
|
if @block.update(block_params)
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
if params[:modal]
|
|
65
|
+
render(turbo_stream: turbo_stream.update("modal", ""))
|
|
66
|
+
else
|
|
67
|
+
flash[:success] = I18n.t("spina.blocks.saved")
|
|
68
|
+
redirect_to(spina.edit_blocks_admin_block_url(@block, params: { locale: @locale }))
|
|
69
|
+
end
|
|
70
|
+
elsif params[:modal]
|
|
71
|
+
flash.now[:error] = I18n.t("spina.blocks.couldnt_be_saved")
|
|
72
|
+
render(:edit_modal, status: :unprocessable_entity)
|
|
63
73
|
else
|
|
64
|
-
add_breadcrumb
|
|
74
|
+
add_breadcrumb(I18n.t("spina.blocks.title"), spina.blocks_admin_blocks_path, class: "text-gray-400")
|
|
65
75
|
Mobility.locale = I18n.locale
|
|
66
|
-
add_breadcrumb
|
|
67
|
-
flash.now[:error] = I18n.t(
|
|
68
|
-
render
|
|
76
|
+
add_breadcrumb(@block.name)
|
|
77
|
+
flash.now[:error] = I18n.t("spina.blocks.couldnt_be_saved")
|
|
78
|
+
render(:edit, status: :unprocessable_entity)
|
|
69
79
|
end
|
|
70
80
|
end
|
|
71
81
|
|
|
@@ -74,14 +84,14 @@ module Spina
|
|
|
74
84
|
Spina::Blocks::Block.where(id: id).update_all(position: index + 1)
|
|
75
85
|
end
|
|
76
86
|
|
|
77
|
-
flash.now[:info] = I18n.t(
|
|
87
|
+
flash.now[:info] = I18n.t("spina.blocks.sorting_saved")
|
|
78
88
|
render_flash
|
|
79
89
|
end
|
|
80
90
|
|
|
81
91
|
def destroy
|
|
82
|
-
flash[:info] = I18n.t(
|
|
92
|
+
flash[:info] = I18n.t("spina.blocks.deleted")
|
|
83
93
|
@block.destroy
|
|
84
|
-
redirect_to
|
|
94
|
+
redirect_to(spina.blocks_admin_blocks_url)
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
private
|
|
@@ -95,7 +105,7 @@ module Spina
|
|
|
95
105
|
end
|
|
96
106
|
|
|
97
107
|
def set_tabs
|
|
98
|
-
@tabs =
|
|
108
|
+
@tabs = ["block_content", "block_settings"]
|
|
99
109
|
end
|
|
100
110
|
|
|
101
111
|
def block_params
|
|
@@ -12,18 +12,18 @@ module Spina
|
|
|
12
12
|
|
|
13
13
|
def edit
|
|
14
14
|
@category = Spina::Blocks::Category.find(params[:id])
|
|
15
|
-
add_breadcrumb
|
|
16
|
-
add_breadcrumb
|
|
15
|
+
add_breadcrumb(I18n.t("spina.blocks.title"), spina.blocks_admin_blocks_path, class: "text-gray-400")
|
|
16
|
+
add_breadcrumb(@category.label)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def update
|
|
20
20
|
@category = Spina::Blocks::Category.find(params[:id])
|
|
21
21
|
if @category.update(category_params)
|
|
22
|
-
flash[:success] = I18n.t(
|
|
23
|
-
redirect_to
|
|
22
|
+
flash[:success] = I18n.t("spina.block_categories.saved")
|
|
23
|
+
redirect_to(spina.blocks_admin_blocks_url)
|
|
24
24
|
else
|
|
25
|
-
flash.now[:error] = I18n.t(
|
|
26
|
-
render
|
|
25
|
+
flash.now[:error] = I18n.t("spina.block_categories.couldnt_be_saved")
|
|
26
|
+
render(:edit, status: :unprocessable_entity)
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -9,9 +9,9 @@ module Spina
|
|
|
9
9
|
before_action :set_page
|
|
10
10
|
|
|
11
11
|
def index
|
|
12
|
-
add_breadcrumb
|
|
13
|
-
add_breadcrumb
|
|
14
|
-
add_breadcrumb
|
|
12
|
+
add_breadcrumb(I18n.t("spina.website.pages"), spina.admin_pages_path, class: "text-gray-400")
|
|
13
|
+
add_breadcrumb(@page.title, spina.edit_admin_page_path(@page), class: "text-gray-400")
|
|
14
|
+
add_breadcrumb(I18n.t("spina.page_blocks.title"))
|
|
15
15
|
|
|
16
16
|
@page_blocks = @page.page_blocks.sorted.includes(:block)
|
|
17
17
|
@available_blocks = Spina::Blocks::Block.active.sorted.where.not(id: @page.block_ids)
|
|
@@ -22,19 +22,19 @@ module Spina
|
|
|
22
22
|
@page_block.position = @page.page_blocks.maximum(:position).to_i + 1
|
|
23
23
|
|
|
24
24
|
if @page_block.save
|
|
25
|
-
flash[:success] = I18n.t(
|
|
25
|
+
flash[:success] = I18n.t("spina.page_blocks.added")
|
|
26
26
|
else
|
|
27
|
-
flash[:error] = I18n.t(
|
|
27
|
+
flash[:error] = I18n.t("spina.page_blocks.couldnt_be_added")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
redirect_to
|
|
30
|
+
redirect_to(spina.blocks_admin_page_page_blocks_url(page_id: @page.id))
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def destroy
|
|
34
34
|
@page_block = @page.page_blocks.find(params[:id])
|
|
35
35
|
@page_block.destroy
|
|
36
|
-
flash[:info] = I18n.t(
|
|
37
|
-
redirect_to
|
|
36
|
+
flash[:info] = I18n.t("spina.page_blocks.removed")
|
|
37
|
+
redirect_to(spina.blocks_admin_page_page_blocks_url(page_id: @page.id))
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def sort
|
|
@@ -42,7 +42,7 @@ module Spina
|
|
|
42
42
|
@page.page_blocks.where(id: id).update_all(position: index + 1)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
flash.now[:info] = I18n.t(
|
|
45
|
+
flash.now[:info] = I18n.t("spina.page_blocks.sorting_saved")
|
|
46
46
|
render_flash
|
|
47
47
|
end
|
|
48
48
|
|
|
@@ -13,7 +13,7 @@ module Spina
|
|
|
13
13
|
block = page_block.block
|
|
14
14
|
next unless block&.active?
|
|
15
15
|
|
|
16
|
-
concat
|
|
16
|
+
concat(render_block(block))
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -28,7 +28,7 @@ module Spina
|
|
|
28
28
|
partial_path = "#{theme_name}/blocks/#{block.block_template}"
|
|
29
29
|
|
|
30
30
|
if lookup_context.exists?(partial_path, [], true)
|
|
31
|
-
render
|
|
31
|
+
render(partial: partial_path, locals: { block: block })
|
|
32
32
|
else
|
|
33
33
|
render_block_fallback(block)
|
|
34
34
|
end
|
|
@@ -49,7 +49,7 @@ module Spina
|
|
|
49
49
|
|
|
50
50
|
def render_block_fallback(block)
|
|
51
51
|
content_tag(:div, class: "spina-block spina-block--#{block.block_template}") do
|
|
52
|
-
content_tag(:p, block.
|
|
52
|
+
content_tag(:p, block.name, class: "spina-block__title")
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Blocks
|
|
5
|
+
module AccountExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
after_save :bootstrap_block_categories
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def bootstrap_block_categories
|
|
15
|
+
theme_config = Spina::Theme.find_by_name(theme)
|
|
16
|
+
return unless theme_config
|
|
17
|
+
return unless theme_config.respond_to?(:block_categories) && theme_config.block_categories.present?
|
|
18
|
+
|
|
19
|
+
theme_config.block_categories.each_with_index do |category, index|
|
|
20
|
+
Spina::Blocks::Category.where(name: category[:name])
|
|
21
|
+
.first_or_create(label: category[:label])
|
|
22
|
+
.update(label: category[:label], position: index)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -8,18 +8,18 @@ module Spina
|
|
|
8
8
|
include Spina::Partable
|
|
9
9
|
include Spina::TranslatedContent
|
|
10
10
|
|
|
11
|
-
belongs_to :category, class_name:
|
|
12
|
-
has_many :page_blocks, class_name:
|
|
13
|
-
has_many :pages, through: :page_blocks, class_name:
|
|
11
|
+
belongs_to :category, class_name: "Spina::Blocks::Category", optional: true
|
|
12
|
+
has_many :page_blocks, class_name: "Spina::Blocks::PageBlock", dependent: :destroy
|
|
13
|
+
has_many :pages, through: :page_blocks, class_name: "Spina::Page"
|
|
14
14
|
|
|
15
|
-
validates :
|
|
15
|
+
validates :name, presence: true
|
|
16
16
|
validates :block_template, presence: true
|
|
17
17
|
|
|
18
18
|
scope :active, -> { where(active: true) }
|
|
19
19
|
scope :sorted, -> { order(:position) }
|
|
20
20
|
|
|
21
21
|
def to_s
|
|
22
|
-
|
|
22
|
+
name
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Spina
|
|
4
4
|
module Blocks
|
|
5
5
|
class Category < ApplicationRecord
|
|
6
|
-
has_many :blocks, class_name:
|
|
6
|
+
has_many :blocks, class_name: "Spina::Blocks::Block", foreign_key: :category_id, dependent: :nullify
|
|
7
7
|
|
|
8
8
|
validates :name, presence: true, uniqueness: true
|
|
9
9
|
validates :label, presence: true
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Spina
|
|
4
4
|
module Blocks
|
|
5
5
|
class PageBlock < ApplicationRecord
|
|
6
|
-
belongs_to :page, class_name:
|
|
7
|
-
belongs_to :block, class_name:
|
|
6
|
+
belongs_to :page, class_name: "Spina::Page"
|
|
7
|
+
belongs_to :block, class_name: "Spina::Blocks::Block"
|
|
8
8
|
|
|
9
9
|
validates :block_id, uniqueness: { scope: :page_id }
|
|
10
10
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Blocks
|
|
5
|
+
module PageExtension
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
has_many :page_blocks, class_name: "Spina::Blocks::PageBlock", foreign_key: :page_id, dependent: :destroy
|
|
10
|
+
has_many :blocks, through: :page_blocks, class_name: "Spina::Blocks::Block"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
blocks_json = blocks.map { |b|
|
|
16
16
|
{
|
|
17
17
|
id: b.id,
|
|
18
|
-
|
|
18
|
+
name: b.name,
|
|
19
19
|
templateName: b.block_template.to_s,
|
|
20
20
|
templateTitle: template_titles[b.block_template.to_s] || b.block_template.to_s.titleize
|
|
21
21
|
}
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
data-controller="block-collection"
|
|
27
27
|
data-block-collection-blocks-value="<%= blocks_json.to_json %>"
|
|
28
28
|
data-block-collection-selected-ids-value="<%= selected_ids.to_json %>"
|
|
29
|
+
data-block-collection-edit-url-value="<%= spina.edit_modal_blocks_admin_block_path("__ID__") %>"
|
|
29
30
|
data-field-name="<%= field_name %>">
|
|
30
31
|
|
|
31
32
|
<label class="block text-sm leading-5 font-medium text-gray-700"><%= f.object.title %></label>
|
|
@@ -78,6 +79,12 @@
|
|
|
78
79
|
<span class="text-sm font-medium text-gray-800 truncate" data-role="title"></span>
|
|
79
80
|
<span class="ml-2 text-xs text-gray-400" data-role="template-label"></span>
|
|
80
81
|
</div>
|
|
82
|
+
<a class="ml-2 text-gray-300 hover:text-gray-600 transition-colors"
|
|
83
|
+
data-role="edit-link" href="" data-turbo-frame="modal" title="Edit">
|
|
84
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
85
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
|
|
86
|
+
</svg>
|
|
87
|
+
</a>
|
|
81
88
|
<button type="button" class="ml-2 text-gray-300 hover:text-red-500 transition-colors"
|
|
82
89
|
data-action="block-collection#remove" data-block-id="" title="Remove">
|
|
83
90
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<div class="mt-1">
|
|
7
7
|
<% blocks = Spina::Blocks::Block.active.sorted %>
|
|
8
8
|
<%= f.select :block_id,
|
|
9
|
-
blocks.map { |b| [b.
|
|
9
|
+
blocks.map { |b| [b.name, b.id] },
|
|
10
10
|
{include_blank: t('spina.blocks.select_block')},
|
|
11
11
|
class: "form-select text-sm w-full shadow-xs" %>
|
|
12
12
|
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<%= link_to spina.edit_blocks_admin_block_path(block), class: "block px-2 leading-tight", data: {selected: index.zero?, search_target: "result", action: "mouseenter->search#select", turbo_frame: "_top"} do %>
|
|
2
|
+
<div class="flex items-center justify-between w-full px-4 py-3 rounded-md bg-gray-400 bg-opacity-0 <%= 'bg-opacity-25' if index.zero? %>" data-selected-class="bg-opacity-25">
|
|
3
|
+
<div>
|
|
4
|
+
<div class="font-medium flex items-center">
|
|
5
|
+
<%= block.name %>
|
|
6
|
+
<% unless block.active? %>
|
|
7
|
+
<div class="opacity-50 text-xs ml-1">(inactive)</div>
|
|
8
|
+
<% end %>
|
|
9
|
+
<div class="flex items-center ml-2 text-xs px-1.5 py-0.5 space-x-1 bg-gray-900 bg-opacity-5 rounded">
|
|
10
|
+
<%= heroicon('cube', style: :outline, class: 'w-3 h-3 opacity-50') %>
|
|
11
|
+
<div class="opacity-75"><%= block.block_template %></div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<% if block.category %>
|
|
15
|
+
<div class="opacity-50 text-xs"><%= block.category.label %></div>
|
|
16
|
+
<% end %>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div class="opacity-0 <%= 'opacity-100' if index.zero? %>" data-selected-class="opacity-100">
|
|
20
|
+
<%= heroicon('arrow-narrow-right', style: :solid, class: 'w-5 h-5 text-gray-600') %>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<% end %>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<div class="max-w-5xl">
|
|
2
2
|
<label class="font-medium text-gray-700">
|
|
3
|
-
<%= Spina::Blocks::Block.human_attribute_name(:
|
|
3
|
+
<%= Spina::Blocks::Block.human_attribute_name(:name) %>
|
|
4
4
|
</label>
|
|
5
|
-
<%= render Spina::Forms::TextFieldComponent.new(f, :
|
|
5
|
+
<%= render Spina::Forms::TextFieldComponent.new(f, :name, size: "lg") %>
|
|
6
6
|
<turbo-frame id="block_content" src="<%= spina.edit_content_blocks_admin_block_path(f.object, locale: @locale) %>">
|
|
7
7
|
</turbo-frame>
|
|
8
8
|
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<%# Reusable modal form for editing a block. Used by edit_modal action.
|
|
2
|
+
Expects: @block, @tabs, @locale %>
|
|
3
|
+
<%= turbo_frame_tag dom_id(@block, :modal_form) do %>
|
|
4
|
+
<div data-controller="tabs"
|
|
5
|
+
data-tabs-active="cursor-default text-gray-900 bg-spina-dark/10"
|
|
6
|
+
data-tabs-inactive="cursor-pointer bg-transparent text-gray-400 border-transparent">
|
|
7
|
+
|
|
8
|
+
<%# Modal header %>
|
|
9
|
+
<div class="px-6 pt-5 pb-4 border-b border-gray-200">
|
|
10
|
+
<div class="flex items-center justify-between">
|
|
11
|
+
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
|
12
|
+
<%= @block.name %>
|
|
13
|
+
</h3>
|
|
14
|
+
|
|
15
|
+
<%# Tab navigation %>
|
|
16
|
+
<nav>
|
|
17
|
+
<ul class="inline-flex w-auto rounded-md bg-gray-100">
|
|
18
|
+
<% @tabs.each do |tab_name| %>
|
|
19
|
+
<%= render partial: "button_#{tab_name}" %>
|
|
20
|
+
<% end %>
|
|
21
|
+
</ul>
|
|
22
|
+
</nav>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<%# Form %>
|
|
27
|
+
<%= form_with model: @block,
|
|
28
|
+
url: spina.blocks_admin_block_path(@block),
|
|
29
|
+
id: dom_id(@block, :modal),
|
|
30
|
+
data: { turbo_frame: dom_id(@block, :modal_form) } do |f| %>
|
|
31
|
+
<%= hidden_field_tag :locale, @locale %>
|
|
32
|
+
<%= hidden_field_tag :modal, 1 %>
|
|
33
|
+
|
|
34
|
+
<div class="px-6 py-4 max-h-[70vh] overflow-y-auto">
|
|
35
|
+
<% Mobility.with_locale(@locale) do %>
|
|
36
|
+
<% @tabs.each_with_index do |tab_name, idx| %>
|
|
37
|
+
<div data-tabs-target="pane" id="<%= tab_name %>" <%= 'hidden' unless idx.zero? %>>
|
|
38
|
+
<%= render partial: "form_#{tab_name}", locals: { f: f } %>
|
|
39
|
+
</div>
|
|
40
|
+
<% end %>
|
|
41
|
+
<% end %>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<%# Action buttons %>
|
|
45
|
+
<div class="px-6 py-4 border-t border-gray-200 flex flex-row-reverse">
|
|
46
|
+
<button type="submit"
|
|
47
|
+
form="<%= dom_id(@block, :modal) %>"
|
|
48
|
+
class="btn btn-primary ml-3"
|
|
49
|
+
data-controller="button"
|
|
50
|
+
data-action="button#loading"
|
|
51
|
+
data-loading-message="<%= t('spina.blocks.save') %>...">
|
|
52
|
+
<%= t('spina.blocks.save') %>
|
|
53
|
+
</button>
|
|
54
|
+
|
|
55
|
+
<button type="button" class="btn btn-default" data-action="modal#close">
|
|
56
|
+
<%= t('spina.ui.cancel') %>
|
|
57
|
+
</button>
|
|
58
|
+
</div>
|
|
59
|
+
<% end %>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
</div>
|
|
76
76
|
<div class="flex-1 py-3 pr-4">
|
|
77
77
|
<%= link_to spina.edit_blocks_admin_block_path(block), class: "block" do %>
|
|
78
|
-
<div class="text-gray-900 font-medium text-sm"><%= block.
|
|
78
|
+
<div class="text-gray-900 font-medium text-sm"><%= block.name %></div>
|
|
79
79
|
<div class="text-gray-400 text-xs">
|
|
80
80
|
<%= block.block_template %>
|
|
81
81
|
<% unless block.active? %>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
params: {page_block: {block_id: block.id}},
|
|
19
19
|
method: :post,
|
|
20
20
|
class: "block w-full text-left px-4 py-2 text-sm font-medium leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900" do %>
|
|
21
|
-
<div><%= block.
|
|
21
|
+
<div><%= block.name %></div>
|
|
22
22
|
<div class="text-xs text-gray-400"><%= block.block_template %></div>
|
|
23
23
|
<% end %>
|
|
24
24
|
<% end %>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
params: {page_block: {block_id: block.id}},
|
|
34
34
|
method: :post,
|
|
35
35
|
class: "block w-full text-left px-4 py-2 text-sm font-medium leading-5 text-gray-700 hover:bg-gray-100 hover:text-gray-900" do %>
|
|
36
|
-
<div><%= block.
|
|
36
|
+
<div><%= block.name %></div>
|
|
37
37
|
<div class="text-xs text-gray-400"><%= block.block_template %></div>
|
|
38
38
|
<% end %>
|
|
39
39
|
<% end %>
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
</div>
|
|
65
65
|
<div class="flex-1 py-3">
|
|
66
66
|
<%= link_to spina.edit_blocks_admin_block_path(page_block.block), class: "block" do %>
|
|
67
|
-
<div class="text-gray-900 font-medium text-sm"><%= page_block.block.
|
|
67
|
+
<div class="text-gray-900 font-medium text-sm"><%= page_block.block.name %></div>
|
|
68
68
|
<div class="text-gray-400 text-xs">
|
|
69
69
|
<%= page_block.block.block_template %>
|
|
70
70
|
</div>
|
data/config/locales/en.yml
CHANGED
data/config/routes.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
Spina::Engine.routes.draw do
|
|
4
|
-
namespace :blocks, path:
|
|
4
|
+
namespace :blocks, path: "", module: "blocks" do
|
|
5
5
|
namespace :admin, path: Spina.config.backend_path do
|
|
6
6
|
resources :blocks do
|
|
7
7
|
member do
|
|
8
8
|
get :edit_content
|
|
9
|
+
get :edit_modal
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
collection do
|
|
@@ -13,10 +14,10 @@ Spina::Engine.routes.draw do
|
|
|
13
14
|
end
|
|
14
15
|
end
|
|
15
16
|
|
|
16
|
-
resources :categories, only:
|
|
17
|
+
resources :categories, only: [:index, :edit, :update]
|
|
17
18
|
|
|
18
19
|
resources :pages, only: [] do
|
|
19
|
-
resources :page_blocks, only:
|
|
20
|
+
resources :page_blocks, only: [:index, :create, :destroy] do
|
|
20
21
|
collection do
|
|
21
22
|
post :sort
|
|
22
23
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateSpinaBlocksCategories < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
if previous_version_applied?("20250101000001")
|
|
6
|
+
cleanup_version!("20250101000001")
|
|
7
|
+
return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
return if connection.table_exists?(:spina_blocks_categories)
|
|
11
|
+
|
|
12
|
+
create_table(:spina_blocks_categories) do |t|
|
|
13
|
+
t.string(:name, null: false)
|
|
14
|
+
t.string(:label, null: false)
|
|
15
|
+
t.integer(:position, default: 0)
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
add_index(:spina_blocks_categories, :name, unique: true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def previous_version_applied?(version)
|
|
26
|
+
connection.select_value(
|
|
27
|
+
"SELECT 1 FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def cleanup_version!(version)
|
|
32
|
+
connection.execute(
|
|
33
|
+
"DELETE FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateSpinaBlocksBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
if previous_version_applied?("20250101000002")
|
|
6
|
+
cleanup_version!("20250101000002")
|
|
7
|
+
return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
return if connection.table_exists?(:spina_blocks_blocks)
|
|
11
|
+
|
|
12
|
+
create_table(:spina_blocks_blocks) do |t|
|
|
13
|
+
t.string(:name, null: false)
|
|
14
|
+
t.string(:block_template, null: false)
|
|
15
|
+
t.references(:category, foreign_key: { to_table: :spina_blocks_categories }, null: true)
|
|
16
|
+
t.integer(:position, default: 0)
|
|
17
|
+
t.boolean(:active, default: true)
|
|
18
|
+
t.json(:json_attributes)
|
|
19
|
+
|
|
20
|
+
t.timestamps
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
add_index(:spina_blocks_blocks, :name, unique: true)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def previous_version_applied?(version)
|
|
29
|
+
connection.select_value(
|
|
30
|
+
"SELECT 1 FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def cleanup_version!(version)
|
|
35
|
+
connection.execute(
|
|
36
|
+
"DELETE FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateSpinaBlocksPageBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
if previous_version_applied?("20250101000003")
|
|
6
|
+
cleanup_version!("20250101000003")
|
|
7
|
+
return
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
return if connection.table_exists?(:spina_blocks_page_blocks)
|
|
11
|
+
|
|
12
|
+
create_table(:spina_blocks_page_blocks) do |t|
|
|
13
|
+
t.references(:page, null: false, foreign_key: { to_table: :spina_pages })
|
|
14
|
+
t.references(:block, null: false, foreign_key: { to_table: :spina_blocks_blocks })
|
|
15
|
+
t.integer(:position, default: 0)
|
|
16
|
+
|
|
17
|
+
t.timestamps
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
add_index(:spina_blocks_page_blocks, [:page_id, :block_id], unique: true)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def previous_version_applied?(version)
|
|
26
|
+
connection.select_value(
|
|
27
|
+
"SELECT 1 FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def cleanup_version!(version)
|
|
32
|
+
connection.execute(
|
|
33
|
+
"DELETE FROM schema_migrations WHERE version = #{connection.quote(version)}",
|
|
34
|
+
)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RenameTitleToNameInSpinaBlocksBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
+
def change
|
|
5
|
+
# For fresh installs: migration 2 already creates only `name`, no `title`.
|
|
6
|
+
return unless connection.column_exists?(:spina_blocks_blocks, :title)
|
|
7
|
+
|
|
8
|
+
# Edge case: both `title` and `name` exist (migration 2 ran with both
|
|
9
|
+
# columns but migration 4, which removed `name`, was never executed).
|
|
10
|
+
if connection.column_exists?(:spina_blocks_blocks, :name)
|
|
11
|
+
remove_index(:spina_blocks_blocks, :name) if connection.index_exists?(:spina_blocks_blocks, :name)
|
|
12
|
+
remove_column(:spina_blocks_blocks, :name, :string)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
rename_column(:spina_blocks_blocks, :title, :name)
|
|
16
|
+
add_index(:spina_blocks_blocks, :name, unique: true)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spina
|
|
4
|
+
module Blocks
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
desc "Install Spina Blocks plugin: copy migrations and run them"
|
|
7
|
+
|
|
8
|
+
def copy_migrations
|
|
9
|
+
rake("spina_blocks:install:migrations")
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def run_migrations
|
|
13
|
+
rake("db:migrate")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/spina/blocks/engine.rb
CHANGED
|
@@ -7,8 +7,8 @@ module Spina
|
|
|
7
7
|
|
|
8
8
|
config.before_initialize do
|
|
9
9
|
::Spina::Plugin.register do |plugin|
|
|
10
|
-
plugin.name =
|
|
11
|
-
plugin.namespace =
|
|
10
|
+
plugin.name = "blocks"
|
|
11
|
+
plugin.namespace = "blocks"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
::Spina::Theme.class_eval do
|
|
@@ -26,55 +26,55 @@ module Spina
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
initializer
|
|
30
|
-
unless app.root.to_s.match?(root.to_s)
|
|
31
|
-
config.paths['db/migrate'].expanded.each do |expanded_path|
|
|
32
|
-
app.config.paths['db/migrate'] << expanded_path
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
initializer 'spina.blocks.importmap', before: 'spina.blocks.register_parts' do
|
|
29
|
+
initializer "spina.blocks.importmap", before: "spina.blocks.register_parts" do
|
|
38
30
|
Spina.config.importmap.draw do
|
|
39
|
-
pin_all_from Spina::Blocks::Engine.root.join(
|
|
40
|
-
|
|
31
|
+
pin_all_from Spina::Blocks::Engine.root.join("app/assets/javascripts/spina/controllers"),
|
|
32
|
+
under: "controllers",
|
|
33
|
+
to: "spina/controllers"
|
|
41
34
|
end
|
|
42
35
|
end
|
|
43
36
|
|
|
44
|
-
initializer
|
|
45
|
-
app.config.assets.precompile +=
|
|
37
|
+
initializer "spina.blocks.assets.precompile" do |app|
|
|
38
|
+
app.config.assets.precompile += ["spina/controllers/block_collection_controller.js"] if defined?(Sprockets)
|
|
46
39
|
end
|
|
47
40
|
|
|
48
|
-
initializer
|
|
41
|
+
initializer "spina.blocks.register_parts" do
|
|
49
42
|
config.to_prepare do
|
|
50
43
|
::Spina::Part.register(
|
|
51
44
|
Spina::Parts::BlockReference,
|
|
52
|
-
Spina::Parts::BlockCollection
|
|
45
|
+
Spina::Parts::BlockCollection,
|
|
53
46
|
)
|
|
54
47
|
end
|
|
55
48
|
end
|
|
56
49
|
|
|
57
|
-
initializer
|
|
50
|
+
initializer "spina.blocks.extend_models" do
|
|
51
|
+
config.to_prepare do
|
|
52
|
+
Spina::Page.include(Spina::Blocks::PageExtension) unless Spina::Page < Spina::Blocks::PageExtension
|
|
53
|
+
Spina::Account.include(Spina::Blocks::AccountExtension) unless Spina::Account < Spina::Blocks::AccountExtension
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
initializer "spina.blocks.search" do
|
|
58
58
|
config.to_prepare do
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
if defined?(Spina::Pro::Search)
|
|
60
|
+
Spina::Blocks::Block.include(Spina::Blocks::SearchExtension) unless Spina::Blocks::Block < Spina::Blocks::SearchExtension
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
initializer
|
|
65
|
+
initializer "spina.blocks.tailwind_content" do
|
|
66
66
|
::Spina.config.tailwind_content << "#{Spina::Blocks::Engine.root}/app/views/**/*.*"
|
|
67
67
|
::Spina.config.tailwind_content << "#{Spina::Blocks::Engine.root}/app/helpers/**/*.*"
|
|
68
68
|
::Spina.config.tailwind_content << "#{Spina::Blocks::Engine.root}/app/assets/javascripts/**/*.js"
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
initializer
|
|
72
|
-
config.i18n.load_path += Dir[Spina::Blocks::Engine.root.join(
|
|
71
|
+
initializer "spina.blocks.i18n" do
|
|
72
|
+
config.i18n.load_path += Dir[Spina::Blocks::Engine.root.join("config", "locales", "*.{rb,yml}")]
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
initializer
|
|
75
|
+
initializer "spina.blocks.helpers" do
|
|
76
76
|
config.to_prepare do
|
|
77
|
-
ActionView::Base.include
|
|
77
|
+
ActionView::Base.include(Spina::Blocks::BlocksHelper)
|
|
78
78
|
end
|
|
79
79
|
end
|
|
80
80
|
end
|
data/lib/spina/blocks/version.rb
CHANGED
data/lib/spina-blocks.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spina-blocks
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Konstantin Kanashchuk
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: spina
|
|
@@ -24,6 +23,62 @@ dependencies:
|
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
25
|
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: database_cleaner-active_record
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: factory_bot_rails
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: pg
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rspec-rails
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0'
|
|
27
82
|
description: A plugin for Spina CMS that adds reusable block components that can be
|
|
28
83
|
assembled into pages.
|
|
29
84
|
email:
|
|
@@ -39,34 +94,39 @@ files:
|
|
|
39
94
|
- app/controllers/spina/blocks/admin/categories_controller.rb
|
|
40
95
|
- app/controllers/spina/blocks/admin/page_blocks_controller.rb
|
|
41
96
|
- app/helpers/spina/blocks/blocks_helper.rb
|
|
97
|
+
- app/models/spina/blocks/account_extension.rb
|
|
42
98
|
- app/models/spina/blocks/application_record.rb
|
|
43
99
|
- app/models/spina/blocks/block.rb
|
|
44
100
|
- app/models/spina/blocks/category.rb
|
|
45
101
|
- app/models/spina/blocks/page_block.rb
|
|
102
|
+
- app/models/spina/blocks/page_extension.rb
|
|
103
|
+
- app/models/spina/blocks/search_extension.rb
|
|
46
104
|
- app/models/spina/parts/block_collection.rb
|
|
47
105
|
- app/models/spina/parts/block_reference.rb
|
|
48
|
-
- app/overrides/spina/account_override.rb
|
|
49
|
-
- app/overrides/spina/page_override.rb
|
|
50
106
|
- app/views/spina/admin/hooks/blocks/_website_secondary_navigation.html.erb
|
|
51
107
|
- app/views/spina/admin/parts/block_collections/_form.html.erb
|
|
52
108
|
- app/views/spina/admin/parts/block_references/_form.html.erb
|
|
109
|
+
- app/views/spina/admin/pro/search/results/_block.html.erb
|
|
53
110
|
- app/views/spina/blocks/admin/blocks/_button_block_content.html.erb
|
|
54
111
|
- app/views/spina/blocks/admin/blocks/_button_block_settings.html.erb
|
|
55
112
|
- app/views/spina/blocks/admin/blocks/_form.html.erb
|
|
56
113
|
- app/views/spina/blocks/admin/blocks/_form_block_content.html.erb
|
|
57
114
|
- app/views/spina/blocks/admin/blocks/_form_block_settings.html.erb
|
|
115
|
+
- app/views/spina/blocks/admin/blocks/_modal_form.html.erb
|
|
58
116
|
- app/views/spina/blocks/admin/blocks/_new_block_form.html.erb
|
|
59
117
|
- app/views/spina/blocks/admin/blocks/edit.html.erb
|
|
60
118
|
- app/views/spina/blocks/admin/blocks/edit_content.html.erb
|
|
119
|
+
- app/views/spina/blocks/admin/blocks/edit_modal.html.erb
|
|
61
120
|
- app/views/spina/blocks/admin/blocks/index.html.erb
|
|
62
121
|
- app/views/spina/blocks/admin/blocks/new.html.erb
|
|
63
122
|
- app/views/spina/blocks/admin/page_blocks/index.html.erb
|
|
64
123
|
- config/locales/en.yml
|
|
65
124
|
- config/routes.rb
|
|
66
|
-
- db/migrate/
|
|
67
|
-
- db/migrate/
|
|
68
|
-
- db/migrate/
|
|
69
|
-
- db/migrate/
|
|
125
|
+
- db/migrate/1_create_spina_blocks_categories.rb
|
|
126
|
+
- db/migrate/2_create_spina_blocks_blocks.rb
|
|
127
|
+
- db/migrate/3_create_spina_blocks_page_blocks.rb
|
|
128
|
+
- db/migrate/5_rename_title_to_name_in_spina_blocks_blocks.rb
|
|
129
|
+
- lib/generators/spina/blocks/install_generator.rb
|
|
70
130
|
- lib/spina-blocks.rb
|
|
71
131
|
- lib/spina/blocks/engine.rb
|
|
72
132
|
- lib/spina/blocks/version.rb
|
|
@@ -75,7 +135,6 @@ licenses:
|
|
|
75
135
|
- MIT
|
|
76
136
|
metadata:
|
|
77
137
|
rubygems_mfa_required: 'true'
|
|
78
|
-
post_install_message:
|
|
79
138
|
rdoc_options: []
|
|
80
139
|
require_paths:
|
|
81
140
|
- lib
|
|
@@ -83,15 +142,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
83
142
|
requirements:
|
|
84
143
|
- - ">="
|
|
85
144
|
- !ruby/object:Gem::Version
|
|
86
|
-
version: 2.
|
|
145
|
+
version: 3.2.0
|
|
87
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
147
|
requirements:
|
|
89
148
|
- - ">="
|
|
90
149
|
- !ruby/object:Gem::Version
|
|
91
150
|
version: '0'
|
|
92
151
|
requirements: []
|
|
93
|
-
rubygems_version: 3.
|
|
94
|
-
signing_key:
|
|
152
|
+
rubygems_version: 3.6.9
|
|
95
153
|
specification_version: 4
|
|
96
154
|
summary: Block component library for Spina CMS
|
|
97
155
|
test_files: []
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Spina::Account.class_eval do
|
|
4
|
-
after_save :bootstrap_block_categories
|
|
5
|
-
|
|
6
|
-
private
|
|
7
|
-
|
|
8
|
-
def bootstrap_block_categories
|
|
9
|
-
theme_config = Spina::Theme.find_by_name(theme)
|
|
10
|
-
return unless theme_config
|
|
11
|
-
return unless theme_config.respond_to?(:block_categories) && theme_config.block_categories.present?
|
|
12
|
-
|
|
13
|
-
theme_config.block_categories.each_with_index do |category, index|
|
|
14
|
-
Spina::Blocks::Category.where(name: category[:name])
|
|
15
|
-
.first_or_create(label: category[:label])
|
|
16
|
-
.update(label: category[:label], position: index)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class CreateSpinaBlocksCategories < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
if legacy_migration_applied?
|
|
6
|
-
cleanup_legacy_version!
|
|
7
|
-
return
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
create_table :spina_blocks_categories do |t|
|
|
11
|
-
t.string :name, null: false
|
|
12
|
-
t.string :label, null: false
|
|
13
|
-
t.integer :position, default: 0
|
|
14
|
-
|
|
15
|
-
t.timestamps
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
add_index :spina_blocks_categories, :name, unique: true
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def legacy_migration_applied?
|
|
24
|
-
ActiveRecord::Base.connection
|
|
25
|
-
.select_value("SELECT 1 FROM schema_migrations WHERE version = '1'")
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def cleanup_legacy_version!
|
|
29
|
-
execute "DELETE FROM schema_migrations WHERE version = '1'"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class CreateSpinaBlocksBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
if legacy_migration_applied?
|
|
6
|
-
cleanup_legacy_version!
|
|
7
|
-
return
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
create_table :spina_blocks_blocks do |t|
|
|
11
|
-
t.string :title, null: false
|
|
12
|
-
t.string :name, null: false
|
|
13
|
-
t.string :block_template, null: false
|
|
14
|
-
t.references :category, foreign_key: { to_table: :spina_blocks_categories }, null: true
|
|
15
|
-
t.integer :position, default: 0
|
|
16
|
-
t.boolean :active, default: true
|
|
17
|
-
t.json :json_attributes
|
|
18
|
-
|
|
19
|
-
t.timestamps
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
add_index :spina_blocks_blocks, :name, unique: true
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
def legacy_migration_applied?
|
|
28
|
-
ActiveRecord::Base.connection
|
|
29
|
-
.select_value("SELECT 1 FROM schema_migrations WHERE version = '2'")
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def cleanup_legacy_version!
|
|
33
|
-
execute "DELETE FROM schema_migrations WHERE version = '2'"
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class CreateSpinaBlocksPageBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
if legacy_migration_applied?
|
|
6
|
-
cleanup_legacy_version!
|
|
7
|
-
return
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
create_table :spina_blocks_page_blocks do |t|
|
|
11
|
-
t.references :page, null: false, foreign_key: { to_table: :spina_pages }
|
|
12
|
-
t.references :block, null: false, foreign_key: { to_table: :spina_blocks_blocks }
|
|
13
|
-
t.integer :position, default: 0
|
|
14
|
-
|
|
15
|
-
t.timestamps
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
add_index :spina_blocks_page_blocks, %i[page_id block_id], unique: true
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def legacy_migration_applied?
|
|
24
|
-
ActiveRecord::Base.connection
|
|
25
|
-
.select_value("SELECT 1 FROM schema_migrations WHERE version = '3'")
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def cleanup_legacy_version!
|
|
29
|
-
execute "DELETE FROM schema_migrations WHERE version = '3'"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
class RemoveNameFromSpinaBlocksBlocks < ActiveRecord::Migration[7.0]
|
|
4
|
-
def change
|
|
5
|
-
if legacy_migration_applied?
|
|
6
|
-
cleanup_legacy_version!
|
|
7
|
-
return
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
remove_index :spina_blocks_blocks, :name
|
|
11
|
-
remove_column :spina_blocks_blocks, :name, :string
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
|
|
16
|
-
def legacy_migration_applied?
|
|
17
|
-
ActiveRecord::Base.connection
|
|
18
|
-
.select_value("SELECT 1 FROM schema_migrations WHERE version = '4'")
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def cleanup_legacy_version!
|
|
22
|
-
execute "DELETE FROM schema_migrations WHERE version = '4'"
|
|
23
|
-
end
|
|
24
|
-
end
|