diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 63acda3a70..cd81aa3af3 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -57,3 +57,5 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadshe 'name' => $l->t('Recent'), ]; }); + +\OCP\Util::connectHook('\OCP\Config', 'js', '\OCA\Files\App', 'extendJsConfig'); diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 47011d23f4..6e4e8c1b13 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -93,7 +93,8 @@ direction: $('#defaultFileSortingDirection').val() }, config: this._filesConfig, - enableUpload: true + enableUpload: true, + maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size } ); this.files.initialize(); diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index b86b42bdb9..5dc18907c7 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -220,8 +220,8 @@ OC.FileUpload.prototype = { this.data.headers['If-None-Match'] = '*'; } - var userName = this.uploader.filesClient.getUserName(); - var password = this.uploader.filesClient.getPassword(); + var userName = this.uploader.davClient.getUserName(); + var password = this.uploader.davClient.getPassword(); if (userName) { // copy username/password from DAV client this.data.headers['Authorization'] = @@ -234,7 +234,7 @@ OC.FileUpload.prototype = { && this.getFile().size > this.uploader.fileUploadParam.maxChunkSize ) { data.isChunked = true; - chunkFolderPromise = this.uploader.filesClient.createDirectory( + chunkFolderPromise = this.uploader.davClient.createDirectory( 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId()) ); // TODO: if fails, it means same id already existed, need to retry @@ -260,9 +260,18 @@ OC.FileUpload.prototype = { } var uid = OC.getCurrentUser().uid; - return this.uploader.filesClient.move( + return this.uploader.davClient.move( 'uploads/' + encodeURIComponent(uid) + '/' + encodeURIComponent(this.getId()) + '/.file', - 'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()) + 'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()), + true, + {'X-OC-Mtime': this.getFile().lastModified / 1000} + ); + }, + + _deleteChunkFolder: function() { + // delete transfer directory for this upload + this.uploader.davClient.remove( + 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId()) ); }, @@ -271,12 +280,20 @@ OC.FileUpload.prototype = { */ 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._deleteChunkFolder(); } this.data.abort(); + this.deleteUpload(); + }, + + /** + * Fail the upload + */ + fail: function() { + this.deleteUpload(); + if (this.data.isChunked) { + this._deleteChunkFolder(); + } }, /** @@ -375,6 +392,13 @@ OC.Uploader.prototype = _.extend({ */ filesClient: null, + /** + * Webdav client pointing at the root "dav" endpoint + * + * @type OC.Files.Client + */ + davClient: 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 @@ -721,6 +745,13 @@ OC.Uploader.prototype = _.extend({ this.fileList = options.fileList; this.filesClient = options.filesClient || OC.Files.getClient(); + 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() + }); $uploadEl = $($uploadEl); this.$uploadEl = $uploadEl; @@ -920,7 +951,7 @@ OC.Uploader.prototype = _.extend({ } if (upload) { - upload.deleteUpload(); + upload.fail(); } }, /** @@ -951,6 +982,10 @@ OC.Uploader.prototype = _.extend({ } }; + if (options.maxChunkSize) { + this.fileUploadParam.maxChunkSize = options.maxChunkSize; + } + // initialize jquery fileupload (blueimp) var fileupload = this.$uploadEl.fileupload(this.fileUploadParam); @@ -1041,7 +1076,6 @@ OC.Uploader.prototype = _.extend({ self.log('progress handle fileuploadstop', e, data); self.clear(); - self._hideProgressBar(); self.trigger('stop', e, data); }); fileupload.on('fileuploadfail', function(e, data) { @@ -1096,7 +1130,7 @@ OC.Uploader.prototype = _.extend({ // 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]; + var chunkId = range.split('/')[0].split('-')[0]; data.url = OC.getRootPath() + '/remote.php/dav/uploads' + '/' + encodeURIComponent(OC.getCurrentUser().uid) + @@ -1108,7 +1142,20 @@ OC.Uploader.prototype = _.extend({ fileupload.on('fileuploaddone', function(e, data) { var upload = self.getUpload(data); upload.done().then(function() { + self._hideProgressBar(); self.trigger('done', e, upload); + }).fail(function(status) { + self._hideProgressBar(); + if (status === 507) { + // not enough space + OC.Notification.show(t('files', 'Not enough free space'), {type: 'error'}); + self.cancelUploads(); + } else if (status === 409) { + OC.Notification.show(t('files', 'Target folder does not exist any more'), {type: 'error'}); + } else { + OC.Notification.show(t('files', 'Error when assembling chunks, status code {status}', {status: status}), {type: 'error'}); + } + self.trigger('fail', e, data); }); }); fileupload.on('fileuploaddrop', function(e, data) { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4790afcf4d..b8fadc6c43 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -357,7 +357,8 @@ this._uploader = new OC.Uploader($uploadEl, { fileList: this, filesClient: this.filesClient, - dropZone: $('#content') + dropZone: $('#content'), + maxChunkSize: options.maxChunkSize }); this.setupUploadEvents(this._uploader); diff --git a/apps/files/lib/App.php b/apps/files/lib/App.php index 34d3ab4384..d16291c6fc 100644 --- a/apps/files/lib/App.php +++ b/apps/files/lib/App.php @@ -53,4 +53,14 @@ class App { return self::$navigationManager; } + public static function extendJsConfig($settings) { + $appConfig = json_decode($settings['array']['oc_appconfig'], true); + + $maxChunkSize = (int)(\OC::$server->getConfig()->getAppValue('files', 'max_chunk_size', (10 * 1024 * 1024))); + $appConfig['files'] = [ + 'max_chunk_size' => $maxChunkSize + ]; + + $settings['array']['oc_appconfig'] = json_encode($appConfig); + } } diff --git a/core/js/files/client.js b/core/js/files/client.js index dc9f6ade64..e810381342 100644 --- a/core/js/files/client.js +++ b/core/js/files/client.js @@ -37,6 +37,7 @@ } url += options.host + this._root; + this._host = options.host; this._defaultHeaders = options.defaultHeaders || { 'X-Requested-With': 'XMLHttpRequest', 'requesttoken': OC.requestToken @@ -698,10 +699,11 @@ * @param {String} destinationPath destination path * @param {boolean} [allowOverwrite=false] true to allow overwriting, * false otherwise + * @param {Object} [headers=null] additional headers * * @return {Promise} promise */ - move: function(path, destinationPath, allowOverwrite) { + move: function(path, destinationPath, allowOverwrite, headers) { if (!path) { throw 'Missing argument "path"'; } @@ -712,9 +714,9 @@ var self = this; var deferred = $.Deferred(); var promise = deferred.promise(); - var headers = { + headers = _.extend({}, headers, { 'Destination' : this._buildUrl(destinationPath) - }; + }); if (!allowOverwrite) { headers.Overwrite = 'F'; @@ -828,6 +830,16 @@ */ getBaseUrl: function() { return this._client.baseUrl; + }, + + /** + * Returns the host + * + * @since 13.0.0 + * @return {String} base URL + */ + getHost: function() { + return this._host; } };