diff options
author | Morris Jobke <hey@morrisjobke.de> | 2016-11-03 14:21:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-11-03 14:21:35 +0100 |
commit | ae5eb795932f6ee72f249cfe82cef813d80553c0 (patch) | |
tree | 731c00b93afe21f40cca379b80fe08c7b7479297 /apps/files | |
parent | 7ba7949dc12cc444399a3681904a3d0dd2d2ee32 (diff) | |
parent | 6a4ea2c15adb254291b095cfe21818aa28c26138 (diff) | |
download | nextcloud-server-ae5eb795932f6ee72f249cfe82cef813d80553c0.tar.gz nextcloud-server-ae5eb795932f6ee72f249cfe82cef813d80553c0.zip |
Merge pull request #1908 from nextcloud/downstream-26078
Upload autorename on client side
Diffstat (limited to 'apps/files')
-rw-r--r-- | apps/files/js/file-upload.js | 85 | ||||
-rw-r--r-- | apps/files/js/templates/detailsview.handlebars.js | 28 | ||||
-rw-r--r-- | apps/files/tests/js/fileUploadSpec.js | 61 |
3 files changed, 139 insertions, 35 deletions
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 18d0d973586..8bcaa2e24aa 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -100,26 +100,14 @@ OC.FileUpload.prototype = { /** * Return the final filename. - * Either this is the original file name or the file name - * after an autorename. * * @return {String} file name */ getFileName: function() { - // in case of autorename + // autorenamed name if (this._newName) { return this._newName; } - - if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { - - var locationUrl = this.getResponseHeader('Content-Location'); - if (locationUrl) { - this._newName = decodeURIComponent(OC.basename(locationUrl)); - return this._newName; - } - } - return this.getFile().name; }, @@ -142,8 +130,19 @@ OC.FileUpload.prototype = { }, /** + * Returns conflict resolution mode. + * + * @return {int} conflict mode + */ + getConflictMode: function() { + return this._conflictMode || OC.FileUpload.CONFLICT_MODE_DETECT; + }, + + /** * Set conflict resolution mode. * See CONFLICT_MODE_* constants. + * + * @param {int} mode conflict mode */ setConflictMode: function(mode) { this._conflictMode = mode; @@ -163,6 +162,30 @@ OC.FileUpload.prototype = { }, /** + * Trigger autorename and append "(2)". + * Multiple calls will increment the appended number. + */ + autoRename: function() { + var name = this.getFile().name; + if (!this._renameAttempt) { + this._renameAttempt = 1; + } + + var dotPos = name.lastIndexOf('.'); + var extPart = ''; + if (dotPos > 0) { + this._newName = name.substr(0, dotPos); + extPart = name.substr(dotPos); + } else { + this._newName = name; + } + + // generate new name + this._renameAttempt++; + this._newName = this._newName + ' (' + this._renameAttempt + ')' + extPart; + }, + + /** * Submit the upload */ submit: function() { @@ -179,7 +202,7 @@ OC.FileUpload.prototype = { } if (this.uploader.fileList) { - this.data.url = this.uploader.fileList.getUploadUrl(file.name, this.getFullPath()); + this.data.url = this.uploader.fileList.getUploadUrl(this.getFileName(), this.getFullPath()); } if (!this.data.headers) { @@ -191,12 +214,9 @@ OC.FileUpload.prototype = { this.data.type = 'PUT'; delete this.data.headers['If-None-Match']; - if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT) { + if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT + || this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { this.data.headers['If-None-Match'] = '*'; - } else if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { - // POST to parent folder, with slug - this.data.type = 'POST'; - this.data.url = this.uploader.fileList.getUploadUrl('&' + file.name, this.getFullPath()); } if (file.lastModified) { @@ -212,18 +232,6 @@ OC.FileUpload.prototype = { 'Basic ' + btoa(userName + ':' + (password || '')); } - if (!this.uploader.isXHRUpload()) { - data.formData = []; - - // pass headers as parameters - data.formData.push({name: 'headers', value: JSON.stringify(this.data.headers)}); - data.formData.push({name: 'requesttoken', value: OC.requestToken}); - if (data.type === 'POST') { - // still add the method to the URL - data.url += '?_method=POST'; - } - } - var chunkFolderPromise; if ($.support.blobSlice && this.uploader.fileUploadParam.maxChunkSize @@ -491,6 +499,14 @@ OC.Uploader.prototype = _.extend({ //show "file already exists" dialog var self = this; var file = fileUpload.getFile(); + // already attempted autorename but the server said the file exists ? (concurrently added) + if (fileUpload.getConflictMode() === OC.FileUpload.CONFLICT_MODE_AUTORENAME) { + // attempt another autorename, defer to let the current callback finish + _.defer(function() { + self.onAutorename(fileUpload); + }); + return; + } // retrieve more info about this file this.filesClient.getFileInfo(fileUpload.getFullPath()).then(function(status, fileInfo) { var original = fileInfo; @@ -603,6 +619,13 @@ OC.Uploader.prototype = _.extend({ onAutorename:function(upload) { this.log('autorename', null, upload); upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_AUTORENAME); + + do { + upload.autoRename(); + // if file known to exist on the client side, retry + } while (this.fileList && this.fileList.inList(upload.getFileName())); + + // resubmit upload this.submitUploads([upload]); }, _trace:false, //TODO implement log handler for JS per class? diff --git a/apps/files/js/templates/detailsview.handlebars.js b/apps/files/js/templates/detailsview.handlebars.js new file mode 100644 index 00000000000..c109da77a63 --- /dev/null +++ b/apps/files/js/templates/detailsview.handlebars.js @@ -0,0 +1,28 @@ +(function() { + var template = Handlebars.template, templates = OCA.Files.Templates = OCA.Files.Templates || {}; +templates['detailsview'] = template({"1":function(container,depth0,helpers,partials,data) { + var stack1; + + return "<ul class=\"tabHeaders\">\n" + + ((stack1 = helpers.each.call(depth0 != null ? depth0 : {},(depth0 != null ? depth0.tabHeaders : depth0),{"name":"each","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + "</ul>\n"; +},"2":function(container,depth0,helpers,partials,data) { + var helper, alias1=depth0 != null ? depth0 : {}, alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression; + + return " <li class=\"tabHeader\" data-tabid=\"" + + alias4(((helper = (helper = helpers.tabId || (depth0 != null ? depth0.tabId : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"tabId","hash":{},"data":data}) : helper))) + + "\" data-tabindex=\"" + + alias4(((helper = (helper = helpers.tabIndex || (depth0 != null ? depth0.tabIndex : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"tabIndex","hash":{},"data":data}) : helper))) + + "\">\n <a href=\"#\">" + + alias4(((helper = (helper = helpers.label || (depth0 != null ? depth0.label : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"label","hash":{},"data":data}) : helper))) + + "</a>\n </li>\n"; +},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { + var stack1, helper, alias1=depth0 != null ? depth0 : {}; + + return "<div class=\"detailFileInfoContainer\"></div>\n" + + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.tabHeaders : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "") + + "<div class=\"tabsContainer\">\n</div>\n<a class=\"close icon-close\" href=\"#\" alt=\"" + + container.escapeExpression(((helper = (helper = helpers.closeLabel || (depth0 != null ? depth0.closeLabel : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(alias1,{"name":"closeLabel","hash":{},"data":data}) : helper))) + + "\"></a>\n"; +},"useData":true}); +})(); diff --git a/apps/files/tests/js/fileUploadSpec.js b/apps/files/tests/js/fileUploadSpec.js index fa686dbf3e2..81a0a2df610 100644 --- a/apps/files/tests/js/fileUploadSpec.js +++ b/apps/files/tests/js/fileUploadSpec.js @@ -144,6 +144,10 @@ describe('OC.Upload tests', function() { uploader = new OC.Uploader($dummyUploader, { fileList: fileList }); + + var deferred = $.Deferred(); + conflictDialogStub.returns(deferred.promise()); + deferred.resolve(); }); afterEach(function() { conflictDialogStub.restore(); @@ -158,10 +162,6 @@ describe('OC.Upload tests', function() { expect(result[1].submit.calledOnce).toEqual(true); }); it('shows conflict dialog when no client side conflict', function() { - var deferred = $.Deferred(); - conflictDialogStub.returns(deferred.promise()); - deferred.resolve(); - var result = addFiles(uploader, [ {name: 'conflict.txt'}, {name: 'conflict2.txt'}, @@ -185,5 +185,58 @@ describe('OC.Upload tests', function() { expect(result[1].submit.calledOnce).toEqual(false); expect(result[2].submit.calledOnce).toEqual(true); }); + it('cancels upload when skipping file in conflict mode', function() { + var fileData = {name: 'conflict.txt'}; + var uploadData = addFiles(uploader, [ + fileData + ]); + + var upload = new OC.FileUpload(uploader, uploadData[0]); + var deleteStub = sinon.stub(upload, 'deleteUpload'); + + uploader.onSkip(upload); + expect(deleteStub.calledOnce).toEqual(true); + }); + it('overwrites file when choosing replace in conflict mode', function() { + var fileData = {name: 'conflict.txt'}; + var uploadData = addFiles(uploader, [ + fileData + ]); + + expect(uploadData[0].submit.notCalled).toEqual(true); + + var upload = new OC.FileUpload(uploader, uploadData[0]); + + uploader.onReplace(upload); + expect(upload.getConflictMode()).toEqual(OC.FileUpload.CONFLICT_MODE_OVERWRITE); + expect(uploadData[0].submit.calledOnce).toEqual(true); + }); + it('autorenames file when choosing replace in conflict mode', function() { + // needed for _.defer call + var clock = sinon.useFakeTimers(); + var fileData = {name: 'conflict.txt'}; + var uploadData = addFiles(uploader, [ + fileData + ]); + + expect(uploadData[0].submit.notCalled).toEqual(true); + + var upload = new OC.FileUpload(uploader, uploadData[0]); + var getResponseStatusStub = sinon.stub(upload, 'getResponseStatus'); + + uploader.onAutorename(upload); + expect(upload.getConflictMode()).toEqual(OC.FileUpload.CONFLICT_MODE_AUTORENAME); + expect(upload.getFileName()).toEqual('conflict (2).txt'); + expect(uploadData[0].submit.calledOnce).toEqual(true); + + // in case of server-side conflict, tries to rename again + getResponseStatusStub.returns(412); + uploader.fileUploadParam.fail.call($dummyUploader[0], {}, uploadData[0]); + clock.tick(500); + expect(upload.getFileName()).toEqual('conflict (3).txt'); + expect(uploadData[0].submit.calledTwice).toEqual(true); + + clock.restore(); + }); }); }); |