diff --git a/frontend/src/api/files.js b/frontend/src/api/files.js index e57d11f7..5942e71a 100644 --- a/frontend/src/api/files.js +++ b/frontend/src/api/files.js @@ -112,25 +112,25 @@ export async function post (url, content = '', overwrite = false, onupload) { }) } -function moveCopy (items, copy = false, overwrite = false) { +function moveCopy (items, copy = false, overwrite = false, rename = false) { let promises = [] for (let item of items) { const from = removePrefix(item.from) const to = encodeURIComponent(removePrefix(item.to)) - const url = `${from}?action=${copy ? 'copy' : 'rename'}&destination=${to}&override=${overwrite}` + const url = `${from}?action=${copy ? 'copy' : 'rename'}&destination=${to}&override=${overwrite}&rename=${rename}` promises.push(resourceAction(url, 'PATCH')) } return Promise.all(promises) } -export function move (items, overwrite = false) { - return moveCopy(items, false, overwrite) +export function move (items, overwrite = false, rename = false) { + return moveCopy(items, false, overwrite, rename) } -export function copy (items, overwrite = false) { - return moveCopy(items, true, overwrite) +export function copy (items, overwrite = false, rename = false) { + return moveCopy(items, true, overwrite, rename) } export async function checksum (url, algo) { diff --git a/frontend/src/components/files/Listing.vue b/frontend/src/components/files/Listing.vue index 3e1f9ba3..0ebc2a9c 100644 --- a/frontend/src/components/files/Listing.vue +++ b/frontend/src/components/files/Listing.vue @@ -268,15 +268,15 @@ export default { return } - let action = (overwrite) => { - api.copy(items, overwrite).then(() => { + let action = (overwrite, rename) => { + api.copy(items, overwrite, rename).then(() => { this.$store.commit('setReload', true) }).catch(this.$showError) } if (this.$store.state.clipboard.key === 'x') { - action = (overwrite) => { - api.move(items, overwrite).then(() => { + action = (overwrite, rename) => { + api.move(items, overwrite, rename).then(() => { this.$store.commit('setReload', true) }).catch(this.$showError) } @@ -284,20 +284,26 @@ export default { let conflict = upload.checkConflict(items, this.req.items) + let overwrite = false + let rename = false + if (conflict) { this.$store.commit('showHover', { - prompt: 'replace', - confirm: (event) => { + prompt: 'replace-rename', + confirm: (event, option) => { + overwrite = option == 'overwrite' + rename = option == 'rename' + event.preventDefault() this.$store.commit('closeHovers') - action(true) + action(overwrite, rename) } }) return } - action(false) + action(overwrite, rename) }, resizeEvent () { // Update the columns size based on the window width. diff --git a/frontend/src/components/files/ListingItem.vue b/frontend/src/components/files/ListingItem.vue index 7a0b512b..9b0c5c6c 100644 --- a/frontend/src/components/files/ListingItem.vue +++ b/frontend/src/components/files/ListingItem.vue @@ -138,28 +138,34 @@ export default { let path = this.$route.path + base let baseItems = (await api.fetch(path)).items - let action = (overwrite) => { - api.move(items, overwrite).then(() => { + let action = (overwrite, rename) => { + api.move(items, overwrite, rename).then(() => { this.$store.commit('setReload', true) }).catch(this.$showError) } let conflict = upload.checkConflict(items, baseItems) + let overwrite = false + let rename = false + if (conflict) { this.$store.commit('showHover', { - prompt: 'replace', - confirm: (event) => { + prompt: 'replace-rename', + confirm: (event, option) => { + overwrite = option == 'overwrite' + rename = option == 'rename' + event.preventDefault() this.$store.commit('closeHovers') - action(true) + action(overwrite, rename) } }) return } - action(false) + action(overwrite, rename) }, click: function (event) { if (this.selectedCount !== 0) event.preventDefault() diff --git a/frontend/src/components/prompts/Copy.vue b/frontend/src/components/prompts/Copy.vue index a548804d..d31b05df 100644 --- a/frontend/src/components/prompts/Copy.vue +++ b/frontend/src/components/prompts/Copy.vue @@ -54,10 +54,10 @@ export default { }) } - let action = async (overwrite) => { + let action = async (overwrite, rename) => { buttons.loading('copy') - await api.copy(items, overwrite).then(() => { + await api.copy(items, overwrite, rename).then(() => { buttons.success('copy') this.$router.push({ path: this.dest }) }).catch((e) => { @@ -69,20 +69,26 @@ export default { let dstItems = (await api.fetch(this.dest)).items let conflict = upload.checkConflict(items, dstItems) + let overwrite = false + let rename = false + if (conflict) { this.$store.commit('showHover', { - prompt: 'replace', - confirm: (event) => { + prompt: 'replace-rename', + confirm: (event, option) => { + overwrite = option == 'overwrite' + rename = option == 'rename' + event.preventDefault() this.$store.commit('closeHovers') - action(true) + action(overwrite, rename) } }) return } - action(false) + action(overwrite, rename) } } } diff --git a/frontend/src/components/prompts/Move.vue b/frontend/src/components/prompts/Move.vue index 57300df9..0dd23f0c 100644 --- a/frontend/src/components/prompts/Move.vue +++ b/frontend/src/components/prompts/Move.vue @@ -52,10 +52,10 @@ export default { }) } - let action = async (overwrite) => { + let action = async (overwrite, rename) => { buttons.loading('move') - await api.move(items, overwrite).then(() => { + await api.move(items, overwrite, rename).then(() => { buttons.success('move') this.$router.push({ path: this.dest }) }).catch((e) => { @@ -67,20 +67,26 @@ export default { let dstItems = (await api.fetch(this.dest)).items let conflict = upload.checkConflict(items, dstItems) + let overwrite = false + let rename = false + if (conflict) { this.$store.commit('showHover', { - prompt: 'replace', - confirm: (event) => { + prompt: 'replace-rename', + confirm: (event, option) => { + overwrite = option == 'overwrite' + rename = option == 'rename' + event.preventDefault() this.$store.commit('closeHovers') - action(true) + action(overwrite, rename) } }) return } - action(false) + action(overwrite, rename) } } } diff --git a/frontend/src/components/prompts/Prompts.vue b/frontend/src/components/prompts/Prompts.vue index 00ed511f..bb4a7c9b 100644 --- a/frontend/src/components/prompts/Prompts.vue +++ b/frontend/src/components/prompts/Prompts.vue @@ -16,6 +16,7 @@ import Copy from './Copy' import NewFile from './NewFile' import NewDir from './NewDir' import Replace from './Replace' +import ReplaceRename from './ReplaceRename' import Share from './Share' import Upload from './Upload' import { mapState } from 'vuex' @@ -35,6 +36,7 @@ export default { NewDir, Help, Replace, + ReplaceRename, Upload }, data: function () { @@ -52,7 +54,7 @@ export default { return let prompt = this.$refs.currentComponent; - + // Enter if (event.keyCode == 13) { switch (this.show) { @@ -87,6 +89,7 @@ export default { 'newDir', 'download', 'replace', + 'replace-rename', 'share', 'upload' ].indexOf(this.show) >= 0; diff --git a/frontend/src/components/prompts/ReplaceRename.vue b/frontend/src/components/prompts/ReplaceRename.vue new file mode 100644 index 00000000..058f5a5a --- /dev/null +++ b/frontend/src/components/prompts/ReplaceRename.vue @@ -0,0 +1,35 @@ + + + + {{ $t('prompts.replace') }} + + + + {{ $t('prompts.replaceMessage') }} + + + + {{ $t('buttons.cancel') }} + showConfirm(event, 'rename')" + :aria-label="$t('buttons.rename')" + :title="$t('buttons.rename')">{{ $t('buttons.rename') }} + showConfirm(event, 'overwrite')" + :aria-label="$t('buttons.replace')" + :title="$t('buttons.replace')">{{ $t('buttons.replace') }} + + + + + diff --git a/frontend/src/css/_buttons.css b/frontend/src/css/_buttons.css index 79dab790..087c6286 100644 --- a/frontend/src/css/_buttons.css +++ b/frontend/src/css/_buttons.css @@ -25,8 +25,8 @@ background: var(--red); } -.button--red:hover { - background: var(--dark-red); +.button--blue { + background: var(--blue); } .button--flat { diff --git a/http/resource.go b/http/resource.go index e3859549..20376968 100644 --- a/http/resource.go +++ b/http/resource.go @@ -148,12 +148,31 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request, return http.StatusForbidden, nil } - if r.URL.Query().Get("override") != "true" { - if _, err := d.user.Fs.Stat(dst); err == nil { + override := r.URL.Query().Get("override") == "true" + rename := r.URL.Query().Get("rename") == "true" + + if !override && !rename { + if _, err = d.user.Fs.Stat(dst); err == nil { return http.StatusConflict, nil } } + if rename { + counter := 1 + dir, name := filepath.Split(dst) + ext := filepath.Ext(name) + base := strings.TrimSuffix(name, ext) + + for { + if _, err = d.user.Fs.Stat(dst); err != nil { + break + } + new := fmt.Sprintf("%s(%d)%s", base, counter, ext) + dst = filepath.Join(dir, new) + counter++ + } + } + err = d.RunHook(func() error { switch action { // TODO: use enum
{{ $t('prompts.replaceMessage') }}