pages_core 3.12.3 → 3.12.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/assets/builds/pages_core/admin-dist.js +1 -44
  4. data/app/assets/builds/pages_core/admin-dist.js.map +3 -3
  5. data/app/assets/builds/pages_core/admin.css +79 -46
  6. data/app/assets/stylesheets/pages_core/admin/components/attachments.css +3 -4
  7. data/app/assets/stylesheets/pages_core/admin/components/forms.css +17 -16
  8. data/app/assets/stylesheets/pages_core/admin/components/image_editor.css +8 -4
  9. data/app/assets/stylesheets/pages_core/admin/components/image_grid.css +1 -1
  10. data/app/assets/stylesheets/pages_core/admin/components/list_table.css +11 -3
  11. data/app/assets/stylesheets/pages_core/admin/components/modal.css +9 -5
  12. data/app/assets/stylesheets/pages_core/admin/components/page_tree.css +5 -1
  13. data/app/assets/stylesheets/pages_core/admin/components/toast.css +2 -2
  14. data/app/assets/stylesheets/pages_core/admin/controllers/pages.css +4 -2
  15. data/app/assets/stylesheets/pages_core/admin/vars.css +2 -1
  16. data/app/controllers/admin/calendars_controller.rb +2 -2
  17. data/app/controllers/admin/categories_controller.rb +3 -3
  18. data/app/controllers/admin/news_controller.rb +6 -6
  19. data/app/controllers/admin/pages_controller.rb +12 -11
  20. data/app/controllers/admin/users_controller.rb +1 -1
  21. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +15 -17
  22. data/app/controllers/pages_core/admin_controller.rb +2 -2
  23. data/app/controllers/pages_core/base_controller.rb +1 -8
  24. data/app/controllers/pages_core/frontend/pages_controller.rb +13 -5
  25. data/app/controllers/pages_core/frontend_controller.rb +12 -7
  26. data/app/helpers/admin/menu_helper.rb +2 -0
  27. data/app/helpers/admin/pages_helper.rb +1 -4
  28. data/app/helpers/pages_core/admin/admin_helper.rb +0 -1
  29. data/app/helpers/pages_core/admin/content_tabs_helper.rb +9 -2
  30. data/app/helpers/pages_core/application_helper.rb +2 -3
  31. data/app/helpers/pages_core/frontend_helper.rb +1 -1
  32. data/app/helpers/pages_core/head_tags_helper.rb +15 -46
  33. data/app/helpers/pages_core/images_helper.rb +76 -21
  34. data/app/helpers/pages_core/locales_helper.rb +9 -0
  35. data/app/helpers/pages_core/open_graph_tags_helper.rb +3 -5
  36. data/app/helpers/pages_core/page_path_helper.rb +1 -1
  37. data/app/javascript/components/Attachments/Attachment.tsx +55 -52
  38. data/app/javascript/components/Attachments/AttachmentEditor.tsx +45 -50
  39. data/app/javascript/components/Attachments/Placeholder.tsx +1 -2
  40. data/app/javascript/components/Attachments.jsx +69 -57
  41. data/app/javascript/components/DateRangeSelect.jsx +94 -54
  42. data/app/javascript/components/EditableImage.tsx +20 -16
  43. data/app/javascript/components/FileUploadButton.tsx +12 -12
  44. data/app/javascript/components/ImageCropper/FocalPoint.tsx +22 -20
  45. data/app/javascript/components/ImageCropper/Image.tsx +20 -16
  46. data/app/javascript/components/ImageCropper/Toolbar.tsx +35 -27
  47. data/app/javascript/components/ImageCropper/useCrop.ts +105 -91
  48. data/app/javascript/components/ImageCropper.tsx +34 -25
  49. data/app/javascript/components/ImageEditor/Form.tsx +32 -43
  50. data/app/javascript/components/ImageEditor.tsx +29 -21
  51. data/app/javascript/components/ImageGrid/DragElement.tsx +6 -4
  52. data/app/javascript/components/ImageGrid/GridImage.tsx +56 -52
  53. data/app/javascript/components/ImageGrid/Placeholder.tsx +1 -1
  54. data/app/javascript/components/ImageGrid.jsx +132 -101
  55. data/app/javascript/components/ImageUploader.tsx +59 -55
  56. data/app/javascript/components/Modal.tsx +2 -4
  57. data/app/javascript/components/PageDates.jsx +25 -20
  58. data/app/javascript/components/PageFiles.jsx +7 -5
  59. data/app/javascript/components/PageImages.tsx +9 -7
  60. data/app/javascript/components/PageTree/Draggable.tsx +46 -40
  61. data/app/javascript/components/PageTree/Node.tsx +111 -95
  62. data/app/javascript/components/PageTree/types.ts +9 -9
  63. data/app/javascript/components/PageTree.tsx +44 -29
  64. data/app/javascript/components/RichTextArea.jsx +51 -37
  65. data/app/javascript/components/RichTextToolbarButton.tsx +8 -5
  66. data/app/javascript/components/TagEditor/AddTagForm.tsx +11 -10
  67. data/app/javascript/components/TagEditor/Tag.tsx +10 -8
  68. data/app/javascript/components/TagEditor.tsx +15 -10
  69. data/app/javascript/components/Toast.tsx +3 -7
  70. data/app/javascript/components/drag/draggedOrder.ts +17 -16
  71. data/app/javascript/components/drag/types.ts +12 -12
  72. data/app/javascript/components/drag/useDragCollection.ts +36 -42
  73. data/app/javascript/components/drag/useDragUploader.ts +3 -2
  74. data/app/javascript/components/drag.ts +5 -4
  75. data/app/javascript/controllers/LoginController.ts +0 -1
  76. data/app/javascript/controllers/MainController.ts +6 -2
  77. data/app/javascript/controllers/PageOptionsController.js +7 -2
  78. data/app/javascript/features/RichText.tsx +9 -7
  79. data/app/javascript/index.ts +5 -3
  80. data/app/javascript/lib/Tree.ts +27 -24
  81. data/app/javascript/lib/copyToClipboard.ts +5 -4
  82. data/app/javascript/lib/readyHandler.ts +4 -4
  83. data/app/javascript/lib/request.ts +7 -3
  84. data/app/javascript/stores/useModalStore.ts +3 -3
  85. data/app/javascript/stores/useToastStore.ts +14 -12
  86. data/app/javascript/types.ts +22 -22
  87. data/app/models/concerns/pages_core/page_model/templateable.rb +1 -1
  88. data/app/views/admin/calendars/show.html.erb +1 -1
  89. data/app/views/admin/news/index.html.erb +1 -1
  90. data/app/views/admin/pages/_edit_files.html.erb +1 -1
  91. data/app/views/admin/pages/_edit_images.html.erb +1 -1
  92. data/app/views/admin/pages/_list_item.html.erb +1 -1
  93. data/app/views/admin/pages/_search_bar.html.erb +1 -1
  94. data/app/views/admin/pages/deleted.html.erb +2 -2
  95. data/app/views/admin/pages/edit.html.erb +3 -3
  96. data/app/views/admin/pages/index.html.erb +4 -4
  97. data/app/views/admin/pages/new.html.erb +1 -1
  98. data/app/views/admin/pages/search.html.erb +3 -3
  99. data/app/views/feeds/pages.rss.builder +2 -2
  100. data/app/views/layouts/admin/_page_header.html.erb +4 -4
  101. data/app/views/layouts/admin.html.erb +1 -2
  102. data/config/locales/en.yml +1 -0
  103. data/lib/pages_core/pages_plugin.rb +5 -3
  104. data/lib/rails/generators/pages_core/frontend/templates/application.html.erb +15 -13
  105. data/lib/tasks/pages/reports.rake +26 -0
  106. metadata +32 -4
  107. data/app/helpers/pages_core/admin/deprecated_admin_helper.rb +0 -40
  108. data/app/views/pages_core/_google_analytics.html.erb +0 -8
