pages_core 3.12.1 → 3.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +135 -50
  4. data/app/assets/builds/pages_core/admin-dist.js.map +7 -0
  5. data/app/assets/builds/pages_core/admin.css +72 -20
  6. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +1 -1
  7. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +2 -2
  8. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +8 -8
  9. data/app/assets/stylesheets/pages_core/admin/components/image_uploader.css +2 -2
  10. data/app/assets/stylesheets/pages_core/admin/components/layout.css +2 -2
  11. data/app/assets/stylesheets/pages_core/admin/components/modal.css +2 -2
  12. data/app/assets/stylesheets/pages_core/admin/components/search.css +27 -0
  13. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +6 -0
  14. data/app/controllers/admin/pages_controller.rb +12 -11
  15. data/app/controllers/concerns/pages_core/rss_controller.rb +17 -1
  16. data/app/controllers/pages_core/admin_controller.rb +6 -0
  17. data/app/controllers/pages_core/frontend/pages_controller.rb +9 -5
  18. data/app/controllers/pages_core/sitemaps_controller.rb +3 -5
  19. data/app/helpers/admin/pages_helper.rb +32 -0
  20. data/app/helpers/pages_core/images_helper.rb +28 -7
  21. data/app/javascript/admin-dist.ts +2 -0
  22. data/app/javascript/components/Attachments/{Attachment.jsx → Attachment.tsx} +42 -33
  23. data/app/javascript/components/Attachments/{AttachmentEditor.jsx → AttachmentEditor.tsx} +23 -23
  24. data/app/javascript/components/{EditableImage.jsx → EditableImage.tsx} +27 -24
  25. data/app/javascript/components/{FileUploadButton.jsx → FileUploadButton.tsx} +15 -16
  26. data/app/javascript/components/ImageCropper/FocalPoint.tsx +94 -0
  27. data/app/javascript/components/ImageCropper/{Image.jsx → Image.tsx} +13 -14
  28. data/app/javascript/components/ImageCropper/{Toolbar.jsx → Toolbar.tsx} +16 -12
  29. data/app/javascript/components/ImageCropper/{useCrop.js → useCrop.ts} +80 -37
  30. data/app/javascript/components/{ImageCropper.jsx → ImageCropper.tsx} +17 -15
  31. data/app/javascript/components/ImageEditor/{Form.jsx → Form.tsx} +24 -23
  32. data/app/javascript/components/{ImageEditor.jsx → ImageEditor.tsx} +17 -15
  33. data/app/javascript/components/ImageGrid/{DragElement.jsx → DragElement.tsx} +12 -10
  34. data/app/javascript/components/ImageGrid/{GridImage.jsx → GridImage.tsx} +40 -30
  35. data/app/javascript/components/ImageGrid/{Placeholder.jsx → Placeholder.tsx} +5 -6
  36. data/app/javascript/components/ImageGrid.jsx +3 -4
  37. data/app/javascript/components/{ImageUploader.jsx → ImageUploader.tsx} +46 -41
  38. data/app/javascript/components/Modal.tsx +48 -0
  39. data/app/javascript/components/PageImages.tsx +28 -0
  40. data/app/javascript/components/{PageTreeDraggable.jsx → PageTree/Draggable.tsx} +79 -57
  41. data/app/javascript/components/{PageTreeNode.jsx → PageTree/Node.tsx} +79 -70
  42. data/app/javascript/components/PageTree/types.ts +15 -0
  43. data/app/javascript/components/PageTree.tsx +206 -0
  44. data/app/javascript/components/RichTextToolbarButton.tsx +17 -0
  45. data/app/javascript/components/TagEditor/{AddTagForm.jsx → AddTagForm.tsx} +9 -10
  46. data/app/javascript/components/TagEditor/{Tag.jsx → Tag.tsx} +8 -9
  47. data/app/javascript/components/{TagEditor.jsx → TagEditor.tsx} +12 -13
  48. data/app/javascript/components/Toast.tsx +61 -0
  49. data/app/javascript/components/drag/{draggedOrder.js → draggedOrder.ts} +22 -12
  50. data/app/javascript/components/drag/types.ts +28 -0
  51. data/app/javascript/components/drag/{useDragCollection.js → useDragCollection.ts} +40 -22
  52. data/app/javascript/components/drag/{useDragUploader.js → useDragUploader.ts} +34 -25
  53. data/app/javascript/components/drag/useDraggable.ts +21 -0
  54. data/app/javascript/components/{drag.js → drag.ts} +1 -0
  55. data/app/javascript/controllers/{EditPageController.js → EditPageController.ts} +3 -1
  56. data/app/javascript/controllers/{LoginController.js → LoginController.ts} +7 -3
  57. data/app/javascript/controllers/{MainController.js → MainController.ts} +19 -14
  58. data/app/javascript/features/{RichText.jsx → RichText.tsx} +3 -3
  59. data/app/javascript/{index.js → index.ts} +8 -7
  60. data/app/javascript/lib/{Tree.js → Tree.ts} +106 -85
  61. data/app/javascript/lib/{copyToClipboard.js → copyToClipboard.ts} +1 -1
  62. data/app/javascript/lib/{readyHandler.js → readyHandler.ts} +4 -2
  63. data/app/javascript/lib/{request.js → request.ts} +11 -5
  64. data/app/javascript/stores/useModalStore.ts +15 -0
  65. data/app/javascript/stores/useToastStore.ts +26 -0
  66. data/app/javascript/stores.ts +2 -0
  67. data/app/javascript/types.ts +30 -0
  68. data/app/policies/page_policy.rb +4 -0
  69. data/app/views/admin/calendars/_sidebar.html.erb +3 -0
  70. data/app/views/admin/news/_sidebar.html.erb +3 -0
  71. data/app/views/admin/pages/_list_item.html.erb +4 -22
  72. data/app/views/admin/pages/_search_bar.html.erb +12 -0
  73. data/app/views/admin/pages/index.html.erb +3 -0
  74. data/app/views/admin/pages/search.html.erb +54 -0
  75. data/app/views/feeds/pages.rss.builder +3 -9
  76. data/config/routes.rb +1 -0
  77. data/lib/pages_core/configuration/pages.rb +0 -1
  78. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +33 -17
  79. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +0 -1
  80. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/gridOverlay.ts +40 -0
  81. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/responsiveEmbeds.ts +68 -0
  82. data/lib/rails/generators/pages_core/frontend/templates/postcss.config.js +17 -0
  83. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.postcss.css +4 -0
  84. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.css +24 -0
  85. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/layout.css +21 -0
  86. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.css +5 -0
  87. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/animation.css +5 -0
  88. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.css +18 -0
  89. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/fonts.css +6 -0
  90. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/grid.css +65 -0
  91. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.css +131 -0
  92. data/lib/rails/generators/pages_core/install/templates/pages_initializer.rb +0 -3
  93. metadata +69 -63
  94. data/app/javascript/admin-dist.js +0 -2
  95. data/app/javascript/components/ImageCropper/FocalPoint.jsx +0 -93
  96. data/app/javascript/components/Modal.jsx +0 -59
  97. data/app/javascript/components/PageImages.jsx +0 -25
  98. data/app/javascript/components/PageTree.jsx +0 -196
  99. data/app/javascript/components/RichTextToolbarButton.jsx +0 -20
  100. data/app/javascript/components/Toast.jsx +0 -72
  101. data/app/javascript/components/drag/useDraggable.js +0 -17
  102. data/app/javascript/stores/ModalStore.jsx +0 -12
  103. data/app/javascript/stores/ToastStore.jsx +0 -14
  104. data/app/javascript/stores.js +0 -2
  105. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/GridOverlay.js +0 -66
  106. data/lib/rails/generators/pages_core/frontend/templates/javascript/lib/ResponsiveEmbeds.js +0 -72
  107. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/application.sass.scss +0 -15
  108. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/components/base.scss +0 -12
  109. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/config.scss +0 -26
  110. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/breakpoints.scss +0 -42
  111. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/clearfix.scss +0 -7
  112. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/fonts.scss +0 -32
  113. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid.scss +0 -168
  114. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/framework/grid_overlay.scss +0 -44
  115. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/colors.scss +0 -8
  116. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/global/typography.scss +0 -90
  117. data/lib/rails/generators/pages_core/frontend/templates/stylesheets/vendor/normalize.css +0 -349
  118. /data/app/javascript/components/Attachments/{Placeholder.jsx → Placeholder.tsx} +0 -0
  119. /data/app/javascript/components/ImageGrid/{FilePlaceholder.jsx → FilePlaceholder.tsx} +0 -0
  120. /data/app/javascript/{components.js → components.ts} +0 -0
  121. /data/app/javascript/{hooks.js → hooks.ts} +0 -0
