Skip to content

Commit 14b6cd1

Browse files
committed
Started migration of attachment manager from vue
- Created new dropzone component. - Added standard component event system using custom DOM events. - Added tabs component. - Added ajax-delete-row component.
1 parent 8dc9689 commit 14b6cd1

15 files changed

Lines changed: 315 additions & 72 deletions

File tree

app/Http/Controllers/AttachmentController.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,9 @@ public function listForPage(int $pageId)
152152
{
153153
$page = $this->pageRepo->getById($pageId);
154154
$this->checkOwnablePermission('page-view', $page);
155-
return response()->json($page->attachments);
155+
return view('pages.attachment-list', [
156+
'attachments' => $page->attachments->all(),
157+
]);
156158
}
157159

158160
/**
@@ -163,14 +165,13 @@ public function listForPage(int $pageId)
163165
public function sortForPage(Request $request, int $pageId)
164166
{
165167
$this->validate($request, [
166-
'files' => 'required|array',
167-
'files.*.id' => 'required|integer',
168+
'order' => 'required|array',
168169
]);
169170
$page = $this->pageRepo->getById($pageId);
170171
$this->checkOwnablePermission('page-update', $page);
171172

172-
$attachments = $request->get('files');
173-
$this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
173+
$attachmentOrder = $request->get('order');
174+
$this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId);
174175
return response()->json(['message' => trans('entities.attachments_order_updated')]);
175176
}
176177

app/Uploads/Attachment.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,8 @@ public function page()
3030

3131
/**
3232
* Get the url of this file.
33-
* @return string
3433
*/
35-
public function getUrl()
34+
public function getUrl(): string
3635
{
3736
if ($this->external && strpos($this->path, 'http') !== 0) {
3837
return $this->path;

app/Uploads/AttachmentService.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,14 @@ public function saveNewFromLink($name, $link, $page_id)
109109
}
110110

111111
/**
112-
* Updates the file ordering for a listing of attached files.
113-
* @param array $attachmentList
114-
* @param $pageId
112+
* Updates the ordering for a listing of attached files.
115113
*/
116-
public function updateFileOrderWithinPage($attachmentList, $pageId)
114+
public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId)
117115
{
118-
foreach ($attachmentList as $index => $attachment) {
119-
Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
116+
foreach ($attachmentOrder as $index => $attachmentId) {
117+
Attachment::query()->where('uploaded_to', '=', $pageId)
118+
->where('id', '=', $attachmentId)
119+
->update(['order' => $index]);
120120
}
121121
}
122122

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* AjaxDelete
3+
* @extends {Component}
4+
*/
5+
import {onSelect} from "../services/dom";
6+
7+
class AjaxDeleteRow {
8+
setup() {
9+
this.row = this.$el;
10+
this.url = this.$opts.url;
11+
this.deleteButtons = this.$manyRefs.delete;
12+
13+
onSelect(this.deleteButtons, this.runDelete.bind(this));
14+
}
15+
16+
runDelete() {
17+
this.row.style.opacity = '0.7';
18+
this.row.style.pointerEvents = 'none';
19+
20+
window.$http.delete(this.url).then(resp => {
21+
if (typeof resp.data === 'object' && resp.data.message) {
22+
window.$events.emit('success', resp.data.message);
23+
}
24+
this.row.remove();
25+
}).catch(err => {
26+
this.row.style.opacity = null;
27+
this.row.style.pointerEvents = null;
28+
});
29+
}
30+
}
31+
32+
export default AjaxDeleteRow;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
/**
3+
* Attachments
4+
* @extends {Component}
5+
*/
6+
class Attachments {
7+
8+
setup() {
9+
this.container = this.$el;
10+
this.pageId = this.$opts.pageId;
11+
this.editContainer = this.$refs.editContainer;
12+
this.mainTabs = this.$refs.mainTabs;
13+
this.list = this.$refs.list;
14+
15+
this.setupListeners();
16+
}
17+
18+
setupListeners() {
19+
this.container.addEventListener('dropzone-success', event => {
20+
this.mainTabs.components.tabs.show('items');
21+
window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
22+
this.list.innerHTML = resp.data;
23+
window.components.init(this.list);
24+
})
25+
});
26+
27+
this.container.addEventListener('sortable-list-sort', event => {
28+
this.updateOrder(event.detail.ids);
29+
});
30+
31+
this.editContainer.addEventListener('keypress', event => {
32+
if (event.key === 'Enter') {
33+
// TODO - Update editing file
34+
}
35+
})
36+
}
37+
38+
updateOrder(idOrder) {
39+
window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
40+
window.$events.emit('success', resp.data.message);
41+
});
42+
}
43+
44+
}
45+
46+
export default Attachments;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import DropZoneLib from "dropzone";
2+
import {fadeOut} from "../services/animations";
3+
4+
/**
5+
* Dropzone
6+
* @extends {Component}
7+
*/
8+
class Dropzone {
9+
setup() {
10+
this.container = this.$el;
11+
this.url = this.$opts.url;
12+
13+
const _this = this;
14+
this.dz = new DropZoneLib(this.container, {
15+
addRemoveLinks: true,
16+
dictRemoveFile: window.trans('components.image_upload_remove'),
17+
timeout: Number(window.uploadTimeout) || 60000,
18+
maxFilesize: Number(window.uploadLimit) || 256,
19+
url: this.url,
20+
withCredentials: true,
21+
init() {
22+
this.dz = this;
23+
this.dz.on('sending', _this.onSending.bind(_this));
24+
this.dz.on('success', _this.onSuccess.bind(_this));
25+
this.dz.on('error', _this.onError.bind(_this));
26+
}
27+
});
28+
}
29+
30+
onSending(file, xhr, data) {
31+
32+
const token = window.document.querySelector('meta[name=token]').getAttribute('content');
33+
data.append('_token', token);
34+
35+
xhr.ontimeout = function (e) {
36+
this.dz.emit('complete', file);
37+
this.dz.emit('error', file, window.trans('errors.file_upload_timeout'));
38+
}
39+
}
40+
41+
onSuccess(file, data) {
42+
this.container.dispatchEvent(new Event('dropzone'))
43+
this.$emit('success', {file, data});
44+
fadeOut(file.previewElement, 800, () => {
45+
this.dz.removeFile(file);
46+
});
47+
}
48+
49+
onError(file, errorMessage, xhr) {
50+
this.$emit('error', {file, errorMessage, xhr});
51+
52+
const setMessage = (message) => {
53+
const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
54+
messsageEl.textContent = message;
55+
}
56+
57+
if (xhr && xhr.status === 413) {
58+
setMessage(window.trans('errors.server_upload_limit'))
59+
} else if (errorMessage.file) {
60+
setMessage(errorMessage.file);
61+
}
62+
}
63+
64+
removeAll() {
65+
this.dz.removeAllFiles(true);
66+
}
67+
}
68+
69+
export default Dropzone;

