pages_core 3.7.0 → 3.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -2
  3. data/Rakefile +37 -0
  4. data/app/assets/builds/pages_core/admin-dist.js +55 -0
  5. data/app/assets/stylesheets/pages/admin/components/image_editor.scss +1 -0
  6. data/app/assets/stylesheets/pages/admin/components/image_grid.scss +33 -5
  7. data/app/assets/stylesheets/pages/admin/components/layout.scss +2 -1
  8. data/app/assets/stylesheets/pages/admin/components/login.scss +6 -0
  9. data/app/assets/stylesheets/pages/admin/components/tabs.scss +5 -0
  10. data/app/assets/stylesheets/pages/admin/components/tag_editor.scss +13 -7
  11. data/app/assets/stylesheets/pages/admin/controllers/pages.scss +13 -5
  12. data/app/assets/stylesheets/pages/admin.scss +0 -1
  13. data/app/controller_dummies/admin/admin_controller.rb +1 -1
  14. data/app/controller_dummies/application_controller.rb +1 -1
  15. data/app/controller_dummies/attachments_controller.rb +1 -1
  16. data/app/controller_dummies/frontend_controller.rb +1 -1
  17. data/app/controller_dummies/images_controller.rb +1 -1
  18. data/app/controller_dummies/page_files_controller.rb +1 -1
  19. data/app/controller_dummies/pages_controller.rb +1 -1
  20. data/app/controller_dummies/sitemaps_controller.rb +1 -1
  21. data/app/controllers/admin/attachments_controller.rb +1 -1
  22. data/app/controllers/admin/images_controller.rb +11 -8
  23. data/app/controllers/concerns/pages_core/authentication.rb +9 -4
  24. data/app/controllers/concerns/pages_core/preview_pages_controller.rb +5 -0
  25. data/app/controllers/pages_core/frontend/pages_controller.rb +5 -2
  26. data/app/controllers/sessions_controller.rb +1 -1
  27. data/app/formatters/pages_core/link_renderer.rb +2 -2
  28. data/app/helpers/application_helper.rb +1 -1
  29. data/app/helpers/frontend_helper.rb +1 -1
  30. data/app/helpers/pages_core/admin/content_tabs_helper.rb +5 -2
  31. data/app/helpers/pages_core/admin/image_uploads_helper.rb +2 -3
  32. data/app/helpers/pages_core/admin/tag_editor_helper.rb +9 -39
  33. data/app/helpers/pages_core/head_tags_helper.rb +11 -20
  34. data/app/helpers/pages_core/open_graph_tags_helper.rb +1 -1
  35. data/app/javascript/admin-dist.js +2 -0
  36. data/app/javascript/components/Attachments/Attachment.jsx +121 -0
  37. data/app/javascript/components/Attachments/AttachmentEditor.jsx +116 -0
  38. data/app/javascript/components/Attachments/Placeholder.jsx +10 -0
  39. data/app/javascript/components/Attachments.jsx +165 -0
  40. data/app/{assets/javascripts/pages/admin/components/date_range_select.jsx → javascript/components/DateRangeSelect.jsx} +16 -5
  41. data/app/javascript/components/EditableImage.jsx +61 -0
  42. data/app/javascript/components/FileUploadButton.jsx +47 -0
  43. data/app/{assets/javascripts/pages/admin/components/focal_point.jsx → javascript/components/ImageCropper/FocalPoint.jsx} +12 -1
  44. data/app/javascript/components/ImageCropper/Image.jsx +65 -0
  45. data/app/javascript/components/ImageCropper/Toolbar.jsx +73 -0
  46. data/app/javascript/components/ImageCropper/useCrop.js +199 -0
  47. data/app/javascript/components/ImageCropper.jsx +90 -0
  48. data/app/javascript/components/ImageEditor/Form.jsx +98 -0
  49. data/app/javascript/components/ImageEditor.jsx +62 -0
  50. data/app/javascript/components/ImageGrid/DragElement.jsx +30 -0
  51. data/app/javascript/components/ImageGrid/FilePlaceholder.jsx +9 -0
  52. data/app/javascript/components/ImageGrid/GridImage.jsx +103 -0
  53. data/app/javascript/components/ImageGrid/Placeholder.jsx +23 -0
  54. data/app/javascript/components/ImageGrid.jsx +257 -0
  55. data/app/javascript/components/ImageUploader.jsx +171 -0
  56. data/app/{assets/javascripts/pages/admin/components/modal.jsx → javascript/components/Modal.jsx} +13 -2
  57. data/app/{assets/javascripts/pages/admin/components/page_dates.jsx → javascript/components/PageDates.jsx} +11 -1
  58. data/app/{assets/javascripts/pages/admin/components/page_files.jsx → javascript/components/PageFiles.jsx} +11 -2
  59. data/app/{assets/javascripts/pages/admin/components/page_images.jsx → javascript/components/PageImages.jsx} +11 -2
  60. data/app/{assets/javascripts/pages/admin/components/page_tree_store.jsx → javascript/components/PageTree.jsx} +127 -137
  61. data/app/{assets/javascripts/pages/admin/components/page_tree.jsx → javascript/components/PageTreeDraggable.jsx} +35 -29
  62. data/app/{assets/javascripts/pages/admin/components/page_tree_node.jsx → javascript/components/PageTreeNode.jsx} +35 -20
  63. data/app/javascript/components/RichTextArea.jsx +213 -0
  64. data/app/javascript/components/RichTextToolbarButton.jsx +20 -0
  65. data/app/javascript/components/TagEditor/AddTagForm.jsx +42 -0
  66. data/app/javascript/components/TagEditor/Tag.jsx +32 -0
  67. data/app/javascript/components/TagEditor.jsx +61 -0
  68. data/app/javascript/components/Toast.jsx +72 -0
  69. data/app/javascript/components/drag/draggedOrder.js +51 -0
  70. data/app/javascript/components/drag/useDragCollection.js +84 -0
  71. data/app/javascript/components/drag/useDragUploader.js +112 -0
  72. data/app/javascript/components/drag/useDraggable.js +17 -0
  73. data/app/javascript/components/drag.js +6 -0
  74. data/app/javascript/components.js +15 -0
  75. data/app/javascript/controllers/EditPageController.js +20 -0
  76. data/app/javascript/controllers/LoginController.js +29 -0
  77. data/app/javascript/controllers/MainController.js +65 -0
  78. data/app/javascript/controllers/PageOptionsController.js +62 -0
  79. data/app/javascript/features/RichText.jsx +34 -0
  80. data/app/javascript/hooks.js +2 -0
  81. data/app/javascript/index.js +38 -0
  82. data/app/{assets/javascripts/pages/admin/lib/tree.jsx → javascript/lib/Tree.js} +55 -54
  83. data/app/javascript/lib/copyToClipboard.js +13 -0
  84. data/app/javascript/lib/readyHandler.js +22 -0
  85. data/app/javascript/lib/request.js +36 -0
  86. data/app/javascript/stores/ModalStore.jsx +12 -0
  87. data/app/javascript/stores/ToastStore.jsx +14 -0
  88. data/app/javascript/stores.js +2 -0
  89. data/app/models/concerns/pages_core/page_model/images.rb +3 -1
  90. data/app/models/concerns/pages_core/page_model/searchable.rb +19 -0
  91. data/app/models/concerns/pages_core/searchable_document.rb +71 -0
  92. data/app/models/concerns/pages_core/taggable.rb +27 -12
  93. data/app/models/page.rb +2 -0
  94. data/app/models/page_exporter.rb +2 -2
  95. data/app/models/page_image.rb +0 -2
  96. data/app/models/role.rb +1 -1
  97. data/app/models/search_document.rb +72 -0
  98. data/app/models/tag.rb +1 -0
  99. data/app/models/user.rb +1 -1
  100. data/app/{serializers/admin/attachment_serializer.rb → resources/admin/attachment_resource.rb} +6 -5
  101. data/app/{serializers/admin/image_serializer.rb → resources/admin/image_resource.rb} +9 -9
  102. data/app/resources/admin/page_file_resource.rb +10 -0
  103. data/app/{serializers/admin/page_image_serializer.rb → resources/admin/page_image_resource.rb} +4 -2
  104. data/app/resources/export/attachment_resource.rb +10 -0
  105. data/app/resources/export/page_image_resource.rb +45 -0
  106. data/app/resources/export/page_resource.rb +42 -0
  107. data/app/{serializers/page_image_serializer.rb → resources/page_image_resource.rb} +8 -16
  108. data/app/resources/page_resource.rb +33 -0
  109. data/app/services/pages_core/destroy_invite_service.rb +2 -2
  110. data/app/services/pages_core/invite_service.rb +2 -2
  111. data/app/views/admin/pages/_edit_content.html.erb +1 -1
  112. data/app/views/admin/pages/_edit_files.html.erb +1 -5
  113. data/app/views/admin/pages/_edit_images.html.erb +1 -5
  114. data/app/views/admin/pages/_edit_options.html.erb +74 -55
  115. data/app/views/admin/pages/_form.html.erb +19 -0
  116. data/app/views/admin/pages/edit.html.erb +35 -61
  117. data/app/views/admin/pages/index.html.erb +0 -1
  118. data/app/views/admin/pages/new.html.erb +32 -32
  119. data/app/views/admin/users/_access_control.html.erb +5 -1
  120. data/app/views/admin/users/login.html.erb +12 -4
  121. data/app/views/feeds/pages.rss.builder +1 -2
  122. data/app/views/layouts/admin/_header.html.erb +1 -1
  123. data/app/views/layouts/admin/_page_header.html.erb +33 -0
  124. data/app/views/layouts/admin.html.erb +23 -42
  125. data/app/views/pages_core/_google_analytics.html.erb +8 -0
  126. data/db/migrate/20180625154059_enable_search_extensions.rb +10 -0
  127. data/db/migrate/20210209151400_create_search_configurations.rb +35 -0
  128. data/db/migrate/20210210235200_create_search_documents.rb +74 -0
  129. data/lib/pages_core/engine.rb +1 -5
  130. data/lib/pages_core/templates/block_configuration.rb +1 -1
  131. data/lib/pages_core/templates/configuration_handler.rb +1 -1
  132. data/lib/pages_core/version.rb +3 -1
  133. data/lib/pages_core.rb +3 -5
  134. data/lib/rails/generators/pages_core/frontend/frontend_generator.rb +0 -7
  135. data/lib/rails/generators/pages_core/install/templates/page_templates_initializer.rb +2 -2
  136. metadata +116 -115
  137. data/app/assets/javascripts/pages/admin/components/attachment.jsx +0 -130
  138. data/app/assets/javascripts/pages/admin/components/attachment_editor.jsx +0 -131
  139. data/app/assets/javascripts/pages/admin/components/attachments.jsx +0 -211
  140. data/app/assets/javascripts/pages/admin/components/drag_uploader.jsx +0 -174
  141. data/app/assets/javascripts/pages/admin/components/editable_image.jsx +0 -57
  142. data/app/assets/javascripts/pages/admin/components/file_upload_button.jsx +0 -44
  143. data/app/assets/javascripts/pages/admin/components/grid_image.jsx +0 -124
  144. data/app/assets/javascripts/pages/admin/components/image_editor.jsx +0 -496
  145. data/app/assets/javascripts/pages/admin/components/image_grid.jsx +0 -306
  146. data/app/assets/javascripts/pages/admin/components/image_uploader.jsx +0 -176
  147. data/app/assets/javascripts/pages/admin/components/modal_store.jsx +0 -20
  148. data/app/assets/javascripts/pages/admin/components/rich_text_area.jsx +0 -64
  149. data/app/assets/javascripts/pages/admin/components/rich_text_toolbar.jsx +0 -91
  150. data/app/assets/javascripts/pages/admin/components/toast.jsx +0 -34
  151. data/app/assets/javascripts/pages/admin/components/toast_store.jsx +0 -52
  152. data/app/assets/javascripts/pages/admin/components.jsx +0 -2
  153. data/app/assets/javascripts/pages/admin/features/content_tabs.jsx +0 -72
  154. data/app/assets/javascripts/pages/admin/features/edit_page.jsx +0 -97
  155. data/app/assets/javascripts/pages/admin/features/rich_text.jsx +0 -14
  156. data/app/assets/javascripts/pages/admin/features/tag_editor.jsx +0 -160
  157. data/app/assets/javascripts/pages/admin.jsx +0 -17
  158. data/app/assets/javascripts/pages/login_form.jsx +0 -21
  159. data/app/serializers/admin/page_file_serializer.rb +0 -8
  160. data/app/serializers/page_export_serializer.rb +0 -32
  161. data/app/serializers/page_file_export_serializer.rb +0 -6
  162. data/app/serializers/page_image_export_serializer.rb +0 -42
  163. data/app/serializers/page_serializer.rb +0 -23
  164. data/app/views/layouts/admin/_analytics.html.erb +0 -16
  165. data/lib/rails/generators/pages_core/frontend/templates/application.js.erb +0 -15
  166. data/vendor/assets/javascripts/ReactCrop.min.js +0 -1
  167. data/vendor/assets/javascripts/reflux.min.js +0 -1