@@ -8,22 +8,22 @@ import { AttachmentResource, Locale } from "../../types";
8
8
  import { useDraggable, Draggable } from "../drag";
9
9
 
10
10
  interface Record {
11
- id: number | null,
12
- attachment: AttachmentResource,
13
- uploading: boolean
11
+ id: number | null;
12
+ attachment: AttachmentResource;
13
+ uploading: boolean;
14
14
  }
15
15
 
16
16
  interface AttachmentProps {
17
- attributeName: string,
18
- placeholder: boolean,
19
- draggable: { record: Record },
20
- locale: string,
21
- locales: { [index: string]: Locale },
22
- deleteRecord: () => void,
23
- showEmbed: boolean,
24
- position: number,
25
- onUpdate: (localizations: Record<string, Record<string, string>>) => void,
26
- startDrag: (evt: Event, draggable: Draggable) => void
17
+ attributeName: string;
18
+ placeholder: boolean;
19
+ draggable: { record: Record };
20
+ locale: string;
21
+ locales: { [index: string]: Locale };
22
+ deleteRecord: () => void;
23
+ showEmbed: boolean;
24
+ position: number;
25
+ onUpdate: (localizations: Record<string, Record<string, string>>) => void;
26
+ startDrag: (evt: Event, draggable: Draggable) => void;
27
27
  }
