pages_core 3.14.0 → 3.15.1
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/VERSION +1 -1
- data/app/assets/builds/fonts/6569749d.ttf +0 -0
- data/app/assets/builds/fonts/7b7db107.woff2 +0 -0
- data/app/assets/builds/fonts/921961e9.woff2 +0 -0
- data/app/assets/builds/fonts/ee32bc60.ttf +0 -0
- data/app/assets/builds/pages_core/admin-dist.js +19 -8
- data/app/assets/builds/pages_core/admin-dist.js.map +4 -4
- data/app/assets/builds/pages_core/admin.css +699 -394
- data/app/assets/builds/pages_core/mailer.css +99 -0
- data/app/assets/fonts/Inter-Black.woff2 +0 -0
- data/app/assets/fonts/Inter-BlackItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-Bold.woff2 +0 -0
- data/app/assets/fonts/Inter-BoldItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-ExtraBold.woff2 +0 -0
- data/app/assets/fonts/Inter-ExtraBoldItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-ExtraLight.woff2 +0 -0
- data/app/assets/fonts/Inter-ExtraLightItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-Italic.woff2 +0 -0
- data/app/assets/fonts/Inter-Light.woff2 +0 -0
- data/app/assets/fonts/Inter-LightItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-Medium.woff2 +0 -0
- data/app/assets/fonts/Inter-MediumItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-Regular.woff2 +0 -0
- data/app/assets/fonts/Inter-SemiBold.woff2 +0 -0
- data/app/assets/fonts/Inter-SemiBoldItalic.woff2 +0 -0
- data/app/assets/fonts/Inter-Thin.woff2 +0 -0
- data/app/assets/fonts/Inter-ThinItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Black.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-BlackItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Bold.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-BoldItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-ExtraBold.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-ExtraBoldItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-ExtraLight.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-ExtraLightItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Italic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Light.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-LightItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Medium.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-MediumItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Regular.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-SemiBold.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-SemiBoldItalic.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-Thin.woff2 +0 -0
- data/app/assets/fonts/InterDisplay-ThinItalic.woff2 +0 -0
- data/app/assets/fonts/InterVariable-Italic.woff2 +0 -0
- data/app/assets/fonts/InterVariable.woff2 +0 -0
- data/app/assets/stylesheets/pages_core/admin/components/archive.css +1 -1
- data/app/assets/stylesheets/pages_core/admin/components/attachments.css +22 -34
- data/app/assets/stylesheets/pages_core/admin/components/base.css +1 -68
- data/app/assets/stylesheets/pages_core/admin/components/forms.css +109 -48
- data/app/assets/stylesheets/pages_core/admin/components/header.css +56 -58
- data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +35 -24
- data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +28 -27
- data/app/assets/stylesheets/pages_core/admin/components/image_uploader.css +5 -5
- data/app/assets/stylesheets/pages_core/admin/components/layout.css +7 -1
- data/app/assets/stylesheets/pages_core/admin/components/list_table.css +24 -15
- data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +63 -104
- data/app/assets/stylesheets/pages_core/admin/components/pagination.css +12 -13
- data/app/assets/stylesheets/pages_core/admin/components/search.css +1 -16
- data/app/assets/stylesheets/pages_core/admin/components/sidebar.css +5 -11
- data/app/assets/stylesheets/pages_core/admin/components/tag_editor.css +22 -36
- data/app/assets/stylesheets/pages_core/admin/components/toast.css +1 -2
- data/app/assets/stylesheets/pages_core/admin/components/toolbar.css +10 -10
- data/app/assets/stylesheets/pages_core/admin/components/totp.css +1 -1
- data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +37 -51
- data/app/assets/stylesheets/pages_core/admin/global/fonts.css +271 -0
- data/app/assets/stylesheets/pages_core/admin/global/typography.css +109 -0
- data/app/assets/stylesheets/pages_core/admin/vars.css +1 -3
- data/app/assets/stylesheets/pages_core/{admin.postcss.css → admin.css} +1 -0
- data/app/assets/stylesheets/pages_core/mailer.css +90 -0
- data/app/controllers/admin/account_recoveries_controller.rb +2 -2
- data/app/controllers/admin/pages_controller.rb +22 -42
- data/app/controllers/concerns/pages_core/error_reporting.rb +1 -1
- data/app/controllers/concerns/pages_core/page_parameters.rb +29 -0
- data/app/controllers/concerns/pages_core/policies_helper.rb +1 -1
- data/app/controllers/concerns/pages_core/preview_pages_controller.rb +20 -20
- data/app/controllers/pages_core/admin_controller.rb +0 -2
- data/app/controllers/pages_core/frontend/pages_controller.rb +2 -6
- data/app/formatters/pages_core/html_formatter.rb +2 -4
- data/app/helpers/admin/menu_helper.rb +5 -4
- data/app/helpers/admin/pages_helper.rb +1 -21
- data/app/helpers/pages_core/admin/admin_helper.rb +2 -3
- data/app/helpers/pages_core/admin/content_tabs_helper.rb +1 -2
- data/app/helpers/pages_core/admin/labelled_field_helper.rb +1 -1
- data/app/helpers/pages_core/attachments_helper.rb +1 -1
- data/app/helpers/pages_core/frontend_helper.rb +1 -1
- data/app/helpers/pages_core/images_helper.rb +10 -8
- data/app/helpers/pages_core/labelled_form_builder.rb +2 -7
- data/app/helpers/pages_core/page_path_helper.rb +1 -1
- data/app/javascript/components/Attachments/Attachment.tsx +20 -18
- data/app/javascript/components/Attachments/AttachmentEditor.tsx +11 -9
- data/app/javascript/components/{Attachments.jsx → Attachments/List.tsx} +58 -63
- data/app/javascript/components/Attachments/useAttachments.ts +15 -0
- data/app/javascript/components/Attachments.tsx +14 -0
- data/app/javascript/components/DateRangeSelect.tsx +105 -0
- data/app/javascript/components/DateTimeSelect.tsx +136 -0
- data/app/javascript/components/EditableImage.tsx +11 -9
- data/app/javascript/components/FileUploadButton.tsx +7 -7
- data/app/javascript/components/ImageCropper/FocalPoint.tsx +9 -12
- data/app/javascript/components/ImageCropper/Image.tsx +10 -8
- data/app/javascript/components/ImageCropper/Toolbar.tsx +11 -12
- data/app/javascript/components/ImageCropper/useCrop.ts +24 -53
- data/app/javascript/components/ImageCropper.tsx +10 -15
- data/app/javascript/components/ImageEditor/Form.tsx +12 -8
- data/app/javascript/components/ImageEditor.tsx +12 -7
- data/app/javascript/components/ImageGrid/DragElement.tsx +9 -12
- data/app/javascript/components/{ImageGrid.jsx → ImageGrid/Grid.tsx} +62 -71
- data/app/javascript/components/ImageGrid/GridImage.tsx +22 -23
- data/app/javascript/components/ImageGrid/Placeholder.tsx +2 -2
- data/app/javascript/components/ImageGrid/useImageGrid.ts +26 -0
- data/app/javascript/components/ImageGrid.tsx +15 -0
- data/app/javascript/components/ImageUploader.tsx +35 -22
- data/app/javascript/components/LabelledField.tsx +34 -0
- data/app/javascript/components/Modal.tsx +2 -2
- data/app/javascript/components/PageForm/Block.tsx +81 -0
- data/app/javascript/components/PageForm/Content.tsx +54 -0
- data/app/javascript/components/PageForm/Dates.tsx +66 -0
- data/app/javascript/components/PageForm/Files.tsx +28 -0
- data/app/javascript/components/PageForm/Form.tsx +41 -0
- data/app/javascript/components/PageForm/Images.tsx +28 -0
- data/app/javascript/components/PageForm/LocaleLinks.tsx +36 -0
- data/app/javascript/components/PageForm/Metadata.tsx +67 -0
- data/app/javascript/components/PageForm/Options.tsx +180 -0
- data/app/javascript/components/PageForm/PageDescription.tsx +48 -0
- data/app/javascript/components/PageForm/PathSegment.tsx +65 -0
- data/app/javascript/components/PageForm/TabPanel.tsx +21 -0
- data/app/javascript/components/PageForm/Tabs.tsx +33 -0
- data/app/javascript/components/PageForm/UnconfiguredContent.tsx +42 -0
- data/app/javascript/components/PageForm/pageParams.ts +95 -0
- data/app/javascript/components/PageForm/preview.ts +23 -0
- data/app/javascript/components/PageForm/usePage.ts +169 -0
- data/app/javascript/components/PageForm/useTabs.ts +46 -0
- data/app/javascript/components/PageForm.tsx +169 -0
- data/app/javascript/components/PageImages.tsx +7 -9
- data/app/javascript/components/PageTree/Draggable.tsx +40 -39
- data/app/javascript/components/PageTree/Node.tsx +62 -56
- data/app/javascript/components/PageTree/PageName.tsx +28 -0
- data/app/javascript/components/PageTree.tsx +65 -53
- data/app/javascript/components/{RichTextArea.jsx → RichTextArea.tsx} +98 -79
- data/app/javascript/components/RichTextToolbarButton.tsx +4 -6
- data/app/javascript/components/TagEditor/AddTagForm.tsx +19 -12
- data/app/javascript/components/TagEditor/Editor.tsx +32 -0
- data/app/javascript/components/TagEditor/Tag.tsx +6 -4
- data/app/javascript/components/TagEditor/useTags.ts +58 -0
- data/app/javascript/components/TagEditor.tsx +8 -58
- data/app/javascript/components/Toast.tsx +3 -3
- data/app/javascript/components/drag/draggedOrder.ts +22 -14
- data/app/javascript/components/drag/useDragCollection.ts +35 -30
- data/app/javascript/components/drag/useDragUploader.ts +32 -21
- data/app/javascript/components/drag/useDraggable.ts +7 -6
- data/app/javascript/components/drag.ts +0 -1
- data/app/javascript/components.ts +1 -3
- data/app/javascript/features/RichText.tsx +2 -3
- data/app/javascript/features/contentTabs.ts +79 -0
- data/app/javascript/index.ts +5 -12
- data/app/javascript/lib/Tree.ts +31 -45
- data/app/javascript/lib/request.ts +11 -11
- data/app/javascript/stores/useToastStore.ts +1 -1
- data/app/javascript/types/Attachments.ts +29 -0
- data/app/javascript/types/Crop.ts +36 -0
- data/app/javascript/types/Drag.ts +34 -0
- data/app/javascript/types/Images.ts +47 -0
- data/app/javascript/types/PageEditor.ts +26 -0
- data/app/javascript/types/Pages.ts +75 -0
- data/app/javascript/types/Tags.ts +9 -0
- data/app/javascript/types/Template.ts +24 -0
- data/app/javascript/types/Trees.ts +19 -0
- data/app/javascript/types.ts +2 -25
- data/app/mailers/admin_mailer.rb +5 -9
- data/app/models/attachment.rb +1 -1
- data/app/models/autopublisher.rb +1 -1
- data/app/models/concerns/pages_core/authenticable_user.rb +63 -0
- data/app/models/concerns/pages_core/emailable.rb +16 -0
- data/app/models/concerns/pages_core/page_model/dated_page.rb +3 -3
- data/app/models/concerns/pages_core/page_model/templateable.rb +2 -16
- data/app/models/concerns/pages_core/taggable.rb +2 -19
- data/app/models/invite.rb +2 -6
- data/app/models/otp_secret.rb +4 -4
- data/app/models/page.rb +0 -3
- data/app/models/user.rb +2 -46
- data/app/policies/page_policy.rb +6 -2
- data/app/resources/admin/page_resource.rb +95 -0
- data/app/resources/admin/page_tree_resource.rb +27 -0
- data/app/resources/admin/template_configuration_resource.rb +50 -0
- data/app/views/admin/news/_sidebar.html.erb +2 -4
- data/app/views/admin/news/index.html.erb +0 -1
- data/app/views/admin/pages/_form.html.erb +10 -30
- data/app/views/admin/pages/_search_bar.html.erb +1 -1
- data/app/views/admin/pages/edit.html.erb +1 -57
- data/app/views/admin/pages/index.html.erb +1 -1
- data/app/views/admin/pages/new.html.erb +1 -44
- data/app/views/admin/sessions/new.html.erb +9 -11
- data/app/views/admin/users/_access_control.html.erb +5 -1
- data/app/views/admin/users/_list.html.erb +12 -7
- data/app/views/admin_mailer/account_recovery.html.erb +20 -0
- data/app/views/admin_mailer/invite.html.erb +11 -0
- data/app/views/layouts/admin/_header.html.erb +2 -4
- data/app/views/layouts/admin/_page_header.html.erb +1 -2
- data/app/views/layouts/admin.html.erb +1 -1
- data/app/views/layouts/pages_core/mailer.html.erb +11 -0
- data/config/locales/en.yml +0 -4
- data/config/routes.rb +3 -7
- data/db/migrate/20240126160700_add_2fa_fields.rb +5 -1
- data/db/migrate/20240131140700_change_email_to_citext.rb +18 -0
- data/db/migrate/20240201160700_remove_persistent_data.rb +7 -0
- data/db/migrate/20240508145300_remove_categories.rb +21 -0
- data/lib/pages_core/configuration/base.rb +2 -2
- data/lib/pages_core/engine.rb +1 -0
- data/lib/pages_core/templates/configuration.rb +1 -1
- data/lib/pages_core/templates/configuration_proxy.rb +2 -2
- data/lib/pages_core/templates/template_configuration.rb +11 -1
- data/lib/pages_core/templates.rb +6 -4
- data/lib/pages_core/version.rb +1 -1
- data/lib/pages_core.rb +1 -0
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +6 -7
- data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +17 -12
- data/lib/rails/generators/pages_core/rspec/rspec_generator.rb +0 -2
- data/lib/rails/generators/pages_core/rspec/templates/rails_helper.rb +3 -4
- metadata +119 -36
- data/app/assets/builds/fonts/2a3059ad.ttf +0 -0
- data/app/assets/builds/fonts/47262711.woff2 +0 -0
- data/app/assets/builds/fonts/500ddeb0.woff2 +0 -0
- data/app/assets/builds/fonts/81221036.ttf +0 -0
- data/app/assets/stylesheets/pages_core/admin/components/login.css +0 -27
- data/app/controllers/admin/categories_controller.rb +0 -56
- data/app/controllers/concerns/pages_core/admin/persistent_params.rb +0 -75
- data/app/helpers/pages_core/admin/page_blocks_helper.rb +0 -66
- data/app/helpers/pages_core/admin/page_json_helper.rb +0 -23
- data/app/javascript/components/DateRangeSelect.jsx +0 -225
- data/app/javascript/components/PageDates.jsx +0 -73
- data/app/javascript/components/PageFiles.jsx +0 -25
- data/app/javascript/components/PageTree/types.ts +0 -15
- data/app/javascript/components/drag/types.ts +0 -28
- data/app/javascript/controllers/EditPageController.ts +0 -22
- data/app/javascript/controllers/MainController.ts +0 -74
- data/app/javascript/controllers/PageOptionsController.js +0 -67
- data/app/models/category.rb +0 -22
- data/app/models/concerns/pages_core/has_otp.rb +0 -27
- data/app/models/page_category.rb +0 -6
- data/app/views/admin/pages/_edit_content.html.erb +0 -19
- data/app/views/admin/pages/_edit_files.html.erb +0 -4
- data/app/views/admin/pages/_edit_images.html.erb +0 -4
- data/app/views/admin/pages/_edit_metadata.html.erb +0 -35
- data/app/views/admin/pages/_edit_options.html.erb +0 -91
- data/app/views/admin_mailer/account_recovery.text.erb +0 -10
- data/app/views/admin_mailer/invite.text.erb +0 -7
- data/lib/rails/generators/pages_core/rspec/templates/mailer_macros.rb +0 -11
data/app/javascript/lib/Tree.ts
CHANGED
@@ -50,42 +50,27 @@
|
|
50
50
|
SOFTWARE.
|
51
51
|
*/
|
52
52
|
|
53
|
-
|
54
|
-
type MovePlacement = "before" | "after" | "prepend" | "append";
|
55
|
-
|
56
|
-
export interface TreeNode {
|
57
|
-
children: TreeNode[];
|
58
|
-
collapsed: boolean;
|
59
|
-
}
|
53
|
+
import * as Trees from "../types/Trees";
|
60
54
|
|
61
|
-
|
62
|
-
id: number;
|
63
|
-
node: T;
|
64
|
-
children: TreeIndex<T>[];
|
65
|
-
parent: TreeIndex<T>;
|
66
|
-
next: TreeIndex<T> | null;
|
67
|
-
prev: TreeIndex<T> | null;
|
68
|
-
top: number;
|
69
|
-
height: number;
|
70
|
-
}
|
55
|
+
type MovePlacement = "before" | "after" | "prepend" | "append";
|
71
56
|
|
72
|
-
function indexName(id:
|
57
|
+
function indexName(id: Trees.Id): string {
|
73
58
|
return `${id}`;
|
74
59
|
}
|
75
60
|
|
76
|
-
export default class Tree<N extends
|
61
|
+
export default class Tree<N extends Trees.Node = Trees.Node> {
|
77
62
|
cnt: number;
|
78
63
|
obj: N;
|
79
|
-
indexes: Record<string,
|
64
|
+
indexes: Record<string, Trees.Index<N>>;
|
80
65
|
|
81
66
|
constructor(obj: N) {
|
82
67
|
this.cnt = 1;
|
83
|
-
this.obj = obj || { children: [] };
|
68
|
+
this.obj = obj || ({ children: [] } as N);
|
84
69
|
this.indexes = {};
|
85
70
|
this.build(this.obj);
|
86
71
|
}
|
87
72
|
|
88
|
-
build(obj: N):
|
73
|
+
build(obj: N): Trees.Index<N> {
|
89
74
|
const indexes = this.indexes;
|
90
75
|
const startId = this.cnt;
|
91
76
|
|
@@ -93,12 +78,13 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
93
78
|
indexes[indexName(this.cnt)] = index;
|
94
79
|
this.cnt++;
|
95
80
|
|
96
|
-
const walk = (objs:
|
97
|
-
const children:
|
98
|
-
objs.forEach((obj) => {
|
99
|
-
const index:
|
100
|
-
|
101
|
-
|
81
|
+
const walk = (objs: Trees.Node[], parent: Trees.Index<N>) => {
|
82
|
+
const children: Trees.Id[] = [];
|
83
|
+
objs.forEach((obj: N) => {
|
84
|
+
const index: Trees.Index<N> = {
|
85
|
+
id: this.cnt,
|
86
|
+
node: obj
|
87
|
+
};
|
102
88
|
|
103
89
|
if (parent) {
|
104
90
|
index.parent = parent.id;
|
@@ -132,12 +118,12 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
132
118
|
return index;
|
133
119
|
}
|
134
120
|
|
135
|
-
getIndex(id:
|
121
|
+
getIndex(id: Trees.Id) {
|
136
122
|
return this.indexes[indexName(id)];
|
137
123
|
}
|
138
124
|
|
139
|
-
removeIndex(index:
|
140
|
-
const del = (index:
|
125
|
+
removeIndex(index: Trees.Index<N>) {
|
126
|
+
const del = (index: Trees.Index<N>) => {
|
141
127
|
delete this.indexes[indexName(index.id)];
|
142
128
|
if (index.children && index.children.length) {
|
143
129
|
index.children.forEach((child) => {
|
@@ -148,11 +134,11 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
148
134
|
del(index);
|
149
135
|
}
|
150
136
|
|
151
|
-
get(id:
|
137
|
+
get(id: Trees.Id) {
|
152
138
|
return this.getIndex(id).node;
|
153
139
|
}
|
154
140
|
|
155
|
-
remove(id:
|
141
|
+
remove(id: Trees.Id) {
|
156
142
|
const index = this.getIndex(id);
|
157
143
|
const node = this.get(id);
|
158
144
|
|
@@ -168,7 +154,7 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
168
154
|
return node;
|
169
155
|
}
|
170
156
|
|
171
|
-
updateChildren(children:
|
157
|
+
updateChildren(children: Trees.Id[]) {
|
172
158
|
children.forEach((id, i) => {
|
173
159
|
const index = this.getIndex(id);
|
174
160
|
index.prev = index.next = null;
|
@@ -181,7 +167,7 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
181
167
|
});
|
182
168
|
}
|
183
169
|
|
184
|
-
insert(obj: N, parentId:
|
170
|
+
insert(obj: N, parentId: Trees.Id, i: number) {
|
185
171
|
const parentIndex = this.getIndex(parentId);
|
186
172
|
const parentNode = this.get(parentId);
|
187
173
|
|
@@ -202,25 +188,25 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
202
188
|
return index;
|
203
189
|
}
|
204
190
|
|
205
|
-
insertBefore(obj: N, destId:
|
191
|
+
insertBefore(obj: N, destId: Trees.Id) {
|
206
192
|
const destIndex = this.getIndex(destId);
|
207
193
|
const parentId = destIndex.parent;
|
208
194
|
const i = this.getIndex(parentId).children.indexOf(destId);
|
209
195
|
return this.insert(obj, parentId, i);
|
210
196
|
}
|
211
197
|
|
212
|
-
insertAfter(obj: N, destId:
|
198
|
+
insertAfter(obj: N, destId: Trees.Id) {
|
213
199
|
const destIndex = this.getIndex(destId);
|
214
200
|
const parentId = destIndex.parent;
|
215
201
|
const i = this.getIndex(parentId).children.indexOf(destId);
|
216
202
|
return this.insert(obj, parentId, i + 1);
|
217
203
|
}
|
218
204
|
|
219
|
-
prepend(obj: N, destId:
|
205
|
+
prepend(obj: N, destId: Trees.Id) {
|
220
206
|
return this.insert(obj, destId, 0);
|
221
207
|
}
|
222
208
|
|
223
|
-
append(obj: N, destId:
|
209
|
+
append(obj: N, destId: Trees.Id) {
|
224
210
|
const destIndex = this.getIndex(destId);
|
225
211
|
destIndex.children = destIndex.children || [];
|
226
212
|
return this.insert(obj, destId, destIndex.children.length);
|
@@ -237,13 +223,13 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
237
223
|
root.left = left++;
|
238
224
|
|
239
225
|
const walk = (
|
240
|
-
children:
|
241
|
-
parent:
|
226
|
+
children: Trees.Id[],
|
227
|
+
parent: Trees.Index<N>,
|
242
228
|
left: number,
|
243
229
|
collapsed: boolean
|
244
230
|
) => {
|
245
231
|
let height = 1;
|
246
|
-
children.forEach((id:
|
232
|
+
children.forEach((id: Trees.Id) => {
|
247
233
|
const node = this.getIndex(id);
|
248
234
|
if (collapsed) {
|
249
235
|
node.top = null;
|
@@ -276,13 +262,13 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
276
262
|
}
|
277
263
|
}
|
278
264
|
|
279
|
-
move(fromId:
|
265
|
+
move(fromId: Trees.Id, toId: Trees.Id, placement: MovePlacement) {
|
280
266
|
if (fromId === toId || toId === 1) {
|
281
267
|
return;
|
282
268
|
}
|
283
269
|
|
284
270
|
const obj = this.remove(fromId);
|
285
|
-
let index:
|
271
|
+
let index: Trees.Index<N>;
|
286
272
|
|
287
273
|
if (placement === "before") {
|
288
274
|
index = this.insertBefore(obj, toId);
|
@@ -306,7 +292,7 @@ export default class Tree<N extends TreeNode = TreeNode> {
|
|
306
292
|
}
|
307
293
|
}
|
308
294
|
|
309
|
-
getNodeByTop(top) {
|
295
|
+
getNodeByTop(top: Trees.Id) {
|
310
296
|
const indexes = this.indexes;
|
311
297
|
for (const id in indexes) {
|
312
298
|
if (Object.prototype.hasOwnProperty.call(indexes, id)) {
|
@@ -19,24 +19,24 @@ function jsonFetchOptions() {
|
|
19
19
|
}
|
20
20
|
|
21
21
|
export async function postJson(url: string, data: Record<string, unknown>) {
|
22
|
-
const
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
const response = await fetch(url, {
|
23
|
+
...jsonFetchOptions(),
|
24
|
+
method: "POST",
|
25
|
+
body: data ? JSON.stringify(data) : null
|
26
|
+
});
|
27
27
|
return response.json();
|
28
28
|
}
|
29
29
|
|
30
30
|
export async function putJson(url: string, data: Record<string, unknown>) {
|
31
|
-
const
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
const response = await fetch(url, {
|
32
|
+
...jsonFetchOptions(),
|
33
|
+
method: "PUT",
|
34
|
+
body: data ? JSON.stringify(data) : null
|
35
|
+
});
|
36
36
|
return response.json();
|
37
37
|
}
|
38
38
|
|
39
|
-
export async function post(url: string, data:
|
39
|
+
export async function post(url: string, data: FormData) {
|
40
40
|
const response = await fetch(url, {
|
41
41
|
method: "POST",
|
42
42
|
body: data,
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import * as Drag from "./Drag";
|
2
|
+
import { Locale, LocalizedValue } from "../types";
|
3
|
+
|
4
|
+
export interface Resource {
|
5
|
+
id: number | null;
|
6
|
+
name: LocalizedValue;
|
7
|
+
description: LocalizedValue;
|
8
|
+
url: string;
|
9
|
+
filename: string;
|
10
|
+
}
|
11
|
+
|
12
|
+
export interface Record extends Drag.DraggableRecord {
|
13
|
+
id?: number;
|
14
|
+
attachment: Resource;
|
15
|
+
uploading?: boolean;
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface Options {
|
19
|
+
attribute: string;
|
20
|
+
locale: string;
|
21
|
+
locales: { [index: string]: Locale };
|
22
|
+
showEmbed: boolean;
|
23
|
+
}
|
24
|
+
|
25
|
+
export interface State {
|
26
|
+
collection: Drag.Collection<Record>;
|
27
|
+
deleted: Record[];
|
28
|
+
setDeleted: (records: Record[]) => void;
|
29
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import * as Images from "./Images";
|
2
|
+
|
3
|
+
export interface Position {
|
4
|
+
x: number;
|
5
|
+
y: number;
|
6
|
+
}
|
7
|
+
|
8
|
+
export interface Size {
|
9
|
+
width: number;
|
10
|
+
height: number;
|
11
|
+
}
|
12
|
+
|
13
|
+
export interface Params {
|
14
|
+
crop_start_x: number;
|
15
|
+
crop_start_y: number;
|
16
|
+
crop_width: number;
|
17
|
+
crop_height: number;
|
18
|
+
crop_gravity_x: number;
|
19
|
+
crop_gravity_y: number;
|
20
|
+
}
|
21
|
+
|
22
|
+
export interface State extends Params {
|
23
|
+
aspect: number | null;
|
24
|
+
cropping: boolean;
|
25
|
+
image: Images.Resource;
|
26
|
+
}
|
27
|
+
|
28
|
+
export interface CropSize extends Position, Size {
|
29
|
+
aspect?: number;
|
30
|
+
}
|
31
|
+
|
32
|
+
export type Action =
|
33
|
+
| { type: "completeCrop" | "startCrop" | "toggleFocal" }
|
34
|
+
| { type: "setCrop"; payload: CropSize }
|
35
|
+
| { type: "setAspect"; payload: number }
|
36
|
+
| { type: "setFocal"; payload: Position };
|
@@ -0,0 +1,34 @@
|
|
1
|
+
export type DraggableRecord = Record<string, unknown>;
|
2
|
+
|
3
|
+
export interface Draggable<T = DraggableRecord> {
|
4
|
+
record: T;
|
5
|
+
ref: React.MutableRefObject<HTMLDivElement>;
|
6
|
+
rect: DOMRect | null;
|
7
|
+
handle: string;
|
8
|
+
}
|
9
|
+
|
10
|
+
export type Item<T = DraggableRecord> = Draggable<T> | "Files";
|
11
|
+
|
12
|
+
export type CollectionAction<T = DraggableRecord> =
|
13
|
+
| {
|
14
|
+
type: "append" | "prepend" | "insertFiles" | "replace" | "reorder";
|
15
|
+
payload: Item<T>[];
|
16
|
+
}
|
17
|
+
| { type: "update"; payload: Item<T> }
|
18
|
+
| { type: "remove"; payload: Draggable<T> }
|
19
|
+
| { type: "updatePositions"; payload?: Draggable<T> };
|
20
|
+
|
21
|
+
export interface Collection<T = DraggableRecord> {
|
22
|
+
ref: React.MutableRefObject<HTMLDivElement>;
|
23
|
+
draggables: Item<T>[];
|
24
|
+
dispatch: (CollectionAction) => void;
|
25
|
+
}
|
26
|
+
|
27
|
+
export interface Position {
|
28
|
+
x: number | null;
|
29
|
+
y: number | null;
|
30
|
+
}
|
31
|
+
|
32
|
+
export interface State<T = DraggableRecord> extends Position {
|
33
|
+
dragging: Item<T> | false;
|
34
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import * as Drag from "./Drag";
|
2
|
+
import { Locale, LocalizedValue } from "../types";
|
3
|
+
|
4
|
+
export interface Resource {
|
5
|
+
id: number | null;
|
6
|
+
alternative: LocalizedValue;
|
7
|
+
caption: LocalizedValue;
|
8
|
+
content_type: string;
|
9
|
+
filename: string;
|
10
|
+
crop_start_x: number | null;
|
11
|
+
crop_start_y: number | null;
|
12
|
+
crop_width: number | null;
|
13
|
+
crop_height: number | null;
|
14
|
+
crop_gravity_x: number;
|
15
|
+
crop_gravity_y: number;
|
16
|
+
real_width: number;
|
17
|
+
real_height: number;
|
18
|
+
original_url: string;
|
19
|
+
thumbnail_url: string;
|
20
|
+
uncropped_url: string;
|
21
|
+
}
|
22
|
+
|
23
|
+
export interface Record extends Drag.DraggableRecord {
|
24
|
+
id?: number;
|
25
|
+
image: Resource;
|
26
|
+
primary?: boolean;
|
27
|
+
src?: string;
|
28
|
+
file?: File;
|
29
|
+
}
|
30
|
+
|
31
|
+
export type Response = Resource | { status: "error"; error: string };
|
32
|
+
|
33
|
+
export interface GridOptions {
|
34
|
+
attribute: string;
|
35
|
+
enablePrimary: boolean;
|
36
|
+
locale: string;
|
37
|
+
locales: { [index: string]: Locale };
|
38
|
+
primaryAttribute: string;
|
39
|
+
showEmbed: boolean;
|
40
|
+
}
|
41
|
+
|
42
|
+
export interface GridState {
|
43
|
+
primary: Drag.Collection<Record>;
|
44
|
+
images: Drag.Collection<Record>;
|
45
|
+
deleted: Record[];
|
46
|
+
setDeleted: (records: Record[]) => void;
|
47
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import * as Pages from "./Pages";
|
2
|
+
import * as Template from "./Template";
|
3
|
+
import { Locale } from "../types";
|
4
|
+
|
5
|
+
export type Action =
|
6
|
+
| { type: "setPage"; payload: Pages.SerializedResource }
|
7
|
+
| { type: "setDatesEnabled"; payload: boolean }
|
8
|
+
| { type: "setLocale"; payload: string }
|
9
|
+
| { type: "update"; payload: Partial<Pages.Resource> }
|
10
|
+
| { type: "updateBlocks"; payload: Partial<Pages.Blocks> };
|
11
|
+
|
12
|
+
export interface State<T = Pages.Resource> {
|
13
|
+
locale: string;
|
14
|
+
locales: { [index: string]: Locale };
|
15
|
+
page: T;
|
16
|
+
templates: Template.Config[];
|
17
|
+
datesEnabled?: boolean;
|
18
|
+
inputDir?: "ltr" | "rtl";
|
19
|
+
templateConfig?: Template.Config;
|
20
|
+
}
|
21
|
+
|
22
|
+
export interface Tab {
|
23
|
+
id: string;
|
24
|
+
name: string;
|
25
|
+
enabled: boolean;
|
26
|
+
}
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import * as Attachments from "./Attachments";
|
2
|
+
import * as Images from "./Images";
|
3
|
+
import * as Trees from "./Trees";
|
4
|
+
import { LocalizedValue, MaybeLocalizedValue } from "../types";
|
5
|
+
|
6
|
+
export type Author = [name: string, id: number];
|
7
|
+
|
8
|
+
export interface Ancestor {
|
9
|
+
id: number;
|
10
|
+
name: LocalizedValue;
|
11
|
+
path_segment: LocalizedValue;
|
12
|
+
}
|
13
|
+
|
14
|
+
export interface StatusLabels {
|
15
|
+
[index: number]: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface TreeResource {
|
19
|
+
id?: number;
|
20
|
+
blocks: Blocks;
|
21
|
+
news_page: boolean;
|
22
|
+
parent_page_id: number;
|
23
|
+
permissions: string[];
|
24
|
+
pinned: boolean;
|
25
|
+
published_at: Date;
|
26
|
+
status: number;
|
27
|
+
}
|
28
|
+
|
29
|
+
export interface Blocks {
|
30
|
+
[index: string]: MaybeLocalizedValue;
|
31
|
+
name: LocalizedValue;
|
32
|
+
}
|
33
|
+
|
34
|
+
export interface MetaImage {
|
35
|
+
src?: string;
|
36
|
+
image?: Images.Resource;
|
37
|
+
}
|
38
|
+
|
39
|
+
export interface Resource extends TreeResource {
|
40
|
+
all_day: boolean;
|
41
|
+
ancestors: Ancestor[];
|
42
|
+
blocks: Blocks;
|
43
|
+
enabled_tags: string[];
|
44
|
+
ends_at: Date;
|
45
|
+
errors: { attribute: string; message: string }[];
|
46
|
+
feed_enabled: boolean;
|
47
|
+
meta_image: MetaImage;
|
48
|
+
page_files: Attachments.Record[];
|
49
|
+
page_images: Images.Record[];
|
50
|
+
path_segment: MaybeLocalizedValue;
|
51
|
+
redirect_to: string;
|
52
|
+
starts_at: Date;
|
53
|
+
tags_and_suggestions: string[];
|
54
|
+
template: string;
|
55
|
+
unique_name: string;
|
56
|
+
urls: LocalizedValue;
|
57
|
+
user_id: number;
|
58
|
+
}
|
59
|
+
|
60
|
+
export interface SerializedResource
|
61
|
+
extends Omit<Resource, "published_at" | "starts_at" | "ends_at"> {
|
62
|
+
published_at: string;
|
63
|
+
starts_at: string;
|
64
|
+
ends_at: string;
|
65
|
+
}
|
66
|
+
|
67
|
+
export interface TreeItem extends Partial<TreeResource> {
|
68
|
+
blocks: Blocks;
|
69
|
+
editing?: boolean;
|
70
|
+
permissions?: string[];
|
71
|
+
}
|
72
|
+
|
73
|
+
export interface TreeNode extends TreeItem, Trees.Node {
|
74
|
+
children: TreeNode[];
|
75
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export interface Block {
|
2
|
+
name: string;
|
3
|
+
title: string;
|
4
|
+
description?: string;
|
5
|
+
optional: boolean;
|
6
|
+
enforced: boolean;
|
7
|
+
size: string;
|
8
|
+
class?: string;
|
9
|
+
localized?: boolean;
|
10
|
+
placeholder?: string;
|
11
|
+
options?: [string, string][];
|
12
|
+
type?: string;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface Config {
|
16
|
+
name: string;
|
17
|
+
template_name: string;
|
18
|
+
blocks: Block[];
|
19
|
+
metadata_blocks: Block[];
|
20
|
+
images: boolean;
|
21
|
+
dates: boolean;
|
22
|
+
tags: boolean;
|
23
|
+
files: boolean;
|
24
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
export type Id = number | string;
|
2
|
+
|
3
|
+
export interface Node {
|
4
|
+
children: Array<Node>;
|
5
|
+
collapsed: boolean;
|
6
|
+
root?: boolean;
|
7
|
+
}
|
8
|
+
|
9
|
+
export interface Index<T extends Node = Node> {
|
10
|
+
id: number;
|
11
|
+
node: T;
|
12
|
+
children?: Id[];
|
13
|
+
parent?: Id;
|
14
|
+
top?: number;
|
15
|
+
height?: number;
|
16
|
+
next?: Id;
|
17
|
+
prev?: Id;
|
18
|
+
left?: number;
|
19
|
+
}
|
data/app/javascript/types.ts
CHANGED
@@ -3,28 +3,5 @@ export interface Locale {
|
|
3
3
|
dir: "ltr" | "rtl";
|
4
4
|
}
|
5
5
|
|
6
|
-
export
|
7
|
-
|
8
|
-
name: Record<string, string>;
|
9
|
-
description: Record<string, string>;
|
10
|
-
url: string;
|
11
|
-
}
|
12
|
-
|
13
|
-
export interface ImageResource {
|
14
|
-
id: number | null;
|
15
|
-
alternative: Record<string, string>;
|
16
|
-
caption: Record<string, string>;
|
17
|
-
content_type: string;
|
18
|
-
filename: string;
|
19
|
-
crop_start_x: number | null;
|
20
|
-
crop_start_y: number | null;
|
21
|
-
crop_width: number | null;
|
22
|
-
crop_height: number | null;
|
23
|
-
crop_gravity_x: number;
|
24
|
-
crop_gravity_y: number;
|
25
|
-
real_width: number;
|
26
|
-
real_height: number;
|
27
|
-
original_url: string;
|
28
|
-
thumbnail_url: string;
|
29
|
-
uncropped_url: string;
|
30
|
-
}
|
6
|
+
export type LocalizedValue = Record<string, string>;
|
7
|
+
export type MaybeLocalizedValue = LocalizedValue | string;
|
data/app/mailers/admin_mailer.rb
CHANGED
@@ -2,23 +2,19 @@
|
|
2
2
|
|
3
3
|
class AdminMailer < ApplicationMailer
|
4
4
|
default from: proc { "\"Pages\" <no-reply@anyone.no>" }
|
5
|
-
layout
|
5
|
+
layout "pages_core/mailer"
|
6
6
|
|
7
7
|
def account_recovery(user, url)
|
8
8
|
@user = user
|
9
9
|
@url = url
|
10
|
-
mail(
|
11
|
-
|
12
|
-
subject: "Recover your account on #{PagesCore.config(:site_name)}"
|
13
|
-
)
|
10
|
+
mail(to: @user.email,
|
11
|
+
subject: "Recover your account on #{PagesCore.config(:site_name)}")
|
14
12
|
end
|
15
13
|
|
16
14
|
def invite(invite, url)
|
17
15
|
@invite = invite
|
18
16
|
@url = url
|
19
|
-
mail(
|
20
|
-
|
21
|
-
subject: "#{PagesCore.config(:site_name)} has invited you to Pages"
|
22
|
-
)
|
17
|
+
mail(to: @invite.email,
|
18
|
+
subject: "#{PagesCore.config(:site_name)} has invited you to Pages")
|
23
19
|
end
|
24
20
|
end
|
data/app/models/attachment.rb
CHANGED
data/app/models/autopublisher.rb
CHANGED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PagesCore
|
4
|
+
module AuthenticableUser
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
has_secure_password
|
9
|
+
|
10
|
+
validates(:otp_secret, presence: true, if: :otp_enabled?)
|
11
|
+
validates(
|
12
|
+
:password,
|
13
|
+
length: {
|
14
|
+
minimum: 8,
|
15
|
+
maximum: ActiveModel::SecurePassword::MAX_PASSWORD_LENGTH_ALLOWED,
|
16
|
+
allow_blank: true
|
17
|
+
}
|
18
|
+
)
|
19
|
+
|
20
|
+
after_initialize { |u| u.session_token ||= u.class.random_session_token }
|
21
|
+
before_validation :update_session_token
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def authenticate(email, password:)
|
26
|
+
User.find_by(email:).try(:authenticate, password)
|
27
|
+
end
|
28
|
+
|
29
|
+
def random_session_token
|
30
|
+
SecureRandom.hex(32)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def can_login?
|
35
|
+
activated?
|
36
|
+
end
|
37
|
+
|
38
|
+
def recovery_codes=(codes)
|
39
|
+
self.hashed_recovery_codes = codes.map do |c|
|
40
|
+
BCrypt::Password.create(c, cost: 8)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def use_recovery_code!(code)
|
45
|
+
valid_hashes = hashed_recovery_codes.select do |c|
|
46
|
+
BCrypt::Password.new(c) == code
|
47
|
+
end
|
48
|
+
return false unless valid_hashes.any?
|
49
|
+
|
50
|
+
update(hashed_recovery_codes: hashed_recovery_codes - valid_hashes)
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def update_session_token
|
57
|
+
return unless password_digest_changed? ||
|
58
|
+
otp_enabled_changed?
|
59
|
+
|
60
|
+
self.session_token = self.class.random_session_token
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|