Support replace feature; close #188; multiple bug fixes on upload
parent
a117f83256
commit
6ee846ef0e
|
@ -8,7 +8,6 @@
|
|||
</div>
|
||||
<div v-else id="listing"
|
||||
:class="req.display"
|
||||
@drop="drop"
|
||||
@dragenter="dragEnter"
|
||||
@dragend="dragEnd">
|
||||
<div>
|
||||
|
@ -230,7 +229,7 @@ export default {
|
|||
if (columns === 0) columns = 1
|
||||
items.style.width = `calc(${100 / columns}% - 1em)`
|
||||
},
|
||||
dragEnter: function (event) {
|
||||
dragEnter (event) {
|
||||
// When the user starts dragging an item, put every
|
||||
// file on the listing with 50% opacity.
|
||||
let items = document.getElementsByClassName('item')
|
||||
|
@ -239,51 +238,85 @@ export default {
|
|||
file.style.opacity = 0.5
|
||||
})
|
||||
},
|
||||
dragEnd: function (event) {
|
||||
dragEnd (event) {
|
||||
this.resetOpacity()
|
||||
},
|
||||
drop: function (event) {
|
||||
event.preventDefault()
|
||||
this.resetOpacity()
|
||||
|
||||
let dt = event.dataTransfer
|
||||
let files = dt.files
|
||||
let el = event.target
|
||||
|
||||
if (files.length <= 0) return
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
if (el !== null && !el.classList.contains('item')) {
|
||||
el = el.parentElement
|
||||
}
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
if (el !== null && el.classList.contains('item') && el.dataset.dir === 'true') {
|
||||
this.handleFiles(files, el.querySelector('.name').innerHTML + '/')
|
||||
return
|
||||
}
|
||||
|
||||
this.handleFiles(files, '')
|
||||
} else {
|
||||
this.resetOpacity()
|
||||
let base = ''
|
||||
if (el !== null && el.classList.contains('item') && el.dataset.dir === 'true') {
|
||||
base = el.querySelector('.name').innerHTML + '/'
|
||||
}
|
||||
|
||||
if (base !== '') {
|
||||
api.fetch(this.$route.path + base)
|
||||
.then(req => {
|
||||
this.checkConflict(files, req.items, base)
|
||||
})
|
||||
.catch(error => { console.log(error) })
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.checkConflict(files, this.req.items, base)
|
||||
},
|
||||
uploadInput: function (event) {
|
||||
this.handleFiles(event.currentTarget.files, '')
|
||||
checkConflict (files, items, base) {
|
||||
let conflict = false
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
let res = items.findIndex(function hasConflict (element) {
|
||||
return (element.name === this)
|
||||
}, files[i].name)
|
||||
|
||||
if (res >= 0) {
|
||||
conflict = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!conflict) {
|
||||
this.handleFiles(files, base)
|
||||
return
|
||||
}
|
||||
|
||||
this.$store.commit('showHover', {
|
||||
prompt: 'replace',
|
||||
confirm: (event) => {
|
||||
event.preventDefault()
|
||||
this.$store.commit('closeHovers')
|
||||
this.handleFiles(files, base, true)
|
||||
}
|
||||
})
|
||||
},
|
||||
resetOpacity: function () {
|
||||
uploadInput (event) {
|
||||
this.checkConflict(event.currentTarget.files, this.req.items, '')
|
||||
},
|
||||
resetOpacity () {
|
||||
let items = document.getElementsByClassName('item')
|
||||
|
||||
Array.from(items).forEach(file => {
|
||||
file.style.opacity = 1
|
||||
})
|
||||
},
|
||||
handleFiles: function (files, base) {
|
||||
this.resetOpacity()
|
||||
|
||||
handleFiles (files, base, overwrite = false) {
|
||||
buttons.loading('upload')
|
||||
let promises = []
|
||||
|
||||
for (let file of files) {
|
||||
promises.push(api.post(this.$route.path + base + file.name, file))
|
||||
promises.push(api.post(this.$route.path + base + file.name, file, overwrite))
|
||||
}
|
||||
|
||||
Promise.all(promises)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
@click="click"
|
||||
@dblclick="open"
|
||||
@touchstart="touchstart"
|
||||
:data-dir="isDir"
|
||||
:aria-label="name"
|
||||
:aria-selected="isSelected">
|
||||
<div>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<copy v-else-if="showCopy"></copy>
|
||||
<error v-else-if="showError"></error>
|
||||
<success v-else-if="showSuccess"></success>
|
||||
<replace v-else-if="showReplace"></replace>
|
||||
|
||||
<template v-for="plugin in plugins">
|
||||
<form class="prompt"
|
||||
|
@ -54,6 +55,7 @@ import Error from './Error'
|
|||
import Success from './Success'
|
||||
import NewFile from './NewFile'
|
||||
import NewDir from './NewDir'
|
||||
import Replace from './Replace'
|
||||
import { mapState } from 'vuex'
|
||||
import buttons from '@/utils/buttons'
|
||||
import api from '@/utils/api'
|
||||
|
@ -71,7 +73,8 @@ export default {
|
|||
Copy,
|
||||
NewFile,
|
||||
NewDir,
|
||||
Help
|
||||
Help,
|
||||
Replace
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
|
@ -96,6 +99,7 @@ export default {
|
|||
showNewFile: function () { return this.show === 'newFile' },
|
||||
showNewDir: function () { return this.show === 'newDir' },
|
||||
showDownload: function () { return this.show === 'download' },
|
||||
showReplace: function () { return this.show === 'replace' },
|
||||
showOverlay: function () {
|
||||
return (this.show !== null && this.show !== 'search' && this.show !== 'more')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
<div class="prompt">
|
||||
<h3>{{ $t('prompts.replace') }}</h3>
|
||||
<p>{{ $t('prompts.replaceMessage') }}</p>
|
||||
|
||||
<div>
|
||||
<button class="ok"
|
||||
@click="showConfirm"
|
||||
:aria-label="$t('buttons.replace')"
|
||||
:title="$t('buttons.replace')">{{ $t('buttons.replace') }}</button>
|
||||
<button class="cancel"
|
||||
@click="$store.commit('closeHovers')"
|
||||
:aria-label="$t('buttons.cancel')"
|
||||
:title="$t('buttons.cancel')">{{ $t('buttons.cancel') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'replace',
|
||||
computed: mapState(['showConfirm'])
|
||||
}
|
||||
</script>
|
|
@ -13,6 +13,7 @@ buttons:
|
|||
new: New
|
||||
next: Next
|
||||
ok: OK
|
||||
replace: Replace
|
||||
previous: Previous
|
||||
rename: Rename
|
||||
reportIssue: Report Issue
|
||||
|
@ -84,6 +85,10 @@ prompts:
|
|||
newFileMessage: Write the name of the new file.
|
||||
numberDirs: Number of directories
|
||||
numberFiles: Number of files
|
||||
replace: Replace
|
||||
replaceMessage: >
|
||||
One of the files you're trying to upload is conflicting because of its name.
|
||||
Do you wish to replace the existing one?
|
||||
rename: Rename
|
||||
renameMessage: Insert a new name for
|
||||
show: Show
|
||||
|
|
|
@ -15,6 +15,7 @@ buttons:
|
|||
ok: OK
|
||||
previous: Anterior
|
||||
rename: Renomear
|
||||
replace: Substituir
|
||||
reportIssue: Reportar Erro
|
||||
save: Guardar
|
||||
search: Pesquisar
|
||||
|
@ -86,6 +87,10 @@ prompts:
|
|||
numberFiles: Número de ficheiros
|
||||
rename: Renomear
|
||||
renameMessage: Insira um novo nome para
|
||||
replace: Substituir
|
||||
replaceMessage: >
|
||||
Já existe um ficheiro com nome igual a um dos que está a tentar enviar. Deseja
|
||||
substituir?
|
||||
show: Mostrar
|
||||
size: Tamanho
|
||||
settings:
|
||||
|
|
|
@ -20,7 +20,8 @@ const state = {
|
|||
selected: [],
|
||||
multiple: false,
|
||||
show: null,
|
||||
showMessage: null
|
||||
showMessage: null,
|
||||
showConfirm: null
|
||||
}
|
||||
|
||||
export default new Vuex.Store({
|
||||
|
|
|
@ -13,6 +13,7 @@ const mutations = {
|
|||
|
||||
state.show = value.prompt
|
||||
state.showMessage = value.message
|
||||
state.showConfirm = value.confirm
|
||||
},
|
||||
showError: (state, value) => {
|
||||
state.show = 'error'
|
||||
|
|
|
@ -4,9 +4,11 @@ const ssl = (window.location.protocol === 'https:')
|
|||
|
||||
export function removePrefix (url) {
|
||||
if (url.startsWith('/files')) {
|
||||
return url.slice(6)
|
||||
url = url.slice(6)
|
||||
}
|
||||
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
return url
|
||||
}
|
||||
|
||||
|
@ -54,7 +56,7 @@ export function rm (url) {
|
|||
})
|
||||
}
|
||||
|
||||
export function post (url, content = '') {
|
||||
export function post (url, content = '', overwrite = false) {
|
||||
url = removePrefix(url)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -62,15 +64,23 @@ export function post (url, content = '') {
|
|||
request.open('POST', `${store.state.baseURL}/api/resource${url}`, true)
|
||||
request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
|
||||
|
||||
if (overwrite) {
|
||||
request.setRequestHeader('Action', `override`)
|
||||
}
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
resolve(request.responseText)
|
||||
} else if (request.status === 409) {
|
||||
reject(request.status)
|
||||
} else {
|
||||
reject(request.responseText)
|
||||
}
|
||||
}
|
||||
|
||||
request.onerror = (error) => reject(error)
|
||||
request.onerror = (error) => {
|
||||
reject(error)
|
||||
}
|
||||
request.send(content)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -155,6 +156,12 @@ func resourcePostPutHandler(c *RequestContext, w http.ResponseWriter, r *http.Re
|
|||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
// Discard any invalid upload before returning to avoid connection
|
||||
// reset error.
|
||||
defer func() {
|
||||
io.Copy(ioutil.Discard, r.Body)
|
||||
}()
|
||||
|
||||
// Checks if the current request is for a directory and not a file.
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
// If the method is PUT, we return 405 Method not Allowed, because
|
||||
|
@ -171,7 +178,7 @@ func resourcePostPutHandler(c *RequestContext, w http.ResponseWriter, r *http.Re
|
|||
// If using POST method, we are trying to create a new file so it is not
|
||||
// desirable to override an already existent file. Thus, we check
|
||||
// if the file already exists. If so, we just return a 409 Conflict.
|
||||
if r.Method == http.MethodPost {
|
||||
if r.Method == http.MethodPost && r.Header.Get("Action") != "override" {
|
||||
if _, err := c.User.FileSystem.Stat(r.URL.Path); err == nil {
|
||||
return http.StatusConflict, errors.New("There is already a file on that path")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue