summaryrefslogtreecommitdiffstats
path: root/apps/files/js
diff options
context:
space:
mode:
authorLukas Reschke <lukas@statuscode.ch>2016-10-25 10:31:03 +0200
committerGitHub <noreply@github.com>2016-10-25 10:31:03 +0200
commit79706e0ddc6ab970d5709e89b8d0caec4d34662b (patch)
tree168f9bc806e7eed287bce63e7f6d277eb5adb956 /apps/files/js
parent5926da3dd6535e0eea7fe7871d2347f8b33bb337 (diff)
parentc8a13f644ebbc5840d0e632cf86e5ae46856f7f0 (diff)
downloadnextcloud-server-79706e0ddc6ab970d5709e89b8d0caec4d34662b.tar.gz
nextcloud-server-79706e0ddc6ab970d5709e89b8d0caec4d34662b.zip
Merge pull request #1283 from nextcloud/us_files-ui-webdav-upload
Use Webdav PUT for uploads
Diffstat (limited to 'apps/files/js')
-rw-r--r--apps/files/js/app.js1
-rw-r--r--apps/files/js/file-upload.js901
-rw-r--r--apps/files/js/filelist.js338
-rw-r--r--apps/files/js/files.js11
4 files changed, 831 insertions, 420 deletions
diff --git a/apps/files/js/app.js b/apps/files/js/app.js
index fbfa510e07e..17e92de90dd 100644
--- a/apps/files/js/app.js
+++ b/apps/files/js/app.js
@@ -93,6 +93,7 @@
direction: $('#defaultFileSortingDirection').val()
},
config: this._filesConfig,
+ enableUpload: true
}
);
this.files.initialize();
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index 56ea384c9e0..30784528700 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -18,83 +18,518 @@
* - TODO music upload button
*/
-/* global jQuery, oc_requesttoken, humanFileSize, FileList */
+/* global jQuery, humanFileSize, md5 */
/**
- * Function that will allow us to know if Ajax uploads are supported
- * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html
- * also see article @link http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata
+ * File upload object
+ *
+ * @class OC.FileUpload
+ * @classdesc
+ *
+ * Represents a file upload
+ *
+ * @param {OC.Uploader} uploader uploader
+ * @param {Object} data blueimp data
*/
-function supportAjaxUploadWithProgress() {
- return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData();
-
- // Is the File API supported?
- function supportFileAPI() {
- var fi = document.createElement('INPUT');
- fi.type = 'file';
- return 'files' in fi;
+OC.FileUpload = function(uploader, data) {
+ this.uploader = uploader;
+ this.data = data;
+ var path = '';
+ if (this.uploader.fileList) {
+ path = OC.joinPaths(this.uploader.fileList.getCurrentDirectory(), this.getFile().name);
+ } else {
+ path = this.getFile().name;
}
+ this.id = 'web-file-upload-' + md5(path) + '-' + (new Date()).getTime();
+};
+OC.FileUpload.CONFLICT_MODE_DETECT = 0;
+OC.FileUpload.CONFLICT_MODE_OVERWRITE = 1;
+OC.FileUpload.CONFLICT_MODE_AUTORENAME = 2;
+OC.FileUpload.prototype = {
- // Are progress events supported?
- function supportAjaxUploadProgressEvents() {
- var xhr = new XMLHttpRequest();
- return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload));
- }
+ /**
+ * Unique upload id
+ *
+ * @type string
+ */
+ id: null,
- // Is FormData supported?
- function supportFormData() {
- return !! window.FormData;
- }
-}
+ /**
+ * Upload element
+ *
+ * @type Object
+ */
+ $uploadEl: null,
-/**
- * Add form data into the given form data
- *
- * @param {Array|Object} formData form data which can either be an array or an object
- * @param {Object} newData key-values to add to the form data
- *
- * @return updated form data
- */
-function addFormData(formData, newData) {
- // in IE8, formData is an array instead of object
- if (_.isArray(formData)) {
- _.each(newData, function(value, key) {
- formData.push({name: key, value: value});
+ /**
+ * Target folder
+ *
+ * @type string
+ */
+ _targetFolder: '',
+
+ /**
+ * @type int
+ */
+ _conflictMode: OC.FileUpload.CONFLICT_MODE_DETECT,
+
+ /**
+ * New name from server after autorename
+ *
+ * @type String
+ */
+ _newName: null,
+
+ /**
+ * Returns the unique upload id
+ *
+ * @return string
+ */
+ getId: function() {
+ return this.id;
+ },
+
+ /**
+ * Returns the file to be uploaded
+ *
+ * @return {File} file
+ */
+ getFile: function() {
+ return this.data.files[0];
+ },
+
+ /**
+ * 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
+ 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;
+ },
+
+ setTargetFolder: function(targetFolder) {
+ this._targetFolder = targetFolder;
+ },
+
+ getTargetFolder: function() {
+ return this._targetFolder;
+ },
+
+ /**
+ * Get full path for the target file, including relative path,
+ * without the file name.
+ *
+ * @return {String} full path
+ */
+ getFullPath: function() {
+ return OC.joinPaths(this._targetFolder, this.getFile().relativePath || '');
+ },
+
+ /**
+ * Set conflict resolution mode.
+ * See CONFLICT_MODE_* constants.
+ */
+ setConflictMode: function(mode) {
+ this._conflictMode = mode;
+ },
+
+ /**
+ * Returns whether the upload is in progress
+ *
+ * @return {bool}
+ */
+ isPending: function() {
+ return this.data.state() === 'pending';
+ },
+
+ deleteUpload: function() {
+ delete this.data.jqXHR;
+ },
+
+ /**
+ * Submit the upload
+ */
+ submit: function() {
+ var self = this;
+ var data = this.data;
+ var file = this.getFile();
+
+ // it was a folder upload, so make sure the parent directory exists alrady
+ var folderPromise;
+ if (file.relativePath) {
+ folderPromise = this.uploader.ensureFolderExists(this.getFullPath());
+ } else {
+ folderPromise = $.Deferred().resolve().promise();
+ }
+
+ if (this.uploader.fileList) {
+ this.data.url = this.uploader.fileList.getUploadUrl(file.name, this.getFullPath());
+ }
+
+ if (!this.data.headers) {
+ this.data.headers = {};
+ }
+
+ // webdav without multipart
+ this.data.multipart = false;
+ this.data.type = 'PUT';
+
+ delete this.data.headers['If-None-Match'];
+ if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT) {
+ 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) {
+ // preserve timestamp
+ this.data.headers['X-OC-Mtime'] = file.lastModified / 1000;
+ }
+
+ var userName = this.uploader.filesClient.getUserName();
+ var password = this.uploader.filesClient.getPassword();
+ if (userName) {
+ // copy username/password from DAV client
+ this.data.headers['Authorization'] =
+ '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
+ && this.getFile().size > this.uploader.fileUploadParam.maxChunkSize
+ ) {
+ data.isChunked = true;
+ chunkFolderPromise = this.uploader.filesClient.createDirectory(
+ 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId())
+ );
+ // TODO: if fails, it means same id already existed, need to retry
+ } else {
+ chunkFolderPromise = $.Deferred().resolve().promise();
+ }
+
+ // wait for creation of the required directory before uploading
+ $.when(folderPromise, chunkFolderPromise).then(function() {
+ data.submit();
+ }, function() {
+ self.abort();
});
- } else {
- formData = _.extend(formData, newData);
+
+ },
+
+ /**
+ * Process end of transfer
+ */
+ done: function() {
+ if (!this.data.isChunked) {
+ return $.Deferred().resolve().promise();
+ }
+
+ var uid = OC.getCurrentUser().uid;
+ return this.uploader.filesClient.move(
+ 'uploads/' + encodeURIComponent(uid) + '/' + encodeURIComponent(this.getId()) + '/.file',
+ 'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName())
+ );
+ },
+
+ /**
+ * Abort the upload
+ */
+ abort: function() {
+ if (this.data.isChunked) {
+ // delete transfer directory for this upload
+ this.uploader.filesClient.remove(
+ 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId())
+ );
+ }
+ this.data.abort();
+ },
+
+ /**
+ * Returns the server response
+ *
+ * @return {Object} response
+ */
+ getResponse: function() {
+ var response = this.data.response();
+ if (typeof response.result !== 'string') {
+ //fetch response from iframe
+ response = $.parseJSON(response.result[0].body.innerText);
+ if (!response) {
+ // likely due to internal server error
+ response = {status: 500};
+ }
+ } else {
+ response = response.result;
+ }
+ return response;
+ },
+
+ /**
+ * Returns the status code from the response
+ *
+ * @return {int} status code
+ */
+ getResponseStatus: function() {
+ if (this.uploader.isXHRUpload()) {
+ var xhr = this.data.response().jqXHR;
+ if (xhr) {
+ return xhr.status;
+ }
+ return null;
+ }
+ return this.getResponse().status;
+ },
+
+ /**
+ * Returns the response header by name
+ *
+ * @param {String} headerName header name
+ * @return {Array|String} response header value(s)
+ */
+ getResponseHeader: function(headerName) {
+ headerName = headerName.toLowerCase();
+ if (this.uploader.isXHRUpload()) {
+ return this.data.response().jqXHR.getResponseHeader(headerName);
+ }
+
+ var headers = this.getResponse().headers;
+ if (!headers) {
+ return null;
+ }
+
+ var value = _.find(headers, function(value, key) {
+ return key.toLowerCase() === headerName;
+ });
+ if (_.isArray(value) && value.length === 1) {
+ return value[0];
+ }
+ return value;
}
- return formData;
-}
+};
/**
* keeps track of uploads in progress and implements callbacks for the conflicts dialog
* @namespace
*/
-OC.Upload = {
- _uploads: [],
+
+OC.Uploader = function() {
+ this.init.apply(this, arguments);
+};
+
+OC.Uploader.prototype = _.extend({
+ /**
+ * @type Array<OC.FileUpload>
+ */
+ _uploads: {},
+
+ /**
+ * List of directories known to exist.
+ *
+ * Key is the fullpath and value is boolean, true meaning that the directory
+ * was already created so no need to create it again.
+ */
+ _knownDirs: {},
+
+ /**
+ * @type OCA.Files.FileList
+ */
+ fileList: null,
+
+ /**
+ * @type OC.Files.Client
+ */
+ filesClient: null,
+
+ /**
+ * Function that will allow us to know if Ajax uploads are supported
+ * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html
+ * also see article @link http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata
+ */
+ _supportAjaxUploadWithProgress: function() {
+ if (window.TESTING) {
+ return true;
+ }
+ return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData();
+
+ // Is the File API supported?
+ function supportFileAPI() {
+ var fi = document.createElement('INPUT');
+ fi.type = 'file';
+ return 'files' in fi;
+ }
+
+ // Are progress events supported?
+ function supportAjaxUploadProgressEvents() {
+ var xhr = new XMLHttpRequest();
+ return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload));
+ }
+
+ // Is FormData supported?
+ function supportFormData() {
+ return !! window.FormData;
+ }
+ },
+
+ /**
+ * Returns whether an XHR upload will be used
+ *
+ * @return {bool} true if XHR upload will be used,
+ * false for iframe upload
+ */
+ isXHRUpload: function () {
+ return !this.fileUploadParam.forceIframeTransport &&
+ ((!this.fileUploadParam.multipart && $.support.xhrFileUpload) ||
+ $.support.xhrFormDataFileUpload);
+ },
+
/**
- * deletes the jqHXR object from a data selection
- * @param {object} data
+ * Makes sure that the upload folder and its parents exists
+ *
+ * @param {String} fullPath full path
+ * @return {Promise} promise that resolves when all parent folders
+ * were created
*/
- deleteUpload:function(data) {
- delete data.jqXHR;
+ ensureFolderExists: function(fullPath) {
+ if (!fullPath || fullPath === '/') {
+ return $.Deferred().resolve().promise();
+ }
+
+ // remove trailing slash
+ if (fullPath.charAt(fullPath.length - 1) === '/') {
+ fullPath = fullPath.substr(0, fullPath.length - 1);
+ }
+
+ var self = this;
+ var promise = this._knownDirs[fullPath];
+
+ if (this.fileList) {
+ // assume the current folder exists
+ this._knownDirs[this.fileList.getCurrentDirectory()] = $.Deferred().resolve().promise();
+ }
+
+ if (!promise) {
+ var deferred = new $.Deferred();
+ promise = deferred.promise();
+ this._knownDirs[fullPath] = promise;
+
+ // make sure all parents already exist
+ var parentPath = OC.dirname(fullPath);
+ var parentPromise = this._knownDirs[parentPath];
+ if (!parentPromise) {
+ parentPromise = this.ensureFolderExists(parentPath);
+ }
+
+ parentPromise.then(function() {
+ self.filesClient.createDirectory(fullPath).always(function(status) {
+ // 405 is expected if the folder already exists
+ if ((status >= 200 && status < 300) || status === 405) {
+ self.trigger('createdfolder', fullPath);
+ deferred.resolve();
+ return;
+ }
+ OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: fullPath}));
+ deferred.reject();
+ });
+ }, function() {
+ deferred.reject();
+ });
+ }
+
+ return promise;
+ },
+
+ /**
+ * Submit the given uploads
+ *
+ * @param {Array} array of uploads to start
+ */
+ submitUploads: function(uploads) {
+ var self = this;
+ _.each(uploads, function(upload) {
+ self._uploads[upload.data.uploadId] = upload;
+ upload.submit();
+ });
+ },
+
+ /**
+ * Show conflict for the given file object
+ *
+ * @param {OC.FileUpload} file upload object
+ */
+ showConflict: function(fileUpload) {
+ //show "file already exists" dialog
+ var self = this;
+ var file = fileUpload.getFile();
+ // retrieve more info about this file
+ this.filesClient.getFileInfo(fileUpload.getFullPath()).then(function(status, fileInfo) {
+ var original = fileInfo;
+ var replacement = file;
+ OC.dialogs.fileexists(fileUpload, original, replacement, self);
+ });
},
/**
* cancels all uploads
*/
cancelUploads:function() {
this.log('canceling uploads');
- jQuery.each(this._uploads, function(i, jqXHR) {
- jqXHR.abort();
+ jQuery.each(this._uploads, function(i, upload) {
+ upload.abort();
});
- this._uploads = [];
+ this.clear();
},
- rememberUpload:function(jqXHR) {
- if (jqXHR) {
- this._uploads.push(jqXHR);
+ /**
+ * Clear uploads
+ */
+ clear: function() {
+ this._uploads = {};
+ this._knownDirs = {};
+ },
+ /**
+ * Returns an upload by id
+ *
+ * @param {int} data uploadId
+ * @return {OC.FileUpload} file upload
+ */
+ getUpload: function(data) {
+ if (_.isString(data)) {
+ return this._uploads[data];
+ } else if (data.uploadId) {
+ return this._uploads[data.uploadId];
}
+ return null;
},
+
showUploadCancelMessage: _.debounce(function() {
OC.Notification.showTemporary(t('files', 'Upload cancelled.'), {timeout: 10});
}, 500),
@@ -106,8 +541,8 @@ OC.Upload = {
isProcessing:function() {
var count = 0;
- jQuery.each(this._uploads, function(i, data) {
- if (data.state() === 'pending') {
+ jQuery.each(this._uploads, function(i, upload) {
+ if (upload.isPending()) {
count++;
}
});
@@ -115,9 +550,8 @@ OC.Upload = {
},
/**
* callback for the conflicts dialog
- * @param {object} data
*/
- onCancel:function(data) {
+ onCancel:function() {
this.cancelUploads();
},
/**
@@ -147,43 +581,29 @@ OC.Upload = {
},
/**
* handle skipping an upload
- * @param {object} data
+ * @param {OC.FileUpload} upload
*/
- onSkip:function(data) {
- this.log('skip', null, data);
- this.deleteUpload(data);
+ onSkip:function(upload) {
+ this.log('skip', null, upload);
+ upload.deleteUpload();
},
/**
* handle replacing a file on the server with an uploaded file
- * @param {object} data
+ * @param {FileUpload} data
*/
- onReplace:function(data) {
- this.log('replace', null, data);
- if (data.data) {
- data.data.append('resolution', 'replace');
- } else {
- if (!data.formData) {
- data.formData = {};
- }
- addFormData(data.formData, {resolution: 'replace'});
- }
- data.submit();
+ onReplace:function(upload) {
+ this.log('replace', null, upload);
+ upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_OVERWRITE);
+ this.submitUploads([upload]);
},
/**
* handle uploading a file and letting the server decide a new name
- * @param {object} data
+ * @param {object} upload
*/
- onAutorename:function(data) {
- this.log('autorename', null, data);
- if (data.data) {
- data.data.append('resolution', 'autorename');
- } else {
- if (!data.formData) {
- data.formData = {};
- }
- addFormData(data.formData, {resolution: 'autorename'});
- }
- data.submit();
+ onAutorename:function(upload) {
+ this.log('autorename', null, upload);
+ upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_AUTORENAME);
+ this.submitUploads([upload]);
},
_trace:false, //TODO implement log handler for JS per class?
log:function(caption, e, data) {
@@ -205,11 +625,20 @@ OC.Upload = {
* @param {function} callbacks.onCancel
*/
checkExistingFiles: function (selection, callbacks) {
- var fileList = FileList;
+ var fileList = this.fileList;
var conflicts = [];
// only keep non-conflicting uploads
selection.uploads = _.filter(selection.uploads, function(upload) {
- var fileInfo = fileList.findFile(upload.files[0].name);
+ var file = upload.getFile();
+ if (file.relativePath) {
+ // can't check in subfolder contents
+ return true;
+ }
+ if (!fileList) {
+ // no list to check against
+ return true;
+ }
+ var fileInfo = fileList.findFile(file.name);
if (fileInfo) {
conflicts.push([
// original
@@ -225,9 +654,9 @@ OC.Upload = {
});
if (conflicts.length) {
// wait for template loading
- OC.dialogs.fileexists(null, null, null, OC.Upload).done(function() {
+ OC.dialogs.fileexists(null, null, null, this).done(function() {
_.each(conflicts, function(conflictData) {
- OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].files[0], OC.Upload);
+ OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].getFile(), this);
});
});
}
@@ -240,15 +669,16 @@ OC.Upload = {
},
_hideProgressBar: function() {
+ var self = this;
$('#uploadprogresswrapper .stop').fadeOut();
$('#uploadprogressbar').fadeOut(function() {
- $('#file_upload_start').trigger(new $.Event('resized'));
+ self.$uploadEl.trigger(new $.Event('resized'));
});
},
_showProgressBar: function() {
$('#uploadprogressbar').fadeIn();
- $('#file_upload_start').trigger(new $.Event('resized'));
+ this.$uploadEl.trigger(new $.Event('resized'));
},
/**
@@ -269,12 +699,34 @@ OC.Upload = {
return ($tr.attr('data-mounttype') === 'shared-root' && $tr.attr('data-mime') !== 'httpd/unix-directory');
},
- init: function() {
+ /**
+ * Initialize the upload object
+ *
+ * @param {Object} $uploadEl upload element
+ * @param {Object} options
+ * @param {OCA.Files.FileList} [options.fileList] file list object
+ * @param {OC.Files.Client} [options.filesClient] files client object
+ * @param {Object} [options.dropZone] drop zone for drag and drop upload
+ */
+ init: function($uploadEl, options) {
var self = this;
- if ( $('#file_upload_start').exists() ) {
- var file_upload_param = {
- dropZone: $('#app-content'), // restrict dropZone to app-content div
- pasteZone: null,
+
+ options = options || {};
+
+ this.fileList = options.fileList;
+ this.filesClient = options.filesClient || OC.Files.getClient();
+
+ $uploadEl = $($uploadEl);
+ this.$uploadEl = $uploadEl;
+
+ if ($uploadEl.exists()) {
+ $('#uploadprogresswrapper .stop').on('click', function() {
+ self.cancelUploads();
+ });
+
+ this.fileUploadParam = {
+ type: 'PUT',
+ dropZone: options.dropZone, // restrict dropZone to content div
autoUpload: false,
sequentialUploads: true,
//singleFileUploads is on by default, so the data.files array will always have length 1
@@ -295,9 +747,13 @@ OC.Upload = {
* @returns {boolean}
*/
add: function(e, data) {
- OC.Upload.log('add', e, data);
+ self.log('add', e, data);
var that = $(this), freeSpace;
+ var upload = new OC.FileUpload(self, data);
+ // can't link directly due to jQuery not liking cyclic deps on its ajax object
+ data.uploadId = upload.getId();
+
// we need to collect all data upload objects before
// starting the upload so we can check their existence
// and set individual conflict actions. Unfortunately,
@@ -317,16 +773,17 @@ OC.Upload = {
biggestFileBytes: 0
};
}
+ // TODO: move originalFiles to a separate container, maybe inside OC.Upload
var selection = data.originalFiles.selection;
// add uploads
if ( selection.uploads.length < selection.filesToUpload ) {
// remember upload
- selection.uploads.push(data);
+ selection.uploads.push(upload);
}
//examine file
- var file = data.files[0];
+ var file = upload.getFile();
try {
// FIXME: not so elegant... need to refactor that method to return a value
Files.isFileNameValid(file.name);
@@ -336,9 +793,14 @@ OC.Upload = {
data.errorThrown = errorMessage;
}
+ if (data.targetDir) {
+ upload.setTargetFolder(data.targetDir);
+ delete data.targetDir;
+ }
+
// in case folder drag and drop is not supported file will point to a directory
// http://stackoverflow.com/a/20448357
- if ( ! file.type && file.size%4096 === 0 && file.size <= 102400) {
+ if ( ! file.type && file.size % 4096 === 0 && file.size <= 102400) {
var dirUploadFailure = false;
try {
var reader = new FileReader();
@@ -390,7 +852,7 @@ OC.Upload = {
// end upload for whole selection on error
if (data.errorThrown) {
- // trigger fileupload fail
+ // trigger fileupload fail handler
var fu = that.data('blueimp-fileupload') || that.data('fileupload');
fu._trigger('fail', e, data);
return false; //don't upload anything
@@ -405,9 +867,7 @@ OC.Upload = {
var callbacks = {
onNoConflicts: function (selection) {
- $.each(selection.uploads, function(i, upload) {
- upload.submit();
- });
+ self.submitUploads(selection.uploads);
},
onSkipConflicts: function (selection) {
//TODO mark conflicting files as toskip
@@ -425,7 +885,7 @@ OC.Upload = {
}
};
- OC.Upload.checkExistingFiles(selection, callbacks);
+ self.checkExistingFiles(selection, callbacks);
}
@@ -436,106 +896,60 @@ OC.Upload = {
* @param {object} e
*/
start: function(e) {
- OC.Upload.log('start', e, null);
+ self.log('start', e, null);
//hide the tooltip otherwise it covers the progress bar
$('#upload').tipsy('hide');
},
- submit: function(e, data) {
- OC.Upload.rememberUpload(data);
- if (!data.formData) {
- data.formData = {};
- }
-
- var fileDirectory = '';
- if(typeof data.files[0].relativePath !== 'undefined') {
- fileDirectory = data.files[0].relativePath;
+ fail: function(e, data) {
+ var upload = self.getUpload(data);
+ var status = null;
+ if (upload) {
+ status = upload.getResponseStatus();
}
+ self.log('fail', e, upload);
- var params = {
- requesttoken: oc_requesttoken,
- dir: data.targetDir || FileList.getCurrentDirectory(),
- file_directory: fileDirectory,
- };
- if (data.files[0].isReceivedShare) {
- params.isReceivedShare = true;
+ if (data.textStatus === 'abort') {
+ self.showUploadCancelMessage();
+ } else if (status === 412) {
+ // file already exists
+ self.showConflict(upload);
+ } else if (status === 404) {
+ // target folder does not exist any more
+ OC.Notification.showTemporary(
+ t('files', 'Target folder "{dir}" does not exist any more', {dir: upload.getFullPath()})
+ );
+ self.cancelUploads();
+ } else if (status === 507) {
+ // not enough space
+ OC.Notification.showTemporary(
+ t('files', 'Not enough free space')
+ );
+ self.cancelUploads();
+ } else {
+ // HTTP connection problem or other error
+ OC.Notification.showTemporary(data.errorThrown, {timeout: 10});
}
- addFormData(data.formData, params);
- },
- fail: function(e, data) {
- OC.Upload.log('fail', e, data);
- if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) {
- if (data.textStatus === 'abort') {
- OC.Upload.showUploadCancelMessage();
- } else {
- // HTTP connection problem
- var message = t('files', 'Error uploading file "{fileName}": {message}', {
- fileName: escapeHTML(data.files[0].name),
- message: data.errorThrown
- }, undefined, {escape: false});
- OC.Notification.show(message, {timeout: 0, type: 'error'});
- if (data.result) {
- var result = JSON.parse(data.result);
- if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') {
- // abort upload of next files if any
- OC.Upload.cancelUploads();
- }
- }
- }
+ if (upload) {
+ upload.deleteUpload();
}
- OC.Upload.deleteUpload(data);
},
/**
* called for every successful upload
* @param {object} e
* @param {object} data
*/
- done: function(e, data) {
- OC.Upload.log('done', e, data);
- // handle different responses (json or body from iframe for ie)
- var response;
- if (typeof data.result === 'string') {
- response = data.result;
- } else {
- //fetch response from iframe
- response = data.result[0].body.innerText;
- }
- var result = JSON.parse(response);
+ done:function(e, data) {
+ var upload = self.getUpload(data);
+ var that = $(this);
+ self.log('done', e, upload);
- delete data.jqXHR;
-
- var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload');
-
- if (result.status === 'error' && result.data && result.data.message){
- data.textStatus = 'servererror';
- data.errorThrown = result.data.message;
- fu._trigger('fail', e, data);
- } else if (typeof result[0] === 'undefined') {
- data.textStatus = 'servererror';
- data.errorThrown = t('files', 'Could not get result from server.');
- fu._trigger('fail', e, data);
- } else if (result[0].status === 'readonly') {
- var original = result[0];
- var replacement = data.files[0];
- OC.dialogs.fileexists(data, original, replacement, OC.Upload);
- } else if (result[0].status === 'existserror') {
- //show "file already exists" dialog
- var original = result[0];
- var replacement = data.files[0];
- OC.dialogs.fileexists(data, original, replacement, OC.Upload);
- } else if (result[0].status !== 'success') {
- //delete data.jqXHR;
- data.textStatus = 'servererror';
- data.errorThrown = result[0].data.message; // error message has been translated on server
+ var status = upload.getResponseStatus();
+ if (status < 200 || status >= 300) {
+ // trigger fail handler
+ var fu = that.data('blueimp-fileupload') || that.data('fileupload');
fu._trigger('fail', e, data);
- } else { // Successful upload
- // Checking that the uploaded file is the last one and contained in the current directory
- if (data.files[0] === data.originalFiles[data.originalFiles.length - 1] &&
- result[0].directory === FileList.getCurrentDirectory()) {
- // Scroll to the last uploaded file and highlight all of them
- var fileList = _.pluck(data.originalFiles, 'name');
- FileList.highlightFiles(fileList);
- }
+ return;
}
},
/**
@@ -544,15 +958,14 @@ OC.Upload = {
* @param {object} data
*/
stop: function(e, data) {
- OC.Upload.log('stop', e, data);
+ self.log('stop', e, data);
}
};
// initialize jquery fileupload (blueimp)
- var fileupload = $('#file_upload_start').fileupload(file_upload_param);
- window.file_upload_param = fileupload;
+ var fileupload = this.$uploadEl.fileupload(this.fileUploadParam);
- if (supportAjaxUploadWithProgress()) {
+ if (this._supportAjaxUploadWithProgress()) {
//remaining time
var lastUpdate = new Date().getMilliseconds();
var lastSize = 0;
@@ -561,19 +974,17 @@ OC.Upload = {
var bufferIndex = 0;
var bufferTotal = 0;
for(var i = 0; i < bufferSize;i++){
- buffer[i] = 0;
+ buffer[i] = 0;
}
+
// add progress handlers
fileupload.on('fileuploadadd', function(e, data) {
- OC.Upload.log('progress handle fileuploadadd', e, data);
- //show cancel button
- //if (data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie?
- // $('#uploadprogresswrapper .stop').show();
- //}
+ self.log('progress handle fileuploadadd', e, data);
+ self.trigger('add', e, data);
});
// add progress handlers
fileupload.on('fileuploadstart', function(e, data) {
- OC.Upload.log('progress handle fileuploadstart', e, data);
+ self.log('progress handle fileuploadstart', e, data);
$('#uploadprogresswrapper .stop').show();
$('#uploadprogresswrapper .label').show();
$('#uploadprogressbar').progressbar({value: 0});
@@ -584,14 +995,16 @@ OC.Upload = {
+ t('files', '...')
+ '</span></em>');
$('#uploadprogressbar').tipsy({gravity:'n', fade:true, live:true});
- OC.Upload._showProgressBar();
+ self._showProgressBar();
+ self.trigger('start', e, data);
});
fileupload.on('fileuploadprogress', function(e, data) {
- OC.Upload.log('progress handle fileuploadprogress', e, data);
+ self.log('progress handle fileuploadprogress', e, data);
//TODO progressbar in row
+ self.trigger('progress', e, data);
});
fileupload.on('fileuploadprogressall', function(e, data) {
- OC.Upload.log('progress handle fileuploadprogressall', e, data);
+ self.log('progress handle fileuploadprogressall', e, data);
var progress = (data.loaded / data.total) * 100;
var thisUpdate = new Date().getMilliseconds();
var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s
@@ -608,14 +1021,14 @@ OC.Upload = {
var smoothRemainingSeconds = (bufferTotal / bufferSize); //seconds
var date = new Date(smoothRemainingSeconds * 1000);
var timeStringDesktop = "";
- var timeStringMobile = "";
+ var timeStringMobile = "";
if(date.getUTCHours() > 0){
- timeStringDesktop = t('files', '{hours}:{minutes}:{seconds} hour{plural_s} left' , {
+ timeStringDesktop = t('files', '{hours}:{minutes}:{seconds} hour{plural_s} left' , {
hours:date.getUTCHours(),
minutes: ('0' + date.getUTCMinutes()).slice(-2),
seconds: ('0' + date.getUTCSeconds()).slice(-2),
plural_s: ( smoothRemainingSeconds === 3600 ? "": "s") // 1 hour = 1*60m*60s = 3600s
- });
+ });
timeStringMobile = t('files', '{hours}:{minutes}h' , {
hours:date.getUTCHours(),
minutes: ('0' + date.getUTCMinutes()).slice(-2),
@@ -626,12 +1039,12 @@ OC.Upload = {
minutes: date.getUTCMinutes(),
seconds: ('0' + date.getUTCSeconds()).slice(-2),
plural_s: (smoothRemainingSeconds === 60 ? "": "s") // 1 minute = 1*60s = 60s
- });
+ });
timeStringMobile = t('files', '{minutes}:{seconds}m' , {
minutes: date.getUTCMinutes(),
seconds: ('0' + date.getUTCSeconds()).slice(-2)
});
- } else if(date.getUTCSeconds() > 0){
+ } else if(date.getUTCSeconds() > 0){
timeStringDesktop = t('files', '{seconds} second{plural_s} left' , {
seconds: date.getUTCSeconds(),
plural_s: (smoothRemainingSeconds === 1 ? "": "s") // 1 second = 1s = 1s
@@ -651,17 +1064,21 @@ OC.Upload = {
})
);
$('#uploadprogressbar').progressbar('value', progress);
+ self.trigger('progressall', e, data);
});
fileupload.on('fileuploadstop', function(e, data) {
- OC.Upload.log('progress handle fileuploadstop', e, data);
- OC.Upload._hideProgressBar();
+ self.log('progress handle fileuploadstop', e, data);
+
+ self.clear();
+ self._hideProgressBar();
});
fileupload.on('fileuploadfail', function(e, data) {
- OC.Upload.log('progress handle fileuploadfail', e, data);
+ self.log('progress handle fileuploadfail', e, data);
//if user pressed cancel hide upload progress bar and cancel button
if (data.errorThrown === 'abort') {
- OC.Upload._hideProgressBar();
+ self._hideProgressBar();
}
+ self.trigger('fail', e, data);
});
var disableDropState = function() {
$('#app-content').removeClass('file-drag');
@@ -696,55 +1113,53 @@ OC.Upload = {
filerow.find('.thumbnail').addClass('icon-filetype-folder-drag-accept');
}
});
- fileupload.on('fileuploaddragleave fileuploaddrop', disableDropState);
- } else {
- // for all browsers that don't support the progress bar
- // IE 8 & 9
-
- // show a spinner
- fileupload.on('fileuploadstart', function() {
- $('#upload').addClass('icon-loading');
- $('#upload .icon-upload').hide();
+ fileupload.on('fileuploaddragleave fileuploaddrop', function (){
+ $('#app-content').removeClass('file-drag');
+ $('.dropping-to-dir').removeClass('dropping-to-dir');
+ $('.dir-drop').removeClass('dir-drop');
+ $('.icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept');
});
- // hide a spinner
- fileupload.on('fileuploadstop fileuploadfail', function() {
- $('#upload').removeClass('icon-loading');
- $('#upload .icon-upload').show();
+ fileupload.on('fileuploadchunksend', function(e, data) {
+ // modify the request to adjust it to our own chunking
+ var upload = self.getUpload(data);
+ var range = data.contentRange.split(' ')[1];
+ var chunkId = range.split('/')[0];
+ data.url = OC.getRootPath() +
+ '/remote.php/dav/uploads' +
+ '/' + encodeURIComponent(OC.getCurrentUser().uid) +
+ '/' + encodeURIComponent(upload.getId()) +
+ '/' + encodeURIComponent(chunkId);
+ delete data.contentRange;
+ delete data.headers['Content-Range'];
+ });
+ fileupload.on('fileuploaddone', function(e, data) {
+ var upload = self.getUpload(data);
+ upload.done().then(function() {
+ self.trigger('done', e, upload);
+ });
+ });
+ fileupload.on('fileuploaddrop', function(e, data) {
+ self.trigger('drop', e, data);
});
- }
- }
- $.assocArraySize = function(obj) {
- // http://stackoverflow.com/a/6700/11236
- var size = 0, key;
- for (key in obj) {
- if (obj.hasOwnProperty(key)) {
- size++;
- }
}
- return size;
- };
+ }
// warn user not to leave the page while upload is in progress
$(window).on('beforeunload', function(e) {
- if (OC.Upload.isProcessing()) {
+ if (self.isProcessing()) {
return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.');
}
});
//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
if (navigator.userAgent.search(/konqueror/i) === -1) {
- $('#file_upload_start').attr('multiple', 'multiple');
+ this.$uploadEl.attr('multiple', 'multiple');
}
- window.file_upload_param = file_upload_param;
- return file_upload_param;
+ return this.fileUploadParam;
}
-};
-
-$(document).ready(function() {
- OC.Upload.init();
-});
+}, OC.Backbone.Events);
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 53ad8eafeef..e728a816cc0 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -30,6 +30,7 @@
* @param {Object} [options.dragOptions] drag options, disabled by default
* @param {Object} [options.folderDropOptions] folder drop options, disabled by default
* @param {boolean} [options.detailsViewEnabled=true] whether to enable details view
+ * @param {boolean} [options.enableUpload=false] whether to enable uploader
* @param {OC.Files.Client} [options.filesClient] files client to use
*/
var FileList = function($el, options) {
@@ -189,6 +190,11 @@
_folderDropOptions: null,
/**
+ * @type OC.Uploader
+ */
+ _uploader: null,
+
+ /**
* Initialize the file list and its components
*
* @param $el container element with existing markup for the #controls
@@ -328,8 +334,6 @@
this.$el.find('.selectedActions a').tooltip({placement:'top'});
- this.setupUploadEvents();
-
this.$container.on('scroll', _.bind(this._onScroll, this));
if (options.scrollTo) {
@@ -338,6 +342,20 @@
});
}
+ if (options.enableUpload) {
+ // TODO: auto-create this element
+ var $uploadEl = this.$el.find('#file_upload_start');
+ if ($uploadEl.exists()) {
+ this._uploader = new OC.Uploader($uploadEl, {
+ fileList: this,
+ filesClient: this.filesClient,
+ dropZone: $('#content')
+ });
+
+ this.setupUploadEvents(this._uploader);
+ }
+ }
+
OC.Plugins.attach('OCA.Files.FileList', this);
},
@@ -1420,7 +1438,10 @@
return;
}
this._setCurrentDir(targetDir, changeUrl, fileId);
- return this.reload().then(function(success){
+
+ // discard finished uploads list, we'll get it through a regular reload
+ this._uploads = {};
+ this.reload().then(function(success){
if (!success) {
self.changeDirectory(currentDir, true);
}
@@ -1662,6 +1683,24 @@
return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory(), isDir);
},
+ getUploadUrl: function(fileName, dir) {
+ if (_.isUndefined(dir)) {
+ dir = this.getCurrentDirectory();
+ }
+
+ var pathSections = dir.split('/');
+ if (!_.isUndefined(fileName)) {
+ pathSections.push(fileName);
+ }
+ var encodedPath = '';
+ _.each(pathSections, function(section) {
+ if (section !== '') {
+ encodedPath += '/' + encodeURIComponent(section);
+ }
+ });
+ return OC.linkToRemoteBase('webdav') + encodedPath;
+ },
+
/**
* Generates a preview URL based on the URL space.
* @param urlSpec attributes for the URL
@@ -2123,19 +2162,11 @@
)
.done(function() {
// TODO: error handling / conflicts
- self.filesClient.getFileInfo(
- targetPath, {
- properties: self._getWebdavProperties()
- }
- )
- .then(function(status, data) {
- self.add(data, {animate: true, scrollTo: true});
- deferred.resolve(status, data);
- })
- .fail(function(status) {
- OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
- deferred.reject(status);
- });
+ self.addAndFetchFileInfo(targetPath, '', {scrollTo: true}).then(function(status, data) {
+ deferred.resolve(status, data);
+ }, function() {
+ OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
+ });
})
.fail(function(status) {
if (status === 412) {
@@ -2176,32 +2207,19 @@
var targetPath = this.getCurrentDirectory() + '/' + name;
this.filesClient.createDirectory(targetPath)
- .done(function(createStatus) {
- self.filesClient.getFileInfo(
- targetPath, {
- properties: self._getWebdavProperties()
- }
- )
- .done(function(status, data) {
- self.add(data, {animate: true, scrollTo: true});
- deferred.resolve(status, data);
- })
- .fail(function() {
- OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name}));
- deferred.reject(createStatus);
- });
+ .done(function() {
+ self.addAndFetchFileInfo(targetPath, '', {scrollTo:true}).then(function(status, data) {
+ deferred.resolve(status, data);
+ }, function() {
+ OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name}));
+ });
})
.fail(function(createStatus) {
// method not allowed, folder might exist already
if (createStatus === 405) {
- self.filesClient.getFileInfo(
- targetPath, {
- properties: self._getWebdavProperties()
- }
- )
+ // add it to the list, for completeness
+ self.addAndFetchFileInfo(targetPath, '', {scrollTo:true})
.done(function(status, data) {
- // add it to the list, for completeness
- self.add(data, {animate: true, scrollTo: true});
OC.Notification.showTemporary(
t('files', 'Could not create folder "{dir}" because it already exists', {dir: name})
);
@@ -2224,6 +2242,60 @@
},
/**
+ * Add file into the list by fetching its information from the server first.
+ *
+ * If the given directory does not match the current directory, nothing will
+ * be fetched.
+ *
+ * @param {String} fileName file name
+ * @param {String} [dir] optional directory, defaults to the current one
+ * @param {Object} options same options as #add
+ * @return {Promise} promise that resolves with the file info, or an
+ * already resolved Promise if no info was fetched. The promise rejects
+ * if the file was not found or an error occurred.
+ *
+ * @since 9.0
+ */
+ addAndFetchFileInfo: function(fileName, dir, options) {
+ var self = this;
+ var deferred = $.Deferred();
+ if (_.isUndefined(dir)) {
+ dir = this.getCurrentDirectory();
+ } else {
+ dir = dir || '/';
+ }
+
+ var targetPath = OC.joinPaths(dir, fileName);
+
+ if ((OC.dirname(targetPath) || '/') !== this.getCurrentDirectory()) {
+ // no need to fetch information
+ deferred.resolve();
+ return deferred.promise();
+ }
+
+ var addOptions = _.extend({
+ animate: true,
+ scrollTo: false
+ }, options || {});
+
+ this.filesClient.getFileInfo(targetPath, {
+ properties: this._getWebdavProperties()
+ })
+ .then(function(status, data) {
+ // remove first to avoid duplicates
+ self.remove(data.name);
+ self.add(data, addOptions);
+ deferred.resolve(status, data);
+ })
+ .fail(function(status) {
+ OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name}));
+ deferred.reject(status);
+ });
+
+ return deferred.promise();
+ },
+
+ /**
* Returns whether the given file name exists in the list
*
* @param {string} file file name
@@ -2591,19 +2663,19 @@
/**
* Setup file upload events related to the file-upload plugin
+ *
+ * @param {OC.Uploader} uploader
*/
- setupUploadEvents: function() {
+ setupUploadEvents: function(uploader) {
var self = this;
- // handle upload events
- var fileUploadStart = this.$el;
- var delegatedElement = '#file_upload_start';
+ self._uploads = {};
// detect the progress bar resize
- fileUploadStart.on('resized', this._onResize);
+ uploader.on('resized', this._onResize);
- fileUploadStart.on('fileuploaddrop', delegatedElement, function(e, data) {
- OC.Upload.log('filelist handle fileuploaddrop', e, data);
+ uploader.on('drop', function(e, data) {
+ self._uploader.log('filelist handle fileuploaddrop', e, data);
if (self.$el.hasClass('hidden')) {
// do not upload to invisible lists
@@ -2654,25 +2726,20 @@
// add target dir
data.targetDir = dir;
} else {
- // we are dropping somewhere inside the file list, which will
- // upload the file to the current directory
- data.targetDir = self.getCurrentDirectory();
-
// cancel uploads to current dir if no permission
var isCreatable = (self.getDirectoryPermissions() & OC.PERMISSION_CREATE) !== 0;
if (!isCreatable) {
self._showPermissionDeniedNotification();
return false;
}
- }
- });
- fileUploadStart.on('fileuploadadd', function(e, data) {
- OC.Upload.log('filelist handle fileuploadadd', e, data);
- //finish delete if we are uploading a deleted file
- if (self.deleteFiles && self.deleteFiles.indexOf(data.files[0].name)!==-1) {
- self.finishDelete(null, true); //delete file before continuing
+ // we are dropping somewhere inside the file list, which will
+ // upload the file to the current directory
+ data.targetDir = self.getCurrentDirectory();
}
+ });
+ uploader.on('add', function(e, data) {
+ self._uploader.log('filelist handle fileuploadadd', e, data);
// add ui visualization to existing folder
if (data.context && data.context.data('type') === 'dir') {
@@ -2694,135 +2761,74 @@
}
}
+ if (!data.targetDir) {
+ data.targetDir = self.getCurrentDirectory();
+ }
+
});
/*
* when file upload done successfully add row to filelist
* update counter when uploading to sub folder
*/
- fileUploadStart.on('fileuploaddone', function(e, data) {
- OC.Upload.log('filelist handle fileuploaddone', e, data);
+ uploader.on('done', function(e, upload) {
+ self._uploader.log('filelist handle fileuploaddone', e, data);
- var response;
- if (typeof data.result === 'string') {
- response = data.result;
- } else {
- // fetch response from iframe
- response = data.result[0].body.innerText;
+ var data = upload.data;
+ var status = data.jqXHR.status;
+ if (status < 200 || status >= 300) {
+ // error was handled in OC.Uploads already
+ return;
}
- var result = JSON.parse(response);
-
- if (typeof result[0] !== 'undefined' && result[0].status === 'success') {
- var file = result[0];
- var size = 0;
-
- if (data.context && data.context.data('type') === 'dir') {
-
- // update upload counter ui
- var uploadText = data.context.find('.uploadtext');
- var currentUploads = parseInt(uploadText.attr('currentUploads'), 10);
- currentUploads -= 1;
- uploadText.attr('currentUploads', currentUploads);
- var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads);
- if (currentUploads === 0) {
- self.showFileBusyState(uploadText.closest('tr'), false);
- uploadText.text(translatedText);
- uploadText.hide();
- } else {
- uploadText.text(translatedText);
- }
-
- // update folder size
- size = parseInt(data.context.data('size'), 10);
- size += parseInt(file.size, 10);
- data.context.attr('data-size', size);
- data.context.find('td.filesize').text(humanFileSize(size));
- } else {
- // only append new file if uploaded into the current folder
- if (file.directory !== self.getCurrentDirectory()) {
- // Uploading folders actually uploads a list of files
- // for which the target directory (file.directory) might lie deeper
- // than the current directory
-
- var fileDirectory = file.directory.replace('/','').replace(/\/$/, "");
- var currentDirectory = self.getCurrentDirectory().replace('/','').replace(/\/$/, "") + '/';
-
- if (currentDirectory !== '/') {
- // abort if fileDirectory does not start with current one
- if (fileDirectory.indexOf(currentDirectory) !== 0) {
- return;
- }
-
- // remove the current directory part
- fileDirectory = fileDirectory.substr(currentDirectory.length);
- }
-
- // only take the first section of the path
- fileDirectory = fileDirectory.split('/');
-
- var fd;
- // if the first section exists / is a subdir
- if (fileDirectory.length) {
- fileDirectory = fileDirectory[0];
-
- // See whether it is already in the list
- fd = self.findFileEl(fileDirectory);
- if (fd.length === 0) {
- var dir = {
- name: fileDirectory,
- type: 'dir',
- mimetype: 'httpd/unix-directory',
- permissions: file.permissions,
- size: 0,
- id: file.parentId
- };
- fd = self.add(dir, {insert: true});
- }
- // update folder size
- size = parseInt(fd.attr('data-size'), 10);
- size += parseInt(file.size, 10);
- fd.attr('data-size', size);
- fd.find('td.filesize').text(OC.Util.humanFileSize(size));
- }
-
- return;
- }
-
- // add as stand-alone row to filelist
- size = t('files', 'Pending');
- if (data.files[0].size>=0) {
- size=data.files[0].size;
- }
- //should the file exist in the list remove it
- self.remove(file.name);
-
- // create new file context
- data.context = self.add(file, {animate: true});
- }
+ var fileName = upload.getFileName();
+ var fetchInfoPromise = self.addAndFetchFileInfo(fileName, upload.getFullPath());
+ if (!self._uploads) {
+ self._uploads = {};
+ }
+ if (OC.isSamePath(OC.dirname(upload.getFullPath() + '/'), self.getCurrentDirectory())) {
+ self._uploads[fileName] = fetchInfoPromise;
}
- });
- fileUploadStart.on('fileuploadstop', function() {
- OC.Upload.log('filelist handle fileuploadstop');
- //cleanup uploading to a dir
var uploadText = self.$fileList.find('tr .uploadtext');
self.showFileBusyState(uploadText.closest('tr'), false);
uploadText.fadeOut();
uploadText.attr('currentUploads', 0);
-
+ });
+ uploader.on('createdfolder', function(fullPath) {
+ self.addAndFetchFileInfo(OC.basename(fullPath), OC.dirname(fullPath));
+ });
+ uploader.on('stop', function() {
+ self._uploader.log('filelist handle fileuploadstop');
+
+ // prepare list of uploaded file names in the current directory
+ // and discard the other ones
+ var promises = _.values(self._uploads);
+ var fileNames = _.keys(self._uploads);
+ self._uploads = [];
+
+ // as soon as all info is fetched
+ $.when.apply($, promises).then(function() {
+ // highlight uploaded files
+ self.highlightFiles(fileNames);
+ });
self.updateStorageStatistics();
+
+ var uploadText = self.$fileList.find('tr .uploadtext');
+ self.showFileBusyState(uploadText.closest('tr'), false);
+ uploadText.fadeOut();
+ uploadText.attr('currentUploads', 0);
});
- fileUploadStart.on('fileuploadfail', function(e, data) {
- OC.Upload.log('filelist handle fileuploadfail', e, data);
+ uploader.on('fail', function(e, data) {
+ self._uploader.log('filelist handle fileuploadfail', e, data);
+
+ self._uploads = [];
//if user pressed cancel hide upload chrome
- if (data.errorThrown === 'abort') {
- //cleanup uploading to a dir
- var uploadText = self.$fileList.find('tr .uploadtext');
- self.showFileBusyState(uploadText.closest('tr'), false);
- uploadText.fadeOut();
- uploadText.attr('currentUploads', 0);
- }
+ //cleanup uploading to a dir
+ var uploadText = self.$fileList.find('tr .uploadtext');
+ self.showFileBusyState(uploadText.closest('tr'), false);
+ uploadText.fadeOut();
+ uploadText.attr('currentUploads', 0);
self.updateStorageStatistics();
});
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index 2873b84bc9a..0be098b2e73 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -226,17 +226,6 @@
// TODO: move file list related code (upload) to OCA.Files.FileList
$('#file_action_panel').attr('activeAction', false);
- // Triggers invisible file input
- $('#upload a').on('click', function() {
- $(this).parent().children('#file_upload_start').trigger('click');
- return false;
- });
-
- // Trigger cancelling of file upload
- $('#uploadprogresswrapper .stop').on('click', function() {
- OC.Upload.cancelUploads();
- });
-
// drag&drop support using jquery.fileupload
// TODO use OC.dialogs
$(document).bind('drop dragover', function (e) {