summaryrefslogtreecommitdiffstats
path: root/apps/files
diff options
context:
space:
mode:
authorMorris Jobke <hey@morrisjobke.de>2016-11-03 14:21:35 +0100
committerGitHub <noreply@github.com>2016-11-03 14:21:35 +0100
commitae5eb795932f6ee72f249cfe82cef813d80553c0 (patch)
tree731c00b93afe21f40cca379b80fe08c7b7479297 /apps/files
parent7ba7949dc12cc444399a3681904a3d0dd2d2ee32 (diff)
parent6a4ea2c15adb254291b095cfe21818aa28c26138 (diff)
downloadnextcloud-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.js85
-rw-r--r--apps/files/js/templates/detailsview.handlebars.js28
-rw-r--r--apps/files/tests/js/fileUploadSpec.js61
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();
+ });
});
});