resources/js/components/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,14 @@ function initComponent(name, element) {
4040
instance.$refs = allRefs.refs;
4141
instance.$manyRefs = allRefs.manyRefs;
4242
instance.$opts = parseOpts(name, element);
43+
instance.$emit = (eventName, data = {}) => {
44+
data.from = instance;
45+
const event = new CustomEvent(`${name}-${eventName}`, {
46+
bubbles: true,
47+
detail: data
48+
});
49+
instance.$el.dispatchEvent(event);
50+
};
4351
if (typeof instance.setup === 'function') {
4452
instance.setup();
4553
}
@@ -158,4 +166,5 @@ export default initAll;
158166
* @property {Object<String, HTMLElement>} $refs
159167
* @property {Object<String, HTMLElement[]>} $manyRefs
160168
* @property {Object<String, String>} $opts
169+
* @property {function(string, Object)} $emit
161170
*/

resources/js/components/sortable-list.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,12 @@ class SortableList {
99
this.container = this.$el;
1010
this.handleSelector = this.$opts.handleSelector;
1111

12-
new Sortable(this.container, {
12+
const sortable = new Sortable(this.container, {
1313
handle: this.handleSelector,
1414
animation: 150,
15+
onSort: () => {
16+
this.$emit('sort', {ids: sortable.toArray()});
17+
}
1518
});
1619
}
1720
}

resources/js/components/tabs.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Tabs
3+
* Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
4+
* @extends {Component}
5+
*/
6+
import {onSelect} from "../services/dom";
7+
8+
class Tabs {
9+
10+
setup() {
11+
this.tabContentsByName = {};
12+
this.tabButtonsByName = {};
13+
this.allContents = [];
14+
this.allButtons = [];
15+
16+
for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
17+
if (key.startsWith('toggle')) {
18+
const cleanKey = key.replace('toggle', '').toLowerCase();
19+
onSelect(elems, e => this.show(cleanKey));
20+
this.allButtons.push(...elems);
21+
this.tabButtonsByName[cleanKey] = elems;
22+
}
23+
if (key.startsWith('content')) {
24+
const cleanKey = key.replace('content', '').toLowerCase();
25+
this.tabContentsByName[cleanKey] = elems;
26+
this.allContents.push(...elems);
27+
}
28+
}
29+
}
30+
31+
show(key) {
32+
this.allContents.forEach(c => {
33+
c.classList.add('hidden');
34+
c.classList.remove('selected');
35+
});
36+
this.allButtons.forEach(b => b.classList.remove('selected'));
37+
38+
const contents = this.tabContentsByName[key] || [];
39+
const buttons = this.tabButtonsByName[key] || [];
40+
if (contents.length > 0) {
41+
contents.forEach(c => {
42+
c.classList.remove('hidden')
43+
c.classList.add('selected')
44+
});
45+
buttons.forEach(b => b.classList.add('selected'));
46+
}
47+
}
48+
49+
}
50+
51+
export default Tabs;

resources/lang/en/entities.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@
256256
'attachments_upload' => 'Upload File',
257257
'attachments_link' => 'Attach Link',
258258
'attachments_set_link' => 'Set Link',
259-
'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
259+
'attachments_delete' => 'Are you sure you want to delete this attachment?',
260260
'attachments_dropzone' => 'Drop files or click here to attach a file',
261261
'attachments_no_files' => 'No files have been uploaded',
262262
'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',

0 commit comments

Comments
 (0)