28
28
 
29
29
  export default function Attachment(props: AttachmentProps) {
@@ -70,7 +70,8 @@ export default function Attachment(props: AttachmentProps) {
70
70
  attachment={attachment}
71
71
  locale={locale}
72
72
  locales={locales}
73
- onUpdate={props.onUpdate} />
73
+ onUpdate={props.onUpdate}
74
+ />
74
75
  );
75
76
  };
76
77
 
@@ -90,44 +91,46 @@ export default function Attachment(props: AttachmentProps) {
90
91
  }
91
92
 
92
93
  return (
93
- <div className={classes.join(" ")}
94
- {...listeners}>
95
- <input name={`${attributeName}[id]`}
96
- type="hidden" value={record.id || ""} />
97
- <input name={`${attributeName}[attachment_id]`}
98
- type="hidden" value={(attachment && attachment.id) || ""} />
99
- <input name={`${attributeName}[position]`}
100
- type="hidden" value={props.position} />
101
- {!uploading &&
102
- <div className="actions">
103
- <button onClick={editAttachment}>
104
- Edit
105
- </button>
106
- {props.showEmbed && (
107
- <button onClick={copyEmbed}>
108
- Embed
109
- </button>
110
- )}
111
- {props.deleteRecord && (
112
- <button onClick={deleteRecord}>
113
- Remove
114
- </button>
115
- )}
116
- </div>
117
- }
118
- {attachment &&
119
- <div className="attachment-info">
120
- <h3>
121
- <i className={`fa-solid fa-${icon} icon`} />
122
- {name() || <em>Untitled</em>}<br />
123
- </h3>
124
- {!uploading &&
125
- <a href={attachment.url}
126
- rel="noreferrer"
127
- target="_blank">{attachment.filename}</a>}
128
- {!uploading && description() &&
129
- <p dir={localeDir}>{description()}</p>}
130
- </div>}
94
+ <div className={classes.join(" ")} {...listeners}>
95
+ <input
96
+ name={`${attributeName}[id]`}
97
+ type="hidden"
98
+ value={record.id || ""}
99
+ />
100
+ <input
101
+ name={`${attributeName}[attachment_id]`}
102
+ type="hidden"
103
+ value={(attachment && attachment.id) || ""}
104
+ />
105
+ <input
106
+ name={`${attributeName}[position]`}
107
+ type="hidden"
108
+ value={props.position}
109
+ />
110
+ {!uploading && (
111
+ <div className="actions">
112
+ <button onClick={editAttachment}>Edit</button>
113
+ {props.showEmbed && <button onClick={copyEmbed}>Embed</button>}
114
+ {props.deleteRecord && <button onClick={deleteRecord}>Remove</button>}
115
+ </div>
116
+ )}
117
+ {attachment && (
118
+ <div className="attachment-info">
119
+ <h3>
120
+ <i className={`fa-solid fa-${icon} icon`} />
121
+ {name() || <em>Untitled</em>}
122
+ <br />
123
+ </h3>
124
+ {!uploading && (
125
+ <a href={attachment.url} rel="noreferrer" target="_blank">
126
+ {attachment.filename}
127
+ </a>
128
+ )}
129
+ {!uploading && description() && (
130
+ <p dir={localeDir}>{description()}</p>
131
+ )}
132
+ </div>
133
+ )}
131
134
  </div>
132
135
  );
