comfortable_media_surfer 3.1.7 → 3.2.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/.github/workflows/claude-code-review.yml +2 -0
- data/.github/workflows/claude.yml +99 -1
- data/.github/workflows/coveralls.yml +4 -3
- data/.github/workflows/rubyonrails.yml +34 -5
- data/CHANGELOG.md +12 -1
- data/CONTRIBUTING.md +5 -1
- data/Gemfile +6 -5
- data/README.md +4 -4
- data/Rakefile +201 -0
- data/app/assets/javascripts/comfy/admin/cms/application.js +3 -0
- data/app/assets/javascripts/comfy/admin/cms/base.js +1 -0
- data/app/assets/javascripts/comfy/admin/cms/file_link.js +34 -11
- data/app/assets/javascripts/comfy/admin/cms/file_upload.js +37 -28
- data/app/assets/javascripts/comfy/admin/cms/files_modal.js +76 -26
- data/app/controllers/comfy/admin/cms/categories_controller.rb +1 -1
- data/app/controllers/comfy/admin/cms/files_controller.rb +1 -1
- data/app/controllers/comfy/admin/cms/layouts_controller.rb +1 -1
- data/app/controllers/comfy/admin/cms/pages_controller.rb +33 -4
- data/app/controllers/comfy/admin/cms/revisions/base_controller.rb +2 -2
- data/app/controllers/comfy/admin/cms/revisions/page_controller.rb +5 -5
- data/app/controllers/comfy/admin/cms/revisions/translation_controller.rb +5 -5
- data/app/controllers/comfy/admin/cms/sites_controller.rb +3 -3
- data/app/controllers/comfy/admin/cms/snippets_controller.rb +1 -1
- data/app/controllers/comfy/admin/cms/translations_controller.rb +15 -3
- data/app/controllers/comfy/cms/base_controller.rb +16 -7
- data/app/controllers/comfy/cms/content_controller.rb +4 -4
- data/app/controllers/concerns/comfy/reorder_action.rb +3 -4
- data/app/models/comfy/cms/categorization.rb +9 -0
- data/app/models/comfy/cms/category.rb +1 -1
- data/app/models/comfy/cms/fragment.rb +7 -0
- data/app/models/comfy/cms/layout.rb +7 -2
- data/app/models/comfy/cms/page.rb +13 -4
- data/app/models/comfy/cms/site.rb +3 -3
- data/app/models/comfy/cms/translation.rb +5 -0
- data/app/models/concerns/comfy/cms/with_categories.rb +1 -1
- data/app/models/concerns/comfy/cms/with_fragments.rb +1 -1
- data/app/views/comfy/admin/cms/pages/edit.html.haml +5 -1
- data/config/database.yml +1 -1
- data/config/initializers/json.rb +2 -0
- data/config/locales/ar.yml +6 -0
- data/config/locales/ca.yml +6 -0
- data/config/locales/cs.yml +6 -0
- data/config/locales/da.yml +6 -0
- data/config/locales/de.yml +6 -0
- data/config/locales/en.yml +6 -0
- data/config/locales/es.yml +6 -0
- data/config/locales/fi.yml +6 -0
- data/config/locales/fr.yml +6 -0
- data/config/locales/gr.yml +6 -0
- data/config/locales/hr.yml +6 -0
- data/config/locales/it.yml +6 -0
- data/config/locales/ja.yml +6 -0
- data/config/locales/nb.yml +6 -0
- data/config/locales/nl.yml +6 -0
- data/config/locales/pl.yml +6 -0
- data/config/locales/pt-BR.yml +6 -0
- data/config/locales/ru.yml +6 -0
- data/config/locales/sk.yml +6 -0
- data/config/locales/sv.yml +6 -0
- data/config/locales/tr.yml +6 -0
- data/config/locales/uk.yml +6 -0
- data/config/locales/zh-CN.yml +6 -0
- data/config/locales/zh-TW.yml +6 -0
- data/config/storage.yml +1 -1
- data/gemfiles/7.2.gemfile +1 -0
- data/gemfiles/8.0.gemfile +1 -0
- data/gemfiles/8.1.gemfile +1 -0
- data/lib/comfortable_media_surfer/access_control/admin_authentication.rb +2 -2
- data/lib/comfortable_media_surfer/extensions/has_revisions.rb +2 -2
- data/lib/comfortable_media_surfer/routing.rb +2 -0
- data/lib/comfortable_media_surfer/seeds/page/importer.rb +40 -12
- data/lib/comfortable_media_surfer/seeds.rb +1 -3
- data/lib/comfortable_media_surfer/version.rb +1 -1
- metadata +2 -3
- data/gemfiles/7.1.gemfile +0 -35
|
@@ -41,7 +41,7 @@ import plupload from "plupload";
|
|
|
41
41
|
|
|
42
42
|
destroy() {
|
|
43
43
|
this.uploader.destroy();
|
|
44
|
-
for (const cleanupFn of this.cleanupFns) {
|
|
44
|
+
for (const cleanupFn of this.cleanupFns.splice(0)) {
|
|
45
45
|
cleanupFn();
|
|
46
46
|
}
|
|
47
47
|
}
|
|
@@ -75,9 +75,13 @@ import plupload from "plupload";
|
|
|
75
75
|
this.addCleanup(() => {
|
|
76
76
|
document.removeEventListener("dragenter", onDragEnter);
|
|
77
77
|
});
|
|
78
|
+
const onDragEnd = () => {
|
|
79
|
+
this.ui.dropElement.classList.remove(DROP_TARGET_ACTIVE_CLASS);
|
|
80
|
+
};
|
|
78
81
|
for (const eventName of ["drop", "dragleave"]) {
|
|
79
|
-
this.ui.dropElement.addEventListener(eventName,
|
|
80
|
-
|
|
82
|
+
this.ui.dropElement.addEventListener(eventName, onDragEnd);
|
|
83
|
+
this.addCleanup(() => {
|
|
84
|
+
this.ui.dropElement.removeEventListener(eventName, onDragEnd);
|
|
81
85
|
});
|
|
82
86
|
}
|
|
83
87
|
}
|
|
@@ -131,11 +135,9 @@ import plupload from "plupload";
|
|
|
131
135
|
}
|
|
132
136
|
|
|
133
137
|
addFile(file) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
138
|
-
this.fileListItem(file)
|
|
138
|
+
const listItem = FileUpload.buildListItem(file);
|
|
139
|
+
this.ui.list.prepend(listItem);
|
|
140
|
+
listItem
|
|
139
141
|
.querySelector(".cms-uploader-file-delete")
|
|
140
142
|
.addEventListener("click", (evt) => {
|
|
141
143
|
evt.preventDefault();
|
|
@@ -158,7 +160,7 @@ import plupload from "plupload";
|
|
|
158
160
|
case plupload.FAILED:
|
|
159
161
|
progressBar.style.width = "100%";
|
|
160
162
|
progressBar.classList.add("progress-bar-danger");
|
|
161
|
-
progressBar.querySelector("span").
|
|
163
|
+
progressBar.querySelector("span").textContent = file.error_message;
|
|
162
164
|
break;
|
|
163
165
|
}
|
|
164
166
|
}
|
|
@@ -167,19 +169,22 @@ import plupload from "plupload";
|
|
|
167
169
|
return this.ui.container.querySelector(`#${id}`);
|
|
168
170
|
}
|
|
169
171
|
|
|
170
|
-
static
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
172
|
+
static buildListItem({ id, name }) {
|
|
173
|
+
const listItem = document.createElement("li");
|
|
174
|
+
listItem.id = id;
|
|
175
|
+
listItem.className = "row temp";
|
|
176
|
+
listItem.innerHTML = `<div class='col-md-9 d-flex align-items-center'>
|
|
177
|
+
<div class='progress'>
|
|
178
|
+
<div class='progress-bar progress-bar-striped progress-bar-animated'>
|
|
179
|
+
<span></span>
|
|
177
180
|
</div>
|
|
178
181
|
</div>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
</
|
|
182
|
-
</
|
|
182
|
+
</div>
|
|
183
|
+
<div class='col-md-3 d-flex align-items-center justify-content-md-end'>
|
|
184
|
+
<a class='btn btn-sm btn-danger float-right cms-uploader-file-delete' href='#'>Delete</a>
|
|
185
|
+
</div>`;
|
|
186
|
+
listItem.querySelector("span").textContent = name;
|
|
187
|
+
return listItem;
|
|
183
188
|
}
|
|
184
189
|
|
|
185
190
|
static defaultUploaderSettings(id) {
|
|
@@ -194,12 +199,15 @@ import plupload from "plupload";
|
|
|
194
199
|
}
|
|
195
200
|
}
|
|
196
201
|
|
|
197
|
-
const uploaders =
|
|
202
|
+
const uploaders = new Map();
|
|
198
203
|
window.CMS.fileUpload = {
|
|
199
204
|
init(root = document) {
|
|
200
|
-
const el = root.
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
const el = root.matches?.("#cms-uploader")
|
|
206
|
+
? root
|
|
207
|
+
: root.querySelector("#cms-uploader");
|
|
208
|
+
if (el === null || uploaders.has(el)) return;
|
|
209
|
+
uploaders.set(
|
|
210
|
+
el,
|
|
203
211
|
new FileUpload(el, {
|
|
204
212
|
url: el.dataset.cmsUploaderUrl,
|
|
205
213
|
multipart_params: {
|
|
@@ -210,11 +218,12 @@ import plupload from "plupload";
|
|
|
210
218
|
})
|
|
211
219
|
);
|
|
212
220
|
},
|
|
213
|
-
dispose() {
|
|
214
|
-
for (const uploader of
|
|
215
|
-
|
|
221
|
+
dispose(root = null) {
|
|
222
|
+
for (const [el, uploader] of uploaders) {
|
|
223
|
+
if (root !== null && el !== root && !root.contains(el)) continue;
|
|
224
|
+
uploader.destroy();
|
|
225
|
+
uploaders.delete(el);
|
|
216
226
|
}
|
|
217
|
-
uploaders.length = 0;
|
|
218
227
|
},
|
|
219
228
|
};
|
|
220
229
|
})();
|
|
@@ -1,47 +1,97 @@
|
|
|
1
|
-
import Modal from "bootstrap/js/src/
|
|
1
|
+
import Modal from "bootstrap/js/src/modal";
|
|
2
2
|
|
|
3
3
|
// Site files modal.
|
|
4
4
|
(() => {
|
|
5
5
|
let modal = null;
|
|
6
|
+
let modalToggle = null;
|
|
7
|
+
let modalContainer = null;
|
|
8
|
+
let modalContent = null;
|
|
9
|
+
let onModalToggle = null;
|
|
10
|
+
let onModalContentDragStart = null;
|
|
11
|
+
let requestId = 0;
|
|
6
12
|
|
|
7
|
-
const initModalContent = (
|
|
13
|
+
const initModalContent = () => {
|
|
8
14
|
window.CMS.fileUpload.init(modalContent);
|
|
9
15
|
window.CMS.fileLinks(modalContent);
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const dispose = () => {
|
|
19
|
+
requestId += 1;
|
|
20
|
+
if (modalToggle !== null && onModalToggle !== null) {
|
|
21
|
+
modalToggle.removeEventListener("click", onModalToggle);
|
|
22
|
+
}
|
|
23
|
+
if (modalContent !== null && onModalContentDragStart !== null) {
|
|
24
|
+
modalContent.removeEventListener("dragstart", onModalContentDragStart);
|
|
25
|
+
window.CMS.fileUpload.dispose(modalContent);
|
|
26
|
+
window.CMS.fileLinks.dispose(modalContent);
|
|
27
|
+
}
|
|
28
|
+
if (modal !== null) {
|
|
29
|
+
const wasAnimated = modalContainer.classList.contains("fade");
|
|
30
|
+
modalContainer.classList.remove("fade");
|
|
31
|
+
modal.hide();
|
|
32
|
+
if (wasAnimated) modalContainer.classList.add("fade");
|
|
33
|
+
modal.dispose();
|
|
34
|
+
}
|
|
35
|
+
modal = null;
|
|
36
|
+
modalToggle = null;
|
|
37
|
+
modalContainer = null;
|
|
38
|
+
modalContent = null;
|
|
39
|
+
onModalToggle = null;
|
|
40
|
+
onModalContentDragStart = null;
|
|
19
41
|
};
|
|
20
42
|
|
|
21
43
|
window.CMS.files = {
|
|
22
44
|
init() {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
if (
|
|
26
|
-
|
|
27
|
-
|
|
45
|
+
const nextModalToggle = document.querySelector(".cms-files-open-modal");
|
|
46
|
+
const nextModalContainer = document.querySelector(".cms-files-modal");
|
|
47
|
+
if (nextModalToggle === null || nextModalContainer === null) return;
|
|
48
|
+
if (
|
|
49
|
+
modalToggle === nextModalToggle &&
|
|
50
|
+
modalContainer === nextModalContainer
|
|
51
|
+
) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
dispose();
|
|
56
|
+
modalToggle = nextModalToggle;
|
|
57
|
+
modalContainer = nextModalContainer;
|
|
58
|
+
modalContent = modalContainer.querySelector(".modal-content");
|
|
59
|
+
onModalContentDragStart = (evt) => {
|
|
60
|
+
if (
|
|
61
|
+
evt.target.nodeType === Node.ELEMENT_NODE &&
|
|
62
|
+
evt.target.matches(".cms-uploader-filelist .item-title a") &&
|
|
63
|
+
modal !== null
|
|
64
|
+
) {
|
|
65
|
+
modal.hide();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
modalContent.addEventListener("dragstart", onModalContentDragStart);
|
|
69
|
+
onModalToggle = (evt) => {
|
|
28
70
|
evt.preventDefault();
|
|
29
|
-
|
|
71
|
+
const currentRequestId = ++requestId;
|
|
72
|
+
const currentModalContent = modalContent;
|
|
73
|
+
const url = new URL(modalContainer.dataset.url, document.location.href);
|
|
74
|
+
url.username = "";
|
|
75
|
+
url.password = "";
|
|
76
|
+
fetch(url, { credentials: "same-origin" })
|
|
30
77
|
.then((resp) => resp.text())
|
|
31
78
|
.then((html) => {
|
|
79
|
+
if (
|
|
80
|
+
currentRequestId !== requestId ||
|
|
81
|
+
currentModalContent !== modalContent
|
|
82
|
+
) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
window.CMS.fileUpload.dispose(modalContent);
|
|
86
|
+
window.CMS.fileLinks.dispose(modalContent);
|
|
32
87
|
modalContent.innerHTML = `<div class="modal-body">${html}</div>`;
|
|
33
|
-
initModalContent(
|
|
88
|
+
initModalContent();
|
|
34
89
|
});
|
|
35
90
|
modal = modal || new Modal(modalContainer);
|
|
36
91
|
modal.show();
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
dispose() {
|
|
40
|
-
if (modal !== null) {
|
|
41
|
-
modal.hide();
|
|
42
|
-
modal.dispose();
|
|
43
|
-
modal = null;
|
|
44
|
-
}
|
|
92
|
+
};
|
|
93
|
+
modalToggle.addEventListener("click", onModalToggle);
|
|
45
94
|
},
|
|
95
|
+
dispose,
|
|
46
96
|
};
|
|
47
97
|
})();
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Comfy::Admin::Cms::PagesController < Comfy::Admin::Cms::BaseController
|
|
4
|
+
FRAGMENT_PARAMS = [
|
|
5
|
+
:identifier, :tag, :content, :datetime, :boolean, :files,
|
|
6
|
+
{ files: [], file_ids_destroy: [] }
|
|
7
|
+
].freeze
|
|
8
|
+
|
|
4
9
|
include ::Comfy::ReorderAction
|
|
5
10
|
|
|
6
11
|
self.reorder_action_resource = ::Comfy::Cms::Page
|
|
7
12
|
|
|
8
13
|
before_action :check_for_layouts, only: %i[new edit]
|
|
9
14
|
before_action :build_page, only: %i[new create]
|
|
10
|
-
before_action :load_page, only: %i[edit update destroy]
|
|
15
|
+
before_action :load_page, only: %i[edit update destroy publish_children unpublish_children]
|
|
11
16
|
|
|
12
17
|
before_action :authorize
|
|
13
|
-
|
|
18
|
+
before_action :assign_page_params, only: :update
|
|
14
19
|
before_action :preview_page, only: %i[create update]
|
|
15
20
|
|
|
16
21
|
def index
|
|
@@ -89,8 +94,26 @@ class Comfy::Admin::Cms::PagesController < Comfy::Admin::Cms::BaseController
|
|
|
89
94
|
render nothing: true
|
|
90
95
|
end
|
|
91
96
|
|
|
97
|
+
def publish_children
|
|
98
|
+
update_children_publication(true, :children_published)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def unpublish_children
|
|
102
|
+
update_children_publication(false, :children_unpublished)
|
|
103
|
+
end
|
|
104
|
+
|
|
92
105
|
protected
|
|
93
106
|
|
|
107
|
+
def update_children_publication(is_published, message)
|
|
108
|
+
descendant_ids = @page.descendants.map(&:id)
|
|
109
|
+
@site.pages
|
|
110
|
+
.where(id: descendant_ids)
|
|
111
|
+
.update_all(is_published: is_published, updated_at: Time.current)
|
|
112
|
+
default = is_published ? 'Child pages published' : 'Child pages unpublished'
|
|
113
|
+
flash[:success] = I18n.t("comfy.admin.cms.pages.#{message}", default: default)
|
|
114
|
+
redirect_to action: :edit, id: @page
|
|
115
|
+
end
|
|
116
|
+
|
|
94
117
|
def index_for_redactor
|
|
95
118
|
tree_walker = ->(page, list, offset) do
|
|
96
119
|
return unless page.present?
|
|
@@ -134,12 +157,15 @@ protected
|
|
|
134
157
|
|
|
135
158
|
def load_page
|
|
136
159
|
@page = @site.pages.find(params[:id])
|
|
137
|
-
@page.attributes = page_params
|
|
138
160
|
rescue ActiveRecord::RecordNotFound
|
|
139
161
|
flash[:danger] = I18n.t('comfy.admin.cms.pages.not_found')
|
|
140
162
|
redirect_to action: :index
|
|
141
163
|
end
|
|
142
164
|
|
|
165
|
+
def assign_page_params
|
|
166
|
+
@page.assign_attributes(page_params)
|
|
167
|
+
end
|
|
168
|
+
|
|
143
169
|
def preview_page
|
|
144
170
|
return unless params[:preview]
|
|
145
171
|
|
|
@@ -159,6 +185,9 @@ protected
|
|
|
159
185
|
end
|
|
160
186
|
|
|
161
187
|
def page_params
|
|
162
|
-
params.fetch(:page, {}).permit
|
|
188
|
+
params.fetch(:page, {}).permit(
|
|
189
|
+
:label, :slug, :parent_id, :layout_id, :target_page_id, :is_published,
|
|
190
|
+
category_ids: [], fragments_attributes: FRAGMENT_PARAMS
|
|
191
|
+
)
|
|
163
192
|
end
|
|
164
193
|
end
|
|
@@ -17,8 +17,8 @@ class Comfy::Admin::Cms::Revisions::BaseController < Comfy::Admin::Cms::BaseCont
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def show
|
|
20
|
-
@current_content = @record.revision_fields.
|
|
21
|
-
@versioned_content = @record.revision_fields.
|
|
20
|
+
@current_content = @record.revision_fields.to_h { |field| [field, @record.send(field)] }
|
|
21
|
+
@versioned_content = @record.revision_fields.to_h { |field| [field, @revision.data[field]] }
|
|
22
22
|
|
|
23
23
|
render 'comfy/admin/cms/revisions/show'
|
|
24
24
|
end
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
class Comfy::Admin::Cms::Revisions::PageController < Comfy::Admin::Cms::Revisions::BaseController
|
|
4
4
|
def show
|
|
5
|
-
@current_content = @record.fragments.
|
|
6
|
-
|
|
5
|
+
@current_content = @record.fragments.to_h do |fragment|
|
|
6
|
+
[fragment.identifier, fragment.content]
|
|
7
7
|
end
|
|
8
|
-
@versioned_content = @record.fragments.
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
@versioned_content = @record.fragments.to_h do |fragment|
|
|
9
|
+
data = @revision.data['fragments_attributes'].detect { |item| item[:identifier] == fragment.identifier }
|
|
10
|
+
[fragment.identifier, data.try(:[], :content)]
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
render 'comfy/admin/cms/revisions/show'
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
class Comfy::Admin::Cms::Revisions::TranslationController < Comfy::Admin::Cms::Revisions::BaseController
|
|
4
4
|
def show
|
|
5
|
-
@current_content = @record.fragments.
|
|
6
|
-
|
|
5
|
+
@current_content = @record.fragments.to_h do |fragment|
|
|
6
|
+
[fragment.identifier, fragment.content]
|
|
7
7
|
end
|
|
8
|
-
@versioned_content = @record.fragments.
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
@versioned_content = @record.fragments.to_h do |fragment|
|
|
9
|
+
data = @revision.data['fragments_attributes'].detect { |item| item[:identifier] == fragment.identifier }
|
|
10
|
+
[fragment.identifier, data.try(:[], :content)]
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
render 'comfy/admin/cms/revisions/show'
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Comfy::Admin::Cms::SitesController < Comfy::Admin::Cms::BaseController
|
|
4
|
-
skip_before_action
|
|
5
|
-
|
|
4
|
+
skip_before_action :load_admin_site,
|
|
5
|
+
:load_seeds
|
|
6
6
|
|
|
7
7
|
before_action :build_site, only: %i[new create]
|
|
8
8
|
before_action :load_site, only: %i[edit update destroy]
|
|
@@ -63,6 +63,6 @@ protected
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def site_params
|
|
66
|
-
params.fetch(:site, {}).permit
|
|
66
|
+
params.fetch(:site, {}).permit(:label, :identifier, :hostname, :path, :locale)
|
|
67
67
|
end
|
|
68
68
|
end
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Comfy::Admin::Cms::TranslationsController < Comfy::Admin::Cms::BaseController
|
|
4
|
+
FRAGMENT_PARAMS = [
|
|
5
|
+
:identifier, :tag, :content, :datetime, :boolean, :files,
|
|
6
|
+
{ files: [], file_ids_destroy: [] }
|
|
7
|
+
].freeze
|
|
8
|
+
|
|
4
9
|
helper_method :translation_select_options
|
|
5
10
|
|
|
6
11
|
before_action :load_page
|
|
7
12
|
before_action :build_translation, only: %i[new create]
|
|
8
13
|
before_action :load_translation, only: %i[edit update destroy]
|
|
9
14
|
before_action :authorize
|
|
15
|
+
before_action :assign_translation_params, only: :update
|
|
10
16
|
before_action :preview_translation, only: %i[create update]
|
|
11
17
|
|
|
12
18
|
def new
|
|
@@ -27,7 +33,7 @@ class Comfy::Admin::Cms::TranslationsController < Comfy::Admin::Cms::BaseControl
|
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
def update
|
|
30
|
-
@translation.
|
|
36
|
+
@translation.save!
|
|
31
37
|
flash[:success] = I18n.t('comfy.admin.cms.translations.updated')
|
|
32
38
|
redirect_to action: :edit, id: @translation
|
|
33
39
|
rescue ActiveRecord::RecordInvalid
|
|
@@ -74,14 +80,20 @@ private
|
|
|
74
80
|
|
|
75
81
|
def load_translation
|
|
76
82
|
@translation = @page.translations.find(params[:id])
|
|
77
|
-
@translation.attributes = translation_params
|
|
78
83
|
rescue ActiveRecord::RecordNotFound
|
|
79
84
|
flash[:danger] = I18n.t('comfy.admin.cms.translations.not_found')
|
|
80
85
|
redirect_to edit_comfy_admin_cms_site_page_path(@site, @page)
|
|
81
86
|
end
|
|
82
87
|
|
|
88
|
+
def assign_translation_params
|
|
89
|
+
@translation.assign_attributes(translation_params)
|
|
90
|
+
end
|
|
91
|
+
|
|
83
92
|
def translation_params
|
|
84
|
-
params.fetch(:translation, {}).permit
|
|
93
|
+
params.fetch(:translation, {}).permit(
|
|
94
|
+
:locale, :label, :layout_id, :is_published,
|
|
95
|
+
fragments_attributes: FRAGMENT_PARAMS
|
|
96
|
+
)
|
|
85
97
|
end
|
|
86
98
|
|
|
87
99
|
def preview_translation
|
|
@@ -8,19 +8,28 @@ class Comfy::Cms::BaseController < ComfortableMediaSurfer.config.public_base_con
|
|
|
8
8
|
protected
|
|
9
9
|
|
|
10
10
|
def load_cms_site
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
route_site_id = request.path_parameters[:site_id]
|
|
12
|
+
@cms_site ||= if route_site_id
|
|
13
|
+
find_cms_site_for_asset(route_site_id)
|
|
14
|
+
else
|
|
15
|
+
::Comfy::Cms::Site.find_site(request.host_with_port.downcase, request.fullpath)
|
|
16
|
+
end
|
|
17
17
|
|
|
18
18
|
raise ActionController::RoutingError, 'Site Not Found' unless @cms_site
|
|
19
19
|
|
|
20
|
-
return unless @cms_site.path.present? && !
|
|
20
|
+
return unless @cms_site.path.present? && !route_site_id
|
|
21
21
|
raise ActionController::RoutingError, 'Site Not Found' unless params[:cms_path]&.match(%r{\A#{@cms_site.path}})
|
|
22
22
|
|
|
23
23
|
params[:cms_path].gsub!(%r{\A#{@cms_site.path}}, '')
|
|
24
24
|
params[:cms_path]&.gsub!(%r{\A/}, '')
|
|
25
25
|
end
|
|
26
|
+
|
|
27
|
+
def find_cms_site_for_asset(site_id)
|
|
28
|
+
site = ::Comfy::Cms::Site.find_by_id(site_id)
|
|
29
|
+
return site if site && ::Comfy::Cms::Site.one?
|
|
30
|
+
return unless site
|
|
31
|
+
|
|
32
|
+
request_host = ::Comfy::Cms::Site.real_host_from_aliases(request.host_with_port.downcase)
|
|
33
|
+
site if site.hostname.downcase == request_host
|
|
34
|
+
end
|
|
26
35
|
end
|
|
@@ -34,10 +34,10 @@ class Comfy::Cms::ContentController < Comfy::Cms::BaseController
|
|
|
34
34
|
protected
|
|
35
35
|
|
|
36
36
|
def render_page(status = :ok)
|
|
37
|
-
render
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
render inline: @cms_page.content_cache,
|
|
38
|
+
layout: app_layout,
|
|
39
|
+
status: status,
|
|
40
|
+
content_type: mime_type
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# it's possible to control mimetype of a page by creating a `mime_type` field
|
|
@@ -9,12 +9,11 @@ module Comfy::ReorderAction
|
|
|
9
9
|
|
|
10
10
|
def reorder
|
|
11
11
|
resource_class = self.class.reorder_action_resource
|
|
12
|
+
site_resources = resource_class.where(site_id: @site.id)
|
|
12
13
|
(params.permit(order: [])[:order] || []).each_with_index do |id, index|
|
|
13
|
-
|
|
14
|
-
end
|
|
15
|
-
if resource_class == ::Comfy::Cms::Page
|
|
16
|
-
Comfy::Cms::Page.all.each(&:save!)
|
|
14
|
+
site_resources.where(id: id).update_all(position: index)
|
|
17
15
|
end
|
|
16
|
+
@site.pages.each(&:save!) if resource_class == ::Comfy::Cms::Page
|
|
18
17
|
head :ok
|
|
19
18
|
end
|
|
20
19
|
end
|
|
@@ -11,4 +11,13 @@ class Comfy::Cms::Categorization < ActiveRecord::Base
|
|
|
11
11
|
# -- Validations -------------------------------------------------------------
|
|
12
12
|
validates :category_id,
|
|
13
13
|
uniqueness: { scope: %i[categorized_type categorized_id] }
|
|
14
|
+
validate :category_belongs_to_site
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def category_belongs_to_site
|
|
19
|
+
return unless category && categorized.respond_to?(:site_id)
|
|
20
|
+
|
|
21
|
+
errors.add(:category_id, :invalid) if category.site_id != categorized.site_id
|
|
22
|
+
end
|
|
14
23
|
end
|
|
@@ -13,7 +13,7 @@ class Comfy::Cms::Category < ActiveRecord::Base
|
|
|
13
13
|
presence: true,
|
|
14
14
|
uniqueness: { scope: %i[categorized_type site_id] }
|
|
15
15
|
validates :categorized_type,
|
|
16
|
-
presence:
|
|
16
|
+
presence: true
|
|
17
17
|
|
|
18
18
|
# -- Scopes ---------------------------------------------------------------
|
|
19
19
|
scope :of_type, ->(type) {
|
|
@@ -20,6 +20,7 @@ class Comfy::Cms::Fragment < ActiveRecord::Base
|
|
|
20
20
|
validates :identifier,
|
|
21
21
|
presence: true,
|
|
22
22
|
uniqueness: { scope: :record }
|
|
23
|
+
validate :record_is_immutable, on: :update
|
|
23
24
|
|
|
24
25
|
# -- Instance Methods --------------------------------------------------------
|
|
25
26
|
|
|
@@ -38,6 +39,12 @@ class Comfy::Cms::Fragment < ActiveRecord::Base
|
|
|
38
39
|
|
|
39
40
|
protected
|
|
40
41
|
|
|
42
|
+
def record_is_immutable
|
|
43
|
+
return unless will_save_change_to_record_id? || will_save_change_to_record_type?
|
|
44
|
+
|
|
45
|
+
errors.add(:record, :invalid)
|
|
46
|
+
end
|
|
47
|
+
|
|
41
48
|
def remove_attachments
|
|
42
49
|
return unless @file_ids_destroy.present?
|
|
43
50
|
|
|
@@ -20,13 +20,14 @@ class Comfy::Cms::Layout < ActiveRecord::Base
|
|
|
20
20
|
|
|
21
21
|
# -- Validations -------------------------------------------------------------
|
|
22
22
|
validates :site_id,
|
|
23
|
-
presence:
|
|
23
|
+
presence: true
|
|
24
24
|
validates :label,
|
|
25
|
-
presence:
|
|
25
|
+
presence: true
|
|
26
26
|
validates :identifier,
|
|
27
27
|
presence: true,
|
|
28
28
|
uniqueness: { scope: :site_id },
|
|
29
29
|
format: { with: %r{\A\w[a-z0-9_-]*\z}i }
|
|
30
|
+
validate :parent_belongs_to_site
|
|
30
31
|
|
|
31
32
|
# -- Class Methods -----------------------------------------------------------
|
|
32
33
|
# Tree-like structure for layouts
|
|
@@ -101,6 +102,10 @@ class Comfy::Cms::Layout < ActiveRecord::Base
|
|
|
101
102
|
|
|
102
103
|
protected
|
|
103
104
|
|
|
105
|
+
def parent_belongs_to_site
|
|
106
|
+
errors.add(:parent_id, :invalid) if site && parent && site_id != parent.site_id
|
|
107
|
+
end
|
|
108
|
+
|
|
104
109
|
def assign_label
|
|
105
110
|
self.label = label.blank? ? identifier.try(:titleize) : label
|
|
106
111
|
end
|
|
@@ -40,6 +40,7 @@ class Comfy::Cms::Page < ActiveRecord::Base
|
|
|
40
40
|
}
|
|
41
41
|
validate :validate_target_page
|
|
42
42
|
validate :validate_format_of_unescaped_slug
|
|
43
|
+
validate :associations_belong_to_site
|
|
43
44
|
|
|
44
45
|
# -- Scopes ------------------------------------------------------------------
|
|
45
46
|
scope :published, -> { where(is_published: true) }
|
|
@@ -106,6 +107,14 @@ class Comfy::Cms::Page < ActiveRecord::Base
|
|
|
106
107
|
|
|
107
108
|
protected
|
|
108
109
|
|
|
110
|
+
def associations_belong_to_site
|
|
111
|
+
return unless site
|
|
112
|
+
|
|
113
|
+
errors.add(:parent_id, :invalid) if parent && site_id != parent.site_id
|
|
114
|
+
errors.add(:layout_id, :invalid) if layout && site_id != layout.site_id
|
|
115
|
+
errors.add(:target_page_id, :invalid) if target_page && site_id != target_page.site_id
|
|
116
|
+
end
|
|
117
|
+
|
|
109
118
|
def assigns_label
|
|
110
119
|
self.label = label.blank? ? slug.try(:titleize) : label
|
|
111
120
|
end
|
|
@@ -118,18 +127,18 @@ protected
|
|
|
118
127
|
|
|
119
128
|
def assign_full_path
|
|
120
129
|
self.full_path =
|
|
121
|
-
if
|
|
122
|
-
[CGI.escape(
|
|
130
|
+
if parent
|
|
131
|
+
[CGI.escape(parent.full_path).gsub('%2F', '/'), slug].join('/').squeeze('/')
|
|
123
132
|
else
|
|
124
133
|
'/'
|
|
125
134
|
end
|
|
126
135
|
end
|
|
127
136
|
|
|
128
137
|
def assign_position
|
|
129
|
-
return unless
|
|
138
|
+
return unless parent
|
|
130
139
|
return if position.to_i.positive?
|
|
131
140
|
|
|
132
|
-
max =
|
|
141
|
+
max = parent.children.maximum(:position)
|
|
133
142
|
self.position = max ? max + 1 : 0
|
|
134
143
|
end
|
|
135
144
|
|