lato_cms 3.0.5 → 3.0.7
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/app/controllers/lato_cms/application_controller.rb +18 -3
- data/app/controllers/lato_cms/pages_controller.rb +87 -0
- data/app/helpers/lato_cms/pages_helper.rb +59 -0
- data/app/models/lato_cms/page.rb +82 -0
- data/app/views/lato_cms/pages/_component_accordion.html.erb +42 -14
- data/app/views/lato_cms/pages/_fields_editor.html.erb +3 -1
- data/app/views/lato_cms/pages/_translations.html.erb +41 -0
- data/app/views/lato_cms/pages/index.html.erb +2 -2
- data/app/views/lato_cms/pages/show.html.erb +22 -16
- data/app/views/lato_cms/pages/translations.html.erb +14 -0
- data/config/locales/en.yml +25 -0
- data/config/locales/it.yml +25 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20260705000000_add_translation_group_id_to_lato_cms_pages.rb +8 -0
- data/db/migrate/20260712092241_change_lato_cms_admin_to_lato_cms_admin_role_on_lato_user.rb +18 -0
- data/lib/lato_cms/config.rb +8 -1
- data/lib/lato_cms/engine.rb +27 -0
- data/lib/lato_cms/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 73f72c7845f56b80f62423a1709a3ea8d5bd80813d8c61b4eaf6af2b353d660c
|
|
4
|
+
data.tar.gz: f222800a0be54f4e7022ecebfe6259515e56fd82554d1784d7bf55fa1a65074e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59fa3bb5a49ce31a770192a6708c59f95313d44896bb493bf0d009b523f950d8f3165609b38da4dbfa4c239c9e6f7d44f2f8bcd870c7d01c4a3b147d11877c7b
|
|
7
|
+
data.tar.gz: 2d31910d84e7df094b4c85ae3f790bcd8e47e2c66dd7cfd472f9c2fde5016b85b1fbb9de1c7a12a86e8946ea527ab0fe8d8434d6e3ab1e5683df9d1fb402731d
|
|
@@ -4,9 +4,11 @@ module LatoCms
|
|
|
4
4
|
layout 'lato/application'
|
|
5
5
|
before_action :authenticate_session
|
|
6
6
|
before_action :authenticate_group
|
|
7
|
-
before_action :
|
|
7
|
+
before_action :authenticate_lato_cms_access
|
|
8
8
|
before_action { active_sidebar(:lato_cms); active_navbar(:lato_cms) }
|
|
9
9
|
|
|
10
|
+
helper_method :lato_cms_admin?
|
|
11
|
+
|
|
10
12
|
def index
|
|
11
13
|
redirect_to lato_cms.pages_path
|
|
12
14
|
end
|
|
@@ -17,10 +19,23 @@ module LatoCms
|
|
|
17
19
|
@query_pages ||= LatoCms::Page.for_lato_spaces_group(@session.get(:spaces_group_id))
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
# Section-level guard: any CMS role (operator or admin) may enter.
|
|
23
|
+
def authenticate_lato_cms_access
|
|
24
|
+
return true if @session.user&.lato_cms_access?
|
|
22
25
|
|
|
23
26
|
redirect_to lato.root_path, alert: t('lato_cms.unauthorized_section')
|
|
24
27
|
end
|
|
28
|
+
|
|
29
|
+
# Action-level guard for page management and translation links: admin only.
|
|
30
|
+
def authenticate_lato_cms_admin
|
|
31
|
+
return true if lato_cms_admin?
|
|
32
|
+
|
|
33
|
+
redirect_to lato_cms.pages_path, alert: t('lato_cms.unauthorized_action')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Exposed to views to conditionally render admin-only controls.
|
|
37
|
+
def lato_cms_admin?
|
|
38
|
+
@session.user&.lato_cms_admin? || false
|
|
39
|
+
end
|
|
25
40
|
end
|
|
26
41
|
end
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
module LatoCms
|
|
2
2
|
class PagesController < ApplicationController
|
|
3
|
+
# Page management and translation links are reserved to the admin role.
|
|
4
|
+
# Operators keep read access plus field editing and component toggles.
|
|
5
|
+
ADMIN_ONLY_ACTIONS = %i[
|
|
6
|
+
create create_action
|
|
7
|
+
update update_action
|
|
8
|
+
destroy_action
|
|
9
|
+
translations link_translation_action unlink_translation_action
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
3
12
|
before_action { active_sidebar(:lato_cms_pages) }
|
|
13
|
+
before_action :authenticate_lato_cms_admin, only: ADMIN_ONLY_ACTIONS
|
|
4
14
|
|
|
5
15
|
def index
|
|
6
16
|
pages = query_pages
|
|
@@ -125,6 +135,56 @@ module LatoCms
|
|
|
125
135
|
end
|
|
126
136
|
end
|
|
127
137
|
|
|
138
|
+
def clone_component_action
|
|
139
|
+
@page = query_pages.find(params[:id])
|
|
140
|
+
source = @page.translations.find_by(id: params[:source_page_id])
|
|
141
|
+
template_component_id = params[:template_component_id].to_s
|
|
142
|
+
|
|
143
|
+
respond_to do |format|
|
|
144
|
+
if source && clone_component_fields(source, @page, template_component_id)
|
|
145
|
+
format.html { redirect_to lato_cms.pages_show_path(@page), notice: t('lato_cms.component_cloned') }
|
|
146
|
+
format.json { render json: { message: t('lato_cms.component_cloned') } }
|
|
147
|
+
else
|
|
148
|
+
format.html { redirect_to lato_cms.pages_show_path(@page), alert: t('lato_cms.component_clone_failed') }
|
|
149
|
+
format.json { render json: { error: t('lato_cms.component_clone_failed') }, status: :unprocessable_entity }
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def translations
|
|
155
|
+
@page = query_pages.find(params[:id])
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def link_translation_action
|
|
159
|
+
@page = query_pages.find(params[:id])
|
|
160
|
+
other_page = query_pages.find(params[:translation_page_id])
|
|
161
|
+
|
|
162
|
+
respond_to do |format|
|
|
163
|
+
if @page.link_translation(other_page)
|
|
164
|
+
format.html { redirect_to lato_cms.pages_translations_path(@page), notice: t('lato_cms.translation_linked') }
|
|
165
|
+
format.json { render json: @page }
|
|
166
|
+
else
|
|
167
|
+
format.html { redirect_to lato_cms.pages_translations_path(@page), alert: t('lato_cms.translation_link_failed') }
|
|
168
|
+
format.json { render json: { error: t('lato_cms.translation_link_failed') }, status: :unprocessable_entity }
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def unlink_translation_action
|
|
174
|
+
@page = query_pages.find(params[:id])
|
|
175
|
+
other_page = query_pages.find(params[:translation_page_id])
|
|
176
|
+
|
|
177
|
+
respond_to do |format|
|
|
178
|
+
if @page.unlink_translation(other_page)
|
|
179
|
+
format.html { redirect_to lato_cms.pages_translations_path(@page), notice: t('lato_cms.translation_unlinked') }
|
|
180
|
+
format.json { render json: @page }
|
|
181
|
+
else
|
|
182
|
+
format.html { redirect_to lato_cms.pages_translations_path(@page), alert: t('lato_cms.translation_unlink_failed') }
|
|
183
|
+
format.json { render json: { error: t('lato_cms.translation_unlink_failed') }, status: :unprocessable_entity }
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
128
188
|
def destroy_action
|
|
129
189
|
@page = query_pages.find(params[:id])
|
|
130
190
|
|
|
@@ -141,6 +201,33 @@ module LatoCms
|
|
|
141
201
|
|
|
142
202
|
private
|
|
143
203
|
|
|
204
|
+
# Replaces the target page's fields for a component with copies of the source
|
|
205
|
+
# page's fields for the same component. Attachments share the source blobs.
|
|
206
|
+
def clone_component_fields(source, target, template_component_id)
|
|
207
|
+
# Components are template-specific: only clone between pages using the same template.
|
|
208
|
+
return false unless source.template_id == target.template_id
|
|
209
|
+
|
|
210
|
+
source_fields = source.fields.select { |f| f.template_component_id == template_component_id }
|
|
211
|
+
return false if source_fields.empty?
|
|
212
|
+
|
|
213
|
+
target.transaction do
|
|
214
|
+
target.fields.where(template_component_id: template_component_id).destroy_all
|
|
215
|
+
|
|
216
|
+
source_fields.each do |src|
|
|
217
|
+
field = target.fields.create!(
|
|
218
|
+
template_id: target.template_id,
|
|
219
|
+
template_component_id: src.template_component_id,
|
|
220
|
+
component_id: src.component_id,
|
|
221
|
+
field_id: src.field_id,
|
|
222
|
+
value: src.value
|
|
223
|
+
)
|
|
224
|
+
src.files.each { |file| field.files.attach(file.blob) }
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
true
|
|
229
|
+
end
|
|
230
|
+
|
|
144
231
|
def save_repeater_fields(template_component, component, repeater_items, repeater_order, errors)
|
|
145
232
|
template_component_id = template_component[:template_component_id]
|
|
146
233
|
component_id = template_component[:component_id]
|
|
@@ -10,6 +10,7 @@ module LatoCms
|
|
|
10
10
|
'#'
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
# Index locale cell (also the `locale` column viewer). Static badge.
|
|
13
14
|
def lato_cms_page_locale(page)
|
|
14
15
|
content_tag(:span, class: 'badge bg-secondary') do
|
|
15
16
|
concat locale_to_flag(page.locale)
|
|
@@ -17,6 +18,7 @@ module LatoCms
|
|
|
17
18
|
end
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
# Index actions cell (also the `actions` column viewer). Inline buttons.
|
|
20
22
|
def lato_cms_page_actions(page, show_edit: false, show_delete: false, hide_show: false)
|
|
21
23
|
btn_group = capture do
|
|
22
24
|
content_tag(:div, class: 'btn-group btn-group-sm') do
|
|
@@ -42,6 +44,63 @@ module LatoCms
|
|
|
42
44
|
end
|
|
43
45
|
end
|
|
44
46
|
|
|
47
|
+
# Show-page locale: dropdown to jump to the same page in another language.
|
|
48
|
+
def lato_cms_page_locale_dropdown(page)
|
|
49
|
+
siblings = page.translations.order(:locale).to_a
|
|
50
|
+
badge = capture do
|
|
51
|
+
concat locale_to_flag(page.locale)
|
|
52
|
+
concat " #{page.locale.upcase}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
return content_tag(:span, badge, class: 'badge bg-secondary') if siblings.empty?
|
|
56
|
+
|
|
57
|
+
content_tag(:div, class: 'dropdown') do
|
|
58
|
+
concat button_tag(badge, type: 'button', class: 'btn btn-sm btn-secondary dropdown-toggle', data: { bs_toggle: 'dropdown' })
|
|
59
|
+
concat(content_tag(:ul, class: 'dropdown-menu') do
|
|
60
|
+
siblings.each do |sibling|
|
|
61
|
+
concat(content_tag(:li) do
|
|
62
|
+
link_to lato_cms.pages_show_path(sibling), class: 'dropdown-item' do
|
|
63
|
+
concat locale_to_flag(sibling.locale)
|
|
64
|
+
concat " #{sibling.locale.upcase} — #{sibling.title}"
|
|
65
|
+
end
|
|
66
|
+
end)
|
|
67
|
+
end
|
|
68
|
+
end)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Show-page actions: single dropdown (view frontend, show, edit settings,
|
|
73
|
+
# translations, delete).
|
|
74
|
+
def lato_cms_page_actions_dropdown(page, show_edit: false, show_translations: false, show_delete: false, hide_show: false)
|
|
75
|
+
content_tag(:div, class: 'dropdown', title: t('lato_cms.cta_actions'), data: { controller: 'lato-tooltip' }) do
|
|
76
|
+
concat button_tag(tag.i(class: 'bi bi-three-dots-vertical'), type: 'button',
|
|
77
|
+
class: 'btn btn-sm btn-outline-primary', data: { bs_toggle: 'dropdown' })
|
|
78
|
+
concat(content_tag(:ul, class: 'dropdown-menu dropdown-menu-end') do
|
|
79
|
+
if page.frontend_url.present?
|
|
80
|
+
concat content_tag(:li, link_to(t('lato_cms.action_view_frontend'), page.frontend_url, class: 'dropdown-item', target: '_blank'))
|
|
81
|
+
else
|
|
82
|
+
concat content_tag(:li, content_tag(:span, t('lato_cms.action_view_frontend'), class: 'dropdown-item disabled text-muted'))
|
|
83
|
+
end
|
|
84
|
+
unless hide_show
|
|
85
|
+
concat content_tag(:li, link_to(t('lato_cms.cta_show'), lato_cms.pages_show_path(page), class: 'dropdown-item'))
|
|
86
|
+
end
|
|
87
|
+
if show_edit
|
|
88
|
+
concat content_tag(:li, link_to(t('lato_cms.cta_edit'), lato_cms.pages_update_path(page), class: 'dropdown-item',
|
|
89
|
+
data: { lato_action_target: 'trigger', turbo_frame: dom_id(page, 'form'), action_title: t('lato_cms.page_update_title') }))
|
|
90
|
+
end
|
|
91
|
+
if show_translations
|
|
92
|
+
concat content_tag(:li, link_to(t('lato_cms.translations_title'), lato_cms.pages_translations_path(page), class: 'dropdown-item',
|
|
93
|
+
data: { lato_action_target: 'trigger', turbo_frame: dom_id(page, 'translations'), action_title: t('lato_cms.translations_title') }))
|
|
94
|
+
end
|
|
95
|
+
if show_delete
|
|
96
|
+
concat content_tag(:li, tag.hr(class: 'dropdown-divider'))
|
|
97
|
+
concat content_tag(:li, link_to(t('lato_cms.cta_delete'), lato_cms.pages_destroy_action_path(page), class: 'dropdown-item text-danger',
|
|
98
|
+
data: { turbo_method: 'DELETE', turbo_confirm: t('lato_cms.cta_delete_confirm') }))
|
|
99
|
+
end
|
|
100
|
+
end)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
45
104
|
def lato_cms_render_field(field_id:, field_config:, page_field:, input_name_prefix: nil, dom_id_prefix: nil)
|
|
46
105
|
render resolve_lato_cms_field_partial(field_config),
|
|
47
106
|
field_id: field_id,
|
data/app/models/lato_cms/page.rb
CHANGED
|
@@ -10,11 +10,73 @@ module LatoCms
|
|
|
10
10
|
validates :locale, presence: true, inclusion: { in: ->(page) { LatoCms.config.locales.map(&:to_s) } }
|
|
11
11
|
validates :permalink, presence: true, format: { with: /\A\/([a-zA-Z0-9\-_]+(\/[a-zA-Z0-9\-_]+)*)?\z/, message: 'must start with / and contain only letters, numbers, hyphens, and underscores' }
|
|
12
12
|
validate :permalink_unique_within_lato_spaces_group
|
|
13
|
+
validate :locale_unique_within_translation_group
|
|
13
14
|
|
|
14
15
|
before_validation :generate_permalink, on: :create
|
|
15
16
|
|
|
16
17
|
has_many :fields, class_name: 'LatoCms::PageField', dependent: :destroy
|
|
17
18
|
|
|
19
|
+
# ── Translations ────────────────────────────────────────────────────────
|
|
20
|
+
# Pages sharing the same translation_group_id (within the same spaces group)
|
|
21
|
+
# are translations of each other, at most one per locale.
|
|
22
|
+
|
|
23
|
+
# Sibling pages linked as translations of this page.
|
|
24
|
+
def translations
|
|
25
|
+
return LatoCms::Page.none if translation_group_id.blank?
|
|
26
|
+
|
|
27
|
+
LatoCms::Page
|
|
28
|
+
.for_lato_spaces_group(lato_spaces_group_id)
|
|
29
|
+
.where(translation_group_id: translation_group_id)
|
|
30
|
+
.where.not(id: id)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# The linked translation for a given locale, if any.
|
|
34
|
+
def translation_for(locale)
|
|
35
|
+
translations.find_by(locale: locale.to_s)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Pages of a given locale that can be linked to this page: same spaces group,
|
|
39
|
+
# not this page, and not already part of any translation group.
|
|
40
|
+
def translation_candidates(locale)
|
|
41
|
+
LatoCms::Page
|
|
42
|
+
.for_lato_spaces_group(lato_spaces_group_id)
|
|
43
|
+
.where(locale: locale.to_s, translation_group_id: nil)
|
|
44
|
+
.where.not(id: id)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Links another page as a translation of this one, merging any existing
|
|
48
|
+
# groups so all members share a single translation_group_id.
|
|
49
|
+
def link_translation(other_page)
|
|
50
|
+
return false unless other_page && other_page.id != id
|
|
51
|
+
return false unless other_page.lato_spaces_group_id == lato_spaces_group_id
|
|
52
|
+
|
|
53
|
+
group_id = translation_group_id.presence || other_page.translation_group_id.presence || SecureRandom.uuid
|
|
54
|
+
old_group_id = other_page.translation_group_id.presence
|
|
55
|
+
|
|
56
|
+
transaction do
|
|
57
|
+
update!(translation_group_id: group_id)
|
|
58
|
+
# Re-point every member of the other page's former group to keep groups merged.
|
|
59
|
+
if old_group_id && old_group_id != group_id
|
|
60
|
+
LatoCms::Page
|
|
61
|
+
.for_lato_spaces_group(lato_spaces_group_id)
|
|
62
|
+
.where(translation_group_id: old_group_id)
|
|
63
|
+
.update_all(translation_group_id: group_id)
|
|
64
|
+
else
|
|
65
|
+
other_page.update!(translation_group_id: group_id)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Removes a page from this page's translation group.
|
|
73
|
+
def unlink_translation(other_page)
|
|
74
|
+
return false unless other_page && other_page.translation_group_id == translation_group_id
|
|
75
|
+
|
|
76
|
+
other_page.update!(translation_group_id: nil)
|
|
77
|
+
true
|
|
78
|
+
end
|
|
79
|
+
|
|
18
80
|
def template
|
|
19
81
|
LatoCms::TemplateManager.find_template(template_id)
|
|
20
82
|
end
|
|
@@ -64,6 +126,7 @@ module LatoCms
|
|
|
64
126
|
template_id: template_id,
|
|
65
127
|
template_name: template_name,
|
|
66
128
|
frontend_url: frontend_url,
|
|
129
|
+
translations: translations_json,
|
|
67
130
|
created_at: created_at,
|
|
68
131
|
updated_at: updated_at
|
|
69
132
|
}
|
|
@@ -78,6 +141,13 @@ module LatoCms
|
|
|
78
141
|
|
|
79
142
|
private
|
|
80
143
|
|
|
144
|
+
# Map of locale => permalink/id for linked translations, for API consumers.
|
|
145
|
+
def translations_json
|
|
146
|
+
translations.each_with_object({}) do |page, hash|
|
|
147
|
+
hash[page.locale] = { id: page.id, permalink: page.permalink, frontend_url: page.frontend_url }
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
81
151
|
def build_fields_json(comps)
|
|
82
152
|
fields_by_component = fields.group_by(&:template_component_id)
|
|
83
153
|
|
|
@@ -160,5 +230,17 @@ module LatoCms
|
|
|
160
230
|
errors.add(:permalink, :taken)
|
|
161
231
|
end
|
|
162
232
|
end
|
|
233
|
+
|
|
234
|
+
def locale_unique_within_translation_group
|
|
235
|
+
return if translation_group_id.blank?
|
|
236
|
+
|
|
237
|
+
conflict = LatoCms::Page
|
|
238
|
+
.for_lato_spaces_group(lato_spaces_group_id)
|
|
239
|
+
.where(translation_group_id: translation_group_id, locale: locale)
|
|
240
|
+
.where.not(id: id)
|
|
241
|
+
.exists?
|
|
242
|
+
|
|
243
|
+
errors.add(:locale, :taken) if conflict
|
|
244
|
+
end
|
|
163
245
|
end
|
|
164
246
|
end
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
<% component_fields = page.fields.select { |f| f.template_component_id == tc[:template_component_id] } %>
|
|
3
3
|
<% required_component = tc[:required] == true %>
|
|
4
4
|
<% enabled_component = page.component_effectively_enabled?(tc[:template_component_id]) %>
|
|
5
|
+
<%# Clonable translations: linked pages using the same template (components differ
|
|
6
|
+
across templates, so cross-template clone is not allowed). %>
|
|
7
|
+
<% clonable_translations = tc[:component] ? translations.select { |s| s.template_id == page.template_id } : [] %>
|
|
5
8
|
|
|
6
9
|
<div class="accordion-item">
|
|
7
10
|
<h2 class="accordion-header" id="heading-<%= accordion_id %>">
|
|
@@ -23,25 +26,50 @@
|
|
|
23
26
|
data-lato-cms-component-toggle-saving-label-value="<%= t('lato_cms.component_state_saving') %>"
|
|
24
27
|
data-lato-cms-component-toggle-saved-label-value="<%= t('lato_cms.component_state_saved') %>"
|
|
25
28
|
data-lato-cms-component-toggle-error-label-value="<%= t('lato_cms.component_state_save_error') %>">
|
|
29
|
+
<% end %>
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
<div class="d-flex justify-content-between align-items-center mb-3 pb-2 border-bottom">
|
|
30
|
-
<div class="d-flex align-items-center gap-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
<%# Component settings header: enable/disable toggle (left) and clone (right). %>
|
|
32
|
+
<% if !required_component || clonable_translations.any? %>
|
|
33
|
+
<div class="d-flex justify-content-between align-items-center gap-2 mb-3 pb-2 border-bottom">
|
|
34
|
+
<div class="d-flex align-items-center gap-3">
|
|
35
|
+
<% unless required_component %>
|
|
36
|
+
<%= form_with url: lato_cms.pages_toggle_component_action_path(page, tc[:template_component_id]), method: :patch,
|
|
37
|
+
data: { turbo: false, action: 'submit->lato-cms-component-toggle#toggle' } do %>
|
|
38
|
+
<div class="d-flex align-items-center gap-3">
|
|
39
|
+
<div class="form-check form-switch m-0">
|
|
40
|
+
<%= hidden_field_tag :enabled, '0' %>
|
|
41
|
+
<%= check_box_tag :enabled, '1', enabled_component, class: 'form-check-input', id: "toggle-#{accordion_id}", onchange: 'this.form.requestSubmit()', 'aria-label': t('lato_cms.component_enabled_label'), data: { lato_cms_component_toggle_target: 'checkbox' } %>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="d-flex align-items-center gap-2 small fw-medium <%= enabled_component ? 'text-success' : 'text-muted' %>" data-lato-cms-component-toggle-target="stateWrapper">
|
|
44
|
+
<i class="<%= enabled_component ? 'bi bi-eye' : 'bi bi-eye-slash' %>" data-lato-cms-component-toggle-target="stateIcon"></i>
|
|
45
|
+
<span data-lato-cms-component-toggle-target="stateLabel"><%= enabled_component ? t('lato_cms.component_active_label') : t('lato_cms.component_inactive_label') %></span>
|
|
46
|
+
</div>
|
|
47
|
+
<span data-lato-cms-component-toggle-target="status"></span>
|
|
48
|
+
</div>
|
|
49
|
+
<% end %>
|
|
50
|
+
<% end %>
|
|
33
51
|
</div>
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
<div class="
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
52
|
+
|
|
53
|
+
<% if clonable_translations.any? %>
|
|
54
|
+
<div class="dropdown flex-shrink-0" data-controller="lato-tooltip" title="<%= t('lato_cms.component_clone_cta') %>">
|
|
55
|
+
<button class="btn btn-sm btn-outline-primary" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
56
|
+
<i class="bi bi-files"></i>
|
|
57
|
+
</button>
|
|
58
|
+
<ul class="dropdown-menu dropdown-menu-end">
|
|
59
|
+
<% clonable_translations.each do |sibling| %>
|
|
60
|
+
<li>
|
|
61
|
+
<%= button_to lato_cms.pages_clone_component_action_path(page, tc[:template_component_id]),
|
|
62
|
+
params: { source_page_id: sibling.id }, method: :post, class: 'dropdown-item',
|
|
63
|
+
form: { data: { turbo_confirm: t('lato_cms.component_clone_confirm', lang: sibling.locale.upcase) } } do %>
|
|
64
|
+
<%= locale_to_flag(sibling.locale) %> <%= t('lato_cms.component_clone_from', lang: sibling.locale.upcase) %>
|
|
65
|
+
<% end %>
|
|
66
|
+
</li>
|
|
67
|
+
<% end %>
|
|
68
|
+
</ul>
|
|
40
69
|
</div>
|
|
41
|
-
|
|
70
|
+
<% end %>
|
|
42
71
|
</div>
|
|
43
72
|
<% end %>
|
|
44
|
-
<% end %>
|
|
45
73
|
|
|
46
74
|
<% if tc[:component] %>
|
|
47
75
|
<% if required_component %>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<% page ||= @page %>
|
|
2
2
|
<% template_components ||= @template_components %>
|
|
3
|
+
<% translations = page.translations.order(:locale).to_a %>
|
|
3
4
|
|
|
4
5
|
<div class="accordion" id="components-accordion" data-controller="lato-cms-component-accordion">
|
|
5
6
|
<% template_components.each_with_index do |tc, index| %>
|
|
@@ -7,6 +8,7 @@
|
|
|
7
8
|
page: page,
|
|
8
9
|
tc: tc,
|
|
9
10
|
index: index,
|
|
10
|
-
expanded: index == 0
|
|
11
|
+
expanded: index == 0,
|
|
12
|
+
translations: translations %>
|
|
11
13
|
<% end %>
|
|
12
14
|
</div>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<%= turbo_frame_tag dom_id(page, 'translations') do %>
|
|
2
|
+
<%= lato_form_notices class: %w[mb-3] %>
|
|
3
|
+
|
|
4
|
+
<% other_locales = LatoCms.config.locales.map(&:to_s) - [page.locale.to_s] %>
|
|
5
|
+
<% if other_locales.empty? %>
|
|
6
|
+
<p class="text-muted mb-0"><%= t('lato_cms.translation_no_other_locales') %></p>
|
|
7
|
+
<% else %>
|
|
8
|
+
<% other_locales.each do |locale| %>
|
|
9
|
+
<div class="d-flex align-items-center justify-content-between gap-2 mb-2">
|
|
10
|
+
<span class="badge bg-secondary">
|
|
11
|
+
<%= locale_to_flag(locale) %> <%= locale.upcase %>
|
|
12
|
+
</span>
|
|
13
|
+
|
|
14
|
+
<% sibling = page.translation_for(locale) %>
|
|
15
|
+
<% if sibling %>
|
|
16
|
+
<%# Linked translation: show it with an unlink button. %>
|
|
17
|
+
<div class="d-flex align-items-center gap-2 flex-grow-1 justify-content-end">
|
|
18
|
+
<%= link_to sibling.title, lato_cms.pages_show_path(sibling), class: 'text-truncate' %>
|
|
19
|
+
<%= button_to t('lato_cms.translation_unlink_cta'), lato_cms.pages_unlink_translation_action_path(page),
|
|
20
|
+
method: :delete, params: { translation_page_id: sibling.id },
|
|
21
|
+
class: 'btn btn-sm btn-outline-danger', form: { data: { turbo_confirm: t('lato_cms.translation_unlink_confirm') } } %>
|
|
22
|
+
</div>
|
|
23
|
+
<% else %>
|
|
24
|
+
<%# No translation for this locale: let the user link an existing page. %>
|
|
25
|
+
<% candidates = page.translation_candidates(locale) %>
|
|
26
|
+
<% if candidates.any? %>
|
|
27
|
+
<%= form_with url: lato_cms.pages_link_translation_action_path(page), method: :post,
|
|
28
|
+
class: 'd-flex align-items-center gap-2 flex-grow-1 justify-content-end' do %>
|
|
29
|
+
<%= select_tag :translation_page_id,
|
|
30
|
+
options_from_collection_for_select(candidates, :id, :title),
|
|
31
|
+
class: 'form-select form-select-sm', style: 'max-width: 260px;' %>
|
|
32
|
+
<%= submit_tag t('lato_cms.translation_link_cta'), class: 'btn btn-sm btn-outline-primary' %>
|
|
33
|
+
<% end %>
|
|
34
|
+
<% else %>
|
|
35
|
+
<span class="text-muted small"><%= t('lato_cms.translation_no_candidates') %></span>
|
|
36
|
+
<% end %>
|
|
37
|
+
<% end %>
|
|
38
|
+
</div>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
41
|
+
<% end %>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
<div class="card-body">
|
|
15
15
|
<%= lato_index @pages,
|
|
16
|
-
custom_actions: {
|
|
16
|
+
custom_actions: lato_cms_admin? ? {
|
|
17
17
|
create: {
|
|
18
18
|
path: lato_cms.pages_create_path,
|
|
19
19
|
icon: 'bi bi-plus',
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
action_title: t('lato_cms.page_create_title')
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
},
|
|
28
|
+
} : {},
|
|
29
29
|
pagination_options: [10, 20, 50, 100]
|
|
30
30
|
%>
|
|
31
31
|
</div>
|
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
<i class="bi bi-globe fs-1 mb-3"></i>
|
|
21
21
|
<p class="mb-1"><%= t('lato_cms.show_no_preview_title') %></p>
|
|
22
22
|
<p class="small"><%= t('lato_cms.show_no_preview_description').html_safe %></p>
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
<% if lato_cms_admin? %>
|
|
24
|
+
<%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-primary mt-2',
|
|
25
|
+
data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
|
|
26
|
+
<% end %>
|
|
25
27
|
</div>
|
|
26
28
|
</div>
|
|
27
29
|
<% end %>
|
|
@@ -33,24 +35,24 @@
|
|
|
33
35
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
34
36
|
<h2 class="fs-5 mb-0"><%= t('lato_cms.show_editor_title') %></h2>
|
|
35
37
|
<div class="d-flex align-items-center gap-2">
|
|
36
|
-
<%=
|
|
38
|
+
<%= lato_cms_page_locale_dropdown(@page) %>
|
|
37
39
|
<span class="vr"></span>
|
|
38
|
-
<%=
|
|
40
|
+
<%= lato_cms_page_actions_dropdown(@page, show_edit: lato_cms_admin?, show_translations: lato_cms_admin?, show_delete: lato_cms_admin?, hide_show: true) %>
|
|
39
41
|
<span class="vr"></span>
|
|
40
|
-
<button class="btn btn-sm btn-outline-
|
|
41
|
-
title="<%= t('lato_cms.show_advanced_editor_tooltip') %>"
|
|
42
|
-
data-controller="lato-tooltip"
|
|
43
|
-
data-action="click->lato-cms-advanced-editor#toggle"
|
|
44
|
-
data-lato-cms-advanced-editor-target="toggleBtn">
|
|
45
|
-
<i class="bi bi-fullscreen"></i>
|
|
46
|
-
</button>
|
|
47
|
-
<button class="btn btn-sm btn-outline-secondary d-none"
|
|
42
|
+
<button class="btn btn-sm btn-outline-primary d-none"
|
|
48
43
|
title="<%= t('lato_cms.show_hide_panel_tooltip') %>"
|
|
49
44
|
data-controller="lato-tooltip"
|
|
50
45
|
data-action="click->lato-cms-advanced-editor#toggleSidebar"
|
|
51
46
|
data-lato-cms-advanced-editor-target="collapseBtn">
|
|
52
47
|
<i class="bi bi-layout-sidebar-reverse"></i>
|
|
53
48
|
</button>
|
|
49
|
+
<button class="btn btn-sm btn-outline-primary"
|
|
50
|
+
title="<%= t('lato_cms.show_advanced_editor_tooltip') %>"
|
|
51
|
+
data-controller="lato-tooltip"
|
|
52
|
+
data-action="click->lato-cms-advanced-editor#toggle"
|
|
53
|
+
data-lato-cms-advanced-editor-target="toggleBtn">
|
|
54
|
+
<i class="bi bi-fullscreen"></i>
|
|
55
|
+
</button>
|
|
54
56
|
</div>
|
|
55
57
|
</div>
|
|
56
58
|
<div class="card-body" style="overflow-y: auto; max-height: calc(100dvh - 230px);">
|
|
@@ -59,15 +61,19 @@
|
|
|
59
61
|
<i class="bi bi-file-earmark-code fs-1 mb-3 d-block"></i>
|
|
60
62
|
<p class="mb-1"><%= t('lato_cms.show_no_template_title') %></p>
|
|
61
63
|
<p class="small"><%= t('lato_cms.show_no_template_description') %></p>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
<% if lato_cms_admin? %>
|
|
65
|
+
<%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-primary mt-2',
|
|
66
|
+
data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
|
|
67
|
+
<% end %>
|
|
64
68
|
</div>
|
|
65
69
|
<% elsif !@page.template_available? %>
|
|
66
70
|
<div class="alert alert-warning">
|
|
67
71
|
<i class="bi bi-exclamation-triangle me-1"></i>
|
|
68
72
|
<%= t('lato_cms.show_template_unavailable', template_id: @page.template_id) %>
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
<% if lato_cms_admin? %>
|
|
74
|
+
<%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-warning ms-2',
|
|
75
|
+
data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
|
|
76
|
+
<% end %>
|
|
71
77
|
</div>
|
|
72
78
|
<% existing_fields = @page.fields.group_by(&:template_component_id) %>
|
|
73
79
|
<% if existing_fields.any? %>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<%= lato_page_head t('lato_cms.translations_title'), [
|
|
2
|
+
{ label: t('lato_cms.pages_index_title'), path: lato_cms.pages_path },
|
|
3
|
+
{ label: @page.title, path: lato_cms.pages_show_path(@page) },
|
|
4
|
+
{ label: t('lato_cms.translations_title') }
|
|
5
|
+
] %>
|
|
6
|
+
|
|
7
|
+
<div class="card mb-4">
|
|
8
|
+
<div class="card-header">
|
|
9
|
+
<h2 class="fs-4 mb-0"><%= t('lato_cms.translations_title') %></h2>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="card-body">
|
|
12
|
+
<%= render 'lato_cms/pages/translations', page: @page %>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
data/config/locales/en.yml
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
en:
|
|
2
|
+
activerecord:
|
|
3
|
+
attributes:
|
|
4
|
+
lato/user:
|
|
5
|
+
lato_cms_admin_role: Lato CMS role
|
|
2
6
|
lato_cms:
|
|
7
|
+
admin_roles:
|
|
8
|
+
none: No access
|
|
9
|
+
operator: Operator
|
|
10
|
+
admin: Administrator
|
|
3
11
|
cta_show: Show
|
|
4
12
|
cta_edit: Edit
|
|
5
13
|
cta_update: Update
|
|
@@ -11,6 +19,7 @@ en:
|
|
|
11
19
|
fields_saved: Fields saved successfully
|
|
12
20
|
page_deleted: Page deleted successfully
|
|
13
21
|
unauthorized_section: You do not have access to this section.
|
|
22
|
+
unauthorized_action: You do not have permission to perform this action.
|
|
14
23
|
page_delete_failed: Failed to delete page
|
|
15
24
|
api_page_not_found: Page not found
|
|
16
25
|
pages_index_title: Pages
|
|
@@ -23,6 +32,17 @@ en:
|
|
|
23
32
|
form_no_template_option: "-- No template --"
|
|
24
33
|
form_template_unavailable: "The template \"%{template_id}\" is no longer available. Please select a different template."
|
|
25
34
|
action_view_frontend: View
|
|
35
|
+
cta_actions: Actions
|
|
36
|
+
translations_title: Translations
|
|
37
|
+
translation_no_other_locales: No other locales are configured.
|
|
38
|
+
translation_link_cta: Link
|
|
39
|
+
translation_unlink_cta: Unlink
|
|
40
|
+
translation_unlink_confirm: Are you sure you want to unlink this translation?
|
|
41
|
+
translation_no_candidates: No pages available for this language
|
|
42
|
+
translation_linked: Translation linked successfully
|
|
43
|
+
translation_unlinked: Translation unlinked successfully
|
|
44
|
+
translation_link_failed: Failed to link translation
|
|
45
|
+
translation_unlink_failed: Failed to unlink translation
|
|
26
46
|
show_no_preview_title: No preview available
|
|
27
47
|
show_no_preview_description: Set the <strong>Frontend URL</strong> to see the page preview.
|
|
28
48
|
show_edit_settings_cta: Edit page settings
|
|
@@ -42,6 +62,11 @@ en:
|
|
|
42
62
|
component_inactive_label: This component is not active
|
|
43
63
|
component_disabled_alert: This component is disabled and will not be exposed by API.
|
|
44
64
|
component_state_updated: Component state updated
|
|
65
|
+
component_clone_cta: Clone
|
|
66
|
+
component_clone_from: "Clone from %{lang}"
|
|
67
|
+
component_clone_confirm: "Replace this component's fields with the ones from the %{lang} page?"
|
|
68
|
+
component_cloned: Component cloned successfully
|
|
69
|
+
component_clone_failed: Failed to clone component
|
|
45
70
|
component_state_saving: Updating...
|
|
46
71
|
component_state_saved: State updated
|
|
47
72
|
component_state_save_error: Failed to update state
|
data/config/locales/it.yml
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
it:
|
|
2
|
+
activerecord:
|
|
3
|
+
attributes:
|
|
4
|
+
lato/user:
|
|
5
|
+
lato_cms_admin_role: Ruolo Lato CMS
|
|
2
6
|
lato_cms:
|
|
7
|
+
admin_roles:
|
|
8
|
+
none: Nessun accesso
|
|
9
|
+
operator: Operatore
|
|
10
|
+
admin: Amministratore
|
|
3
11
|
cta_show: Visualizza
|
|
4
12
|
cta_edit: Modifica
|
|
5
13
|
cta_update: Aggiorna
|
|
@@ -11,6 +19,7 @@ it:
|
|
|
11
19
|
fields_saved: Campi salvati con successo
|
|
12
20
|
page_deleted: Pagina eliminata con successo
|
|
13
21
|
unauthorized_section: Non hai accesso a questa sezione.
|
|
22
|
+
unauthorized_action: Non hai i permessi per eseguire questa azione.
|
|
14
23
|
page_delete_failed: Impossibile eliminare la pagina
|
|
15
24
|
api_page_not_found: Pagina non trovata
|
|
16
25
|
pages_index_title: Pagine
|
|
@@ -23,6 +32,17 @@ it:
|
|
|
23
32
|
form_no_template_option: "-- Nessun template --"
|
|
24
33
|
form_template_unavailable: "Il template \"%{template_id}\" non e piu disponibile. Seleziona un template diverso."
|
|
25
34
|
action_view_frontend: Anteprima
|
|
35
|
+
cta_actions: Azioni
|
|
36
|
+
translations_title: Traduzioni
|
|
37
|
+
translation_no_other_locales: Nessuna altra lingua configurata.
|
|
38
|
+
translation_link_cta: Collega
|
|
39
|
+
translation_unlink_cta: Scollega
|
|
40
|
+
translation_unlink_confirm: Sei sicuro di voler scollegare questa traduzione?
|
|
41
|
+
translation_no_candidates: Nessuna pagina disponibile per questa lingua
|
|
42
|
+
translation_linked: Traduzione collegata con successo
|
|
43
|
+
translation_unlinked: Traduzione scollegata con successo
|
|
44
|
+
translation_link_failed: Impossibile collegare la traduzione
|
|
45
|
+
translation_unlink_failed: Impossibile scollegare la traduzione
|
|
26
46
|
show_no_preview_title: Anteprima non disponibile
|
|
27
47
|
show_no_preview_description: Imposta il <strong>URL Frontend</strong> per visualizzare l'anteprima della pagina.
|
|
28
48
|
show_edit_settings_cta: Modifica impostazioni pagina
|
|
@@ -42,6 +62,11 @@ it:
|
|
|
42
62
|
component_inactive_label: Questa componente non e attiva
|
|
43
63
|
component_disabled_alert: Questa componente e disattivata e non verra esposta dalle API.
|
|
44
64
|
component_state_updated: Stato componente aggiornato
|
|
65
|
+
component_clone_cta: Clona
|
|
66
|
+
component_clone_from: "Clona da %{lang}"
|
|
67
|
+
component_clone_confirm: "Sostituire i campi di questa componente con quelli della pagina in %{lang}?"
|
|
68
|
+
component_cloned: Componente clonata con successo
|
|
69
|
+
component_clone_failed: Impossibile clonare la componente
|
|
45
70
|
component_state_saving: Aggiornamento in corso...
|
|
46
71
|
component_state_saved: Stato aggiornato
|
|
47
72
|
component_state_save_error: Errore durante aggiornamento stato
|
data/config/routes.rb
CHANGED
|
@@ -17,6 +17,10 @@ LatoCms::Engine.routes.draw do
|
|
|
17
17
|
patch ':id/update', to: 'pages#update_action', as: :pages_update_action
|
|
18
18
|
post ':id/fields', to: 'pages#save_fields_action', as: :pages_save_fields_action
|
|
19
19
|
patch ':id/components/:template_component_id/toggle', to: 'pages#toggle_component_action', as: :pages_toggle_component_action
|
|
20
|
+
post ':id/components/:template_component_id/clone', to: 'pages#clone_component_action', as: :pages_clone_component_action
|
|
21
|
+
get ':id/translations', to: 'pages#translations', as: :pages_translations
|
|
22
|
+
post ':id/translations', to: 'pages#link_translation_action', as: :pages_link_translation_action
|
|
23
|
+
delete ':id/translations', to: 'pages#unlink_translation_action', as: :pages_unlink_translation_action
|
|
20
24
|
delete ':id', to: 'pages#destroy_action', as: :pages_destroy_action
|
|
21
25
|
end
|
|
22
26
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class AddTranslationGroupIdToLatoCmsPages < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
# Pages sharing the same translation_group_id are translations of each other
|
|
4
|
+
# (one page per locale within a group). Nil means the page is not linked.
|
|
5
|
+
add_column :lato_cms_pages, :translation_group_id, :string
|
|
6
|
+
add_index :lato_cms_pages, :translation_group_id
|
|
7
|
+
end
|
|
8
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
class ChangeLatoCmsAdminToLatoCmsAdminRoleOnLatoUser < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
add_column :lato_users, :lato_cms_admin_role, :integer, default: 0, null: false
|
|
4
|
+
|
|
5
|
+
# Backfill: true -> 1, false/nil -> 0 (already the column default)
|
|
6
|
+
execute 'UPDATE lato_users SET lato_cms_admin_role = 1 WHERE lato_cms_admin = 1'
|
|
7
|
+
|
|
8
|
+
remove_column :lato_users, :lato_cms_admin
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def down
|
|
12
|
+
add_column :lato_users, :lato_cms_admin, :boolean, default: false
|
|
13
|
+
|
|
14
|
+
execute 'UPDATE lato_users SET lato_cms_admin = 1 WHERE lato_cms_admin_role = 1'
|
|
15
|
+
|
|
16
|
+
remove_column :lato_users, :lato_cms_admin_role
|
|
17
|
+
end
|
|
18
|
+
end
|
data/lib/lato_cms/config.rb
CHANGED
|
@@ -3,11 +3,18 @@ module LatoCms
|
|
|
3
3
|
# This class contains the default configuration of the engine.
|
|
4
4
|
##
|
|
5
5
|
class Config
|
|
6
|
-
attr_accessor :locales, :templates_path
|
|
6
|
+
attr_accessor :locales, :templates_path, :admin_roles
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
9
|
@locales = [:en]
|
|
10
10
|
@templates_path = 'config/lato_cms'
|
|
11
|
+
|
|
12
|
+
# Admin roles exposed on Lato::User#lato_cms_admin_role and rendered
|
|
13
|
+
# as a select by lato_users. Ordered map of role key => integer value;
|
|
14
|
+
# labels are resolved via i18n (lato_cms.admin_roles.<key>).
|
|
15
|
+
# `operator` has read/edit access; `admin` also manages pages
|
|
16
|
+
# (create, update, delete) and translation links.
|
|
17
|
+
@admin_roles = { none: 0, operator: 1, admin: 2 }
|
|
11
18
|
end
|
|
12
19
|
end
|
|
13
20
|
end
|
data/lib/lato_cms/engine.rb
CHANGED
|
@@ -9,5 +9,32 @@ module LatoCms
|
|
|
9
9
|
initializer "lato_cms.precompile" do |app|
|
|
10
10
|
app.config.assets.precompile << "lato_cms_manifest.js"
|
|
11
11
|
end
|
|
12
|
+
|
|
13
|
+
# Expose the CMS admin role options on the shared Lato::User model.
|
|
14
|
+
# lato_users (when mounted) discovers the `lato_cms_admin_role` column by
|
|
15
|
+
# convention and calls this method to build the role selector. Defined
|
|
16
|
+
# here so it works regardless of whether lato_users is installed.
|
|
17
|
+
config.to_prepare do
|
|
18
|
+
Lato::User.class_eval do
|
|
19
|
+
# Role options for the lato_cms_admin_role permission.
|
|
20
|
+
# Returns [[label, value], ...] with labels resolved at call time.
|
|
21
|
+
def self.lato_cms_admin_role_options
|
|
22
|
+
LatoCms.config.admin_roles.map do |key, value|
|
|
23
|
+
[I18n.t("lato_cms.admin_roles.#{key}"), value]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# True when the user can access the CMS section (operator or admin).
|
|
28
|
+
def lato_cms_access?
|
|
29
|
+
lato_cms_admin_role.to_i.positive?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# True when the user has the admin role: full page management
|
|
33
|
+
# (create, update, delete) and translation links.
|
|
34
|
+
def lato_cms_admin?
|
|
35
|
+
lato_cms_admin_role.to_i >= LatoCms.config.admin_roles.fetch(:admin, 0)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
12
39
|
end
|
|
13
40
|
end
|
data/lib/lato_cms/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lato_cms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.7
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gregorio Galante
|
|
@@ -120,6 +120,7 @@ files:
|
|
|
120
120
|
- app/views/lato_cms/pages/_form_update.html.erb
|
|
121
121
|
- app/views/lato_cms/pages/_repeater.html.erb
|
|
122
122
|
- app/views/lato_cms/pages/_repeater_item.html.erb
|
|
123
|
+
- app/views/lato_cms/pages/_translations.html.erb
|
|
123
124
|
- app/views/lato_cms/pages/create.html.erb
|
|
124
125
|
- app/views/lato_cms/pages/fields/_boolean.html.erb
|
|
125
126
|
- app/views/lato_cms/pages/fields/_color.html.erb
|
|
@@ -137,6 +138,7 @@ files:
|
|
|
137
138
|
- app/views/lato_cms/pages/fields/_textarea.html.erb
|
|
138
139
|
- app/views/lato_cms/pages/index.html.erb
|
|
139
140
|
- app/views/lato_cms/pages/show.html.erb
|
|
141
|
+
- app/views/lato_cms/pages/translations.html.erb
|
|
140
142
|
- app/views/lato_cms/pages/update.html.erb
|
|
141
143
|
- config/importmap.rb
|
|
142
144
|
- config/locales/en.yml
|
|
@@ -145,6 +147,8 @@ files:
|
|
|
145
147
|
- db/migrate/20250328072343_add_lato_cms_admin_to_lato_user.rb
|
|
146
148
|
- db/migrate/20260320070819_create_lato_cms_pages.rb
|
|
147
149
|
- db/migrate/20260323171241_create_lato_cms_page_fields.rb
|
|
150
|
+
- db/migrate/20260705000000_add_translation_group_id_to_lato_cms_pages.rb
|
|
151
|
+
- db/migrate/20260712092241_change_lato_cms_admin_to_lato_cms_admin_role_on_lato_user.rb
|
|
148
152
|
- lib/lato_cms.rb
|
|
149
153
|
- lib/lato_cms/config.rb
|
|
150
154
|
- lib/lato_cms/engine.rb
|