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
@@ -1,130 +0,0 @@
1
- class Attachment extends React.Component {
2
- constructor(props) {
3
- super(props);
4
- this.copyEmbed = this.copyEmbed.bind(this);
5
- this.editAttachment = this.editAttachment.bind(this);
6
- this.deleteRecord = this.deleteRecord.bind(this);
7
- this.dragStart = this.dragStart.bind(this);
8
- }
9
-
10
- copyEmbed(evt) {
11
- let attachment = this.props.record.attachment;
12
- evt.preventDefault();
13
- const el = document.createElement("textarea");
14
- el.value = `[attachment:${attachment.id}]`;
15
- document.body.appendChild(el);
16
- el.select();
17
- document.execCommand("copy");
18
- document.body.removeChild(el);
19
- ToastActions.notice("Embed code copied to clipboard");
20
- }
21
-
22
- deleteRecord(evt) {
23
- evt.preventDefault();
24
- if (this.props.deleteRecord) {
25
- this.props.deleteRecord(this.props.record);
26
- }
27
- }
28
-
29
- dragStart(evt) {
30
- evt.preventDefault();
31
- evt.stopPropagation();
32
- if (this.props.startDrag) {
33
- this.props.startDrag(evt, this.props.record);
34
- }
35
- }
36
-
37
- description() {
38
- let attachment = this.props.record.attachment;
39
- if (attachment.description && attachment.description[this.props.locale]) {
40
- return attachment.description[this.props.locale];
41
- }
42
- return null;
43
- }
44
-
45
- name() {
46
- let attachment = this.props.record.attachment;
47
- if (attachment.name && attachment.name[this.props.locale]) {
48
- return attachment.name[this.props.locale];
49
- }
50
- return null;
51
- }
52
-
53
- editAttachment(evt) {
54
- evt.preventDefault();
55
- ModalActions.open(
56
- <AttachmentEditor attachment={this.props.record.attachment}
57
- locale={this.props.locale}
58
- locales={this.props.locales}
59
- csrf_token={this.props.csrf_token}
60
- onUpdate={this.props.onUpdate} />
61
- );
62
- }
63
-
64
- renderAttachment() {
65
- let { attachment, uploading } = this.props.record;
66
-
67
- let icon = uploading ? "cloud-upload" : "paperclip";
68
-
69
- return(
70
- <div className="attachment-info">
71
- <h3>
72
- <i className={`fa fa-${icon} icon`} />
73
- {this.name() || <em>Untitled</em>}<br />
74
- </h3>
75
- {!uploading &&
76
- <a href={attachment.url}
77
- target="_blank">{attachment.filename}</a>}
78
- {!uploading && this.description() && <p>{this.description()}</p>}
79
- </div>
80
- );
81
- }
82
-
83
- render() {
84
- let { attributeName, record } = this.props;
85
- let uploading = record.uploading;
86
- let attachment = record.attachment;
87
- let classes = ["attachment"];
88
- if (this.props.placeholder) {
89
- classes.push("placeholder");
90
- }
91
- if (this.props.record.uploading) {
92
- classes.push("uploading");
93
- }
94
- return (
95
- <div className={classes.join(" ")}
96
- onDragStart={this.dragStart}
97
- ref={this.props.record.ref}
98
- draggable>
99
- <input name={`${attributeName}[id]`}
100
- type="hidden" value={record.id || ""} />
101
- <input name={`${attributeName}[attachment_id]`}
102
- type="hidden" value={(attachment && attachment.id) || ""} />
103
- <input name={`${attributeName}[position]`}
104
- type="hidden" value={this.props.position} />
105
- {this.props.enablePrimary && (
106
- <input name={`${attributeName}[primary]`}
107
- type="hidden" value={this.props.primary} />
108
- )}
109
- {!uploading &&
110
- <div className="actions">
111
- <button onClick={this.editAttachment}>
112
- Edit
113
- </button>
114
- {this.props.showEmbed && (
115
- <button onClick={this.copyEmbed}>
116
- Embed
117
- </button>
118
- )}
119
- {this.props.deleteRecord && (
120
- <button onClick={this.deleteRecord}>
121
- Remove
122
- </button>
123
- )}
124
- </div>
125
- }
126
- {attachment && this.renderAttachment()}
127
- </div>
128
- );
129
- }
130
- }
@@ -1,131 +0,0 @@
1
- class AttachmentEditor extends React.Component {
2
- constructor(props) {
3
- let attachment = props.attachment;
4
-
5
- super(props);
6
-
7
- this.state = {
8
- locale: this.props.locale,
9
- name: attachment.name || {},
10
- description: attachment.description || {}
11
- };
12
-
13
- this.copyEmbedCode = this.copyEmbedCode.bind(this);
14
- this.save = this.save.bind(this);
15
- }
16
-
17
- copyEmbedCode(evt) {
18
- evt.preventDefault();
19
- const el = document.createElement("textarea");
20
- el.value = `[attachment:${this.props.attachment.id}]`;
21
- document.body.appendChild(el);
22
- el.select();
23
- document.execCommand("copy");
24
- document.body.removeChild(el);
25
- ToastActions.notice("Embed code copied to clipboard");
26
- }
27
-
28
- copySupported() {
29
- return document.queryCommandSupported &&
30
- document.queryCommandSupported("copy");
31
- }
32
-
33
- render() {
34
- let { locale, name, description } = this.state;
35
- let locales = this.props.locales;
36
- let attachment = this.props.attachment;
37
- return (
38
- <div className="attachment-editor">
39
- <form>
40
- {locales && Object.keys(locales).length > 1 && (
41
- <div className="field">
42
- <label>
43
- Locale
44
- </label>
45
- <select name="locale"
46
- onChange={e => this.setState({locale: e.target.value})}>
47
- {Object.keys(locales).map(key => (
48
- <option key={`locale-${key}`} value={key}>
49
- {locales[key]}
50
- </option>
51
- ))}
52
- </select>
53
- </div>
54
- )}
55
- <div className="field">
56
- <label>Name</label>
57
- <input type="text"
58
- className="name"
59
- value={name[locale] || ""}
60
- onChange={e => this.updateLocalized("name",
61
- e.target.value)} />
62
- </div>
63
- <div className="field">
64
- <label>Description</label>
65
- <textarea className="description"
66
- value={description[locale] || ""}
67
- onChange={e => this.updateLocalized("description",
68
- e.target.value)} />
69
- </div>
70
- <div className="field embed-code">
71
- <label>
72
- Embed code
73
- </label>
74
- <input type="text"
75
- value={`[attachment:${attachment.id}]`}
76
- disabled={true} />
77
- {this.copySupported() && (
78
- <button onClick={this.copyEmbedCode}>
79
- Copy
80
- </button>
81
- )}
82
- </div>
83
- <div className="field">
84
- <label>File</label>
85
- <a href={attachment.url}
86
- target="_blank">{attachment.filename}</a>
87
- </div>
88
- <div className="buttons">
89
- <button onClick={this.save}>
90
- Save
91
- </button>
92
- <button onClick={() => ModalActions.close()}>
93
- Cancel
94
- </button>
95
- </div>
96
- </form>
97
- </div>
98
- );
99
- }
100
-
101
- save(evt) {
102
- evt.preventDefault();
103
- evt.stopPropagation();
104
-
105
- let data = { name: this.state.name,
106
- description: this.state.description };
107
-
108
- var xhr = new XMLHttpRequest();
109
- xhr.open("PUT", `/admin/attachments/${this.props.attachment.id}`, true);
110
- xhr.setRequestHeader("Content-Type","application/json; charset=utf-8");
111
- xhr.setRequestHeader("X-CSRF-Token", this.props.csrf_token);
112
- xhr.onload = function () {
113
- if (xhr.readyState == 4 && xhr.status == "200") {
114
- // Success
115
- }
116
- };
117
- xhr.send(JSON.stringify({attachment: data}));
118
-
119
- if (this.props.onUpdate) {
120
- this.props.onUpdate(data);
121
- }
122
- ModalActions.close();
123
- }
124
-
125
- updateLocalized(name, value) {
126
- let locale = this.state.locale;
127
- this.setState({
128
- [name]: { ...this.state[name], [locale]: value }
129
- });
130
- }
131
- }
@@ -1,211 +0,0 @@
1
- class Attachments extends DragUploader {
2
- constructor(props) {
3
- super(props);
4
-
5
- this.state = {
6
- ...this.state,
7
- records: props.records.map(
8
- r => ({ ...r, ref: React.createRef(), handle: this.getHandle() })),
9
- deleted: []
10
- };
11
-
12
- this.container = React.createRef();
13
-
14
- this.deleteRecord = this.deleteRecord.bind(this);
15
- this.receiveFiles = this.receiveFiles.bind(this);
16
- }
17
-
18
- attributeName(record) {
19
- return `${this.props.attribute}[${this.index(record) + 1}]`;
20
- }
21
-
22
- deleteRecord(record) {
23
- let { records, deleted } = this.state;
24
- records = records.filter(i => i != record);
25
- if (record.id) {
26
- deleted = [...deleted, record];
27
- }
28
- this.setState({ records: records, deleted: deleted });
29
- }
30
-
31
- draggables() {
32
- return this.state.records;
33
- }
34
-
35
- getDraggedOrder() {
36
- let dragging = this.state.dragging;
37
- var records = this.state.records;
38
- if (dragging) {
39
- if (this.hovering(this.container)) {
40
- records = [];
41
- this.state.records.filter(r => r !== dragging).forEach(r => {
42
- if (this.hovering(r) && records.indexOf(dragging) === -1) {
43
- records.push(dragging);
44
- }
45
- records.push(r);
46
- });
47
- if (records.indexOf(dragging) === -1) {
48
- records.push(dragging);
49
- }
50
- } else {
51
- records = this.state.records.filter(r => r !== dragging);
52
- if (this.state.y < this.container.current.getBoundingClientRect().top) {
53
- records = [dragging, ...records];
54
- } else {
55
- records.push(dragging);
56
- }
57
- }
58
- }
59
- return records;
60
- }
61
-
62
- index(record) {
63
- let { records, deleted } = this.state;
64
- let ordered = [...records, ...deleted];
65
- return ordered.indexOf(record);
66
- }
67
-
68
- injectUploads(files, records) {
69
- let queue = files.slice();
70
- let source = records;
71
-
72
- if (source.indexOf("Files") === -1) {
73
- return [...source, ...queue];
74
- } else {
75
- records = [];
76
- source.forEach(function (record) {
77
- if (record === "Files") {
78
- records = [...records, ...queue];
79
- } else {
80
- records.push(record);
81
- }
82
- });
83
- }
84
-
85
- return records;
86
- }
87
-
88
- receiveFiles(files, newState) {
89
- this.setState({
90
- ...newState,
91
- records: this.injectUploads(files.map(f => this.uploadAttachment(f)),
92
- this.getDraggedOrder())
93
- });
94
- }
95
-
96
- render() {
97
- let { dragging, deleted } = this.state;
98
- let records = this.getDraggedOrder();
99
- let classes = ["attachments"];
100
- if (dragging) {
101
- classes.push("dragover");
102
- }
103
- return (
104
- <div className={classes.join(" ")}
105
- ref={this.container}
106
- onDragOver={this.drag}
107
- onDrop={this.dragEnd}>
108
- <div className="files">
109
- {records.map(r => this.renderAttachment(r))}
110
- </div>
111
- <div className="deleted">
112
- {deleted.map(r => this.renderDeletedRecord(r))}
113
- </div>
114
- <div className="drop-target">
115
- <FileUploadButton multiple={true}
116
- multiline={true}
117
- callback={this.receiveFiles} />
118
- </div>
119
- </div>
120
- );
121
- }
122
-
123
- renderAttachment(record) {
124
- let dragging = this.state.dragging;
125
-
126
- if (record === "Files") {
127
- return (
128
- <div className="attachment drop-placeholder"
129
- key="file-placeholder">
130
- Upload files here
131
- </div>
132
- );
133
- }
134
-
135
- let onUpdate = (attachment) => {
136
- this.updateAttachment(record, attachment);
137
- };
138
-
139
- return (
140
- <Attachment key={record.handle}
141
- record={record}
142
- locale={this.props.locale}
143
- locales={this.props.locales}
144
- csrf_token={this.props.csrf_token}
145
- showEmbed={this.props.showEmbed}
146
- startDrag={this.startDrag}
147
- position={this.index(record) + 1}
148
- onUpdate={onUpdate}
149
- deleteRecord={this.deleteRecord}
150
- attributeName={this.attributeName(record)}
151
- placeholder={dragging && dragging == record} />
152
- );
153
- }
154
-
155
- renderDeletedRecord(record) {
156
- let attachment = record.attachment;
157
- let attrName = this.attributeName(record);
158
- return (
159
- <span className="deleted-attachment" key={`deleted-${record.id}`}>
160
- <input name={`${attrName}[id]`}
161
- type="hidden" value={record.id} />
162
- <input name={`${attrName}[attachment_id]`}
163
- type="hidden" value={(attachment && attachment.id) || ""} />
164
- <input name={`${attrName}[_destroy]`}
165
- type="hidden" value={true} />
166
- </span>
167
- );
168
- }
169
-
170
- updateAttachment(record, attachment) {
171
- let records = this.state.records.slice();
172
-
173
- records[records.indexOf(record)] = {
174
- ...record,
175
- attachment: { ...record.attachment, ...attachment }
176
- };
177
-
178
- this.setState({ records: records });
179
- }
180
-
181
- filenameToName(str) {
182
- return str.replace(/\.[\w\d]+$/, "").replace(/_/g, " ");
183
- }
184
-
185
- uploadAttachment(file) {
186
- let component = this;
187
- let locale = this.props.locale;
188
- let locales = this.props.locales ? Object.keys(this.props.locales) : [locale];
189
-
190
- let name = {};
191
- locales.forEach((l) => name[l] = file.name);
192
-
193
- let obj = { attachment: { filename: file.name, name: name },
194
- uploading: true,
195
- ref: React.createRef(),
196
- handle: this.getHandle() };
197
- let data = new FormData();
198
-
199
- data.append("attachment[file]", file);
200
- locales.forEach((l) => {
201
- data.append(`attachment[name][${l}]`, this.filenameToName(file.name));
202
- });
203
- this.postFile("/admin/attachments.json", data, function (json) {
204
- obj.attachment = json;
205
- obj.uploading = false;
206
- component.setState({ });
207
- });
208
-
209
- return obj;
210
- }
211
- }
@@ -1,174 +0,0 @@
1
- class DragUploader extends React.Component {
2
- constructor(props) {
3
- super(props);
4
- this.state = { dragging: false,
5
- x: null,
6
- y: null };
7
-
8
- this.cachePositions = this.cachePositions.bind(this);
9
- this.drag = this.drag.bind(this);
10
- this.dragEnd = this.dragEnd.bind(this);
11
- this.dragLeave = this.dragLeave.bind(this);
12
- this.startDrag = this.startDrag.bind(this);
13
- }
14
-
15
- componentDidMount() {
16
- window.addEventListener("mousemove", this.drag);
17
- window.addEventListener("touchmove", this.drag);
18
- window.addEventListener("mouseup", this.dragEnd);
19
- window.addEventListener("touchend", this.dragEnd);
20
- window.addEventListener("mouseout", this.dragLeave);
21
- window.addEventListener("resize", this.cachePositions);
22
- this.cachePositions();
23
- }
24
-
25
- componentWillUnmount() {
26
- window.removeEventListener("mousemove", this.drag);
27
- window.removeEventListener("touchmove", this.drag);
28
- window.removeEventListener("mouseup", this.dragEnd);
29
- window.removeEventListener("touchend", this.dragEnd);
30
- window.removeEventListener("mouseout", this.dragLeave);
31
- window.removeEventListener("resize", this.cachePositions);
32
- }
33
-
34
- draggables() {
35
- // TODO: raise error
36
- return [];
37
- }
38
-
39
- receiveFiles(files, newState) {
40
- this.setState(newState);
41
- }
42
-
43
- cachePositions() {
44
- this.cachedPositions = {};
45
- this.draggables().forEach(d => {
46
- if (d.handle && d.ref && d.ref.current) {
47
- this.cachedPositions[d.handle] = d.ref.current.getBoundingClientRect();
48
- }
49
- });
50
- }
51
-
52
- containsFiles(evt) {
53
- if (!evt.dataTransfer || !evt.dataTransfer.types) {
54
- return false;
55
- }
56
- let types = evt.dataTransfer.types;
57
- for (var i = 0; i < types.length; i++) {
58
- if (types[i] === "Files" || types[i] === "application/x-moz-file") {
59
- return true;
60
- }
61
- }
62
- return false;
63
- }
64
-
65
- drag(evt) {
66
- if (this.state.dragging) {
67
- let position = this.mousePosition(evt);
68
- evt.stopPropagation();
69
- evt.preventDefault();
70
- this.setState({ x: position.x, y: position.y });
71
- } else {
72
- if (this.containsFiles(evt)) {
73
- this.cachePositions();
74
- this.setState({ dragging: "Files" });
75
- }
76
- }
77
- }
78
-
79
- dragEnd(evt) {
80
- if (!this.state.dragging) {
81
- return;
82
- }
83
- evt.preventDefault();
84
- evt.stopPropagation();
85
-
86
- var files = [];
87
- if (this.state.dragging == "Files") {
88
- files = this.getFiles(evt.dataTransfer);
89
- }
90
-
91
- this.receiveFiles(files, { dragging: false, x: null, y: null });
92
- this.cachePositions();
93
- }
94
-
95
- dragLeave(evt) {
96
- if (!this.state.dragging || this.state.dragging !== "Files") {
97
- return;
98
- }
99
- evt.preventDefault();
100
- evt.stopPropagation();
101
- this.setState({ dragging: false, x: null, y: null });
102
- }
103
-
104
- getFiles(dt) {
105
- var files = [];
106
- if (dt.items) {
107
- for (var i = 0; i < dt.items.length; i++) {
108
- if (dt.items[i].kind == "file") {
109
- files.push(dt.items[i].getAsFile());
110
- }
111
- }
112
- } else {
113
- for (var i = 0; i < dt.files.length; i++) {
114
- files.push(dt.files[i]);
115
- }
116
- }
117
- return files.filter(f => (!this.validMimeTypes ||
118
- this.validMimeTypes.indexOf(f.type) !== -1));
119
- }
120
-
121
- getHandle() {
122
- if (!this.handle) {
123
- this.handle = 0;
124
- }
125
- this.handle += 1;
126
- return this.handle;
127
- }
128
-
129
- hovering(target) {
130
- let { x, y } = this.state;
131
- var rect;
132
- if (target.handle && this.cachedPositions[target.handle]) {
133
- rect = this.cachedPositions[target.handle];
134
- } else if (target.current) {
135
- rect = target.current.getBoundingClientRect();
136
- } else {
137
- return false;
138
- }
139
- return (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom);
140
- }
141
-
142
- mousePosition(evt) {
143
- var x, y;
144
- if (evt.type == "touchmove") {
145
- x = evt.touches[0].clientX;
146
- y = evt.touches[0].clientY;
147
- } else {
148
- x = evt.clientX;
149
- y = evt.clientY;
150
- }
151
- return { x: x, y: y };
152
- }
153
-
154
- postFile(url, data, callback) {
155
- let xhr = new XMLHttpRequest();
156
- xhr.open("POST", url);
157
- xhr.setRequestHeader("X-CSRF-Token", this.props.csrf_token);
158
- xhr.addEventListener("load", function () {
159
- if (xhr.readyState == 4 && xhr.status == "200" && callback) {
160
- callback(JSON.parse(xhr.responseText));
161
- }
162
- });
163
- xhr.send(data);
164
- }
165
-
166
- startDrag(evt, record) {
167
- let position = this.mousePosition(evt);
168
- let prevDisplay = record.ref.current.style.display;
169
- record.ref.current.style.display = "none";
170
- this.cachePositions();
171
- record.ref.current.style.display = prevDisplay;
172
- this.setState({ dragging: record, x: position.x, y: position.y });
173
- }
174
- }
@@ -1,57 +0,0 @@
1
- class EditableImage extends React.Component {
2
- constructor(props) {
3
- let image = props.image;
4
- super(props);
5
- this.state = {
6
- image: image,
7
- src: props.src,
8
- width: image.crop_width || image.real_width,
9
- height: image.crop_height || image.real_height
10
- };
11
- this.openEditor = this.openEditor.bind(this);
12
- this.update = this.update.bind(this);
13
- }
14
-
15
- openEditor() {
16
- ModalActions.open(
17
- <ImageEditor image={this.state.image}
18
- caption={this.props.caption}
19
- locale={this.props.locale}
20
- locales={this.props.locales}
21
- csrf_token={this.props.csrf_token}
22
- onUpdate={this.update} />);
23
- }
24
-
25
- height() {
26
- let image = this.state.image;
27
- let width = image.crop_width || image.real_width;
28
- let height = image.crop_height || image.real_height;
29
- return Math.round((height / width) * this.props.width);
30
- }
31
-
32
- update(image, croppedImage) {
33
- let newImage = { ...this.state.image, ...image };
34
- this.setState({image: newImage,
35
- src: croppedImage});
36
- if (this.props.onUpdate) {
37
- this.props.onUpdate(newImage, croppedImage);
38
- }
39
- }
40
-
41
- render() {
42
- let altWarning = !this.state.image.alternative[this.props.locale];
43
-
44
- return (
45
- <div className="editable-image">
46
- {altWarning &&
47
- <span className="alt-warning" title="Alternative text is missing">
48
- <i className="fa fa-exclamation-triangle icon" />
49
- </span>}
50
- <img src={this.state.src}
51
- width={this.props.width}
52
- height={this.height()}
53
- onClick={this.openEditor} />
54
- </div>
55
- );
56
- }
57
- }