133
136
  }
@@ -6,10 +6,10 @@ import { AttachmentResource, Locale } from "../../types";
6
6
  import { putJson } from "../../lib/request";
7
7
 
8
8
  interface AttachmentEditorProps {
9
- attachment: AttachmentResource,
10
- locale: string,
11
- locales: { [index: string]: Locale },
12
- onUpdate: (localizations: Record<string, Record<string, string>>) => void
9
+ attachment: AttachmentResource;
10
+ locale: string;
11
+ locales: { [index: string]: Locale };
12
+ onUpdate: (localizations: Record<string, Record<string, string>>) => void;
13
13
  }
14
14
 
15
15
  export default function AttachmentEditor(props: AttachmentEditorProps) {
@@ -18,19 +18,19 @@ export default function AttachmentEditor(props: AttachmentEditorProps) {
18
18
  const [locale, setLocale] = useState(props.locale);
19
19
  const [localizations, setLocalizations] = useState({
20
20
  name: attachment.name || {},
21
- description: attachment.description || {},
21
+ description: attachment.description || {}
22
22
  });
23
23
 
24
24
  const notice = useToastStore((state) => state.notice);
25
25
  const closeModal = useModalStore((state) => state.close);
26
26
 
27
- const updateLocalization = (name: "name" | "description") => (evt: ChangeEvent<HTMLInputElement>) => {
28
- setLocalizations({
29
- ...localizations,
30
- [name]: { ...localizations[name],
31
- [locale]: evt.target.value }
32
- });
33
- };
27
+ const updateLocalization =
28
+ (name: "name" | "description") => (evt: ChangeEvent<HTMLInputElement>) => {
29
+ setLocalizations({
30
+ ...localizations,
31
+ [name]: { ...localizations[name], [locale]: evt.target.value }
32
+ });
33
+ };
34
34
 
35
35
  const copyEmbedCode = (evt: Event) => {
36
36
  evt.preventDefault();
@@ -59,13 +59,12 @@ export default function AttachmentEditor(props: AttachmentEditorProps) {
59
59
  <form>
60
60
  {props.locales && Object.keys(locales).length > 1 && (
61
61
  <div className="field">
62
- <label>
63
- Locale
64
- </label>
65
- <select name="locale"
66
- value={locale}
67
- onChange={e => setLocale(e.target.value)}>
68
- {Object.keys(locales).map(key => (
62
+ <label>Locale</label>
63
+ <select
64
+ name="locale"
65
+ value={locale}
66
+ onChange={(e) => setLocale(e.target.value)}>
67
+ {Object.keys(locales).map((key) => (
69
68
  <option key={`locale-${key}`} value={key}>
70
69
  {locales[key].name}
71
70
  </option>
@@ -75,47 +74,43 @@ export default function AttachmentEditor(props: AttachmentEditorProps) {
75
74
  )}
76
75
  <div className="field">
77
76
  <label>Name</label>
78
- <input type="text"
79
- className="name"
80
- lang={locale}
81
- dir={inputDir}
82
- value={localizations.name[locale] || ""}
83
- onChange={updateLocalization("name")} />
77
+ <input
78
+ type="text"
79
+ className="name"
80
+ lang={locale}
81
+ dir={inputDir}
82
+ value={localizations.name[locale] || ""}
83
+ onChange={updateLocalization("name")}
84
+ />
84
85
  </div>
85
86
  <div className="field">
86
87
  <label>Description</label>
87
- <textarea className="description"
88
- value={localizations.description[locale] || ""}
89
- lang={locale}
90
- dir={inputDir}
91
- onChange={updateLocalization("description")} />
88
+ <textarea
89
+ className="description"
90
+ value={localizations.description[locale] || ""}
91
+ lang={locale}
92
+ dir={inputDir}
93
+ onChange={updateLocalization("description")}
94
+ />
92
95
  </div>
93
96
  <div className="field embed-code">
94
- <label>
95
- Embed code
96
- </label>
97
- <input type="text"
98
- value={`[attachment:${attachment.id}]`}
99
- disabled={true} />
100
- {copySupported() && (
101
- <button onClick={copyEmbedCode}>
102
- Copy
103
- </button>
104
- )}
97
+ <label>Embed code</label>
98
+ <input
99
+ type="text"
100
+ value={`[attachment:${attachment.id}]`}
101
+ disabled={true}
102
+ />
103
+ {copySupported() && <button onClick={copyEmbedCode}>Copy</button>}
105
104
  </div>
106
105
  <div className="field">
107
106
  <label>File</label>
108
- <a href={attachment.url}
109
- rel="noreferrer"
110
- target="_blank">{attachment.filename}</a>
107
+ <a href={attachment.url} rel="noreferrer" target="_blank">
108
+ {attachment.filename}
109
+ </a>
111
110
  </div>
112
111
  <div className="buttons">
113
- <button onClick={save}>
114
- Save
115
- </button>
116
- <button onClick={closeModal}>
117
- Cancel
118
- </button>
112
+ <button onClick={save}>Save</button>
113
+ <button onClick={closeModal}>Cancel</button>
119
114
  </div>
120
115
  </form>
121
116
  </div>
@@ -2,8 +2,7 @@ import React from "react";
2
2
 
3
3
  export default function Placeholder() {
4
4
  return (
5
- <div className="attachment drop-placeholder"
6
- key="file-placeholder">
5
+ <div className="attachment drop-placeholder" key="file-placeholder">
7
6
  Upload files here
8
7
  </div>
9
8
  );
@@ -5,10 +5,12 @@ import Placeholder from "./Attachments/Placeholder";
5
5
  import FileUploadButton from "./FileUploadButton";
6
6
  import { post } from "../lib/request";
7
7
 
8
- import { createDraggable,
9
- draggedOrder,
10
- useDragCollection,
11
- useDragUploader } from "./drag";
8
+ import {
9
+ createDraggable,
10
+ draggedOrder,
11
+ useDragCollection,
12
+ useDragUploader
13
+ } from "./drag";
12
14
 
13
15
  function filenameToName(str) {
14
16
  return str.replace(/\.[\w\d]+$/, "").replace(/_/g, " ");
@@ -16,18 +18,20 @@ function filenameToName(str) {
16
18
 
17
19
  export default function Attachments(props) {
18
20
  const collection = useDragCollection(props.records);
19
- const locales = props.locales && props.locales.length > 0 ?
20
- Object.keys(props.locales) : [props.locale];
21
+ const locales =
22
+ props.locales && props.locales.length > 0
23
+ ? Object.keys(props.locales)
24
+ : [props.locale];
21
25
  const [deleted, setDeleted] = useState([]);
22
26
 
23
27
  const uploadAttachment = (file) => {
24
28
  let name = {};
25
- locales.forEach((l) => name[l] = file.name);
29
+ locales.forEach((l) => (name[l] = file.name));
26
30
 
27
- const draggable = createDraggable(
28
- { attachment: { filename: file.name, name: name },
29
- uploading: true }
30
- );
31
+ const draggable = createDraggable({
32
+ attachment: { filename: file.name, name: name },
33
+ uploading: true
34
+ });
31
35
 
32
36
  let data = new FormData();
33
37
 
@@ -36,14 +40,15 @@ export default function Attachments(props) {
36
40
  data.append(`attachment[name][${l}]`, filenameToName(file.name));
37
41
  });
38
42
 
39
- post("/admin/attachments.json", data)
40
- .then(json => {
41
- collection.dispatch({
42
- type: "update",
43
- payload: { ...draggable,
44
- record: { attachment: json, uploading: false } }
45
- });
43
+ post("/admin/attachments.json", data).then((json) => {
44
+ collection.dispatch({
45
+ type: "update",
46
+ payload: {
47
+ ...draggable,
48
+ record: { attachment: json, uploading: false }
49
+ }
46
50
  });
51
+ });
47
52
 
48
53
  return draggable;
49
54
  };
@@ -51,7 +56,7 @@ export default function Attachments(props) {
51
56
  const receiveFiles = (files) => {
52
57
  collection.dispatch({
53
58
  type: "append",
54
- payload: files.map(f => uploadAttachment(f))
59
+ payload: files.map((f) => uploadAttachment(f))
55
60
  });
56
61
  };
57
62
 
@@ -62,17 +67,21 @@ export default function Attachments(props) {
62
67
  });
63
68
  collection.dispatch({
64
69
  type: "insertFiles",
65
- payload: files.map(f => uploadAttachment(f))
70
+ payload: files.map((f) => uploadAttachment(f))
66
71
  });
67
72
  };
68
73
 
69
- const [dragState,
70
- dragStart,
71
- listeners] = useDragUploader([collection], dragEnd);
74
+ const [dragState, dragStart, listeners] = useDragUploader(
75
+ [collection],
76
+ dragEnd
77
+ );
72
78
 
73
79
  const position = (record) => {
74
- return [...collection.draggables.map(d => d.record),
75
- ...deleted].indexOf(record) + 1;
80
+ return (
81
+ [...collection.draggables.map((d) => d.record), ...deleted].indexOf(
82
+ record
83
+ ) + 1
84
+ );
76
85
  };
77
86
 
78
87
  const attrName = (record) => {
@@ -102,21 +111,23 @@ export default function Attachments(props) {
102
111
  const { dragging } = dragState;
103
112
 
104
113
  if (draggable === "Files") {
105
- return (<Placeholder key="placeholder" />);
114
+ return <Placeholder key="placeholder" />;
106
115
  }
107
116
 
108
117
  return (
109
- <Attachment key={draggable.handle}
110
- draggable={draggable}
111
- locale={props.locale}
112
- locales={props.locales}
113
- showEmbed={props.showEmbed}
114
- startDrag={dragStart}
115
- position={position(draggable.record)}
116
- onUpdate={update(draggable)}
117
- deleteRecord={remove(draggable)}
118
- attributeName={attrName(draggable.record)}
119
- placeholder={dragging && dragging == draggable} />
118
+ <Attachment
119
+ key={draggable.handle}
120
+ draggable={draggable}
121
+ locale={props.locale}
122
+ locales={props.locales}
123
+ showEmbed={props.showEmbed}
124
+ startDrag={dragStart}
125
+ position={position(draggable.record)}
126
+ onUpdate={update(draggable)}
127
+ deleteRecord={remove(draggable)}
128
+ attributeName={attrName(draggable.record)}
129
+ placeholder={dragging && dragging == draggable}
130
+ />
120
131
  );
121
132
  };
122
133
 
@@ -128,30 +139,31 @@ export default function Attachments(props) {
128
139
  }
129
140
 
130
141
  return (
131
- <div className={classes.join(" ")}
132
- ref={collection.ref}
133
- {...listeners}>
134
- <div className="files">
135
- {dragOrder.map(d => attachment(d))}
136
- </div>
142
+ <div className={classes.join(" ")} ref={collection.ref} {...listeners}>
143
+ <div className="files">{dragOrder.map((d) => attachment(d))}</div>
137
144
  <div className="deleted">
138
- {deleted.map(r =>
145
+ {deleted.map((r) => (
139
146
  <span className="deleted-attachment" key={r.id}>
140
- <input name={`${attrName(r)}[id]`}
141
- type="hidden"
142
- value={r.id} />
143
- <input name={`${attrName(r)}[attachment_id]`}
144
- type="hidden"
145
- value={(r.attachment && r.attachment.id) || ""} />
146
- <input name={`${attrName(r)}[_destroy]`}
147
- type="hidden"
148
- value={true} />
149
- </span>)}
147
+ <input name={`${attrName(r)}[id]`} type="hidden" value={r.id} />
148
+ <input
149
+ name={`${attrName(r)}[attachment_id]`}
150
+ type="hidden"
151
+ value={(r.attachment && r.attachment.id) || ""}
152
+ />
153
+ <input
154
+ name={`${attrName(r)}[_destroy]`}
155
+ type="hidden"
156
+ value={true}
157
+ />
158
+ </span>
159
+ ))}
150
160
  </div>
151
161
  <div className="drop-target">
152
- <FileUploadButton multiple={true}
153
- multiline={true}
154
- callback={receiveFiles} />
162
+ <FileUploadButton
163
+ multiple={true}
164
+ multiline={true}
165
+ callback={receiveFiles}
166
+ />
155
167
  </div>
156
168
  </div>
157
169
  );