pages_core 3.7.0 → 3.8.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 +1 -2
- data/app/assets/builds/pages_core/admin-dist.js +55 -0
- data/app/assets/stylesheets/pages/admin/components/image_editor.scss +1 -0
- data/app/assets/stylesheets/pages/admin/components/image_grid.scss +33 -5
- data/app/assets/stylesheets/pages/admin/components/layout.scss +2 -1
- data/app/assets/stylesheets/pages/admin/components/login.scss +6 -0
- data/app/assets/stylesheets/pages/admin/components/tabs.scss +5 -0
- data/app/assets/stylesheets/pages/admin/components/tag_editor.scss +13 -7
- data/app/assets/stylesheets/pages/admin/controllers/pages.scss +13 -5
- data/app/assets/stylesheets/pages/admin.scss +0 -1
- data/app/controller_dummies/admin/admin_controller.rb +1 -1
- data/app/controller_dummies/application_controller.rb +1 -1
- data/app/controller_dummies/attachments_controller.rb +1 -1
- data/app/controller_dummies/frontend_controller.rb +1 -1
- data/app/controller_dummies/images_controller.rb +1 -1
- data/app/controller_dummies/page_files_controller.rb +1 -1
- data/app/controller_dummies/pages_controller.rb +1 -1
- data/app/controller_dummies/sitemaps_controller.rb +1 -1
- data/app/controllers/admin/attachments_controller.rb +1 -1
- data/app/controllers/admin/images_controller.rb +10 -7
- data/app/controllers/concerns/pages_core/authentication.rb +9 -4
- data/app/controllers/concerns/pages_core/preview_pages_controller.rb +5 -0
- data/app/controllers/pages_core/frontend/pages_controller.rb +5 -2
- data/app/controllers/sessions_controller.rb +1 -1
- data/app/formatters/pages_core/link_renderer.rb +2 -2
- data/app/helpers/application_helper.rb +1 -1
- data/app/helpers/frontend_helper.rb +1 -1
- data/app/helpers/pages_core/admin/content_tabs_helper.rb +5 -2
- data/app/helpers/pages_core/admin/image_uploads_helper.rb +2 -3
- data/app/helpers/pages_core/admin/tag_editor_helper.rb +9 -39
- data/app/helpers/pages_core/head_tags_helper.rb +11 -20
- data/app/helpers/pages_core/open_graph_tags_helper.rb +1 -1
- data/app/javascript/admin-dist.js +2 -0
- data/app/javascript/components/Attachments/Attachment.jsx +121 -0
- data/app/javascript/components/Attachments/AttachmentEditor.jsx +116 -0
- data/app/javascript/components/Attachments/Placeholder.jsx +10 -0
- data/app/javascript/components/Attachments.jsx +165 -0
- data/app/{assets/javascripts/pages/admin/components/date_range_select.jsx → javascript/components/DateRangeSelect.jsx} +16 -5
- data/app/javascript/components/EditableImage.jsx +61 -0
- data/app/{assets/javascripts/pages/admin/components/file_upload_button.jsx → javascript/components/FileUploadButton.jsx} +11 -1
- data/app/{assets/javascripts/pages/admin/components/focal_point.jsx → javascript/components/ImageCropper/FocalPoint.jsx} +12 -1
- data/app/javascript/components/ImageCropper/Image.jsx +65 -0
- data/app/javascript/components/ImageCropper/Toolbar.jsx +73 -0
- data/app/javascript/components/ImageCropper/useCrop.js +199 -0
- data/app/javascript/components/ImageCropper.jsx +90 -0
- data/app/javascript/components/ImageEditor/Form.jsx +98 -0
- data/app/javascript/components/ImageEditor.jsx +62 -0
- data/app/javascript/components/ImageGrid/DragElement.jsx +30 -0
- data/app/javascript/components/ImageGrid/FilePlaceholder.jsx +9 -0
- data/app/javascript/components/ImageGrid/GridImage.jsx +103 -0
- data/app/javascript/components/ImageGrid/Placeholder.jsx +23 -0
- data/app/javascript/components/ImageGrid.jsx +257 -0
- data/app/javascript/components/ImageUploader.jsx +171 -0
- data/app/{assets/javascripts/pages/admin/components/modal.jsx → javascript/components/Modal.jsx} +13 -2
- data/app/javascript/components/ModalStore.jsx +12 -0
- data/app/{assets/javascripts/pages/admin/components/page_dates.jsx → javascript/components/PageDates.jsx} +11 -1
- data/app/{assets/javascripts/pages/admin/components/page_files.jsx → javascript/components/PageFiles.jsx} +11 -2
- data/app/{assets/javascripts/pages/admin/components/page_images.jsx → javascript/components/PageImages.jsx} +11 -2
- data/app/{assets/javascripts/pages/admin/components/page_tree_store.jsx → javascript/components/PageTree.jsx} +127 -137
- data/app/{assets/javascripts/pages/admin/components/page_tree.jsx → javascript/components/PageTreeDraggable.jsx} +35 -29
- data/app/{assets/javascripts/pages/admin/components/page_tree_node.jsx → javascript/components/PageTreeNode.jsx} +35 -20
- data/app/javascript/components/RichTextArea.jsx +213 -0
- data/app/javascript/components/RichTextToolbarButton.jsx +20 -0
- data/app/javascript/components/TagEditor/AddTagForm.jsx +42 -0
- data/app/javascript/components/TagEditor/Tag.jsx +32 -0
- data/app/javascript/components/TagEditor.jsx +61 -0
- data/app/javascript/components/Toast.jsx +72 -0
- data/app/javascript/components/ToastStore.jsx +14 -0
- data/app/javascript/components/drag/draggedOrder.js +51 -0
- data/app/javascript/components/drag/useDragCollection.js +84 -0
- data/app/javascript/components/drag/useDragUploader.js +112 -0
- data/app/javascript/components/drag/useDraggable.js +17 -0
- data/app/javascript/components/drag.js +6 -0
- data/app/javascript/components.js +14 -0
- data/app/javascript/controllers/EditPageController.js +20 -0
- data/app/javascript/controllers/LoginController.js +29 -0
- data/app/javascript/controllers/MainController.js +65 -0
- data/app/javascript/controllers/PageOptionsController.js +62 -0
- data/app/javascript/features/RichText.jsx +34 -0
- data/app/javascript/hooks.js +2 -0
- data/app/javascript/index.js +33 -0
- data/app/{assets/javascripts/pages/admin/lib/tree.jsx → javascript/lib/Tree.js} +55 -54
- data/app/javascript/lib/copyToClipboard.js +13 -0
- data/app/javascript/lib/readyHandler.js +22 -0
- data/app/javascript/lib/request.js +36 -0
- data/app/models/concerns/pages_core/page_model/images.rb +3 -1
- data/app/models/concerns/pages_core/page_model/searchable.rb +19 -0
- data/app/models/concerns/pages_core/searchable_document.rb +71 -0
- data/app/models/concerns/pages_core/taggable.rb +27 -12
- data/app/models/page.rb +2 -0
- data/app/models/page_exporter.rb +2 -2
- data/app/models/page_image.rb +0 -2
- data/app/models/role.rb +1 -1
- data/app/models/search_document.rb +72 -0
- data/app/models/tag.rb +1 -0
- data/app/models/user.rb +1 -1
- data/app/{serializers/admin/attachment_serializer.rb → resources/admin/attachment_resource.rb} +6 -5
- data/app/{serializers/admin/image_serializer.rb → resources/admin/image_resource.rb} +9 -9
- data/app/resources/admin/page_file_resource.rb +10 -0
- data/app/{serializers/admin/page_image_serializer.rb → resources/admin/page_image_resource.rb} +4 -2
- data/app/resources/export/attachment_resource.rb +10 -0
- data/app/resources/export/page_image_resource.rb +45 -0
- data/app/resources/export/page_resource.rb +42 -0
- data/app/{serializers/page_image_serializer.rb → resources/page_image_resource.rb} +8 -16
- data/app/resources/page_resource.rb +33 -0
- data/app/services/pages_core/destroy_invite_service.rb +2 -2
- data/app/services/pages_core/invite_service.rb +2 -2
- data/app/views/admin/pages/_edit_content.html.erb +1 -1
- data/app/views/admin/pages/_edit_files.html.erb +1 -5
- data/app/views/admin/pages/_edit_images.html.erb +1 -5
- data/app/views/admin/pages/_edit_options.html.erb +74 -55
- data/app/views/admin/pages/_form.html.erb +19 -0
- data/app/views/admin/pages/edit.html.erb +35 -61
- data/app/views/admin/pages/index.html.erb +0 -1
- data/app/views/admin/pages/new.html.erb +32 -32
- data/app/views/admin/users/_access_control.html.erb +5 -1
- data/app/views/admin/users/login.html.erb +12 -4
- data/app/views/feeds/pages.rss.builder +1 -2
- data/app/views/layouts/admin/_header.html.erb +1 -1
- data/app/views/layouts/admin/_page_header.html.erb +33 -0
- data/app/views/layouts/admin.html.erb +23 -42
- data/app/views/pages_core/_google_analytics.html.erb +8 -0
- data/db/migrate/20180625154059_enable_search_extensions.rb +10 -0
- data/db/migrate/20210209151400_create_search_configurations.rb +35 -0
- data/db/migrate/20210210235200_create_search_documents.rb +74 -0
- data/lib/pages_core/engine.rb +1 -5
- data/lib/pages_core/templates/block_configuration.rb +1 -1
- data/lib/pages_core/templates/configuration_handler.rb +1 -1
- data/lib/pages_core/version.rb +1 -1
- data/lib/pages_core.rb +3 -5
- data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +0 -7
- data/lib/rails/generators/pages_core/install/templates/page_templates_initializer.rb +2 -2
- metadata +101 -115
- data/app/assets/javascripts/pages/admin/components/attachment.jsx +0 -130
- data/app/assets/javascripts/pages/admin/components/attachment_editor.jsx +0 -131
- data/app/assets/javascripts/pages/admin/components/attachments.jsx +0 -211
- data/app/assets/javascripts/pages/admin/components/drag_uploader.jsx +0 -174
- data/app/assets/javascripts/pages/admin/components/editable_image.jsx +0 -57
- data/app/assets/javascripts/pages/admin/components/grid_image.jsx +0 -124
- data/app/assets/javascripts/pages/admin/components/image_editor.jsx +0 -496
- data/app/assets/javascripts/pages/admin/components/image_grid.jsx +0 -306
- data/app/assets/javascripts/pages/admin/components/image_uploader.jsx +0 -176
- data/app/assets/javascripts/pages/admin/components/modal_store.jsx +0 -20
- data/app/assets/javascripts/pages/admin/components/rich_text_area.jsx +0 -64
- data/app/assets/javascripts/pages/admin/components/rich_text_toolbar.jsx +0 -91
- data/app/assets/javascripts/pages/admin/components/toast.jsx +0 -34
- data/app/assets/javascripts/pages/admin/components/toast_store.jsx +0 -52
- data/app/assets/javascripts/pages/admin/components.jsx +0 -2
- data/app/assets/javascripts/pages/admin/features/content_tabs.jsx +0 -72
- data/app/assets/javascripts/pages/admin/features/edit_page.jsx +0 -97
- data/app/assets/javascripts/pages/admin/features/rich_text.jsx +0 -14
- data/app/assets/javascripts/pages/admin/features/tag_editor.jsx +0 -160
- data/app/assets/javascripts/pages/admin.jsx +0 -17
- data/app/assets/javascripts/pages/login_form.jsx +0 -21
- data/app/serializers/admin/page_file_serializer.rb +0 -8
- data/app/serializers/page_export_serializer.rb +0 -32
- data/app/serializers/page_file_export_serializer.rb +0 -6
- data/app/serializers/page_image_export_serializer.rb +0 -42
- data/app/serializers/page_serializer.rb +0 -23
- data/app/views/layouts/admin/_analytics.html.erb +0 -16
- data/lib/rails/generators/pages_core/frontend/templates/application.js.erb +0 -15
- data/vendor/assets/javascripts/ReactCrop.min.js +0 -1
- data/vendor/assets/javascripts/reflux.min.js +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export default function useDraggable(draggable, startDrag) {
|
|
4
|
+
const ref = useRef();
|
|
5
|
+
|
|
6
|
+
const handleDrag = (evt) => {
|
|
7
|
+
evt.preventDefault();
|
|
8
|
+
evt.stopPropagation();
|
|
9
|
+
startDrag(evt, draggable);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
draggable.ref.current = ref.current;
|
|
14
|
+
}, []);
|
|
15
|
+
|
|
16
|
+
return { ref: ref, onDragStart: handleDrag, draggable: true };
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as useDragCollection,
|
|
2
|
+
createDraggable } from "./drag/useDragCollection";
|
|
3
|
+
export { default as useDragUploader } from "./drag/useDragUploader";
|
|
4
|
+
export { default as useDraggable } from "./drag/useDraggable";
|
|
5
|
+
export { default as draggedOrder,
|
|
6
|
+
collectionOrder } from "./drag/draggedOrder";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { default as Attachments } from "./components/Attachments";
|
|
2
|
+
export { default as DateRangeSelect } from "./components/DateRangeSelect";
|
|
3
|
+
export { default as EditableImage } from "./components/EditableImage";
|
|
4
|
+
export { default as ImageCropper } from "./components/ImageCropper";
|
|
5
|
+
export { default as ImageGrid } from "./components/ImageGrid";
|
|
6
|
+
export { default as ImageUploader } from "./components/ImageUploader";
|
|
7
|
+
export { default as Modal } from "./components/Modal";
|
|
8
|
+
export { default as PageDates } from "./components/PageDates";
|
|
9
|
+
export { default as PageFiles } from "./components/PageFiles";
|
|
10
|
+
export { default as PageImages } from "./components/PageImages";
|
|
11
|
+
export { default as PageTree } from "./components/PageTree";
|
|
12
|
+
export { default as RichTextArea } from "./components/RichTextArea";
|
|
13
|
+
export { default as TagEditor } from "./components/TagEditor";
|
|
14
|
+
export { default as Toast } from "./components/Toast";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Controller } from "stimulus";
|
|
2
|
+
|
|
3
|
+
export default class EditPageController extends Controller {
|
|
4
|
+
static get targets() {
|
|
5
|
+
return ["form"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
preview() {
|
|
9
|
+
const form = this.formTarget;
|
|
10
|
+
const prevAction = form.action;
|
|
11
|
+
const prevTarget = form.target;
|
|
12
|
+
|
|
13
|
+
form.target = "_blank";
|
|
14
|
+
form.action = form.dataset.previewUrl;
|
|
15
|
+
form.submit();
|
|
16
|
+
|
|
17
|
+
form.action = prevAction;
|
|
18
|
+
form.target = prevTarget;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Controller } from "stimulus";
|
|
2
|
+
|
|
3
|
+
export default class LoginController extends Controller {
|
|
4
|
+
static get targets() {
|
|
5
|
+
return ["tab"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
if (this.tabTargets.length > 0) {
|
|
10
|
+
this.showTab(this.tabTargets[0].dataset.tab);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
changeTab(evt) {
|
|
16
|
+
evt.preventDefault();
|
|
17
|
+
this.showTab(evt.target.dataset.tab);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
showTab(tab) {
|
|
21
|
+
this.tabTargets.forEach((t) => {
|
|
22
|
+
if (t.dataset.tab == tab) {
|
|
23
|
+
t.classList.remove("hidden");
|
|
24
|
+
} else {
|
|
25
|
+
t.classList.add("hidden");
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Controller } from "stimulus";
|
|
2
|
+
|
|
3
|
+
export default class MainController extends Controller {
|
|
4
|
+
static get targets() {
|
|
5
|
+
return ["tab", "link"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
let tabs = this.tabNames();
|
|
10
|
+
if (tabs.length > 0) {
|
|
11
|
+
let initTab = null;
|
|
12
|
+
const tabExpression = /#(.*)$/;
|
|
13
|
+
|
|
14
|
+
if (document.location.toString().match(tabExpression)) {
|
|
15
|
+
let id = document.location.toString().match(tabExpression)[1];
|
|
16
|
+
if (tabs.indexOf(id) !== -1) {
|
|
17
|
+
initTab = id;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
this.showTab(initTab || tabs[0]);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
window.addEventListener("popstate", this.stateHandler.bind(this));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
disconnect() {
|
|
28
|
+
window.removeEventListener("popstate", this.stateHandler.bind(this));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
stateHandler(evt) {
|
|
32
|
+
if (evt.state && evt.state.tabId) {
|
|
33
|
+
this.showTab(evt.state.tabId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
changeTab(evt) {
|
|
38
|
+
evt.preventDefault();
|
|
39
|
+
const tab = evt.target.dataset.tab;
|
|
40
|
+
this.showTab(tab);
|
|
41
|
+
history.pushState({ tabId: tab }, "", `${window.location.pathname}#${tab}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
showTab(tab) {
|
|
45
|
+
this.linkTargets.forEach((l) => {
|
|
46
|
+
if (l.dataset.tab == tab) {
|
|
47
|
+
l.classList.add("current");
|
|
48
|
+
} else {
|
|
49
|
+
l.classList.remove("current");
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.tabTargets.forEach((t) => {
|
|
54
|
+
if (t.dataset.tab == tab) {
|
|
55
|
+
t.classList.remove("hidden");
|
|
56
|
+
} else {
|
|
57
|
+
t.classList.add("hidden");
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
tabNames () {
|
|
63
|
+
return this.linkTargets.map((l) => l.dataset.tab);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Controller } from "stimulus";
|
|
2
|
+
|
|
3
|
+
export default class PageOptionsController extends Controller {
|
|
4
|
+
static get targets() {
|
|
5
|
+
return ["advancedOptions", "autoPublishNotice", "published", "publishedDate"];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
connect() {
|
|
9
|
+
this.checkAutoPublish();
|
|
10
|
+
this.checkPublishedDate();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
checkAutoPublish() {
|
|
14
|
+
if (this.publishedDate() > new Date()) {
|
|
15
|
+
this.show(this.autoPublishNoticeTarget);
|
|
16
|
+
} else {
|
|
17
|
+
this.hide(this.autoPublishNoticeTarget);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
checkPublishedDate() {
|
|
22
|
+
if (this.publishedTarget.value == 2) {
|
|
23
|
+
this.show(this.publishedDateTarget);
|
|
24
|
+
} else {
|
|
25
|
+
this.hide(this.publishedDateTarget);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
publishedDate() {
|
|
30
|
+
const lookup = (name) => {
|
|
31
|
+
return this.publishedDateTarget.getElementsByClassName(name)[0].value;
|
|
32
|
+
};
|
|
33
|
+
return new Date(
|
|
34
|
+
lookup("year"),
|
|
35
|
+
(lookup("month") - 1),
|
|
36
|
+
lookup("day"),
|
|
37
|
+
lookup("hour"),
|
|
38
|
+
lookup("minute")
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
show(elem) {
|
|
43
|
+
elem.classList.add("show");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
hide(elem) {
|
|
47
|
+
elem.classList.remove("show");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
toggle(elem) {
|
|
51
|
+
if (elem.classList.contains("show")) {
|
|
52
|
+
this.hide(elem);
|
|
53
|
+
} else {
|
|
54
|
+
this.show(elem);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
toggleAdvancedOptions(evt) {
|
|
59
|
+
evt.preventDefault();
|
|
60
|
+
this.toggle(this.advancedOptionsTarget);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
3
|
+
import RichTextArea from "../components/RichTextArea";
|
|
4
|
+
import readyHandler from "../lib/readyHandler";
|
|
5
|
+
|
|
6
|
+
class RichText {
|
|
7
|
+
apply () {
|
|
8
|
+
const elems = document.querySelectorAll("textarea.rich");
|
|
9
|
+
elems.forEach((elem) => {
|
|
10
|
+
this.enhance(elem);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
enhance (elem) {
|
|
15
|
+
const container = document.createElement("div");
|
|
16
|
+
elem.parentNode.appendChild(container);
|
|
17
|
+
ReactDOM.render(
|
|
18
|
+
<RichTextArea value={elem.value}
|
|
19
|
+
name={elem.name}
|
|
20
|
+
rows={elem.rows}
|
|
21
|
+
id={elem.id} />,
|
|
22
|
+
container
|
|
23
|
+
);
|
|
24
|
+
elem.parentNode.removeChild(elem);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
start () {
|
|
28
|
+
readyHandler.ready(() => {
|
|
29
|
+
this.apply();
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export default new RichText();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import Rails from "@rails/ujs";
|
|
2
|
+
import { Application } from "stimulus";
|
|
3
|
+
require("react_ujs");
|
|
4
|
+
|
|
5
|
+
import * as Components from "./components";
|
|
6
|
+
|
|
7
|
+
import EditPageController from "./controllers/EditPageController";
|
|
8
|
+
import MainController from "./controllers/MainController";
|
|
9
|
+
import LoginController from "./controllers/LoginController";
|
|
10
|
+
import PageOptionsController from "./controllers/PageOptionsController";
|
|
11
|
+
|
|
12
|
+
import RichText from "./features/RichText";
|
|
13
|
+
|
|
14
|
+
export function registerComponent(name, component) {
|
|
15
|
+
window[name] = component;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function startPages () {
|
|
19
|
+
Rails.start();
|
|
20
|
+
for (var name in Components) {
|
|
21
|
+
registerComponent(name, Components[name]);
|
|
22
|
+
}
|
|
23
|
+
RichText.start();
|
|
24
|
+
|
|
25
|
+
const application = Application.start();
|
|
26
|
+
application.register("edit-page", EditPageController);
|
|
27
|
+
application.register("main", MainController);
|
|
28
|
+
application.register("login", LoginController);
|
|
29
|
+
application.register("page-options", PageOptionsController);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export * from "./components";
|
|
33
|
+
export * from "./hooks";
|
|
@@ -1,55 +1,56 @@
|
|
|
1
1
|
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
2
|
+
Based on
|
|
3
|
+
|
|
4
|
+
js-tree by Wang Zuo
|
|
5
|
+
https://github.com/wangzuo/js-tree
|
|
6
|
+
|
|
7
|
+
react-ui-tree
|
|
8
|
+
https://github.com/pqx/react-ui-tree
|
|
9
|
+
|
|
10
|
+
The MIT License (MIT)
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2015 Wang Zuo
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in
|
|
22
|
+
all copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
|
|
32
|
+
Copyright (c) 2015 pqx Limited
|
|
33
|
+
|
|
34
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
35
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
36
|
+
in the Software without restriction, including without limitation the rights
|
|
37
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
38
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
39
|
+
furnished to do so, subject to the following conditions:
|
|
40
|
+
|
|
41
|
+
The above copyright notice and this permission notice shall be included in
|
|
42
|
+
all copies or substantial portions of the Software.
|
|
43
|
+
|
|
44
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
45
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
46
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
47
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
48
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
49
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
50
|
+
SOFTWARE.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
export default class Tree {
|
|
53
54
|
constructor(obj) {
|
|
54
55
|
this.cnt = 1;
|
|
55
56
|
this.obj = obj || { children: [] };
|
|
@@ -72,7 +73,7 @@ class Tree {
|
|
|
72
73
|
|
|
73
74
|
function walk(objs, parent) {
|
|
74
75
|
var children = [];
|
|
75
|
-
objs.forEach(function(obj
|
|
76
|
+
objs.forEach(function(obj) {
|
|
76
77
|
var index = {};
|
|
77
78
|
index.id = self.cnt;
|
|
78
79
|
index.node = obj;
|
|
@@ -276,7 +277,7 @@ class Tree {
|
|
|
276
277
|
|
|
277
278
|
getParent(id) {
|
|
278
279
|
var indexes = this.indexes;
|
|
279
|
-
if (
|
|
280
|
+
if (Object.prototype.hasOwnProperty.call(indexes, id)) {
|
|
280
281
|
return this.getIndex(indexes[id].parent);
|
|
281
282
|
}
|
|
282
283
|
}
|
|
@@ -284,7 +285,7 @@ class Tree {
|
|
|
284
285
|
getNodeByTop(top) {
|
|
285
286
|
var indexes = this.indexes;
|
|
286
287
|
for(var id in indexes) {
|
|
287
|
-
if (
|
|
288
|
+
if (Object.prototype.hasOwnProperty.call(indexes, id)) {
|
|
288
289
|
if(indexes[id].top === top) {
|
|
289
290
|
return indexes[id];
|
|
290
291
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function copySupported () {
|
|
2
|
+
return document.queryCommandSupported &&
|
|
3
|
+
document.queryCommandSupported("copy");
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export default function copyToClipboard (str) {
|
|
7
|
+
const el = document.createElement("textarea");
|
|
8
|
+
el.value = str;
|
|
9
|
+
document.body.appendChild(el);
|
|
10
|
+
el.select();
|
|
11
|
+
document.execCommand("copy");
|
|
12
|
+
document.body.removeChild(el);
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
let readyHandlers = [];
|
|
2
|
+
|
|
3
|
+
const handleState = () => {
|
|
4
|
+
if (["interactive", "complete"].indexOf(document.readyState) > -1) {
|
|
5
|
+
while(readyHandlers.length > 0) {
|
|
6
|
+
(readyHandlers.shift())();
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
class ReadyHandler {
|
|
12
|
+
constructor () {
|
|
13
|
+
document.onreadystatechange = handleState;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
ready (handler) {
|
|
17
|
+
readyHandlers.push(handler);
|
|
18
|
+
handleState();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default new ReadyHandler();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function csrfToken() {
|
|
2
|
+
return document.querySelector("[name=csrf-token]").content;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function jsonFetchOptions() {
|
|
6
|
+
return({ method: "POST",
|
|
7
|
+
headers: { "Content-Type": "application/json; charset=utf-8",
|
|
8
|
+
"X-CSRF-Token": csrfToken() } });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function postJson(url, data) {
|
|
12
|
+
const options = { ...jsonFetchOptions(), method: "POST" };
|
|
13
|
+
if (data) {
|
|
14
|
+
options.body = JSON.stringify(data);
|
|
15
|
+
}
|
|
16
|
+
const response = await fetch(url, options);
|
|
17
|
+
return response.json();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function putJson(url, data) {
|
|
21
|
+
const options = { ...jsonFetchOptions(), method: "PUT" };
|
|
22
|
+
if (data) {
|
|
23
|
+
options.body = JSON.stringify(data);
|
|
24
|
+
}
|
|
25
|
+
const response = await fetch(url, options);
|
|
26
|
+
return response.json();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function post(url, data) {
|
|
30
|
+
const response = await fetch(url, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
body: data,
|
|
33
|
+
headers: { "X-CSRF-Token": csrfToken() }
|
|
34
|
+
});
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
@@ -8,7 +8,9 @@ module PagesCore
|
|
|
8
8
|
included do
|
|
9
9
|
belongs_to_image :image, optional: true
|
|
10
10
|
belongs_to_image :meta_image, class_name: "Image", optional: true
|
|
11
|
-
has_many :page_images, -> { order("position") },
|
|
11
|
+
has_many :page_images, -> { order("position") },
|
|
12
|
+
inverse_of: :page,
|
|
13
|
+
dependent: :destroy
|
|
12
14
|
has_many(
|
|
13
15
|
:images,
|
|
14
16
|
-> { where(page_images: { primary: false }).order("position") },
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PagesCore
|
|
4
|
+
module PageModel
|
|
5
|
+
module Searchable
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
def search_document_attributes
|
|
9
|
+
super.merge(
|
|
10
|
+
published: published?,
|
|
11
|
+
name: name,
|
|
12
|
+
description: try(&:meta_description?) ? meta_description : excerpt,
|
|
13
|
+
# content: "",
|
|
14
|
+
tags: tag_names.join(" ")
|
|
15
|
+
)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PagesCore
|
|
4
|
+
module SearchableDocument
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
has_many :search_documents, as: :searchable, dependent: :destroy
|
|
9
|
+
after_save :update_search_documents!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class Indexer
|
|
13
|
+
attr_reader :record
|
|
14
|
+
|
|
15
|
+
def initialize(record)
|
|
16
|
+
@record = record
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def index_all!(scope)
|
|
21
|
+
scope.all.find_each { |r| new(r).index! }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def index!
|
|
26
|
+
SearchDocument.transaction do
|
|
27
|
+
record.search_documents.where.not(locale: locales).destroy_all
|
|
28
|
+
locales.each do |locale|
|
|
29
|
+
update_index(
|
|
30
|
+
locale,
|
|
31
|
+
localized_record(locale).search_document_attributes
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def locales
|
|
40
|
+
if record.respond_to?(:locales)
|
|
41
|
+
record.locales
|
|
42
|
+
elsif PagesCore.config.locales
|
|
43
|
+
PagesCore.config.locales.keys
|
|
44
|
+
else
|
|
45
|
+
[I18n.default_locale]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def localized_record(locale)
|
|
50
|
+
return record unless record.respond_to?(:localize)
|
|
51
|
+
|
|
52
|
+
record.localize(locale)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def update_index(locale, attrs)
|
|
56
|
+
record.search_documents.create_or_find_by!(locale: locale).update(attrs)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def search_document_attributes
|
|
61
|
+
return {} unless respond_to?(:localized_attributes)
|
|
62
|
+
|
|
63
|
+
content = localized_attributes.keys.map { |a| localizer.get(a) }.join(" ")
|
|
64
|
+
{ content: content }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def update_search_documents!
|
|
68
|
+
PagesCore::SearchableDocument::Indexer.new(self).index!
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -4,25 +4,27 @@ module PagesCore
|
|
|
4
4
|
module Taggable
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
|
+
attr_accessor :new_tags
|
|
8
|
+
|
|
7
9
|
included do
|
|
8
10
|
has_many :taggings,
|
|
9
11
|
as: :taggable,
|
|
10
12
|
dependent: :destroy,
|
|
11
13
|
inverse_of: :taggable
|
|
12
14
|
has_many :tags, through: :taggings
|
|
15
|
+
after_save :update_taggings
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
module ClassMethods
|
|
16
19
|
def tagged_with(*tags)
|
|
17
|
-
all
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
.references(:tags)
|
|
20
|
+
all.includes(:tags)
|
|
21
|
+
.where(tags: { name: Tag.parse(tags) })
|
|
22
|
+
.references(:tags)
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def serialized_tags
|
|
25
|
-
|
|
27
|
+
tag_names.to_json
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
def serialized_tags=(json)
|
|
@@ -30,12 +32,11 @@ module PagesCore
|
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def tag_with(*list)
|
|
33
|
-
Tag.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
end
|
|
35
|
+
@new_tags = Tag.parse(list)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def tag_with!(*list)
|
|
39
|
+
update(tag_list: list)
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def tag_list=(tag_list)
|
|
@@ -47,7 +48,21 @@ module PagesCore
|
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def tag_names
|
|
50
|
-
tags.map(&:name)
|
|
51
|
+
@new_tags || tags.by_name.map(&:name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def update_taggings
|
|
57
|
+
return unless new_tags
|
|
58
|
+
|
|
59
|
+
Tag.transaction do
|
|
60
|
+
taggings.includes(:tag).where.not(tag: { name: new_tags }).destroy_all
|
|
61
|
+
new_tags.map { |n| Tag.find_or_create_by(name: n) }
|
|
62
|
+
.each { |t| taggings.create(tag: t) }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
@new_tags = nil
|
|
51
66
|
end
|
|
52
67
|
end
|
|
53
68
|
end
|