From 246a5a5750dd2b8f990797b2c6cc4270a9a5b59d Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Sun, 27 Aug 2017 15:28:26 +0200 Subject: [PATCH] Allow files to be copied through action menu & multiple files actions Signed-off-by: Thomas Citharel --- apps/files/js/fileactions.js | 14 ++++++ apps/files/js/filelist.js | 86 +++++++++++++++++++++++++++++++++++- core/js/files/client.js | 45 +++++++++++++++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 3e0cf99825..527cbc0bd3 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -631,6 +631,20 @@ } }); + this.registerAction({ + name: 'Copy', + displayName: t('files', 'Copy'), + mime: 'all', + order: -25, + permissions: OC.PERMISSION_READ, + iconClass: 'icon-external', + actionHandler: function (filename, context) { + OC.dialogs.filepicker(t('files', 'Target folder'), function(targetPath) { + context.fileList.copy(filename, targetPath); + }, false, "httpd/unix-directory", true); + } + }); + this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory(); context.fileList.changeDirectory(OC.joinPaths(dir, filename), true, false, parseInt(context.$file.attr('data-id'), 10)); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 9e4d3983ea..946ac12548 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -337,7 +337,11 @@ this.$el.on('urlChanged', _.bind(this._onUrlChanged, this)); this.$el.find('.select-all').click(_.bind(this._onClickSelectAll, this)); this.$el.find('.download').click(_.bind(this._onClickDownloadSelected, this)); +<<<<<<< HEAD this.$el.find('.move').click(_.bind(this._onClickMoveSelected, this)); +======= + this.$el.find('.copy').click(_.bind(this._onClickCopySelected, this)); +>>>>>>> Allow files to be copied through action menu & multiple files actions this.$el.find('.delete-selected').click(_.bind(this._onClickDeleteSelected, this)); this.$el.find('.selectedActions a').tooltip({placement:'top'}); @@ -786,7 +790,6 @@ return false; }, - /** * Event handler for when clicking on "Delete" for the selected files */ @@ -1974,6 +1977,7 @@ } return index; }, + /** * Moves a file to a given target folder. * @@ -2037,6 +2041,86 @@ }, + /** + * Copies a file to a given target folder. + * + * @param fileNames array of file names to copy + * @param targetPath absolute target path + * @param callback to call when copy is finished with success + */ + copy: function(fileNames, targetPath, callback) { + var self = this; + var dir = this.getCurrentDirectory(); + if (dir.charAt(dir.length - 1) !== '/') { + dir += '/'; + } + if (!_.isArray(fileNames)) { + fileNames = [fileNames]; + } + _.each(fileNames, function(fileName) { + var $tr = self.findFileEl(fileName); + self.showFileBusyState($tr, true); + if (targetPath.charAt(targetPath.length - 1) !== '/') { + // make sure we move the files into the target dir, + // not overwrite it + targetPath = targetPath + '/'; + } + self.filesClient.copy(dir + fileName, targetPath + fileName) + .fail(function(status) { + if (status === 412) { + // TODO: some day here we should invoke the conflict dialog + OC.Notification.show(t('files', 'Could not copy "{file}", target exists', + {file: fileName}), {type: 'error'} + ); + } else { + OC.Notification.show(t('files', 'Could not copy "{file}"', + {file: fileName}), {type: 'error'} + ); + } + }) + .always(function() { + self.showFileBusyState($tr, false); + }); + }); + + // Remove leading and ending / + if (targetPath.slice(0, 1) === '/') { + targetPath = targetPath.slice(1, targetPath.length); + } + if (targetPath.slice(-1) === '/') { + targetPath = targetPath.slice(0, -1); + } + + // Since there's no visual indication that the files were copied, let's send some notifications ! + if (fileNames.length === 1) { + OC.Notification.show(t('files', 'Copied {origin} inside {destination}', + { + origin: fileNames[0], + destination: targetPath + } + ), {timeout: 10}); + } else if (fileNames.length < 4) { + OC.Notification.show(t('files', 'Copied {origin} inside {destination}', + { + origin: fileNames.join(', '), + destination: targetPath + } + ), {timeout: 10}); + } else { + OC.Notification.show(t('files', 'Copied {origin} and {nbfiles} other files inside {destination}', + { + origin: fileNames[0], + nbfiles: fileNames.length, + destination: targetPath, + } + ), {timeout: 10}); + } + + if (callback) { + callback(); + } + }, + /** * Updates the given row with the given file info * diff --git a/core/js/files/client.js b/core/js/files/client.js index 176cabf04b..dc9f6ade64 100644 --- a/core/js/files/client.js +++ b/core/js/files/client.js @@ -736,6 +736,51 @@ return promise; }, + /** + * Copies path to another path + * + * @param {String} path path to copy + * @param {String} destinationPath destination path + * @param {boolean} [allowOverwrite=false] true to allow overwriting, + * false otherwise + * + * @return {Promise} promise + */ + copy: function (path, destinationPath, allowOverwrite) { + if (!path) { + throw 'Missing argument "path"'; + } + if (!destinationPath) { + throw 'Missing argument "destinationPath"'; + } + + var self = this; + var deferred = $.Deferred(); + var promise = deferred.promise(); + var headers = { + 'Destination' : this._buildUrl(destinationPath) + }; + + if (!allowOverwrite) { + headers.Overwrite = 'F'; + } + + this._client.request( + 'COPY', + this._buildUrl(path), + headers + ).then( + function(response) { + if (self._isSuccessStatus(response.status)) { + deferred.resolve(response.status); + } else { + deferred.reject(response.status); + } + } + ); + return promise; + }, + /** * Add a file info parser function *