Skip to content
Snippets Groups Projects
Commit 0341cfd6 authored by Sonia Zorba's avatar Sonia Zorba
Browse files

UI: added methods for copying nodes and handles copy/move of multiple nodes at the same time

parent 2b1dfb56
No related branches found
No related tags found
No related merge requests found
......@@ -199,8 +199,9 @@ export default {
data
}, true, true);
},
getNodesForMove(data) {
let url = BASE_API_URL + 'nodesForMove?path=' + escapePath(data.path) + '&nodeToMove=' + data.nodeToMove;
getNodesForMoveOrCopy(data) {
let url = BASE_API_URL + 'nodesForMoveOrCopy?path=' + escapePath(data.path) +
'&' + data.targets.map(t => 'target=' + escapePath(t)).join('&');
return apiRequest({
method: 'GET',
url: url,
......@@ -210,8 +211,8 @@ export default {
}
}, true, true);
},
moveNode(data) {
let url = BASE_API_URL + 'move';
moveOrCopyNodes(data) {
let url = BASE_API_URL + 'moveOrCopy';
return apiRequest({
method: 'POST',
url: url,
......
......@@ -12,6 +12,8 @@
<b-dropdown variant="primary" text="Actions" v-if="actionsEnabled">
<b-dropdown-item :disabled="!asyncButtonEnabled" @click="startAsyncRecallJob">Async recall</b-dropdown-item>
<b-dropdown-item :disabled="!deleteButtonEnabled" @click="deleteNodes">Delete</b-dropdown-item>
<b-dropdown-item :disabled="!moveButtonEnabled" @click="moveNodes">Move</b-dropdown-item>
<b-dropdown-item :disabled="!copyButtonEnabled" @click="copyNodes">Copy</b-dropdown-item>
<b-dropdown-item :disabled="!archiveButtonEnabled" @click="createArchive('zip')">Create zip archive</b-dropdown-item>
<b-dropdown-item :disabled="!archiveButtonEnabled" @click="createArchive('tar')">Create tar archive</b-dropdown-item>
</b-dropdown>
......@@ -44,7 +46,7 @@
<ConfirmDeleteModal />
<ShareModal />
<RenameModal />
<MoveModal />
<MoveOrCopyModal />
<ConfirmArchiveModal />
</div>
</template>
......@@ -56,7 +58,7 @@ import UploadFilesModal from './modal/UploadFilesModal.vue'
import ConfirmDeleteModal from './modal/ConfirmDeleteModal.vue'
import ShareModal from './modal/ShareModal.vue'
import RenameModal from './modal/RenameModal.vue'
import MoveModal from './modal/MoveModal.vue'
import MoveOrCopyModal from './modal/MoveOrCopyModal.vue'
import ConfirmArchiveModal from './modal/ConfirmArchiveModal.vue'
export default {
......@@ -68,7 +70,7 @@ export default {
ConfirmDeleteModal,
ShareModal,
RenameModal,
MoveModal,
MoveOrCopyModal,
ConfirmArchiveModal
},
computed: {
......@@ -98,9 +100,15 @@ export default {
deleteButtonEnabled() {
return this.$store.state.deleteButtonEnabled;
},
moveButtonEnabled() {
return this.$store.state.moveButtonEnabled;
},
archiveButtonEnabled() {
return this.$store.state.archiveButtonEnabled;
},
copyButtonEnabled() {
return this.$store.state.copyButtonEnabled;
},
writable() {
return this.$store.state.writable;
}
......@@ -147,6 +155,42 @@ export default {
this.$store.commit('setSelectedUndeletableNodes', unDeletablePaths);
this.$bvModal.show('confirm-delete-modal');
},
moveNodes() {
let selectedNodesCheckboxes = document.querySelectorAll('#nodes input:checked');
let paths = [];
let unDeletablePaths = [];
for (let i = 0; i < selectedNodesCheckboxes.length; i++) {
let checkbox = selectedNodesCheckboxes[i];
let dataNode = checkbox.getAttribute('data-node');
if (checkbox.classList.contains('deletable')) {
paths.push(dataNode);
} else {
unDeletablePaths.push(dataNode);
}
}
this.$store.commit('setNodesToMoveOrCopy', paths);
this.$store.commit('setMoveOrCopy', 'move');
this.$store.commit('setSelectedNotMovableNodes', unDeletablePaths);
this.$bvModal.show('move-or-copy-modal');
},
copyNodes() {
let selectedNodesCheckboxes = document.querySelectorAll('#nodes input:checked');
let paths = [];
let unCopiablePaths = [];
for (let i = 0; i < selectedNodesCheckboxes.length; i++) {
let checkbox = selectedNodesCheckboxes[i];
let dataNode = checkbox.getAttribute('data-node');
if (checkbox.classList.contains('async')) {
unCopiablePaths.push(dataNode);
} else {
paths.push(dataNode);
}
}
this.$store.commit('setNodesToMoveOrCopy', paths);
this.$store.commit('setMoveOrCopy', 'copy');
this.$store.commit('setSelectedNotCopiableNodes', unCopiablePaths);
this.$bvModal.show('move-or-copy-modal');
},
createArchive(type) {
let selectedNodesCheckboxes = document.querySelectorAll('#nodes input:checked');
let nodesToArchive = [];
......
......@@ -4,26 +4,56 @@
SPDX-License-Identifier: GPL-3.0-or-later
-->
<template>
<b-modal id="move-modal" :title="'Move ' + nodeToMove + ' to ' + destinationPath" okTitle="Move node" @show="afterShow" @ok.prevent="moveNode" :ok-disabled="!writable" size="lg">
<b-modal id="move-or-copy-modal" :title="title" :okTitle="okTitle" @show="afterShow" @ok.prevent="moveOrCopyNodes" :ok-disabled="!writable" size="lg">
<ol class="breadcrumb">
<li class="breadcrumb-item" v-for="(item, i) in breadcrumbs" :key="i" :class="{ 'active' : item.active }">
<a href="#" @click.stop.prevent="breadcrumbClick(i)" v-if="!item.active">{{item.text}}</a>
<span v-if="item.active">{{item.text}}</span>
</li>
</ol>
<div id="move-nodes-wrapper">
<div id="move-nodes"></div>
<div id="move-or-copy-nodes-wrapper">
<div id="move-or-copy-nodes"></div>
</div>
<div v-if="selectedNotCopiableNodes.length > 0" class="mt-3">
<p><strong>Warning</strong>: following selected nodes can't be copied and will be ignored:</p>
<p>
<ul>
<li v-for="node in selectedNotCopiableNodes" :key="node">{{node}}</li>
</ul>
</p>
</div>
<div v-if="selectedNotMovableNodes.length > 0" class="mt-3">
<p><strong>Warning</strong>: following selected nodes can't be moved and will be ignored:</p>
<p>
<ul>
<li v-for="node in selectedNotMovableNodes" :key="node">{{node}}</li>
</ul>
</p>
</div>
</b-modal>
</template>
<script>
export default {
name: 'MoveModal',
name: 'MoveOrCopyModal',
computed: {
nodeToMove() { return this.$store.state.nodeToMove },
destinationPath() { return this.$store.state.nodeToMoveDestination },
writable() { return this.$store.state.nodeToMoveDestinationWritable },
nodesToMoveOrCopy() { return this.$store.state.nodesToMoveOrCopy },
destinationPath() { return this.$store.state.moveOrCopyDestination },
writable() { return this.$store.state.moveOrCopyDestinationWritable },
moveOrCopy() { return this.$store.state.moveOrCopy },
selectedNotMovableNodes() { return this.$store.state.selectedNotMovableNodes },
selectedNotCopiableNodes() { return this.$store.state.selectedNotCopiableNodes },
title() {
let text = this.moveOrCopy === 'move' ? 'Move ' : 'Copy ';
if (this.nodesToMoveOrCopy.length === 1) {
text += this.nodesToMoveOrCopy[0];
} else {
text += this.nodesToMoveOrCopy.length + ' nodes';
}
text += ' to ' + this.destinationPath;
return text;
},
okTitle() { return (this.moveOrCopy === 'move' ? 'Move nodes' : 'Copy nodes') },
breadcrumbs() {
let items = [];
if (this.destinationPath !== null) {
......@@ -41,7 +71,8 @@ export default {
methods: {
afterShow() {
// starts from parent path
this.$store.dispatch('openNodeInMoveModal', this.nodeToMove.substring(0, this.nodeToMove.lastIndexOf('/')));
let firstSelectedNode = this.nodesToMoveOrCopy[0];
this.$store.dispatch('openNodeInMoveOrCopyModal', firstSelectedNode.substring(0, firstSelectedNode.lastIndexOf('/')));
},
breadcrumbClick(i) {
let pathSplit = this.destinationPath.split('/');
......@@ -49,16 +80,17 @@ export default {
if (path === '') {
path = '/';
}
this.$store.commit('setNodeToMoveDestination', path);
window.openNodeInMoveModal(event, path);
this.$store.commit('setMoveOrCopyDestination', path);
window.openNodeInMoveOrCopyModal(event, path);
},
moveNode() {
this.$store.dispatch('moveNode', {
target: this.nodeToMove,
direction: this.destinationPath
moveOrCopyNodes() {
this.$store.dispatch('moveOrCopyNodes', {
targets: this.nodesToMoveOrCopy,
direction: this.destinationPath,
keepBytes: this.moveOrCopy === 'copy'
})
.then(() => {
this.$bvModal.hide('move-modal');
this.$bvModal.hide('move-or-copy-modal');
})
}
}
......@@ -66,13 +98,13 @@ export default {
</script>
<style>
#move-nodes .list-group-item {
#move-or-copy-nodes .list-group-item {
/* reduced padding */
padding-top: .35rem;
padding-bottom: .35rem;
}
#move-nodes-wrapper {
#move-or-copy-nodes-wrapper {
max-height: 300px;
overflow-y: auto;
}
......
......@@ -54,9 +54,10 @@ export default {
this.newNameError = "Name contains an illegal character. Following characters are not allowed: < > ? \" : \\ / | ' * `";
} else {
let parentPath = this.nodeToRename.substring(0, this.nodeToRename.lastIndexOf('/') + 1);
this.$store.dispatch('moveNode', {
target: parentPath + this.oldName,
direction: parentPath + this.newName
this.$store.dispatch('moveOrCopyNodes', {
targets: [parentPath + this.oldName],
direction: parentPath + this.newName,
keepBytes: false
})
.then(() => {
this.$bvModal.hide('rename-modal');
......
......@@ -36,13 +36,23 @@ window.renameNode = function(path) {
vm.$bvModal.show('rename-modal');
}
window.moveNode = function(path) {
store.commit('setNodeToMove', path);
vm.$bvModal.show('move-modal');
moveOrCopy(path, 'move');
}
window.openNodeInMoveModal = function(event, path) {
window.copyNode = function(path) {
moveOrCopy(path, 'copy');
}
window.openNodeInMoveOrCopyModal = function(event, path) {
event.preventDefault();
event.stopPropagation();
store.dispatch('openNodeInMoveModal', path);
store.dispatch('openNodeInMoveOrCopyModal', path);
}
function moveOrCopy(path, moveOrCopy) {
store.commit('setNodesToMoveOrCopy', [path]);
store.commit('setMoveOrCopy', moveOrCopy);
store.commit('setSelectedNotMovableNodes', []);
store.commit('setSelectedNotCopiableNodes', []);
vm.$bvModal.show('move-or-copy-modal');
}
export default {
......
......@@ -29,7 +29,9 @@ export default new Vuex.Store({
nodesLoading: false,
asyncButtonEnabled: false,
deleteButtonEnabled: false,
moveButtonEnabled: false,
archiveButtonEnabled: false,
copyButtonEnabled: false,
jobs: [],
jobsLoading: true,
lastJobsCheckTime: null,
......@@ -43,9 +45,12 @@ export default new Vuex.Store({
groupWrite: null
},
nodeToRename: null,
nodeToMove: null,
nodeToMoveDestination: null,
nodeToMoveDestinationWritable: false,
moveOrCopy: 'move',
nodesToMoveOrCopy: [],
selectedNotMovableNodes: [],
selectedNotCopiableNodes: [],
moveOrCopyDestination: null,
moveOrCopyDestinationWritable: false,
archiveType: null,
nodesToArchive: [],
selectedNotArchivableNodes: []
......@@ -72,9 +77,15 @@ export default new Vuex.Store({
setDeleteButtonEnabled(state, value) {
state.deleteButtonEnabled = value;
},
setMoveButtonEnabled(state, value) {
state.moveButtonEnabled = value;
},
setArchiveButtonEnabled(state, value) {
state.archiveButtonEnabled = value;
},
setCopyButtonEnabled(state, value) {
state.copyButtonEnabled = value;
},
setJobs(state, jobs) {
updateArray(state.jobs, jobs);
},
......@@ -93,6 +104,9 @@ export default new Vuex.Store({
setSelectedUndeletableNodes(state, paths) {
updateArray(state.selectedUndeletableNodes, paths);
},
setSelectedNotCopiableNodes(state, paths) {
updateArray(state.selectedNotCopiableNodes, paths);
},
setWritable(state, value) {
state.writable = value;
},
......@@ -104,14 +118,20 @@ export default new Vuex.Store({
setNodeToRename(state, path) {
state.nodeToRename = path;
},
setNodeToMove(state, path) {
state.nodeToMove = path;
setMoveOrCopy(state, value) {
state.moveOrCopy = value;
},
setNodesToMoveOrCopy(state, paths) {
updateArray(state.nodesToMoveOrCopy, paths);
},
setNodeToMoveDestination(state, path) {
state.nodeToMoveDestination = path;
setMoveOrCopyDestination(state, path) {
state.moveOrCopyDestination = path;
},
setNodeToMoveDestinationWritable(state, value) {
state.nodeToMoveDestinationWritable = value;
setMoveOrCopyDestinationWritable(state, value) {
state.moveOrCopyDestinationWritable = value;
},
setSelectedNotMovableNodes(state, paths) {
updateArray(state.selectedNotMovableNodes, paths);
},
setArchiveType(state, type) {
state.archiveType = type;
......@@ -147,7 +167,9 @@ export default new Vuex.Store({
computeButtonsVisibility({ commit }) {
commit('setAsyncButtonEnabled', document.querySelectorAll('#nodes input.async:checked').length > 0);
commit('setDeleteButtonEnabled', document.querySelectorAll('#nodes input.deletable:checked').length > 0);
commit('setMoveButtonEnabled', document.querySelectorAll('#nodes input.deletable:checked').length > 0);
commit('setArchiveButtonEnabled', document.querySelectorAll('#nodes input:not(.async):checked').length > 0);
commit('setCopyButtonEnabled', document.querySelectorAll('#nodes input:not(.async):checked').length > 0);
},
startAsyncRecallJob({ state, commit, dispatch }) {
let asyncCheckboxes = document.querySelectorAll('#nodes input.async:checked');
......@@ -215,24 +237,27 @@ export default new Vuex.Store({
dispatch('setPath', state.path);
});
},
moveNode({ state, commit, dispatch }, data) {
client.moveNode(data)
.then(job => {
if (job.phase === 'COMPLETED') {
moveOrCopyNodes({ state, commit, dispatch }, data) {
client.moveOrCopyNodes(data)
.then(jobs => {
let uncompletedJobs = jobs.filter(j => j.phase !== 'COMPLETED');
if (uncompletedJobs.length === 0) {
// Reload current node
dispatch('setPath', state.path);
} else {
main.showInfo('Move operation is taking some time and will be handled in background');
main.showInfo('The operation is taking some time and will be handled in background');
for (let job of uncompletedJobs) {
commit('addJob', job);
}
}
});
},
openNodeInMoveModal({ state, commit }, path) {
commit('setNodeToMoveDestination', path);
client.getNodesForMove({ path, nodeToMove: state.nodeToMove })
openNodeInMoveOrCopyModal({ state, commit }, path) {
commit('setMoveOrCopyDestination', path);
client.getNodesForMoveOrCopy({ path, targets: state.nodesToMoveOrCopy })
.then(res => {
commit('setNodeToMoveDestinationWritable', res.writable);
document.getElementById('move-nodes').outerHTML = res.html;
commit('setMoveOrCopyDestinationWritable', res.writable);
document.getElementById('move-or-copy-nodes').outerHTML = res.html;
});
},
createArchive({ state, commit }) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment