2014-01-30 13:41:04 +04:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2014
|
|
|
|
*
|
|
|
|
* This file is licensed under the Affero General Public License version 3
|
|
|
|
* or later.
|
|
|
|
*
|
|
|
|
* See the COPYING-README file.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2013-08-14 19:49:45 +04:00
|
|
|
/**
|
2013-09-08 12:41:20 +04:00
|
|
|
* The file upload code uses several hooks to interact with blueimps jQuery file upload library:
|
|
|
|
* 1. the core upload handling hooks are added when initializing the plugin,
|
|
|
|
* 2. if the browser supports progress events they are added in a separate set after the initialization
|
|
|
|
* 3. every app can add it's own triggers for fileupload
|
|
|
|
* - files adds d'n'd handlers and also reacts to done events to add new rows to the filelist
|
|
|
|
* - TODO pictures upload button
|
|
|
|
* - TODO music upload button
|
2013-08-14 19:49:45 +04:00
|
|
|
*/
|
|
|
|
|
2020-02-07 19:08:50 +03:00
|
|
|
/* global jQuery, md5 */
|
2014-01-30 13:41:04 +04:00
|
|
|
|
2013-09-16 16:10:19 +04:00
|
|
|
/**
|
2015-12-16 19:35:53 +03:00
|
|
|
* File upload object
|
|
|
|
*
|
|
|
|
* @class OC.FileUpload
|
|
|
|
* @classdesc
|
|
|
|
*
|
|
|
|
* Represents a file upload
|
|
|
|
*
|
|
|
|
* @param {OC.Uploader} uploader uploader
|
|
|
|
* @param {Object} data blueimp data
|
2013-09-16 16:10:19 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
OC.FileUpload = function(uploader, data) {
|
|
|
|
this.uploader = uploader;
|
|
|
|
this.data = data;
|
2019-03-14 00:06:05 +03:00
|
|
|
var basePath = '';
|
2016-08-31 17:32:14 +03:00
|
|
|
if (this.uploader.fileList) {
|
2019-03-14 00:06:05 +03:00
|
|
|
basePath = this.uploader.fileList.getCurrentDirectory();
|
2016-08-31 17:32:14 +03:00
|
|
|
}
|
2019-03-14 00:06:05 +03:00
|
|
|
var path = OC.joinPaths(basePath, this.getFile().relativePath || '', this.getFile().name);
|
2016-07-15 17:03:02 +03:00
|
|
|
this.id = 'web-file-upload-' + md5(path) + '-' + (new Date()).getTime();
|
2015-12-16 19:35:53 +03:00
|
|
|
};
|
|
|
|
OC.FileUpload.CONFLICT_MODE_DETECT = 0;
|
|
|
|
OC.FileUpload.CONFLICT_MODE_OVERWRITE = 1;
|
|
|
|
OC.FileUpload.CONFLICT_MODE_AUTORENAME = 2;
|
2020-04-22 14:40:23 +03:00
|
|
|
|
|
|
|
// IE11 polyfill
|
|
|
|
// TODO: nuke out of orbit as well as this legacy code
|
|
|
|
if (!FileReader.prototype.readAsBinaryString) {
|
|
|
|
FileReader.prototype.readAsBinaryString = function(fileData) {
|
|
|
|
var binary = ''
|
|
|
|
var pt = this
|
|
|
|
var reader = new FileReader()
|
|
|
|
reader.onload = function (e) {
|
|
|
|
var bytes = new Uint8Array(reader.result)
|
|
|
|
var length = bytes.byteLength
|
|
|
|
for (var i = 0; i < length; i++) {
|
|
|
|
binary += String.fromCharCode(bytes[i])
|
|
|
|
}
|
|
|
|
// pt.result - readonly so assign binary
|
|
|
|
pt.content = binary
|
|
|
|
$(pt).trigger('onload')
|
|
|
|
}
|
|
|
|
reader.readAsArrayBuffer(fileData)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
OC.FileUpload.prototype = {
|
2013-09-05 12:19:54 +04:00
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
/**
|
|
|
|
* Unique upload id
|
|
|
|
*
|
|
|
|
* @type string
|
|
|
|
*/
|
|
|
|
id: null,
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Upload element
|
|
|
|
*
|
|
|
|
* @type Object
|
|
|
|
*/
|
|
|
|
$uploadEl: null,
|
2013-09-05 12:19:54 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Target folder
|
|
|
|
*
|
|
|
|
* @type string
|
|
|
|
*/
|
|
|
|
_targetFolder: '',
|
2013-08-16 13:40:55 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* @type int
|
|
|
|
*/
|
|
|
|
_conflictMode: OC.FileUpload.CONFLICT_MODE_DETECT,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* New name from server after autorename
|
|
|
|
*
|
|
|
|
* @type String
|
|
|
|
*/
|
|
|
|
_newName: null,
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
/**
|
|
|
|
* Returns the unique upload id
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
getId: function() {
|
|
|
|
return this.id;
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Returns the file to be uploaded
|
|
|
|
*
|
|
|
|
* @return {File} file
|
|
|
|
*/
|
|
|
|
getFile: function() {
|
|
|
|
return this.data.files[0];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the final filename.
|
|
|
|
*
|
|
|
|
* @return {String} file name
|
|
|
|
*/
|
|
|
|
getFileName: function() {
|
2016-09-09 13:19:29 +03:00
|
|
|
// autorenamed name
|
2015-12-16 19:35:53 +03:00
|
|
|
if (this._newName) {
|
|
|
|
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 || '');
|
|
|
|
},
|
|
|
|
|
2016-12-15 12:19:55 +03:00
|
|
|
/**
|
2017-12-21 12:48:43 +03:00
|
|
|
* Get full path for the target file,
|
2016-12-15 12:19:55 +03:00
|
|
|
* including relative path and file name.
|
|
|
|
*
|
|
|
|
* @return {String} full path
|
|
|
|
*/
|
|
|
|
getFullFilePath: function() {
|
|
|
|
return OC.joinPaths(this.getFullPath(), this.getFile().name);
|
|
|
|
},
|
|
|
|
|
2016-09-09 13:19:29 +03:00
|
|
|
/**
|
|
|
|
* Returns conflict resolution mode.
|
|
|
|
*
|
|
|
|
* @return {int} conflict mode
|
|
|
|
*/
|
|
|
|
getConflictMode: function() {
|
|
|
|
return this._conflictMode || OC.FileUpload.CONFLICT_MODE_DETECT;
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Set conflict resolution mode.
|
|
|
|
* See CONFLICT_MODE_* constants.
|
2016-09-09 13:19:29 +03:00
|
|
|
*
|
|
|
|
* @param {int} mode conflict mode
|
2015-12-16 19:35:53 +03:00
|
|
|
*/
|
|
|
|
setConflictMode: function(mode) {
|
|
|
|
this._conflictMode = mode;
|
|
|
|
},
|
|
|
|
|
|
|
|
deleteUpload: function() {
|
|
|
|
delete this.data.jqXHR;
|
|
|
|
},
|
|
|
|
|
2016-09-09 13:19:29 +03:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Submit the upload
|
|
|
|
*/
|
|
|
|
submit: function() {
|
2016-07-15 17:03:02 +03:00
|
|
|
var self = this;
|
2015-12-16 19:35:53 +03:00
|
|
|
var data = this.data;
|
|
|
|
var file = this.getFile();
|
|
|
|
|
2019-02-17 01:27:50 +03:00
|
|
|
if (self.aborted === true) {
|
|
|
|
return $.Deferred().resolve().promise();
|
|
|
|
}
|
2019-01-21 21:11:13 +03:00
|
|
|
// it was a folder upload, so make sure the parent directory exists already
|
2015-12-16 19:35:53 +03:00
|
|
|
var folderPromise;
|
|
|
|
if (file.relativePath) {
|
|
|
|
folderPromise = this.uploader.ensureFolderExists(this.getFullPath());
|
|
|
|
} else {
|
|
|
|
folderPromise = $.Deferred().resolve().promise();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.uploader.fileList) {
|
2016-09-09 13:19:29 +03:00
|
|
|
this.data.url = this.uploader.fileList.getUploadUrl(this.getFileName(), this.getFullPath());
|
2015-12-16 19:35:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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'];
|
2016-09-09 13:19:29 +03:00
|
|
|
if (this._conflictMode === OC.FileUpload.CONFLICT_MODE_DETECT
|
|
|
|
|| this._conflictMode === OC.FileUpload.CONFLICT_MODE_AUTORENAME) {
|
2015-12-16 19:35:53 +03:00
|
|
|
this.data.headers['If-None-Match'] = '*';
|
|
|
|
}
|
|
|
|
|
2016-10-07 17:27:54 +03:00
|
|
|
var userName = this.uploader.davClient.getUserName();
|
|
|
|
var password = this.uploader.davClient.getPassword();
|
2016-09-03 19:06:35 +03:00
|
|
|
if (userName) {
|
|
|
|
// copy username/password from DAV client
|
|
|
|
this.data.headers['Authorization'] =
|
|
|
|
'Basic ' + btoa(userName + ':' + (password || ''));
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
var chunkFolderPromise;
|
|
|
|
if ($.support.blobSlice
|
|
|
|
&& this.uploader.fileUploadParam.maxChunkSize
|
|
|
|
&& this.getFile().size > this.uploader.fileUploadParam.maxChunkSize
|
|
|
|
) {
|
|
|
|
data.isChunked = true;
|
2016-10-07 17:27:54 +03:00
|
|
|
chunkFolderPromise = this.uploader.davClient.createDirectory(
|
2018-01-10 10:35:40 +03:00
|
|
|
'uploads/' + OC.getCurrentUser().uid + '/' + this.getId()
|
2016-07-15 17:03:02 +03:00
|
|
|
);
|
|
|
|
// TODO: if fails, it means same id already existed, need to retry
|
|
|
|
} else {
|
|
|
|
chunkFolderPromise = $.Deferred().resolve().promise();
|
2019-11-07 22:16:00 +03:00
|
|
|
var mtime = this.getFile().lastModified;
|
|
|
|
if (mtime) {
|
|
|
|
data.headers['X-OC-Mtime'] = mtime / 1000;
|
|
|
|
}
|
2016-07-15 17:03:02 +03:00
|
|
|
}
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
// wait for creation of the required directory before uploading
|
2019-02-17 01:27:50 +03:00
|
|
|
return Promise.all([folderPromise, chunkFolderPromise]).then(function() {
|
|
|
|
if (self.aborted !== true) {
|
|
|
|
data.submit();
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
}, function() {
|
2016-07-15 17:03:02 +03:00
|
|
|
self.abort();
|
2015-12-16 19:35:53 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
},
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
/**
|
|
|
|
* Process end of transfer
|
|
|
|
*/
|
|
|
|
done: function() {
|
|
|
|
if (!this.data.isChunked) {
|
|
|
|
return $.Deferred().resolve().promise();
|
|
|
|
}
|
|
|
|
|
|
|
|
var uid = OC.getCurrentUser().uid;
|
2017-08-11 13:01:04 +03:00
|
|
|
var mtime = this.getFile().lastModified;
|
|
|
|
var size = this.getFile().size;
|
|
|
|
var headers = {};
|
|
|
|
if (mtime) {
|
|
|
|
headers['X-OC-Mtime'] = mtime / 1000;
|
|
|
|
}
|
|
|
|
if (size) {
|
|
|
|
headers['OC-Total-Length'] = size;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-10-07 17:27:54 +03:00
|
|
|
return this.uploader.davClient.move(
|
2018-01-10 10:35:40 +03:00
|
|
|
'uploads/' + uid + '/' + this.getId() + '/.file',
|
|
|
|
'files/' + uid + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()),
|
2016-10-07 17:27:54 +03:00
|
|
|
true,
|
2017-08-11 13:01:04 +03:00
|
|
|
headers
|
2016-10-07 17:27:54 +03:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
_deleteChunkFolder: function() {
|
|
|
|
// delete transfer directory for this upload
|
|
|
|
this.uploader.davClient.remove(
|
2018-01-10 10:35:40 +03:00
|
|
|
'uploads/' + OC.getCurrentUser().uid + '/' + this.getId()
|
2016-07-15 17:03:02 +03:00
|
|
|
);
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Abort the upload
|
|
|
|
*/
|
|
|
|
abort: function() {
|
2016-07-15 17:03:02 +03:00
|
|
|
if (this.data.isChunked) {
|
2016-10-07 17:27:54 +03:00
|
|
|
this._deleteChunkFolder();
|
2016-07-15 17:03:02 +03:00
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
this.data.abort();
|
2016-10-07 17:27:54 +03:00
|
|
|
this.deleteUpload();
|
2019-02-17 01:27:50 +03:00
|
|
|
this.aborted = true;
|
2016-10-07 17:27:54 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Fail the upload
|
|
|
|
*/
|
|
|
|
fail: function() {
|
|
|
|
this.deleteUpload();
|
|
|
|
if (this.data.isChunked) {
|
|
|
|
this._deleteChunkFolder();
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the server response
|
|
|
|
*
|
|
|
|
* @return {Object} response
|
|
|
|
*/
|
|
|
|
getResponse: function() {
|
|
|
|
var response = this.data.response();
|
2021-02-10 20:14:24 +03:00
|
|
|
if (response.errorThrown || response.textStatus === 'error') {
|
2017-08-08 17:37:14 +03:00
|
|
|
// attempt parsing Sabre exception is available
|
|
|
|
var xml = response.jqXHR.responseXML;
|
2019-02-17 01:27:50 +03:00
|
|
|
if (xml && xml.documentElement.localName === 'error' && xml.documentElement.namespaceURI === 'DAV:') {
|
2017-08-08 17:37:14 +03:00
|
|
|
var messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message');
|
|
|
|
var exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception');
|
|
|
|
if (messages.length) {
|
|
|
|
response.message = messages[0].textContent;
|
|
|
|
}
|
|
|
|
if (exceptions.length) {
|
|
|
|
response.exception = exceptions[0].textContent;
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof response.result !== 'string' && response.result) {
|
2015-12-16 19:35:53 +03:00
|
|
|
//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;
|
2015-10-21 12:46:51 +03:00
|
|
|
});
|
2015-12-16 19:35:53 +03:00
|
|
|
if (_.isArray(value) && value.length === 1) {
|
|
|
|
return value[0];
|
|
|
|
}
|
|
|
|
return value;
|
2015-10-21 12:46:51 +03:00
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
};
|
2015-10-21 12:46:51 +03:00
|
|
|
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* keeps track of uploads in progress and implements callbacks for the conflicts dialog
|
2014-06-24 01:56:10 +04:00
|
|
|
* @namespace
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
|
|
|
|
OC.Uploader = function() {
|
|
|
|
this.init.apply(this, arguments);
|
|
|
|
};
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
OC.Uploader.prototype = _.extend({
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* @type Array<OC.FileUpload>
|
|
|
|
*/
|
2016-09-03 19:06:35 +03:00
|
|
|
_uploads: {},
|
2015-12-16 19:35:53 +03:00
|
|
|
|
2018-09-07 17:43:16 +03:00
|
|
|
/**
|
|
|
|
* Count of upload done promises that have not finished yet.
|
|
|
|
*
|
|
|
|
* @type int
|
|
|
|
*/
|
|
|
|
_pendingUploadDoneCount: 0,
|
|
|
|
|
2018-11-05 11:24:25 +03:00
|
|
|
/**
|
|
|
|
* Is it currently uploading?
|
|
|
|
*
|
|
|
|
* @type boolean
|
|
|
|
*/
|
|
|
|
_uploading: false,
|
|
|
|
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
2015-12-16 19:35:53 +03:00
|
|
|
* 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,
|
|
|
|
|
2018-11-13 20:30:32 +03:00
|
|
|
/**
|
|
|
|
* @type OCA.Files.OperationProgressBar
|
|
|
|
*/
|
|
|
|
progressBar: null,
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* @type OC.Files.Client
|
|
|
|
*/
|
|
|
|
filesClient: null,
|
|
|
|
|
2016-10-07 17:27:54 +03:00
|
|
|
/**
|
|
|
|
* Webdav client pointing at the root "dav" endpoint
|
|
|
|
*
|
|
|
|
* @type OC.Files.Client
|
|
|
|
*/
|
|
|
|
davClient: null,
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* 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() {
|
2016-08-31 17:32:14 +03:00
|
|
|
if (window.TESTING) {
|
|
|
|
return true;
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
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
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
isXHRUpload: function () {
|
|
|
|
return !this.fileUploadParam.forceIframeTransport &&
|
|
|
|
((!this.fileUploadParam.multipart && $.support.xhrFileUpload) ||
|
|
|
|
$.support.xhrFormDataFileUpload);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
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) {
|
Fix dropping a folder on a folder row
When the uploaded files have a relative path (that is, when a folder is
uploaded) it is first ensured that all the parent folders exist, which
is done by trying to create them. When a folder is created in the
currently opened folder the file list is updated and a row for the new
folder is added. However, this was done too when the folder already
existed, which caused the previous row to be removed and a new one added
to replace it.
For security reasons, some special headers need to be set in requests;
this is done automatically for jQuery by handling the "ajaxSend" event
in the document. In the case of DAV requests, if the headers are not set
the server rejects the request with "CSRF check not passed".
When a file or folder is dropped on a folder row the jQuery upload
events are chained from the initial drop event, which has the row as its
target. In order to upload the file jQuery performs a request, which
triggers the "ajaxSend" event in the row; this event then bubbles up to
the document, which is then handled by adding the special headers to the
request.
However, when a folder was dropped on a folder row that folder row was
removed when ensuring that the folder exists. The jQuery upload events
were still triggered on the row, but as it had been removed it had no
parent nodes, and thus the events did not bubble up. Due to this the
"ajaxSend" event never reached the document when triggered on the
removed row, the headers were not set, and the upload failed.
All this is simply fixed by not removing the folder row when trying to
create it if it existed already.
Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
2019-01-29 13:40:45 +03:00
|
|
|
if (status !== 405) {
|
|
|
|
self.trigger('createdfolder', fullPath);
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
deferred.resolve();
|
|
|
|
return;
|
|
|
|
}
|
2017-02-14 23:26:00 +03:00
|
|
|
OC.Notification.show(t('files', 'Could not create folder "{dir}"', {dir: fullPath}), {type: 'error'});
|
2015-12-16 19:35:53 +03:00
|
|
|
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;
|
2019-02-17 01:27:50 +03:00
|
|
|
});
|
2020-12-23 20:53:01 +03:00
|
|
|
if (!self._uploading) {
|
|
|
|
self.totalToUpload = 0;
|
|
|
|
}
|
|
|
|
self.totalToUpload += _.reduce(uploads, function(memo, upload) { return memo+upload.getFile().size; }, 0);
|
2019-02-17 01:27:50 +03:00
|
|
|
var semaphore = new OCA.Files.Semaphore(5);
|
|
|
|
var promises = _.map(uploads, function(upload) {
|
|
|
|
return semaphore.acquire().then(function(){
|
|
|
|
return upload.submit().then(function(){
|
|
|
|
semaphore.release();
|
|
|
|
});
|
|
|
|
});
|
2015-12-16 19:35:53 +03:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2018-11-05 11:24:25 +03:00
|
|
|
confirmBeforeUnload: function() {
|
|
|
|
if (this._uploading) {
|
|
|
|
return t('files', 'This will stop your current uploads.')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* 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();
|
2016-09-09 13:19:29 +03:00
|
|
|
// 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;
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
// retrieve more info about this file
|
2016-12-15 12:19:55 +03:00
|
|
|
this.filesClient.getFileInfo(fileUpload.getFullFilePath()).then(function(status, fileInfo) {
|
2015-12-16 19:35:53 +03:00
|
|
|
var original = fileInfo;
|
|
|
|
var replacement = file;
|
2016-12-15 12:19:55 +03:00
|
|
|
original.directory = original.path;
|
2015-12-16 19:35:53 +03:00
|
|
|
OC.dialogs.fileexists(fileUpload, original, replacement, self);
|
|
|
|
});
|
2013-09-07 00:40:10 +04:00
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* cancels all uploads
|
|
|
|
*/
|
2013-08-16 13:40:55 +04:00
|
|
|
cancelUploads:function() {
|
2013-09-18 16:39:39 +04:00
|
|
|
this.log('canceling uploads');
|
2015-12-16 19:35:53 +03:00
|
|
|
jQuery.each(this._uploads, function(i, upload) {
|
|
|
|
upload.abort();
|
2013-08-16 13:40:55 +04:00
|
|
|
});
|
2015-12-16 19:35:53 +03:00
|
|
|
this.clear();
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Clear uploads
|
|
|
|
*/
|
|
|
|
clear: function() {
|
|
|
|
this._knownDirs = {};
|
2013-06-27 00:51:38 +04:00
|
|
|
},
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* Returns an upload by id
|
|
|
|
*
|
|
|
|
* @param {int} data uploadId
|
|
|
|
* @return {OC.FileUpload} file upload
|
|
|
|
*/
|
|
|
|
getUpload: function(data) {
|
|
|
|
if (_.isString(data)) {
|
|
|
|
return this._uploads[data];
|
2017-03-30 02:24:28 +03:00
|
|
|
} else if (data.uploadId && this._uploads[data.uploadId]) {
|
|
|
|
this._uploads[data.uploadId].data = data;
|
2015-12-16 19:35:53 +03:00
|
|
|
return this._uploads[data.uploadId];
|
2013-06-27 00:51:38 +04:00
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
return null;
|
2013-08-16 13:40:55 +04:00
|
|
|
},
|
2015-12-16 19:35:53 +03:00
|
|
|
|
2018-04-19 10:52:50 +03:00
|
|
|
/**
|
|
|
|
* Removes an upload from the list of known uploads.
|
|
|
|
*
|
|
|
|
* @param {OC.FileUpload} upload the upload to remove.
|
|
|
|
*/
|
|
|
|
removeUpload: function(upload) {
|
|
|
|
if (!upload || !upload.data || !upload.data.uploadId) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete this._uploads[upload.data.uploadId];
|
|
|
|
},
|
|
|
|
|
2015-10-07 17:34:06 +03:00
|
|
|
showUploadCancelMessage: _.debounce(function() {
|
2017-02-14 23:26:00 +03:00
|
|
|
OC.Notification.show(t('files', 'Upload cancelled.'), {timeout : 7, type: 'error'});
|
2015-10-07 17:34:06 +03:00
|
|
|
}, 500),
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* callback for the conflicts dialog
|
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
onCancel:function() {
|
2013-09-07 00:40:10 +04:00
|
|
|
this.cancelUploads();
|
2013-08-12 14:33:22 +04:00
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* callback for the conflicts dialog
|
|
|
|
* calls onSkip, onReplace or onAutorename for each conflict
|
2013-09-19 13:11:22 +04:00
|
|
|
* @param {object} conflicts - list of conflict elements
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2013-09-05 19:46:19 +04:00
|
|
|
onContinue:function(conflicts) {
|
|
|
|
var self = this;
|
|
|
|
//iterate over all conflicts
|
|
|
|
jQuery.each(conflicts, function (i, conflict) {
|
|
|
|
conflict = $(conflict);
|
|
|
|
var keepOriginal = conflict.find('.original input[type="checkbox"]:checked').length === 1;
|
|
|
|
var keepReplacement = conflict.find('.replacement input[type="checkbox"]:checked').length === 1;
|
|
|
|
if (keepOriginal && keepReplacement) {
|
|
|
|
// when both selected -> autorename
|
|
|
|
self.onAutorename(conflict.data('data'));
|
|
|
|
} else if (keepReplacement) {
|
|
|
|
// when only replacement selected -> overwrite
|
|
|
|
self.onReplace(conflict.data('data'));
|
|
|
|
} else {
|
2019-01-21 21:11:13 +03:00
|
|
|
// when only original selected -> skip
|
2013-09-05 19:46:19 +04:00
|
|
|
// when none selected -> skip
|
|
|
|
self.onSkip(conflict.data('data'));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* handle skipping an upload
|
2015-12-16 19:35:53 +03:00
|
|
|
* @param {OC.FileUpload} upload
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
onSkip:function(upload) {
|
|
|
|
this.log('skip', null, upload);
|
|
|
|
upload.deleteUpload();
|
2013-06-27 00:51:38 +04:00
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* handle replacing a file on the server with an uploaded file
|
2015-12-16 19:35:53 +03:00
|
|
|
* @param {FileUpload} data
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
onReplace:function(upload) {
|
|
|
|
this.log('replace', null, upload);
|
|
|
|
upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_OVERWRITE);
|
2016-09-03 19:06:35 +03:00
|
|
|
this.submitUploads([upload]);
|
2013-06-27 00:51:38 +04:00
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
|
|
|
* handle uploading a file and letting the server decide a new name
|
2015-12-16 19:35:53 +03:00
|
|
|
* @param {object} upload
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
onAutorename:function(upload) {
|
|
|
|
this.log('autorename', null, upload);
|
|
|
|
upload.setConflictMode(OC.FileUpload.CONFLICT_MODE_AUTORENAME);
|
2016-09-09 13:19:29 +03:00
|
|
|
|
|
|
|
do {
|
|
|
|
upload.autoRename();
|
|
|
|
// if file known to exist on the client side, retry
|
|
|
|
} while (this.fileList && this.fileList.inList(upload.getFileName()));
|
|
|
|
|
|
|
|
// resubmit upload
|
2016-09-03 19:06:35 +03:00
|
|
|
this.submitUploads([upload]);
|
2013-08-16 13:40:55 +04:00
|
|
|
},
|
2018-11-05 11:24:25 +03:00
|
|
|
_trace: false, //TODO implement log handler for JS per class?
|
|
|
|
log: function(caption, e, data) {
|
2013-09-18 16:39:39 +04:00
|
|
|
if (this._trace) {
|
|
|
|
console.log(caption);
|
|
|
|
console.log(data);
|
|
|
|
}
|
2013-08-22 16:29:00 +04:00
|
|
|
},
|
2013-09-08 12:41:20 +04:00
|
|
|
/**
|
2015-09-28 18:50:11 +03:00
|
|
|
* checks the list of existing files prior to uploading and shows a simple dialog to choose
|
2013-09-19 13:13:11 +04:00
|
|
|
* skip all, replace all or choose which files to keep
|
2015-09-28 18:50:11 +03:00
|
|
|
*
|
2013-09-19 13:11:22 +04:00
|
|
|
* @param {array} selection of files to upload
|
|
|
|
* @param {object} callbacks - object with several callback methods
|
2019-09-25 19:19:42 +03:00
|
|
|
* @param {Function} callbacks.onNoConflicts
|
|
|
|
* @param {Function} callbacks.onSkipConflicts
|
|
|
|
* @param {Function} callbacks.onReplaceConflicts
|
|
|
|
* @param {Function} callbacks.onChooseConflicts
|
|
|
|
* @param {Function} callbacks.onCancel
|
2013-09-08 12:41:20 +04:00
|
|
|
*/
|
2013-10-22 20:11:03 +04:00
|
|
|
checkExistingFiles: function (selection, callbacks) {
|
2015-12-16 19:35:53 +03:00
|
|
|
var fileList = this.fileList;
|
2015-09-28 18:50:11 +03:00
|
|
|
var conflicts = [];
|
|
|
|
// only keep non-conflicting uploads
|
|
|
|
selection.uploads = _.filter(selection.uploads, function(upload) {
|
2015-12-16 19:35:53 +03:00
|
|
|
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);
|
2015-09-28 18:50:11 +03:00
|
|
|
if (fileInfo) {
|
|
|
|
conflicts.push([
|
|
|
|
// original
|
|
|
|
_.extend(fileInfo, {
|
|
|
|
directory: fileInfo.directory || fileInfo.path || fileList.getCurrentDirectory()
|
|
|
|
}),
|
|
|
|
// replacement (File object)
|
|
|
|
upload
|
|
|
|
]);
|
|
|
|
return false;
|
2014-12-08 17:26:31 +03:00
|
|
|
}
|
2015-09-28 18:50:11 +03:00
|
|
|
return true;
|
2014-12-08 17:26:31 +03:00
|
|
|
});
|
2015-09-28 18:50:11 +03:00
|
|
|
if (conflicts.length) {
|
2015-10-02 10:55:43 +03:00
|
|
|
// wait for template loading
|
2015-12-16 19:35:53 +03:00
|
|
|
OC.dialogs.fileexists(null, null, null, this).done(function() {
|
2015-10-02 10:55:43 +03:00
|
|
|
_.each(conflicts, function(conflictData) {
|
2015-12-16 19:35:53 +03:00
|
|
|
OC.dialogs.fileexists(conflictData[1], conflictData[0], conflictData[1].getFile(), this);
|
2015-10-02 10:55:43 +03:00
|
|
|
});
|
2015-09-28 18:50:11 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// upload non-conflicting files
|
|
|
|
// note: when reaching the server they might still meet conflicts
|
|
|
|
// if the folder was concurrently modified, these will get added
|
|
|
|
// to the already visible dialog, if applicable
|
2013-08-22 16:29:00 +04:00
|
|
|
callbacks.onNoConflicts(selection);
|
2014-03-06 16:49:57 +04:00
|
|
|
},
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2018-09-07 17:43:16 +03:00
|
|
|
_updateProgressBarOnUploadStop: function() {
|
|
|
|
if (this._pendingUploadDoneCount === 0) {
|
|
|
|
// All the uploads ended and there is no pending operation, so hide
|
|
|
|
// the progress bar.
|
|
|
|
// Note that this happens here only with non-chunked uploads; if the
|
|
|
|
// upload was chunked then this will have been executed after all
|
|
|
|
// the uploads ended but before the upload done handler that reduces
|
|
|
|
// the pending operation count was executed.
|
|
|
|
this._hideProgressBar();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-09 15:30:43 +03:00
|
|
|
this._setProgressBarText(t('files', 'Processing files …'), t('files', '…'));
|
2018-09-07 17:43:16 +03:00
|
|
|
|
|
|
|
// Nothing is being uploaded at this point, and the pending operations
|
|
|
|
// can not be cancelled, so the cancel button should be hidden.
|
2018-11-13 00:18:45 +03:00
|
|
|
this._hideCancelButton();
|
|
|
|
},
|
|
|
|
|
|
|
|
_hideProgressBar: function() {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.hideProgressBar();
|
2018-11-13 00:18:45 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_hideCancelButton: function() {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.hideCancelButton();
|
2018-09-07 17:43:16 +03:00
|
|
|
},
|
|
|
|
|
2014-05-23 21:02:50 +04:00
|
|
|
_showProgressBar: function() {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.showProgressBar();
|
2015-12-16 19:35:53 +03:00
|
|
|
},
|
|
|
|
|
2018-11-13 00:18:45 +03:00
|
|
|
_setProgressBarValue: function(value) {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.setProgressBarValue(value);
|
2018-11-13 00:18:45 +03:00
|
|
|
},
|
|
|
|
|
|
|
|
_setProgressBarText: function(textDesktop, textMobile, title) {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.setProgressBarText(textDesktop, textMobile, title);
|
2018-11-13 00:18:45 +03:00
|
|
|
},
|
|
|
|
|
2016-02-16 14:39:44 +03:00
|
|
|
/**
|
|
|
|
* Returns whether the given file is known to be a received shared file
|
|
|
|
*
|
|
|
|
* @param {Object} file file
|
|
|
|
* @return {bool} true if the file is a shared file
|
|
|
|
*/
|
|
|
|
_isReceivedSharedFile: function(file) {
|
|
|
|
if (!window.FileList) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
var $tr = window.FileList.findFileEl(file.name);
|
|
|
|
if (!$tr.length) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ($tr.attr('data-mounttype') === 'shared-root' && $tr.attr('data-mime') !== 'httpd/unix-directory');
|
|
|
|
},
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
/**
|
|
|
|
* 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) {
|
2016-02-16 14:39:44 +03:00
|
|
|
var self = this;
|
2015-12-16 19:35:53 +03:00
|
|
|
|
|
|
|
options = options || {};
|
|
|
|
|
|
|
|
this.fileList = options.fileList;
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar = options.progressBar;
|
2015-12-16 19:35:53 +03:00
|
|
|
this.filesClient = options.filesClient || OC.Files.getClient();
|
2016-10-07 17:27:54 +03:00
|
|
|
this.davClient = new OC.Files.Client({
|
|
|
|
host: this.filesClient.getHost(),
|
|
|
|
root: OC.linkToRemoteBase('dav'),
|
|
|
|
useHTTPS: OC.getProtocol() === 'https',
|
|
|
|
userName: this.filesClient.getUserName(),
|
|
|
|
password: this.filesClient.getPassword()
|
|
|
|
});
|
2015-12-16 19:35:53 +03:00
|
|
|
|
|
|
|
$uploadEl = $($uploadEl);
|
|
|
|
this.$uploadEl = $uploadEl;
|
|
|
|
|
|
|
|
if ($uploadEl.exists()) {
|
2018-11-13 20:30:32 +03:00
|
|
|
this.progressBar.on('cancel', function() {
|
2016-09-03 19:06:35 +03:00
|
|
|
self.cancelUploads();
|
2015-12-16 19:35:53 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
this.fileUploadParam = {
|
|
|
|
type: 'PUT',
|
|
|
|
dropZone: options.dropZone, // restrict dropZone to content div
|
2014-03-06 16:49:57 +04:00
|
|
|
autoUpload: false,
|
2019-01-28 00:59:14 +03:00
|
|
|
sequentialUploads: false,
|
|
|
|
limitConcurrentUploads: 10,
|
2014-03-06 16:49:57 +04:00
|
|
|
/**
|
|
|
|
* on first add of every selection
|
|
|
|
* - check all files of originalFiles array with files in dir
|
|
|
|
* - on conflict show dialog
|
|
|
|
* - skip all -> remember as single skip action for all conflicting files
|
|
|
|
* - replace all -> remember as single replace action for all conflicting files
|
|
|
|
* - choose -> show choose dialog
|
|
|
|
* - mark files to keep
|
|
|
|
* - when only existing -> remember as single skip action
|
|
|
|
* - when only new -> remember as single replace action
|
|
|
|
* - when both -> remember as single autorename action
|
|
|
|
* - start uploading selection
|
|
|
|
* @param {object} e
|
|
|
|
* @param {object} data
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
add: function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('add', e, data);
|
2020-11-13 14:46:12 +03:00
|
|
|
var that = $(this), freeSpace = 0;
|
2014-03-06 16:49:57 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
var upload = new OC.FileUpload(self, data);
|
|
|
|
// can't link directly due to jQuery not liking cyclic deps on its ajax object
|
2016-07-15 17:03:02 +03:00
|
|
|
data.uploadId = upload.getId();
|
2015-12-16 19:35:53 +03:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// create a container where we can store the data objects
|
|
|
|
if ( ! data.originalFiles.selection ) {
|
|
|
|
// initialize selection and remember number of files to upload
|
|
|
|
data.originalFiles.selection = {
|
|
|
|
uploads: [],
|
|
|
|
filesToUpload: data.originalFiles.length,
|
2016-09-19 22:29:48 +03:00
|
|
|
totalBytes: 0
|
2014-03-06 16:49:57 +04:00
|
|
|
};
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
// TODO: move originalFiles to a separate container, maybe inside OC.Upload
|
2014-03-06 16:49:57 +04:00
|
|
|
var selection = data.originalFiles.selection;
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// add uploads
|
|
|
|
if ( selection.uploads.length < selection.filesToUpload ) {
|
|
|
|
// remember upload
|
2015-12-16 19:35:53 +03:00
|
|
|
selection.uploads.push(upload);
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
//examine file
|
2015-12-16 19:35:53 +03:00
|
|
|
var file = upload.getFile();
|
2014-03-06 16:49:57 +04:00
|
|
|
try {
|
|
|
|
// FIXME: not so elegant... need to refactor that method to return a value
|
2014-04-08 19:17:48 +04:00
|
|
|
Files.isFileNameValid(file.name);
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
|
|
|
catch (errorMessage) {
|
|
|
|
data.textStatus = 'invalidcharacters';
|
|
|
|
data.errorThrown = errorMessage;
|
|
|
|
}
|
2013-12-08 19:17:35 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
if (data.targetDir) {
|
|
|
|
upload.setTargetFolder(data.targetDir);
|
|
|
|
delete data.targetDir;
|
|
|
|
}
|
|
|
|
|
2014-04-10 01:32:12 +04:00
|
|
|
// in case folder drag and drop is not supported file will point to a directory
|
2014-04-15 23:39:13 +04:00
|
|
|
// http://stackoverflow.com/a/20448357
|
2020-04-22 14:40:23 +03:00
|
|
|
if ( !file.type && file.size % 4096 === 0 && file.size <= 102400) {
|
2015-02-06 13:15:09 +03:00
|
|
|
var dirUploadFailure = false;
|
2014-04-10 01:32:12 +04:00
|
|
|
try {
|
2014-05-09 16:06:59 +04:00
|
|
|
var reader = new FileReader();
|
|
|
|
reader.readAsBinaryString(file);
|
2020-04-22 14:40:23 +03:00
|
|
|
} catch (error) {
|
|
|
|
console.log(reader, error)
|
2014-04-10 01:32:12 +04:00
|
|
|
//file is a directory
|
2015-02-06 13:15:09 +03:00
|
|
|
dirUploadFailure = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dirUploadFailure) {
|
2014-04-10 01:32:12 +04:00
|
|
|
data.textStatus = 'dirorzero';
|
2014-05-09 16:06:59 +04:00
|
|
|
data.errorThrown = t('files',
|
|
|
|
'Unable to upload {filename} as it is a directory or has 0 bytes',
|
2014-04-10 01:32:12 +04:00
|
|
|
{filename: file.name}
|
|
|
|
);
|
|
|
|
}
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2016-02-16 14:39:44 +03:00
|
|
|
// only count if we're not overwriting an existing shared file
|
|
|
|
if (self._isReceivedSharedFile(file)) {
|
|
|
|
file.isReceivedShare = true;
|
|
|
|
} else {
|
|
|
|
// add size
|
|
|
|
selection.totalBytes += file.size;
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// check free space
|
2020-11-13 14:46:12 +03:00
|
|
|
if (!self.fileList || upload.getTargetFolder() === self.fileList.getCurrentDirectory()) {
|
|
|
|
// Use global free space if there is no file list to check or the current directory is the target
|
|
|
|
freeSpace = $('#free_space').val()
|
|
|
|
} else if (upload.getTargetFolder().indexOf(self.fileList.getCurrentDirectory()) === 0) {
|
|
|
|
// Check subdirectory free space if file is uploaded there
|
|
|
|
var targetSubdir = upload._targetFolder.replace(self.fileList.getCurrentDirectory(), '')
|
|
|
|
freeSpace = parseInt(upload.uploader.fileList.getModelForFile(targetSubdir).get('quotaAvailableBytes'))
|
|
|
|
}
|
2014-03-06 16:50:53 +04:00
|
|
|
if (freeSpace >= 0 && selection.totalBytes > freeSpace) {
|
2014-03-06 16:49:57 +04:00
|
|
|
data.textStatus = 'notenoughspace';
|
2014-05-09 16:06:59 +04:00
|
|
|
data.errorThrown = t('files',
|
|
|
|
'Not enough free space, you are uploading {size1} but only {size2} is left', {
|
2020-02-07 19:08:50 +03:00
|
|
|
'size1': OC.Util.humanFileSize(selection.totalBytes),
|
2020-11-13 14:46:12 +03:00
|
|
|
'size2': OC.Util.humanFileSize(freeSpace)
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
}
|
2013-06-27 00:51:38 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// end upload for whole selection on error
|
|
|
|
if (data.errorThrown) {
|
2015-12-16 19:35:53 +03:00
|
|
|
// trigger fileupload fail handler
|
2014-03-06 16:49:57 +04:00
|
|
|
var fu = that.data('blueimp-fileupload') || that.data('fileupload');
|
|
|
|
fu._trigger('fail', e, data);
|
|
|
|
return false; //don't upload anything
|
|
|
|
}
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// check existing files when all is collected
|
|
|
|
if ( selection.uploads.length >= selection.filesToUpload ) {
|
|
|
|
|
|
|
|
//remove our selection hack:
|
|
|
|
delete data.originalFiles.selection;
|
|
|
|
|
|
|
|
var callbacks = {
|
|
|
|
|
|
|
|
onNoConflicts: function (selection) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.submitUploads(selection.uploads);
|
2014-03-06 16:49:57 +04:00
|
|
|
},
|
|
|
|
onSkipConflicts: function (selection) {
|
|
|
|
//TODO mark conflicting files as toskip
|
|
|
|
},
|
|
|
|
onReplaceConflicts: function (selection) {
|
|
|
|
//TODO mark conflicting files as toreplace
|
|
|
|
},
|
|
|
|
onChooseConflicts: function (selection) {
|
|
|
|
//TODO mark conflicting files as chosen
|
|
|
|
},
|
|
|
|
onCancel: function (selection) {
|
|
|
|
$.each(selection.uploads, function(i, upload) {
|
|
|
|
upload.abort();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
2013-06-27 00:51:38 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
self.checkExistingFiles(selection, callbacks);
|
2014-03-04 19:42:40 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2013-06-27 00:51:38 +04:00
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
return true; // continue adding files
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* called after the first add, does NOT have the data param
|
|
|
|
* @param {object} e
|
|
|
|
*/
|
|
|
|
start: function(e) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('start', e, null);
|
2014-03-20 02:05:03 +04:00
|
|
|
//hide the tooltip otherwise it covers the progress bar
|
2017-02-28 00:25:05 +03:00
|
|
|
$('#upload').tooltip('hide');
|
2018-11-05 11:24:25 +03:00
|
|
|
self._uploading = true;
|
2014-03-06 16:49:57 +04:00
|
|
|
},
|
|
|
|
fail: function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
var upload = self.getUpload(data);
|
2016-07-15 17:03:02 +03:00
|
|
|
var status = null;
|
|
|
|
if (upload) {
|
|
|
|
status = upload.getResponseStatus();
|
|
|
|
}
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('fail', e, upload);
|
|
|
|
|
2018-04-19 10:52:50 +03:00
|
|
|
self.removeUpload(upload);
|
|
|
|
|
2019-02-17 01:27:50 +03:00
|
|
|
if (data.textStatus === 'abort' || data.errorThrown === 'abort') {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.showUploadCancelMessage();
|
|
|
|
} else if (status === 412) {
|
|
|
|
// file already exists
|
|
|
|
self.showConflict(upload);
|
|
|
|
} else if (status === 404) {
|
|
|
|
// target folder does not exist any more
|
2017-02-14 23:26:00 +03:00
|
|
|
OC.Notification.show(t('files', 'Target folder "{dir}" does not exist any more', {dir: upload.getFullPath()} ), {type: 'error'});
|
2015-12-16 19:35:53 +03:00
|
|
|
self.cancelUploads();
|
2018-12-06 00:40:07 +03:00
|
|
|
} else if (data.textStatus === 'notenoughspace') {
|
2015-12-16 19:35:53 +03:00
|
|
|
// not enough space
|
2017-02-14 23:26:00 +03:00
|
|
|
OC.Notification.show(t('files', 'Not enough free space'), {type: 'error'});
|
2015-12-16 19:35:53 +03:00
|
|
|
self.cancelUploads();
|
|
|
|
} else {
|
|
|
|
// HTTP connection problem or other error
|
2018-11-05 11:24:25 +03:00
|
|
|
var message = t('files', 'An unknown error has occurred');
|
2017-08-08 17:37:14 +03:00
|
|
|
if (upload) {
|
|
|
|
var response = upload.getResponse();
|
2018-11-05 11:24:25 +03:00
|
|
|
if (response) {
|
|
|
|
message = response.message;
|
|
|
|
}
|
2017-08-08 17:37:14 +03:00
|
|
|
}
|
2020-04-22 14:40:23 +03:00
|
|
|
console.error(e, data, response)
|
2017-08-08 17:37:14 +03:00
|
|
|
OC.Notification.show(message || data.errorThrown, {type: 'error'});
|
2013-09-16 16:10:19 +04:00
|
|
|
}
|
2016-08-31 17:32:14 +03:00
|
|
|
|
|
|
|
if (upload) {
|
2016-10-07 17:27:54 +03:00
|
|
|
upload.fail();
|
2016-08-31 17:32:14 +03:00
|
|
|
}
|
2014-03-06 16:49:57 +04:00
|
|
|
},
|
|
|
|
/**
|
|
|
|
* called for every successful upload
|
|
|
|
* @param {object} e
|
|
|
|
* @param {object} data
|
|
|
|
*/
|
2015-12-16 19:35:53 +03:00
|
|
|
done:function(e, data) {
|
|
|
|
var upload = self.getUpload(data);
|
|
|
|
var that = $(this);
|
|
|
|
self.log('done', e, upload);
|
2014-03-06 16:49:57 +04:00
|
|
|
|
2018-04-19 10:52:50 +03:00
|
|
|
self.removeUpload(upload);
|
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
var status = upload.getResponseStatus();
|
|
|
|
if (status < 200 || status >= 300) {
|
|
|
|
// trigger fail handler
|
|
|
|
var fu = that.data('blueimp-fileupload') || that.data('fileupload');
|
2014-03-06 16:49:57 +04:00
|
|
|
fu._trigger('fail', e, data);
|
2015-12-16 19:35:53 +03:00
|
|
|
return;
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* called after last upload
|
|
|
|
* @param {object} e
|
|
|
|
* @param {object} data
|
|
|
|
*/
|
|
|
|
stop: function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('stop', e, data);
|
2018-11-05 11:24:25 +03:00
|
|
|
self._uploading = false;
|
2013-09-16 16:10:19 +04:00
|
|
|
}
|
2014-03-06 16:49:57 +04:00
|
|
|
};
|
|
|
|
|
2016-10-07 17:27:54 +03:00
|
|
|
if (options.maxChunkSize) {
|
|
|
|
this.fileUploadParam.maxChunkSize = options.maxChunkSize;
|
|
|
|
}
|
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// initialize jquery fileupload (blueimp)
|
2015-12-16 19:35:53 +03:00
|
|
|
var fileupload = this.$uploadEl.fileupload(this.fileUploadParam);
|
2014-03-06 16:49:57 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
if (this._supportAjaxUploadWithProgress()) {
|
2014-08-12 02:32:46 +04:00
|
|
|
//remaining time
|
2017-05-30 15:58:24 +03:00
|
|
|
var lastUpdate, lastSize, bufferSize, buffer, bufferIndex, bufferIndex2, bufferTotal;
|
2015-12-16 19:35:53 +03:00
|
|
|
|
2018-06-08 21:37:43 +03:00
|
|
|
var dragging = false;
|
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
// add progress handlers
|
|
|
|
fileupload.on('fileuploadadd', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadadd', e, data);
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('add', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
// add progress handlers
|
|
|
|
fileupload.on('fileuploadstart', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadstart', e, data);
|
2018-11-13 00:18:45 +03:00
|
|
|
self._setProgressBarText(t('files', 'Uploading …'), t('files', '…'));
|
|
|
|
self._setProgressBarValue(0);
|
2015-12-16 19:35:53 +03:00
|
|
|
self._showProgressBar();
|
2017-05-30 15:58:24 +03:00
|
|
|
// initial remaining time variables
|
|
|
|
lastUpdate = new Date().getTime();
|
|
|
|
lastSize = 0;
|
|
|
|
bufferSize = 20;
|
|
|
|
buffer = [];
|
|
|
|
bufferIndex = 0;
|
|
|
|
bufferIndex2 = 0;
|
|
|
|
bufferTotal = 0;
|
|
|
|
for(var i = 0; i < bufferSize; i++){
|
|
|
|
buffer[i] = 0;
|
|
|
|
}
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('start', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
fileupload.on('fileuploadprogress', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadprogress', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
//TODO progressbar in row
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('progress', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
fileupload.on('fileuploadprogressall', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadprogressall', e, data);
|
2019-02-17 01:27:50 +03:00
|
|
|
var total = self.totalToUpload;
|
|
|
|
var progress = (data.loaded / total) * 100;
|
2017-05-30 15:58:24 +03:00
|
|
|
var thisUpdate = new Date().getTime();
|
2014-08-12 02:32:46 +04:00
|
|
|
var diffUpdate = (thisUpdate - lastUpdate)/1000; // eg. 2s
|
|
|
|
lastUpdate = thisUpdate;
|
|
|
|
var diffSize = data.loaded - lastSize;
|
|
|
|
lastSize = data.loaded;
|
2017-05-30 15:58:24 +03:00
|
|
|
diffSize = diffSize / diffUpdate; // apply timing factor, eg. 1MiB/2s = 0.5MiB/s, unit is byte per second
|
2019-02-17 01:27:50 +03:00
|
|
|
var remainingSeconds = ((total - data.loaded) / diffSize);
|
2014-08-14 14:32:08 +04:00
|
|
|
if(remainingSeconds >= 0) {
|
2014-08-13 15:18:24 +04:00
|
|
|
bufferTotal = bufferTotal - (buffer[bufferIndex]) + remainingSeconds;
|
2014-08-14 14:32:08 +04:00
|
|
|
buffer[bufferIndex] = remainingSeconds; //buffer to make it smoother
|
2014-08-12 02:32:46 +04:00
|
|
|
bufferIndex = (bufferIndex + 1) % bufferSize;
|
2017-03-17 14:59:33 +03:00
|
|
|
bufferIndex2++;
|
2014-08-12 02:32:46 +04:00
|
|
|
}
|
2017-03-06 14:08:35 +03:00
|
|
|
var smoothRemainingSeconds;
|
2017-03-17 14:59:33 +03:00
|
|
|
if (bufferIndex2 > 0 && bufferIndex2 < 20) {
|
|
|
|
smoothRemainingSeconds = bufferTotal / bufferIndex2;
|
|
|
|
} else if (bufferSize > 0) {
|
|
|
|
smoothRemainingSeconds = bufferTotal / bufferSize;
|
|
|
|
} else {
|
|
|
|
smoothRemainingSeconds = 1;
|
2017-03-06 14:08:35 +03:00
|
|
|
}
|
|
|
|
|
2016-12-19 17:40:36 +03:00
|
|
|
var h = moment.duration(smoothRemainingSeconds, "seconds").humanize();
|
2017-08-23 16:43:52 +03:00
|
|
|
if (!(smoothRemainingSeconds >= 0 && smoothRemainingSeconds < 14400)) {
|
|
|
|
// show "Uploading ..." for durations longer than 4 hours
|
2017-10-25 01:11:05 +03:00
|
|
|
h = t('files', 'Uploading …');
|
2017-08-23 16:43:52 +03:00
|
|
|
}
|
2018-11-13 00:18:45 +03:00
|
|
|
self._setProgressBarText(h, h, t('files', '{loadedSize} of {totalSize} ({bitrate})' , {
|
2020-02-07 19:08:50 +03:00
|
|
|
loadedSize: OC.Util.humanFileSize(data.loaded),
|
|
|
|
totalSize: OC.Util.humanFileSize(total),
|
|
|
|
bitrate: OC.Util.humanFileSize(data.bitrate / 8) + '/s'
|
2018-11-13 00:18:45 +03:00
|
|
|
}));
|
|
|
|
self._setProgressBarValue(progress);
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('progressall', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
fileupload.on('fileuploadstop', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadstop', e, data);
|
|
|
|
|
|
|
|
self.clear();
|
2018-09-07 17:43:16 +03:00
|
|
|
self._updateProgressBarOnUploadStop();
|
2016-09-21 19:49:15 +03:00
|
|
|
self.trigger('stop', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
|
|
|
fileupload.on('fileuploadfail', function(e, data) {
|
2015-12-16 19:35:53 +03:00
|
|
|
self.log('progress handle fileuploadfail', e, data);
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('fail', e, data);
|
2014-03-06 16:49:57 +04:00
|
|
|
});
|
2016-06-15 14:16:11 +03:00
|
|
|
fileupload.on('fileuploaddragover', function(e){
|
2016-06-14 00:00:56 +03:00
|
|
|
$('#app-content').addClass('file-drag');
|
2016-06-23 12:25:03 +03:00
|
|
|
$('#emptycontent .icon-folder').addClass('icon-filetype-folder-drag-accept');
|
2016-06-15 02:03:59 +03:00
|
|
|
|
|
|
|
var filerow = $(e.delegatedEvent.target).closest('tr');
|
|
|
|
|
|
|
|
if(!filerow.hasClass('dropping-to-dir')){
|
2016-06-23 12:25:03 +03:00
|
|
|
$('.dropping-to-dir .icon-filetype-folder-drag-accept').removeClass('icon-filetype-folder-drag-accept');
|
2016-06-15 02:03:59 +03:00
|
|
|
$('.dropping-to-dir').removeClass('dropping-to-dir');
|
2016-06-15 12:28:53 +03:00
|
|
|
$('.dir-drop').removeClass('dir-drop');
|
2016-06-15 02:03:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if(filerow.attr('data-type') === 'dir'){
|
2016-06-15 12:28:53 +03:00
|
|
|
$('#app-content').addClass('dir-drop');
|
2016-06-15 02:03:59 +03:00
|
|
|
filerow.addClass('dropping-to-dir');
|
2016-06-15 14:16:11 +03:00
|
|
|
filerow.find('.thumbnail').addClass('icon-filetype-folder-drag-accept');
|
2016-06-15 02:03:59 +03:00
|
|
|
}
|
2018-06-08 21:37:43 +03:00
|
|
|
|
|
|
|
dragging = true;
|
2016-06-14 00:00:56 +03:00
|
|
|
});
|
2018-06-08 20:07:19 +03:00
|
|
|
|
|
|
|
var disableDropState = 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');
|
2018-06-08 21:37:43 +03:00
|
|
|
|
|
|
|
dragging = false;
|
2018-06-08 20:07:19 +03:00
|
|
|
};
|
|
|
|
|
2018-06-08 12:10:46 +03:00
|
|
|
fileupload.on('fileuploaddragleave fileuploaddrop', disableDropState);
|
2014-12-18 23:33:08 +03:00
|
|
|
|
2018-06-08 14:40:48 +03:00
|
|
|
// In some browsers the "drop" event can be triggered with no
|
|
|
|
// files even if the "dragover" event seemed to suggest that a
|
|
|
|
// file was being dragged (and thus caused "fileuploaddragover"
|
|
|
|
// to be triggered).
|
|
|
|
fileupload.on('fileuploaddropnofiles', function() {
|
2018-06-08 21:37:43 +03:00
|
|
|
if (!dragging) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-06-08 14:40:48 +03:00
|
|
|
disableDropState();
|
|
|
|
|
|
|
|
OC.Notification.show(t('files', 'Uploading that item is not supported'), {type: 'error'});
|
|
|
|
});
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
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];
|
2016-10-07 17:27:54 +03:00
|
|
|
var chunkId = range.split('/')[0].split('-')[0];
|
2016-07-15 17:03:02 +03:00
|
|
|
data.url = OC.getRootPath() +
|
|
|
|
'/remote.php/dav/uploads' +
|
2018-01-10 10:35:40 +03:00
|
|
|
'/' + OC.getCurrentUser().uid +
|
|
|
|
'/' + upload.getId() +
|
|
|
|
'/' + chunkId;
|
2016-07-15 17:03:02 +03:00
|
|
|
delete data.contentRange;
|
|
|
|
delete data.headers['Content-Range'];
|
2014-12-18 23:33:08 +03:00
|
|
|
});
|
2016-07-15 17:03:02 +03:00
|
|
|
fileupload.on('fileuploaddone', function(e, data) {
|
|
|
|
var upload = self.getUpload(data);
|
2018-09-07 17:43:16 +03:00
|
|
|
|
|
|
|
self._pendingUploadDoneCount++;
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
upload.done().then(function() {
|
2018-09-07 17:43:16 +03:00
|
|
|
self._pendingUploadDoneCount--;
|
|
|
|
if (Object.keys(self._uploads).length === 0 && self._pendingUploadDoneCount === 0) {
|
|
|
|
// All the uploads ended and there is no pending
|
|
|
|
// operation, so hide the progress bar.
|
|
|
|
// Note that this happens here only with chunked
|
|
|
|
// uploads; if the upload was non-chunked then this
|
|
|
|
// handler is immediately executed, before the
|
|
|
|
// jQuery upload done handler that removes the
|
|
|
|
// upload from the list, and thus at this point
|
|
|
|
// there is still at least one upload that has not
|
|
|
|
// ended (although the upload stop handler is always
|
|
|
|
// executed after all the uploads have ended, which
|
|
|
|
// hides the progress bar in that case).
|
|
|
|
self._hideProgressBar();
|
|
|
|
}
|
|
|
|
|
2016-07-15 17:03:02 +03:00
|
|
|
self.trigger('done', e, upload);
|
2017-08-08 17:37:14 +03:00
|
|
|
}).fail(function(status, response) {
|
|
|
|
var message = response.message;
|
2016-10-07 17:27:54 +03:00
|
|
|
if (status === 507) {
|
|
|
|
// not enough space
|
2017-08-08 17:37:14 +03:00
|
|
|
OC.Notification.show(message || t('files', 'Not enough free space'), {type: 'error'});
|
2016-10-07 17:27:54 +03:00
|
|
|
self.cancelUploads();
|
|
|
|
} else if (status === 409) {
|
2017-08-08 17:37:14 +03:00
|
|
|
OC.Notification.show(message || t('files', 'Target folder does not exist any more'), {type: 'error'});
|
2020-09-04 09:51:35 +03:00
|
|
|
} else if (status === 403) {
|
|
|
|
OC.Notification.show(message || t('files', 'Operation is blocked by access control'), {type: 'error'});
|
2016-10-07 17:27:54 +03:00
|
|
|
} else {
|
2017-08-08 17:37:14 +03:00
|
|
|
OC.Notification.show(message || t('files', 'Error when assembling chunks, status code {status}', {status: status}), {type: 'error'});
|
2016-10-07 17:27:54 +03:00
|
|
|
}
|
|
|
|
self.trigger('fail', e, data);
|
2016-07-15 17:03:02 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
fileupload.on('fileuploaddrop', function(e, data) {
|
|
|
|
self.trigger('drop', e, data);
|
2017-08-24 15:55:17 +03:00
|
|
|
if (e.isPropagationStopped()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-07-15 17:03:02 +03:00
|
|
|
});
|
2018-11-05 11:24:25 +03:00
|
|
|
}
|
|
|
|
window.onbeforeunload = function() {
|
|
|
|
return self.confirmBeforeUnload();
|
2013-10-22 20:11:03 +04:00
|
|
|
}
|
2013-08-12 14:33:22 +04:00
|
|
|
}
|
|
|
|
|
2014-03-06 16:49:57 +04:00
|
|
|
//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
|
|
|
|
if (navigator.userAgent.search(/konqueror/i) === -1) {
|
2015-12-16 19:35:53 +03:00
|
|
|
this.$uploadEl.attr('multiple', 'multiple');
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2013-08-12 14:33:22 +04:00
|
|
|
|
2015-12-16 19:35:53 +03:00
|
|
|
return this.fileUploadParam;
|
2014-03-06 16:49:57 +04:00
|
|
|
}
|
2016-07-15 17:03:02 +03:00
|
|
|
}, OC.Backbone.Events);
|