@@ -48,43 +48,65 @@
48
48
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
49
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
50
  SOFTWARE.
51
- */
51
+ */
52
52
 
53
- export default class Tree {
54
- constructor(obj) {
53
+ export type TreeId = number | string;
54
+ type MovePlacement = "before" | "after" | "prepend" | "append";
55
+
56
+ export interface TreeNode {
57
+ children: TreeNode[],
58
+ collapsed: boolean
59
+ }
60
+
61
+ export interface TreeIndex<T extends TreeNode = TreeNode> {
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
+ }
71
+
72
+ function indexName(id: number | string): string {
73
+ return `${id}`;
74
+ }
75
+
76
+ export default class Tree<N extends TreeNode = TreeNode> {
77
+ cnt: number;
78
+ obj: N;
79
+ indexes: Record<string, TreeIndex<N>>;
80
+
81
+ constructor(obj: N) {
55
82
  this.cnt = 1;
56
83
  this.obj = obj || { children: [] };
57
84
  this.indexes = {};
58
85
  this.build(this.obj);
59
86
  }
60
87
 
61
- build(obj) {
62
- var indexes = this.indexes;
63
- var startId = this.cnt;
64
- var self = this;
88
+ build(obj: N): TreeIndex<N> {
89
+ const indexes = this.indexes;
90
+ const startId = this.cnt;
65
91
 
66
- var index = { id: startId, node: obj };
67
- indexes[this.cnt + ""] = index;
92
+ const index = { id: startId, node: obj };
93
+ indexes[indexName(this.cnt)] = index;
68
94
  this.cnt++;
69
95
 
70
- if (obj.children && obj.children.length) {
71
- walk(obj.children, index);
72
- }
73
-
74
- function walk(objs, parent) {
75
- var children = [];
76
- objs.forEach(function(obj) {
77
- var index = {};
78
- index.id = self.cnt;
96
+ const walk = (objs: TreeIndex<N>[], parent: TreeIndex<N>) => {
97
+ const children: TreeIndex<N>[] = [];
98
+ objs.forEach((obj) => {
99
+ const index: TreeIndex<N> = {};
100
+ index.id = this.cnt;
79
101
  index.node = obj;
80
102
 
81
103
  if (parent) {
82
104
  index.parent = parent.id;
83
105
  }
84
106
 
85
- indexes[self.cnt + ""] = index;
86
- children.push(self.cnt);
87
- self.cnt++;
107
+ indexes[indexName(this.cnt)] = index;
108
+ children.push(this.cnt);
109
+ this.cnt++;
88
110
 
89
111
  if (obj.children && obj.children.length) {
90
112
  walk(obj.children, index);
@@ -93,7 +115,7 @@ export default class Tree {
93
115
  parent.children = children;
94
116
 
95
117
  children.forEach(function(id, i) {
96
- var index = indexes[id + ""];
118
+ const index = indexes[indexName(id)];
97
119
  if (i > 0) {
98
120
  index.prev = children[i - 1];
99
121
  }
@@ -101,56 +123,54 @@ export default class Tree {
101
123
  index.next = children[i+1];
102
124
  }
103
125
  });
126
+ };
127
+
128
+ if (obj.children && obj.children.length) {
129
+ walk(obj.children, index);
104
130
  }
105
131
 
106
132
  return index;
107
133
  }
108
134
 
109
- getIndex(id) {
110
- var index = this.indexes[id + ""];
111
- if (index) {
112
- return index;
113
- }
135
+ getIndex(id: TreeId): TreeIndex<N> {
136
+ return this.indexes[indexName(id)];
114
137
  }
115
138
 
116
- removeIndex(index) {
117
- var self = this;
118
- del(index);
119
-
120
- function del(index) {
121
- delete self.indexes[index.id + ""];
139
+ removeIndex(index: TreeIndex<N>) {
140
+ const del = (index: TreeIndex<N>) => {
141
+ delete this.indexes[indexName(index.id)];
122
142
  if (index.children && index.children.length) {
123
- index.children.forEach(function(child) {
124
- del(self.getIndex(child));
143
+ index.children.forEach((child) => {
144
+ del(this.getIndex(child));
125
145
  });
126
146
  }
127
- }
147
+ };
148
+ del(index);
128
149
  }
129
150
 
130
- get(id) {
131
- var index = this.getIndex(id);
132
- if (index && index.node) {
133
- return index.node;
134
- }
135
- return null;
151
+ get(id: TreeId): N {
152
+ return this.getIndex(id).node;
136
153
  }
137
154
 
138
- remove(id) {
139
- var index = this.getIndex(id);
140
- var node = this.get(id);
141
- var parentIndex = this.getIndex(index.parent);
142
- var parentNode = this.get(index.parent);
155
+ remove(id: TreeId): N {
156
+ const index = this.getIndex(id);
157
+ const node = this.get(id);
158
+
159
+ const parentIndex = this.getIndex(index.parent);
160
+ const parentNode = this.get(index.parent);
161
+
143
162
  parentNode.children.splice(parentNode.children.indexOf(node), 1);
144
163
  parentIndex.children.splice(parentIndex.children.indexOf(id), 1);
164
+
145
165
  this.removeIndex(index);
146
166
  this.updateChildren(parentIndex.children);
147
167
 
148
168
  return node;
149
169
  }
150
170
 
151
- updateChildren(children) {
152
- children.forEach(function(id, i) {
153
- var index = this.getIndex(id);
171
+ updateChildren(children: TreeIndex<N>[]) {
172
+ children.forEach((id, i) => {
173
+ const index = this.getIndex(id);
154
174
  index.prev = index.next = null;
155
175
  if (i > 0) {
156
176
  index.prev = children[i-1];
@@ -158,14 +178,14 @@ export default class Tree {
158
178
  if (i < children.length-1) {
159
179
  index.next = children[i+1];
160
180
  }
161
- }.bind(this));
181
+ });
162
182
  }
163
183
 
164
- insert(obj, parentId, i) {
165
- var parentIndex = this.getIndex(parentId);
166
- var parentNode = this.get(parentId);
184
+ insert(obj: N, parentId: TreeId, i: number): TreeIndex<N> {
185
+ const parentIndex = this.getIndex(parentId);
186
+ const parentNode = this.get(parentId);
167
187
 
168
- var index = this.build(obj);
188
+ const index = this.build(obj);
169
189
  index.parent = parentId;
170
190
 
171
191
  parentNode.children = parentNode.children || [];
@@ -182,26 +202,26 @@ export default class Tree {
182
202
  return index;
183
203
  }
184
204
 
185
- insertBefore(obj, destId) {
186
- var destIndex = this.getIndex(destId);
187
- var parentId = destIndex.parent;
188
- var i = this.getIndex(parentId).children.indexOf(destId);
205
+ insertBefore(obj: N, destId: TreeId): TreeIndex<N> {
206
+ const destIndex = this.getIndex(destId);
207
+ const parentId = destIndex.parent;
208
+ const i = this.getIndex(parentId).children.indexOf(destId);
189
209
  return this.insert(obj, parentId, i);
190
210
  }
191
211
 
192
- insertAfter(obj, destId) {
193
- var destIndex = this.getIndex(destId);
194
- var parentId = destIndex.parent;
195
- var i = this.getIndex(parentId).children.indexOf(destId);
212
+ insertAfter(obj: N, destId: TreeId): TreeIndex<N> {
213
+ const destIndex = this.getIndex(destId);
214
+ const parentId = destIndex.parent;
215
+ const i = this.getIndex(parentId).children.indexOf(destId);
196
216
  return this.insert(obj, parentId, i+1);
197
217
  }
198
218
 
199
- prepend(obj, destId) {
219
+ prepend(obj: N, destId: TreeId): TreeIndex<N> {
200
220
  return this.insert(obj, destId, 0);
201
221
  }
202
222
 
203
- append(obj, destId) {
204
- var destIndex = this.getIndex(destId);
223
+ append(obj: N, destId: TreeId): TreeIndex<N> {
224
+ const destIndex = this.getIndex(destId);
205
225
  destIndex.children = destIndex.children || [];
206
226
  return this.insert(obj, destId, destIndex.children.length);
207
227
  }
@@ -209,22 +229,19 @@ export default class Tree {
209
229
  // react-ui-tree methods
210
230
 
211
231
  updateNodesPosition() {
212
- var top = 1;
213
- var left = 1;
214
- var root = this.getIndex(1);
215
- var self = this;
232
+ let top = 1;
233
+ let left = 1;
234
+ const root = this.getIndex(1);
216
235
 
217
236
  root.top = top++;
218
237
  root.left = left++;
219
238
 
220
- if (root.children && root.children.length) {
221
- walk(root.children, root, left, root.node.collapsed);
222
- }
223
-
224
- function walk(children, parent, left, collapsed) {
225
- var height = 1;
226
- children.forEach(function(id) {
227
- var node = self.getIndex(id);
239
+ const walk = (
240
+ children: TreeIndex<N>[], parent: TreeIndex<N>, left: number, collapsed: boolean
241
+ ) => {
242
+ let height = 1;
243
+ children.forEach((id: TreeId) => {
244
+ const node = this.getIndex(id);
228
245
  if (collapsed) {
229
246
  node.top = null;
230
247
  node.left = null;
@@ -249,16 +266,20 @@ export default class Tree {
249
266
  if(parent.node.collapsed) parent.height = 1;
250
267
  else parent.height = height;
251
268
  return parent.height;
269
+ };
270
+
271
+ if (root.children && root.children.length) {
272
+ walk(root.children, root, left, root.node.collapsed);
252
273
  }
253
274
  }
254
275
 
255
- move(fromId, toId, placement) {
276
+ move(fromId: TreeId, toId: TreeId, placement: MovePlacement): TreeIndex<N> {
256
277
  if (fromId === toId || toId === 1) {
257
278
  return;
258
279
  }
259
280
 
260
- var obj = this.remove(fromId);
261
- var index = null;
281
+ const obj = this.remove(fromId);
282
+ let index: TreeIndex<N>;
262
283
 
263
284
  if (placement === "before") {
264
285
  index = this.insertBefore(obj, toId);
@@ -275,16 +296,16 @@ export default class Tree {
275
296
  return index;
276
297
  }
277
298
 
278
- getParent(id) {
279
- var indexes = this.indexes;
299
+ getParent(id: string) {
300
+ const indexes = this.indexes;
280
301
  if (Object.prototype.hasOwnProperty.call(indexes, id)) {
281
302
  return this.getIndex(indexes[id].parent);
282
303
  }
283
304
  }
284
305
 
285
306
  getNodeByTop(top) {
286
- var indexes = this.indexes;
287
- for(var id in indexes) {
307
+ const indexes = this.indexes;
308
+ for(const id in indexes) {
288
309
  if (Object.prototype.hasOwnProperty.call(indexes, id)) {
289
310
  if(indexes[id].top === top) {
290
311
  return indexes[id];
@@ -3,7 +3,7 @@ export function copySupported () {
3
3
  document.queryCommandSupported("copy");
4
4
  }
5
5
 
6
- export default function copyToClipboard (str) {
6
+ export default function copyToClipboard (str: string) {
7
7
  const el = document.createElement("textarea");
8
8
  el.value = str;
9
9
  document.body.appendChild(el);
@@ -1,4 +1,6 @@
1
- let readyHandlers = [];
1
+ type ReadyHandlerFunc = () => void;
2
+
3
+ const readyHandlers: ReadyHandlerFunc[] = [];
2
4
 
3
5
  const handleState = () => {
4
6
  if (["interactive", "complete"].indexOf(document.readyState) > -1) {
@@ -13,7 +15,7 @@ class ReadyHandler {
13
15
  document.onreadystatechange = handleState;
14
16
  }
15
17
 
16
- ready (handler) {
18
+ ready (handler: ReadyHandlerFunc) {
17
19
  readyHandlers.push(handler);
18
20
  handleState();
19
21
  }
@@ -1,5 +1,11 @@
1
- export function csrfToken() {
2
- return document.querySelector("[name=csrf-token]").content;
1
+ export function csrfToken(): string {
2
+ const elem = document.querySelector("[name=csrf-token]");
3
+
4
+ if (!elem) {
5
+ return "";
6
+ }
7
+
8
+ return elem.getAttribute("content") || "";
3
9
  }
4
10
 
5
11
  function jsonFetchOptions() {
@@ -8,7 +14,7 @@ function jsonFetchOptions() {
8
14
  "X-CSRF-Token": csrfToken() } });
9
15
  }
10
16
 
11
- export async function postJson(url, data) {
17
+ export async function postJson(url: string, data: Record<string, unknown>) {
12
18
  const options = { ...jsonFetchOptions(), method: "POST" };
13
19
  if (data) {
14
20
  options.body = JSON.stringify(data);
@@ -17,7 +23,7 @@ export async function postJson(url, data) {
17
23
  return response.json();
18
24
  }
19
25
 
20
- export async function putJson(url, data) {
26
+ export async function putJson(url: string, data: Record<string, unknown>) {
21
27
  const options = { ...jsonFetchOptions(), method: "PUT" };
22
28
  if (data) {
23
29
  options.body = JSON.stringify(data);
@@ -26,7 +32,7 @@ export async function putJson(url, data) {
26
32
  return response.json();
27
33
  }
28
34
 
29
- export async function post(url, data) {
35
+ export async function post(url: string, data: Record<string, unknown>) {
30
36
  const response = await fetch(url, {
31
37
  method: "POST",
32
38
  body: data,
@@ -0,0 +1,15 @@
1
+ import { create } from "zustand";
2
+
3
+ interface ModalState {
4
+ component: JSX.Element | null,
5
+ open: (elem: JSX.Element) => void,
6
+ close: () => void
7
+ }
8
+
9
+ const useModalStore = create<ModalState>((set) => ({
10
+ component: null,
11
+ open: (c: JSX.Element) => set({ component: c }),
12
+ close: () => set({ component: null })
13
+ }));
14
+
15
+ export default useModalStore;
@@ -0,0 +1,26 @@
1
+ import { create } from "zustand";
2
+
3
+ export interface Toast {
4
+ type: string,
5
+ message: string
6
+ }
7
+
8
+ interface ToastState {
9
+ toasts: Toast[],
10
+ error: (msg: string) => void,
11
+ notice: (msg: string) => void,
12
+ next: () => void
13
+ }
14
+
15
+ const useToastStore = create<ToastState>((set) => ({
16
+ toasts: [],
17
+ error: (msg: string) => set((state) => (
18
+ { toasts: [...state.toasts, { message: msg, type: "error" }] }
19
+ )),
20
+ notice: (msg: string) => set((state) => (
21
+ { toasts: [...state.toasts, { message: msg, type: "notice" }] }
22
+ )),
23
+ next: () => set((state) => ({ toasts: state.toasts.slice(1) }))
24
+ }));
25
+
26
+ export default useToastStore;
@@ -0,0 +1,2 @@
1
+ export { default as useModalStore } from "./stores/useModalStore";
2
+ export { default as useToastStore } from "./stores/useToastStore";
@@ -0,0 +1,30 @@
1
+ export interface Locale {
2
+ name: string,
3
+ dir: "ltr" | "rtl"
4
+ }
5
+
6
+ export interface AttachmentResource {
7
+ id: number | null,
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
+ }
@@ -13,6 +13,10 @@ class PagePolicy < Policy
13
13
  index?
14
14
  end
15
15
 
16
+ def search?
17
+ index?
18
+ end
19
+
16
20
  def new?
17
21
  user.role?(:pages)
18
22
  end
@@ -1,3 +1,6 @@
1
+ <%= render(partial: "admin/pages/search_bar",
2
+ locals: { query: search_query }) %>
3
+
1
4
  <% if policy(Page).new? %>
2
5
  <h2>
3
6
  New entry
@@ -1,3 +1,6 @@
1
+ <%= render(partial: "admin/pages/search_bar",
2
+ locals: { query: search_query }) %>
3
+
1
4
  <% if policy(Page).new? %>
2
5
  <h2>New article</h2>
3
6
  <p>
@@ -4,19 +4,13 @@
4
4
  section ||= false
5
5
  sections ||= []
6
6
  %>
7
- <tr class="<%= [page.status_label.downcase,((page.autopublish?) ? 'autopublish' : nil), ((page.pinned?) ? 'pinned' : nil)].join(' ') %>">
7
+ <%= page_list_row(page) do %>
8
8
  <td class="name">
9
9
  <%= link_to_if(policy(page).edit?,
10
10
  page_name(page),
11
11
  edit_admin_page_url(@locale, page),
12
12
  class: 'name_link') %>
13
- <% if page.autopublish? %>
14
- <br />
15
- <small>
16
- This page will be published
17
- <strong><%= publish_time(page.published_at) %></strong>
18
- </small>
19
- <% end %>
13
+ <%= autopublish_notice(page) %>
20
14
  </td>
21
15
  <% if date %>
22
16
  <td class="date">
@@ -24,19 +18,7 @@
24
18
  </td>
25
19
  <% end %>
26
20
  <td>
27
- <% if page.published? %>
28
- <% if page.published_at.year == Time.zone.now.year %>
29
- <%= l(page.published_at, format: :pages_date) %>
30
- <% else %>
31
- <%= l(page.published_at, format: :pages_full) %>
32
- <% end %>
33
- <% else %>
34
- <% if page.status_label == 'Published' %>
35
- <em>Not published</em>
36
- <% else %>
37
- <em><%= page.status_label %></em>
38
- <% end %>
39
- <% end %>
21
+ <%= page_published_status(page) %>
40
22
  </td>
41
23
  <% if author %>
42
24
  <td>
@@ -48,4 +30,4 @@
48
30
  <%= news_section_name(page.parent, sections) %>
49
31
  </td>
50
32
  <% end %>
51
- </tr>
33
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <% query ||= "" %>
2
+ <%= form_tag(search_admin_pages_path(@locale),
3
+ method: "get",
4
+ class: "search-bar") do %>
5
+ <%= text_field_tag(:q, query,
6
+ placeholder: "Search all pages",
7
+ aria: { label: "Search all pages" }) %>
8
+ <button type="submit">
9
+ <i class="fa-solid fa-magnifying-glass"></i>
10
+ Search
11
+ </button>
12
+ <% end %>
@@ -9,6 +9,9 @@
9
9
  <%= locale_links { |l| admin_pages_path(l) } %>
10
10
  <% end %>
11
11
 
12
+ <%= render(partial: "admin/pages/search_bar",
13
+ locals: { query: search_query }) %>
14
+
12
15
  <div class="content">
13
16
  <% cache Page.visible.roots.to_a + [current_user, @locale] do %>
14
17
  <%= react_component(
@@ -0,0 +1,54 @@
1
+ <% content_for :page_title, "Search pages" %>
2
+
3
+ <% content_for :page_description do %>
4
+ Search pages
5
+ <% end %>
6
+
7
+ <% content_for :page_description_links do %>
8
+ <%= locale_links { |l| search_admin_pages_path(l, q: search_query) } %>
9
+ <% end %>
10
+
11
+ <%= render(partial: "admin/pages/search_bar",
12
+ locals: { query: search_query }) %>
13
+
14
+ <% if @search_documents.any? %>
15
+ <table class="list calendar-item-list">
16
+ <tr>
17
+ <th>Name</th>
18
+ <th>Published</th>
19
+ <th>Author</th>
20
+ <th>Location</th>
21
+ </tr>
22
+ <% @search_documents.results.each do |page| %>
23
+ <%= page_list_row(page) do %>
24
+ <td class="name">
25
+ <%= link_to_if(policy(page).edit?,
26
+ page_name(page),
27
+ edit_admin_page_url(@locale, page),
28
+ class: 'name_link') %>
29
+ <%= autopublish_notice(page) %>
30
+ </td>
31
+ <td>
32
+ <%= page_published_status(page) %>
33
+ </td>
34
+ <td>
35
+ <%= link_to(page.author.name, admin_user_path(page.author)) %>
36
+ </td>
37
+ <td>
38
+ <% if page.parent %>
39
+ <%= page_name(page.parent, include_parents: true) %>
40
+ <% else %>
41
+ Top level
42
+ <% end %>
43
+ </td>
44
+ <% end %>
45
+ <% end %>
46
+ </table>
47
+ <%= will_paginate(@search_documents, renderer: PagesCore::LinkRenderer) %>
48
+ <% else %>
49
+ <div class="content">
50
+ <p>
51
+ Found no results for your search query.
52
+ </p>
53
+ </div>
54
+ <% end %>
@@ -4,9 +4,7 @@ xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
4
4
  xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
5
5
  xml.channel do
6
6
  xml.title(@title || PagesCore.config(:site_name))
7
- xml.link(
8
- url_for(controller: "pages", action: "index", only_path: false)
9
- )
7
+ xml.link(root_url)
10
8
  xml.description "Recent items"
11
9
  xml.language locale
12
10
  xml.generator "Pages"
@@ -15,12 +13,8 @@ xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
15
13
  xml.item do
16
14
  xml.title { xml.cdata! item.name.to_s }
17
15
  xml.link page_url(@locale, item, only_path: false)
18
- if PagesCore.config.rss_fulltext?
19
- xml.description { xml.cdata! item.body.to_html }
20
- else
21
- xml.description do
22
- xml.cdata!((item.extended? ? item.excerpt : item.body).to_html)
23
- end
16
+ xml.description do
17
+ xml.cdata! item.excerpt.to_html + item.body.to_html
24
18
  end
25
19
  xml.guid page_url(@locale, item, only_path: false)
26
20
  xml.pubDate item.published_at.to_fs(:rfc822)
data/config/routes.rb CHANGED
@@ -88,6 +88,7 @@ Rails.application.routes.draw do
88
88
  resources :pages do
89
89
  collection do
90
90
  get "deleted"
91
+ get "search"
91
92
  end
92
93
 
93
94
  member do
@@ -11,7 +11,6 @@ module PagesCore
11
11
  setting :localizations, :boolean, false
12
12
  setting :locales, :hash
13
13
  setting :text_filter, :symbol, :textile
14
- setting :rss_fulltext, :boolean, true
15
14
  setting :image_fallback_url, :string
16
15
  setting :default_author, :string
17
16
  setting :error_404_layout, :string