@@ -0,0 +1,257 @@
1
+ import React, { useRef, useState } from "react";
2
+ import PropTypes from "prop-types";
3
+ import FileUploadButton from "./FileUploadButton";
4
+ import DragElement from "./ImageGrid/DragElement";
5
+ import FilePlaceholder from "./ImageGrid/FilePlaceholder";
6
+ import GridImage from "./ImageGrid/GridImage";
7
+ import ToastStore from "../stores/ToastStore";
8
+ import { post } from "../lib/request";
9
+
10
+ import { createDraggable,
11
+ collectionOrder,
12
+ useDragCollection,
13
+ useDragUploader } from "./drag";
14
+
15
+ function filterFiles(files) {
16
+ const validMimeTypes = ["image/gif",
17
+ "image/jpeg",
18
+ "image/pjpeg",
19
+ "image/png",
20
+ "image/tiff"];
21
+ return files.filter(f => (validMimeTypes.indexOf(f.type) !== -1));
22
+ }
23
+
24
+ function draggedImageOrder(primaryCollection, imagesCollection, dragState) {
25
+ const [primary, ...rest] = collectionOrder(primaryCollection, dragState);
26
+ let images = [...rest, ...collectionOrder(imagesCollection, dragState)];
27
+
28
+ if (dragState.dragging && [primary, ...images].indexOf(dragState.dragging) === -1) {
29
+ if (dragState.y < imagesCollection.ref.current.getBoundingClientRect().top) {
30
+ images = [dragState.dragging, ...images];
31
+ } else {
32
+ images.push(dragState.dragging);
33
+ }
34
+ }
35
+
36
+ return [primary, images];
37
+ }
38
+
39
+ function initRecords(props) {
40
+ const primary = props.enablePrimary ?
41
+ props.records.filter(r => r.primary).slice(0, 1) :
42
+ [];
43
+
44
+ return [primary, props.records.filter(r => primary.indexOf(r) === -1)];
45
+ }
46
+
47
+ export default function ImageGrid(props) {
48
+ const [initPrimary, initImages] = initRecords(props);
49
+ const primary = useDragCollection(initPrimary);
50
+ const images = useDragCollection(initImages);
51
+ const [deleted, setDeleted] = useState([]);
52
+
53
+ const containerRef = useRef();
54
+
55
+ const dispatchAll = (action) => {
56
+ primary.dispatch(action);
57
+ images.dispatch(action);
58
+ };
59
+
60
+ const dragEnd = (dragState, files) => {
61
+ const [draggedPrimary,
62
+ draggedImages] = draggedImageOrder(primary, images, dragState);
63
+
64
+ primary.dispatch({
65
+ type: "reorder",
66
+ payload: draggedPrimary ? [draggedPrimary] : []
67
+ });
68
+ images.dispatch({ type: "reorder", payload: draggedImages });
69
+
70
+ if (files) {
71
+ const uploads = filterFiles(files).map(f => uploadImage(f));
72
+ dispatchAll({ type: "insertFiles", payload: uploads });
73
+ }
74
+ };
75
+
76
+ const [dragState,
77
+ dragStart,
78
+ listeners] = useDragUploader([primary, images], dragEnd);
79
+
80
+ const position = (record) => {
81
+ return [...primary.draggables.map(d => d.record),
82
+ ...images.draggables.map(d => d.record),
83
+ ...deleted].indexOf(record) + 1;
84
+ };
85
+
86
+ const attrName = (record) => {
87
+ return `${props.attribute}[${position(record)}]`;
88
+ };
89
+
90
+ const uploadImage = (file) => {
91
+ const draggable = createDraggable(
92
+ { image: null, file: file }
93
+ );
94
+
95
+ let data = new FormData();
96
+
97
+ data.append("image[file]", file);
98
+
99
+ post("/admin/images.json", data)
100
+ .then(json => {
101
+ if (json.status === "error") {
102
+ ToastStore.dispatch({
103
+ type: "ERROR", message: "Error uploading image: " + json.error
104
+ });
105
+ dispatchAll({ type: "remove", payload: draggable });
106
+ } else {
107
+ dispatchAll({
108
+ type: "update",
109
+ payload: { ...draggable, record: { image: json } }
110
+ });
111
+ }
112
+ });
113
+
114
+ return draggable;
115
+ };
116
+
117
+ const update = (draggable) => (image) => {
118
+ const { record } = draggable;
119
+ const updated = {
120
+ ...draggable,
121
+ record: {
122
+ ...record,
123
+ image: { ...record.image, ...image }
124
+ }
125
+ };
126
+ dispatchAll({ type: "update", payload: updated });
127
+ };
128
+
129
+ const remove = (draggable) => () => {
130
+ dispatchAll({ type: "remove", payload: draggable });
131
+ if (draggable.record.id) {
132
+ setDeleted([...deleted, draggable.record]);
133
+ }
134
+ };
135
+
136
+ const renderImage = (draggable, isPrimary) => {
137
+ const { dragging } = dragState;
138
+
139
+ if (draggable === "Files") {
140
+ return (<FilePlaceholder key="placeholder" />);
141
+ }
142
+
143
+ return (
144
+ <GridImage key={draggable.handle}
145
+ draggable={draggable}
146
+ locale={props.locale}
147
+ locales={props.locales}
148
+ showEmbed={props.showEmbed}
149
+ startDrag={dragStart}
150
+ position={position(draggable.record)}
151
+ primary={isPrimary}
152
+ onUpdate={update(draggable)}
153
+ enablePrimary={props.enablePrimary}
154
+ deleteImage={remove(draggable)}
155
+ attributeName={attrName(draggable.record)}
156
+ placeholder={dragging && dragging == draggable} />
157
+ );
158
+ };
159
+
160
+ const uploadPrimary = (files) => {
161
+ const [first, ...rest] = filterFiles(files).map(f => uploadImage(f));
162
+ if (first) {
163
+ images.dispatch({
164
+ type: "prepend",
165
+ payload: [...primary.draggables, ...rest]
166
+ });
167
+ primary.dispatch({ type: "replace", payload: [first] });
168
+ }
169
+ };
170
+
171
+ const uploadAdditional = (files) => {
172
+ images.dispatch({
173
+ type: "append",
174
+ payload: filterFiles(files).map(f => uploadImage(f))
175
+ });
176
+ };
177
+
178
+ let classNames = ["image-grid"];
179
+ if (props.enablePrimary) {
180
+ classNames.push("with-primary-image");
181
+ }
182
+
183
+ const [draggedPrimary,
184
+ draggedImages] = draggedImageOrder(primary, images, dragState);
185
+
186
+ return (
187
+ <div className={classNames.join(" ")}
188
+ ref={containerRef}
189
+ {...listeners}>
190
+ {dragState.dragging &&
191
+ <DragElement draggable={dragState.dragging}
192
+ dragState={dragState}
193
+ container={containerRef} />}
194
+ {props.enablePrimary && (
195
+ <div className="primary-image" ref={primary.ref}>
196
+ <h3>
197
+ Main image
198
+ </h3>
199
+ {draggedPrimary &&
200
+ <>
201
+ {renderImage(draggedPrimary, true)}
202
+ {props.primaryAttribute && (
203
+ <input type="hidden" name={props.primaryAttribute}
204
+ value={(draggedPrimary.record &&
205
+ draggedPrimary.record.image &&
206
+ draggedPrimary.record.image.id) || ""} />
207
+ )}
208
+ </>}
209
+ {!draggedPrimary && (
210
+ <div className="drop-target">
211
+ <FileUploadButton multiple={true}
212
+ type="image"
213
+ multiline={true}
214
+ callback={uploadPrimary} />
215
+ </div>)}
216
+ </div>
217
+ )}
218
+ <div className="grid" ref={images.ref}>
219
+ <h3>
220
+ {props.enablePrimary ? "More images" : "Images"}
221
+ </h3>
222
+ <div className="drop-target">
223
+ <FileUploadButton multiple={true}
224
+ type="image"
225
+ callback={uploadAdditional} />
226
+ </div>
227
+ <div className="images">
228
+ {draggedImages.map(r => renderImage(r, false))}
229
+ </div>
230
+ </div>
231
+ <div className="deleted">
232
+ {deleted.map(r =>
233
+ <span className="deleted-image" key={r.id}>
234
+ <input name={`${attrName(r)}[id]`}
235
+ type="hidden"
236
+ value={r.id} />
237
+ <input name={`${attrName(r)}[attachment_id]`}
238
+ type="hidden"
239
+ value={(r.image && r.image.id) || ""} />
240
+ <input name={`${attrName(r)}[_destroy]`}
241
+ type="hidden"
242
+ value={true} />
243
+ </span>)}
244
+ </div>
245
+ </div>
246
+ );
247
+ }
248
+
249
+ ImageGrid.propTypes = {
250
+ attribute: PropTypes.string,
251
+ locale: PropTypes.string,
252
+ locales: PropTypes.array,
253
+ records: PropTypes.array,
254
+ enablePrimary: PropTypes.bool,
255
+ primaryAttribute: PropTypes.string,
256
+ showEmbed: PropTypes.bool
257
+ };
@@ -0,0 +1,171 @@
1
+ import React, { useState } from "react";
2
+ import PropTypes from "prop-types";
3
+ import EditableImage from "./EditableImage";
4
+ import FileUploadButton from "./FileUploadButton";
5
+ import ToastStore from "../stores/ToastStore";
6
+ import { post } from "../lib/request";
7
+
8
+ function getFiles(dt) {
9
+ var files = [];
10
+ if (dt.items) {
11
+ for (let i = 0; i < dt.items.length; i++) {
12
+ if (dt.items[i].kind == "file") {
13
+ files.push(dt.items[i].getAsFile());
14
+ }
15
+ }
16
+ } else {
17
+ for (let i = 0; i < dt.files.length; i++) {
18
+ files.push(dt.files[i]);
19
+ }
20
+ }
21
+ return files;
22
+ }
23
+
24
+ export default function ImageUploader(props) {
25
+ const [uploading, setUploading] = useState(false);
26
+ const [dragover, setDragover] = useState(false);
27
+ const [image, setImage] = useState(props.image);
28
+ const [src, setSrc] = useState(props.src);
29
+
30
+ const handleDragOver = (evt) => {
31
+ evt.preventDefault();
32
+ setDragover(true);
33
+ };
34
+
35
+ const handleDragLeave = () => {
36
+ setDragover(false);
37
+ };
38
+
39
+ const handleDragEnd = (evt) => {
40
+ if (evt.dataTransfer.items) {
41
+ for (var i = 0; i < evt.dataTransfer.items.length; i++) {
42
+ evt.dataTransfer.items.remove(i);
43
+ }
44
+ } else {
45
+ evt.dataTransfer.clearData();
46
+ }
47
+ setDragover(false);
48
+ };
49
+
50
+ const handleDrop = (evt) => {
51
+ let files = getFiles(evt.dataTransfer);
52
+ evt.preventDefault();
53
+ if (files.length > 0) {
54
+ uploadImage(files[0]);
55
+ }
56
+ };
57
+
58
+ const handleRemove = (evt) => {
59
+ evt.preventDefault();
60
+ setImage(null);
61
+ setSrc(null);
62
+ };
63
+
64
+ const receiveFiles = (files) => {
65
+ if (files.length > 0) {
66
+ uploadImage(files[0]);
67
+ }
68
+ };
69
+
70
+ const uploadImage = (file) => {
71
+ let validTypes = ["image/gif",
72
+ "image/jpeg",
73
+ "image/pjpeg",
74
+ "image/png",
75
+ "image/tiff"];
76
+
77
+ if (validTypes.indexOf(file.type) == -1) {
78
+ alert("Invalid file type, only images in JPEG, PNG or GIF " +
79
+ "formats are supported");
80
+ return;
81
+ }
82
+
83
+ let locale = props.locale;
84
+ let locales = props.locales ? Object.keys(props.locales) : [locale];
85
+
86
+ let data = new FormData();
87
+
88
+ setImage(null);
89
+ setSrc(null);
90
+ setDragover(false);
91
+ setUploading(true);
92
+
93
+ data.append("image[file]", file);
94
+ locales.forEach((l) => {
95
+ data.append(`image[alternative][${l}]`, (props.alternative || ""));
96
+ });
97
+
98
+ post("/admin/images.json", data)
99
+ .then(response => {
100
+ setUploading(false);
101
+ if (response.status === "error") {
102
+ ToastStore.dispatch({
103
+ type: "ERROR",
104
+ message: "Error uploading image: " + response.error
105
+ });
106
+ } else {
107
+ setSrc(response.thumbnail_url);
108
+ setImage(response);
109
+ }
110
+ });
111
+ };
112
+
113
+ let classes = ["image-uploader"];
114
+ if (uploading) {
115
+ classes.push("uploading");
116
+ } else if (dragover) {
117
+ classes.push("dragover");
118
+ }
119
+ return (
120
+ <div className={classes.join(" ")}
121
+ onDragOver={handleDragOver}
122
+ onDragLeave={handleDragLeave}
123
+ onDragEnd={handleDragEnd}
124
+ onDrop={handleDrop}>
125
+ <input type="hidden"
126
+ name={props.attr}
127
+ value={image ? image.id : ""} />
128
+ {image &&
129
+ <div className="image">
130
+ <EditableImage image={image}
131
+ src={src}
132
+ width={props.width}
133
+ caption={props.caption}
134
+ locale={props.locale}
135
+ locales={props.locales} />
136
+ </div>}
137
+ <div className="ui-wrapper">
138
+ {uploading && (
139
+ <div className="ui">
140
+ Uploading image...
141
+ </div>
142
+ )}
143
+ {!uploading && (
144
+ <div className="ui">
145
+ <FileUploadButton type="image"
146
+ multiline={true}
147
+ callback={receiveFiles} />
148
+ {image && (
149
+ <a className="delete remove-image"
150
+ href="#"
151
+ onClick={handleRemove}>
152
+ Remove image
153
+ </a>
154
+ )}
155
+ </div>
156
+ )}
157
+ </div>
158
+ </div>
159
+ );
160
+ }
161
+
162
+ ImageUploader.propTypes = {
163
+ locale: PropTypes.string,
164
+ locales: PropTypes.object,
165
+ image: PropTypes.object,
166
+ src: PropTypes.string,
167
+ width: PropTypes.number,
168
+ caption: PropTypes.bool,
169
+ attr: PropTypes.string,
170
+ alternative: PropTypes.string
171
+ };
@@ -1,23 +1,34 @@
1
- class Modal extends Reflux.Component {
1
+ import React from "react";
2
+ import ModalStore from "../stores/ModalStore";
3
+
4
+ export default class Modal extends React.Component {
2
5
  constructor(props) {
3
6
  super(props);
7
+ this.state = { component: null };
4
8
  this.store = ModalStore;
5
9
  this.closeModal = this.closeModal.bind(this);
10
+ this.handleChange = this.handleChange.bind(this);
6
11
  this.handleKeypress = this.handleKeypress.bind(this);
7
12
  }
8
13
 
9
14
  componentDidMount() {
15
+ this.unsubscribe = this.store.subscribe(this.handleChange);
10
16
  window.addEventListener("keypress", this.handleKeypress);
11
17
  }
12
18
 
13
19
  componentWillUnmount() {
20
+ this.unsubscribe();
14
21
  window.removeEventListener("keypress", this.handleKeypress);
15
22
  }
16
23
 
17
24
  closeModal(evt) {
18
25
  evt.stopPropagation();
19
26
  evt.preventDefault();
20
- ModalActions.close();
27
+ ModalStore.dispatch({ type: "CLOSE" });
28
+ }
29
+
30
+ handleChange() {
31
+ this.setState({ ...this.store.getState() });
21
32
  }
22
33
 
23
34
  handleKeypress(evt) {
@@ -1,4 +1,8 @@
1
- class PageDates extends React.Component {
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import DateRangeSelect from "./DateRangeSelect";
4
+
5
+ export default class PageDates extends React.Component {
2
6
  constructor(props) {
3
7
  super(props);
4
8
  this.state = {
@@ -56,3 +60,9 @@ class PageDates extends React.Component {
56
60
  );
57
61
  }
58
62
  }
63
+
64
+ PageDates.propTypes = {
65
+ starts_at: PropTypes.string,
66
+ ends_at: PropTypes.string,
67
+ all_day: PropTypes.bool
68
+ };
@@ -1,4 +1,8 @@
1
- class PageFiles extends React.Component {
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import Attachments from "./Attachments";
4
+
5
+ export default class PageFiles extends React.Component {
2
6
  render() {
3
7
  return (
4
8
  <div className="page-files">
@@ -6,9 +10,14 @@ class PageFiles extends React.Component {
6
10
  showEmbed={true}
7
11
  locale={this.props.locale}
8
12
  locales={this.props.locales}
9
- csrf_token={this.props.csrf_token}
10
13
  records={this.props.records} />
11
14
  </div>
12
15
  );
13
16
  }
14
17
  }
18
+
19
+ PageFiles.propTypes = {
20
+ locale: PropTypes.string,
21
+ locales: PropTypes.object,
22
+ records: PropTypes.array
23
+ };
@@ -1,4 +1,8 @@
1
- class PageImages extends React.Component {
1
+ import React from "react";
2
+ import PropTypes from "prop-types";
3
+ import ImageGrid from "./ImageGrid";
4
+
5
+ export default class PageImages extends React.Component {
2
6
  render() {
3
7
  return (
4
8
  <div className="page-images">
@@ -8,9 +12,14 @@ class PageImages extends React.Component {
8
12
  showEmbed={true}
9
13
  locale={this.props.locale}
10
14
  locales={this.props.locales}
11
- csrf_token={this.props.csrf_token}
12
15
  records={this.props.records} />
13
16
  </div>
14
17
  );
15
18
  }
16
19
  }
20
+
21
+ PageImages.propTypes = {
22
+ locale: PropTypes.string,
23
+ locales: PropTypes.object,
24
+ records: PropTypes.array
25
+ };