diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index d68397dcaa32..e85a67a87593 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -116,6 +116,7 @@ public function initialize(\Sabre\DAV\Server $server) { $this->server->on('afterBind', array($this, 'sendFileIdHeader')); $this->server->on('afterWriteContent', array($this, 'sendFileIdHeader')); $this->server->on('afterMethod:GET', [$this,'httpGet']); + $this->server->on('afterMethod:GET', array($this, 'handleDownloadToken')); $this->server->on('afterResponse', function($request, ResponseInterface $response) { $body = $response->getBody(); if (is_resource($body)) { @@ -148,6 +149,32 @@ function checkMove($source, $destination) { } } + /** + * This sets a cookie to be able to recognize the start of the download + * the content must not be longer than 32 characters and must only contain + * alphanumeric characters + * + * @param RequestInterface $request + * @param ResponseInterface $response + */ + function handleDownloadToken(RequestInterface $request, ResponseInterface $response) { + $queryParams = $request->getQueryParameters(); + + /** + * this sets a cookie to be able to recognize the start of the download + * the content must not be longer than 32 characters and must only contain + * alphanumeric characters + */ + if (isset($queryParams['downloadStartSecret'])) { + $token = $queryParams['downloadStartSecret']; + if (!isset($token[32]) + && preg_match('!^[a-zA-Z0-9]+$!', $token) === 1) { + // FIXME: use $response->setHeader() instead + setcookie('ocDownloadStarted', $token, time() + 20, '/'); + } + } + } + /** * Plugin that adds a 'Content-Disposition: attachment' header to all files * delivered by SabreDAV. diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php deleted file mode 100644 index 2d02869df14f..000000000000 --- a/apps/files/ajax/delete.php +++ /dev/null @@ -1,81 +0,0 @@ - - * @author Frank Karlitschek - * @author Jakob Sack - * @author Joas Schilling - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Robin Appelman - * @author Thomas Müller - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); -\OC::$server->getSession()->close(); - - -// Get data -$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : ''; -$allFiles = isset($_POST["allfiles"]) ? (string)$_POST["allfiles"] : false; - -// delete all files in dir ? -if ($allFiles === 'true') { - $files = array(); - $fileList = \OC\Files\Filesystem::getDirectoryContent($dir); - foreach ($fileList as $fileInfo) { - $files[] = $fileInfo['name']; - } -} else { - $files = isset($_POST["file"]) ? (string)$_POST["file"] : (string)$_POST["files"]; - $files = json_decode($files); -} -$filesWithError = ''; - -$success = true; - -//Now delete -foreach ($files as $file) { - try { - if (\OC\Files\Filesystem::file_exists($dir . '/' . $file) && - !(\OC\Files\Filesystem::isDeletable($dir . '/' . $file) && - \OC\Files\Filesystem::unlink($dir . '/' . $file)) - ) { - $filesWithError .= $file . "\n"; - $success = false; - } - } catch (\Exception $e) { - $filesWithError .= $file . "\n"; - $success = false; - } -} - -// get array with updated storage stats (e.g. max file size) after upload -try { - $storageStats = \OCA\Files\Helper::buildFileStorageStatistics($dir); -} catch(\OCP\Files\NotFoundException $e) { - OCP\JSON::error(['data' => ['message' => 'File not found']]); - return; -} - -if ($success) { - OCP\JSON::success(array("data" => array_merge(array("dir" => $dir, "files" => $files), $storageStats))); -} else { - OCP\JSON::error(array("data" => array_merge(array("message" => "Could not delete:\n" . $filesWithError), $storageStats))); -} diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php deleted file mode 100644 index 0961636a116b..000000000000 --- a/apps/files/ajax/move.php +++ /dev/null @@ -1,59 +0,0 @@ - - * @author Frank Karlitschek - * @author Georg Ehrke - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Robin Appelman - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); -\OC::$server->getSession()->close(); - -// Get data -$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : ''; -$file = isset($_POST['file']) ? (string)$_POST['file'] : ''; -$target = isset($_POST['target']) ? rawurldecode((string)$_POST['target']) : ''; - -$l = \OC::$server->getL10N('files'); - -if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) { - OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s - File with this name already exists", array($file)) ))); - exit; -} - -if ($target != '' || strtolower($file) != 'shared') { - $targetFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file); - $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); - try { - if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file ))); - } else { - OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) ))); - } - } catch (\OCP\Files\NotPermittedException $e) { - OCP\JSON::error(array("data" => array( "message" => $l->t("Permission denied") ))); - } catch (\Exception $e) { - OCP\JSON::error(array("data" => array( "message" => $e->getMessage()))); - } -}else{ - OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) ))); -} diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php deleted file mode 100644 index be09b288d4b4..000000000000 --- a/apps/files/ajax/newfile.php +++ /dev/null @@ -1,103 +0,0 @@ - - * @author Georg Ehrke - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Robin Appelman - * @author Thomas Müller - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -// Init owncloud -global $eventSource; - -\OCP\JSON::checkLoggedIn(); -\OCP\JSON::callCheck(); - -\OC::$server->getSession()->close(); - -// Get the params -$dir = isset( $_REQUEST['dir'] ) ? '/'.trim((string)$_REQUEST['dir'], '/\\') : ''; -$fileName = isset( $_REQUEST['filename'] ) ? trim((string)$_REQUEST['filename'], '/\\') : ''; - -$l10n = \OC::$server->getL10N('files'); - -$result = array( - 'success' => false, - 'data' => NULL -); - -try { - \OC\Files\Filesystem::getView()->verifyPath($dir, $fileName); -} catch (\OCP\Files\InvalidPathException $ex) { - $result['data'] = [ - 'message' => $ex->getMessage()]; - OCP\JSON::error($result); - return; -} - -if (!\OC\Files\Filesystem::file_exists($dir . '/')) { - $result['data'] = array('message' => (string)$l10n->t( - 'The target folder has been moved or deleted.'), - 'code' => 'targetnotfound' - ); - OCP\JSON::error($result); - exit(); -} - -$target = $dir.'/'.$fileName; - -if (\OC\Files\Filesystem::file_exists($target)) { - $result['data'] = array('message' => (string)$l10n->t( - 'The name %s is already used in the folder %s. Please choose a different name.', - array($fileName, $dir)) - ); - OCP\JSON::error($result); - exit(); -} - -$success = false; -$templateManager = OC_Helper::getFileTemplateManager(); -$mimeType = OC_Helper::getMimetypeDetector()->detectPath($target); -$content = $templateManager->getTemplate($mimeType); - -try { - if($content) { - $success = \OC\Files\Filesystem::file_put_contents($target, $content); - } else { - $success = \OC\Files\Filesystem::touch($target); - } -} catch (\Exception $e) { - $result = [ - 'success' => false, - 'data' => [ - 'message' => $e->getMessage() - ] - ]; - OCP\JSON::error($result); - exit(); -} - -if($success) { - $meta = \OC\Files\Filesystem::getFileInfo($target); - OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta))); - return; -} - -OCP\JSON::error(array('data' => array( 'message' => $l10n->t('Error when creating the file') ))); diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php deleted file mode 100644 index a2897dd437ac..000000000000 --- a/apps/files/ajax/newfolder.php +++ /dev/null @@ -1,99 +0,0 @@ - - * @author Björn Schießle - * @author Frank Karlitschek - * @author Georg Ehrke - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Robin Appelman - * @author Thomas Müller - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ -// Init owncloud - - -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); -\OC::$server->getSession()->close(); - -// Get the params -$dir = isset($_POST['dir']) ? (string)$_POST['dir'] : ''; -$folderName = isset($_POST['foldername']) ?(string) $_POST['foldername'] : ''; - -$l10n = \OC::$server->getL10N('files'); - -$result = array( - 'success' => false, - 'data' => NULL - ); - -try { - \OC\Files\Filesystem::getView()->verifyPath($dir, $folderName); -} catch (\OCP\Files\InvalidPathException $ex) { - $result['data'] = [ - 'message' => $ex->getMessage()]; - OCP\JSON::error($result); - return; -} - -if (!\OC\Files\Filesystem::file_exists($dir . '/')) { - $result['data'] = array('message' => (string)$l10n->t( - 'The target folder has been moved or deleted.'), - 'code' => 'targetnotfound' - ); - OCP\JSON::error($result); - exit(); -} - -$target = $dir . '/' . $folderName; - -if (\OC\Files\Filesystem::file_exists($target)) { - $result['data'] = array('message' => $l10n->t( - 'The name %s is already used in the folder %s. Please choose a different name.', - array($folderName, $dir)) - ); - OCP\JSON::error($result); - exit(); -} - -try { - if(\OC\Files\Filesystem::mkdir($target)) { - if ( $dir !== '/') { - $path = $dir.'/'.$folderName; - } else { - $path = '/'.$folderName; - } - $meta = \OC\Files\Filesystem::getFileInfo($path); - $meta['type'] = 'dir'; // missing ?! - OCP\JSON::success(array('data' => \OCA\Files\Helper::formatFileInfo($meta))); - exit(); - } -} catch (\Exception $e) { - $result = [ - 'success' => false, - 'data' => [ - 'message' => $e->getMessage() - ] - ]; - OCP\JSON::error($result); - exit(); -} - -OCP\JSON::error(array('data' => array( 'message' => $l10n->t('Error when creating the folder') ))); diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php deleted file mode 100644 index a24a57b10464..000000000000 --- a/apps/files/ajax/rename.php +++ /dev/null @@ -1,58 +0,0 @@ - - * @author Frank Karlitschek - * @author Jakob Sack - * @author Jörn Friedrich Dreyer - * @author Lukas Reschke - * @author Morris Jobke - * @author Robin Appelman - * @author Vincent Petry - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see - * - */ - -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); -\OC::$server->getSession()->close(); - -$l10n = \OC::$server->getL10N('files'); - -$files = new \OCA\Files\App( - \OC\Files\Filesystem::getView(), - \OC::$server->getL10N('files') -); -try { - $result = $files->rename( - isset($_GET['dir']) ? (string)$_GET['dir'] : '', - isset($_GET['file']) ? (string)$_GET['file'] : '', - isset($_GET['newname']) ? (string)$_GET['newname'] : '' - ); -} catch (\Exception $e) { - $result = [ - 'success' => false, - 'data' => [ - 'message' => $e->getMessage() - ] - ]; -} - -if($result['success'] === true){ - OCP\JSON::success(['data' => $result['data']]); -} else { - OCP\JSON::error(['data' => $result['data']]); -} diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index a784642728fa..18e9cfe6117b 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -41,7 +41,6 @@ // If not, check the login. // If no token is sent along, rely on login only -$allowedPermissions = \OCP\Constants::PERMISSION_ALL; $errorCode = null; $l = \OC::$server->getL10N('files'); @@ -60,8 +59,6 @@ \OC_User::setIncognitoMode(true); - // return only read permissions for public upload - $allowedPermissions = \OCP\Constants::PERMISSION_READ; $publicDirectory = !empty($_POST['subdir']) ? (string)$_POST['subdir'] : '/'; $linkItem = OCP\Share::getShareByToken((string)$_POST['dirToken']); @@ -207,7 +204,7 @@ $data['originalname'] = $files['name'][$i]; $data['uploadMaxFilesize'] = $maxUploadFileSize; $data['maxHumanFilesize'] = $maxHumanFileSize; - $data['permissions'] = $meta['permissions'] & $allowedPermissions; + $data['permissions'] = $meta['permissions']; $data['directory'] = $returnedDir; $result[] = $data; } @@ -234,7 +231,7 @@ $data['originalname'] = $files['name'][$i]; $data['uploadMaxFilesize'] = $maxUploadFileSize; $data['maxHumanFilesize'] = $maxHumanFileSize; - $data['permissions'] = $meta['permissions'] & $allowedPermissions; + $data['permissions'] = $meta['permissions']; $data['directory'] = $returnedDir; $result[] = $data; } diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index ba8bb62494e5..4ab226f39685 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -8,7 +8,7 @@ true - 1.3.0 + 1.4.0 diff --git a/apps/files/controller/viewcontroller.php b/apps/files/controller/viewcontroller.php index c274680e525c..1d1a9111d192 100644 --- a/apps/files/controller/viewcontroller.php +++ b/apps/files/controller/viewcontroller.php @@ -119,6 +119,8 @@ protected function getStorageInfo() { * @throws \OCP\Files\NotFoundException */ public function index($dir = '', $view = '') { + $nav = new \OCP\Template('files', 'appnavigation', ''); + // Load the files we need \OCP\Util::addStyle('files', 'files'); \OCP\Util::addStyle('files', 'upload'); @@ -169,8 +171,6 @@ public function index($dir = '', $view = '') { // FIXME: Make non static $storageInfo = $this->getStorageInfo(); - $nav = new \OCP\Template('files', 'appnavigation', ''); - \OCA\Files\App::getNavigationManager()->add( [ 'id' => 'favorites', diff --git a/apps/files/js/app.js b/apps/files/js/app.js index f31770466fed..ff505d417f1e 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -71,7 +71,8 @@ folderDropOptions: folderDropOptions, fileActions: fileActions, allowLegacyActions: true, - scrollTo: urlParams.scrollto + scrollTo: urlParams.scrollto, + filesClient: OC.Files.getClient() } ); this.files.initialize(); diff --git a/apps/files/js/favoritesplugin.js b/apps/files/js/favoritesplugin.js index 417a32ef804b..454a505c7bd6 100644 --- a/apps/files/js/favoritesplugin.js +++ b/apps/files/js/favoritesplugin.js @@ -92,7 +92,7 @@ // folder in the files app instead of opening it directly fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { OCA.Files.App.setActiveView('files', {silent: true}); - OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); + OCA.Files.App.fileList.changeDirectory(OC.joinPaths(context.$file.attr('data-path'), filename), true, true); }); fileActions.setDefault('dir', 'Open'); return fileActions; diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 6a767d48a289..871a2149c883 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -575,7 +575,8 @@ }, actionHandler: function (filename, context) { var dir = context.dir || context.fileList.getCurrentDirectory(); - var url = context.fileList.getDownloadUrl(filename, dir); + var isDir = context.$file.attr('data-type') === 'dir'; + var url = context.fileList.getDownloadUrl(filename, dir, isDir); var downloadFileaction = $(context.$file).find('.fileactions .action-download'); @@ -611,10 +612,7 @@ this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { var dir = context.$file.attr('data-path') || context.fileList.getCurrentDirectory(); - if (dir !== '/') { - dir = dir + '/'; - } - context.fileList.changeDirectory(dir + filename); + context.fileList.changeDirectory(OC.joinPaths(dir, filename)); }); this.registerAction({ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index d1f68d98eab0..672c39a8bb16 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -22,11 +22,12 @@ * * @param $el container element with existing markup for the #controls * and a table - * @param [options] map of options, see other parameters - * @param [options.scrollContainer] scrollable container, defaults to $(window) - * @param [options.dragOptions] drag options, disabled by default - * @param [options.folderDropOptions] folder drop options, disabled by default - * @param [options.detailsViewEnabled=true] whether to enable details view + * @param {Object} [options] map of options, see other parameters + * @param {Object} [options.scrollContainer] scrollable container, defaults to $(window) + * @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 {OC.Files.Client} [options.filesClient] files client to use */ var FileList = function($el, options) { this.initialize($el, options); @@ -73,6 +74,13 @@ */ _detailsView: null, + /** + * Files client instance + * + * @type OC.Files.Client + */ + filesClient: null, + /** * Whether the file list was initialized already. * @type boolean @@ -92,10 +100,17 @@ * Array of files in the current folder. * The entries are of file data. * - * @type Array. + * @type Array. */ files: [], + /** + * Current directory entry + * + * @type OC.Files.FileInfo + */ + dirInfo: null, + /** * File actions handler, defaults to OCA.Files.FileActions * @type OCA.Files.FileActions @@ -149,7 +164,7 @@ * When false, clicking on a table header will call reload(). * When true, clicking on a table header will simply resort the list. */ - _clientSideSort: false, + _clientSideSort: true, /** * Current directory @@ -170,6 +185,7 @@ * @param options.dragOptions drag options, disabled by default * @param options.folderDropOptions folder drop options, disabled by default * @param options.scrollTo name of file to scroll to after the first load + * @param {OC.Files.Client} [options.filesClient] files API client * @private */ initialize: function($el, options) { @@ -185,6 +201,12 @@ if (options.folderDropOptions) { this._folderDropOptions = options.folderDropOptions; } + if (options.filesClient) { + this.filesClient = options.filesClient; + } else { + // default client if not specified + this.filesClient = OC.Files.getClient(); + } this.$el = $el; if (options.id) { @@ -209,6 +231,8 @@ this.files = []; this._selectedFiles = {}; this._selectionSummary = new OCA.Files.FileSummary(); + // dummy root dir info + this.dirInfo = new OC.Files.FileInfo({}); this.fileSummary = this._createSummary(); @@ -359,7 +383,7 @@ var highlightState = $tr.hasClass('highlighted'); $tr = self.updateRow( $tr, - _.extend({isPreviewAvailable: true}, model.toJSON()), + model.toJSON(), {updateSummary: true, silent: false, animate: true} ); $tr.toggleClass('highlighted', highlightState); @@ -618,7 +642,7 @@ }; OCA.Files.FileActions.updateFileActionSpinner(downloadFileaction, true); - OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir), disableLoadingState); + OCA.Files.Files.handleDownload(this.getDownloadUrl(files, dir, true), disableLoadingState); return false; }, @@ -894,16 +918,39 @@ self.$el.closest('#app-content').trigger(jQuery.Event('apprendered')); }); }, + + /** + * Returns the icon URL matching the given file info + * + * @param {OC.Files.FileInfo} fileInfo file info + * + * @return {string} icon URL + */ + _getIconUrl: function(fileInfo) { + var mimeType = fileInfo.mimetype || 'application/octet-stream'; + if (mimeType === 'httpd/unix-directory') { + // use default folder icon + if (fileInfo.mountType === 'shared' || fileInfo.mountType === 'shared-root') { + return OC.MimeType.getIconUrl('dir-shared'); + } else if (fileInfo.mountType === 'external-root') { + return OC.MimeType.getIconUrl('dir-external'); + } + return OC.MimeType.getIconUrl('dir'); + } + return OC.MimeType.getIconUrl(mimeType); + }, + /** * Creates a new table row element using the given file data. - * @param {OCA.Files.FileInfo} fileData file info attributes + * @param {OC.Files.FileInfo} fileData file info attributes * @param options map of attributes * @return new tr element (not appended to the table) */ _createRow: function(fileData, options) { var td, simpleSize, basename, extension, sizeColor, - icon = OC.MimeType.getIconUrl(fileData.mimetype), + icon = fileData.icon || this._getIconUrl(fileData), name = fileData.name, + // TODO: get rid of type, only use mime type type = fileData.type || 'file', mtime = parseInt(fileData.mtime, 10), mime = fileData.mimetype, @@ -943,6 +990,14 @@ } if (fileData.mountType) { + // FIXME: HACK: detect shared-root + if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared') { + // if parent folder isn't share, assume the displayed folder is a share root + fileData.mountType = 'shared-root'; + } else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external') { + // if parent folder isn't external, assume the displayed folder is the external storage root + fileData.mountType = 'external-root'; + } tr.attr('data-mounttype', fileData.mountType); } @@ -953,24 +1008,16 @@ path = this.getCurrentDirectory(); } - if (type === 'dir') { - // use default folder icon - icon = icon || OC.imagePath('core', 'filetypes/folder'); - } - else { - icon = icon || OC.imagePath('core', 'filetypes/file'); - } - // filename td td = $(''); // linkUrl - if (type === 'dir') { + if (mime === 'httpd/unix-directory') { linkUrl = this.linkTo(path + '/' + name); } else { - linkUrl = this.getDownloadUrl(name, path); + linkUrl = this.getDownloadUrl(name, path, type === 'dir'); } if (this._allowSelection) { td.append( @@ -996,7 +1043,7 @@ basename = ''; extension = name; // split extension from filename for non dirs - } else if (type !== 'dir' && name.indexOf('.') !== -1) { + } else if (mime !== 'httpd/unix-directory' && name.indexOf('.') !== -1) { basename = name.substr(0, name.lastIndexOf('.')); extension = name.substr(name.lastIndexOf('.')); } else { @@ -1018,7 +1065,7 @@ nameSpan.tooltip({placement: 'right'}); } // dirs can show the number of uploaded files - if (type === 'dir') { + if (mime !== 'httpd/unix-directory') { linkElem.append($('').attr({ 'class': 'uploadtext', 'currentUploads': 0 @@ -1074,7 +1121,7 @@ * Adds an entry to the files array and also into the DOM * in a sorted manner. * - * @param {OCA.Files.FileInfo} fileData map of file attributes + * @param {OC.Files.FileInfo} fileData map of file attributes * @param {Object} [options] map of attributes * @param {boolean} [options.updateSummary] true to update the summary * after adding (default), false otherwise. Defaults to true. @@ -1147,7 +1194,7 @@ * Creates a new row element based on the given attributes * and returns it. * - * @param {OCA.Files.FileInfo} fileData map of file attributes + * @param {OC.Files.FileInfo} fileData map of file attributes * @param {Object} [options] map of attributes * @param {int} [options.index] index at which to insert the element * @param {boolean} [options.updateSummary] true to update the summary @@ -1182,7 +1229,7 @@ filenameTd.draggable(this._dragOptions); } // allow dropping on folders - if (this._folderDropOptions && fileData.type === 'dir') { + if (this._folderDropOptions && mime === 'httpd/unix-directory') { filenameTd.droppable(this._folderDropOptions); } @@ -1193,7 +1240,7 @@ // display actions this.fileActions.display(filenameTd, !options.silent, this); - if (fileData.isPreviewAvailable && mime !== 'httpd/unix-directory') { + if (mime !== 'httpd/unix-directory') { var iconDiv = filenameTd.find('.thumbnail'); // lazy load / newly inserted td ? // the typeof check ensures that the default value of animate is true @@ -1329,6 +1376,13 @@ } }, + /** + * Returns list of webdav properties to request + */ + _getWebdavProperties: function() { + return this.filesClient.getPropfindProperties(); + }, + /** * Reloads the file list using ajax call * @@ -1343,17 +1397,12 @@ this._currentFileModel = null; this.$el.find('.select-all').prop('checked', false); this.showMask(); - if (this._reloadCall) { - this._reloadCall.abort(); - } - this._reloadCall = $.ajax({ - url: this.getAjaxUrl('list'), - data: { - dir : this.getCurrentDirectory(), - sort: this._sort, - sortdirection: this._sortDirection + this._reloadCall = this.filesClient.getFolderContents( + this.getCurrentDirectory(), { + includeParent: true, + properties: this._getWebdavProperties() } - }); + ); if (this._detailsView) { // close sidebar this._updateDetailsView(null); @@ -1361,24 +1410,19 @@ var callBack = this.reloadCallback.bind(this); return this._reloadCall.then(callBack, callBack); }, - reloadCallback: function(result) { + reloadCallback: function(status, result) { delete this._reloadCall; this.hideMask(); - if (!result || result.status === 'error') { - // if the error is not related to folder we're trying to load, reload the page to handle logout etc - if (result.data.error === 'authentication_error' || - result.data.error === 'token_expired' || - result.data.error === 'application_not_enabled' - ) { - OC.redirect(OC.generateUrl('apps/files')); - } - OC.Notification.showTemporary(result.data.message); + if (status === 401) { + // TODO: append current URL to be able to get back after logging in again + OC.redirect(OC.generateUrl('apps/files')); + OC.Notification.show(result); return false; } // Firewall Blocked request? - if (result.status === 403) { + if (status === 403) { // Go home this.changeDirectory('/'); OC.Notification.showTemporary(t('files', 'This operation is forbidden')); @@ -1386,32 +1430,49 @@ } // Did share service die or something else fail? - if (result.status === 500) { + if (status === 500) { // Go home this.changeDirectory('/'); - OC.Notification.showTemporary(t('files', 'This directory is unavailable, please check the logs or contact the administrator')); + OC.Notification.showTemporary( + t('files', 'This directory is unavailable, please check the logs or contact the administrator') + ); + return false; + } + + if (status === 503) { + // Go home + if (this.getCurrentDirectory() !== '/') { + this.changeDirectory('/'); + // TODO: read error message from exception + OC.Notification.showTemporary( + t('files', 'Storage not available') + ); + } return false; } - if (result.status === 404) { + if (status === 404) { // go back home this.changeDirectory('/'); return false; } // aborted ? - if (result.status === 0){ + if (status === 0){ return true; } - // TODO: should rather return upload file size through - // the files list ajax call + // TODO: parse remaining quota from PROPFIND response this.updateStorageStatistics(true); - if (result.data.permissions) { - this.setDirectoryPermissions(result.data.permissions); + // first entry is the root + this.dirInfo = result.shift(); + + if (this.dirInfo.permissions) { + this.setDirectoryPermissions(this.dirInfo.permissions); } - this.setFiles(result.data.files); + result.sort(this._sortComparator); + this.setFiles(result); return true; }, @@ -1419,12 +1480,15 @@ OCA.Files.Files.updateStorageStatistics(this.getCurrentDirectory(), force); }, + /** + * @deprecated do not use nor override + */ getAjaxUrl: function(action, params) { return OCA.Files.Files.getAjaxUrl(action, params); }, - getDownloadUrl: function(files, dir) { - return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory()); + getDownloadUrl: function(files, dir, isDir) { + return OCA.Files.Files.getDownloadUrl(files, dir || this.getCurrentDirectory(), isDir); }, /** @@ -1489,8 +1553,6 @@ if (etag){ // use etag as cache buster urlSpec.c = etag; - } else { - console.warn('OCA.Files.FileList.lazyLoadPreview(): missing etag argument'); } previewURL = self.generatePreviewUrl(urlSpec); @@ -1514,6 +1576,9 @@ img.src = previewURL; }, + /** + * @deprecated + */ setDirectoryPermissions: function(permissions) { var isCreatable = (permissions & OC.PERMISSION_CREATE) !== 0; this.$el.find('#permissions').val(permissions); @@ -1610,7 +1675,7 @@ * fileData should be inserted, considering the current * sorting * - * @param {OCA.Files.FileInfo} fileData file info + * @param {OC.Files.FileInfo} fileData file info */ _findInsertionIndex: function(fileData) { var index = 0; @@ -1628,6 +1693,9 @@ move: function(fileNames, targetPath) { var self = this; var dir = this.getCurrentDirectory(); + if (dir.charAt(dir.length - 1) !== '/') { + dir += '/'; + } var target = OC.basename(targetPath); if (!_.isArray(fileNames)) { fileNames = [fileNames]; @@ -1635,46 +1703,42 @@ _.each(fileNames, function(fileName) { var $tr = self.findFileEl(fileName); self.showFileBusyState($tr, true); - // TODO: improve performance by sending all file names in a single call - $.post( - OC.filePath('files', 'ajax', 'move.php'), - { - dir: dir, - file: fileName, - target: targetPath - }, - function(result) { - if (result) { - if (result.status === 'success') { - // if still viewing the same directory - if (self.getCurrentDirectory() === dir) { - // recalculate folder size - var oldFile = self.findFileEl(target); - var newFile = self.findFileEl(fileName); - var oldSize = oldFile.data('size'); - var newSize = oldSize + newFile.data('size'); - oldFile.data('size', newSize); - oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize)); - - // TODO: also update entry in FileList.files - - self.remove(fileName); - } - } else { - OC.Notification.hide(); - if (result.status === 'error' && result.data.message) { - OC.Notification.showTemporary(result.data.message); - } - else { - OC.Notification.showTemporary(t('files', 'Error moving file.')); - } - } + if (targetPath.charAt(targetPath.length - 1) !== '/') { + // make sure we move the files into the target dir, + // not overwrite it + targetPath = targetPath + '/'; + } + self.filesClient.move(dir + fileName, targetPath + fileName) + .done(function() { + // if still viewing the same directory + if (OC.joinPaths(self.getCurrentDirectory(), '/') === dir) { + // recalculate folder size + var oldFile = self.findFileEl(target); + var newFile = self.findFileEl(fileName); + var oldSize = oldFile.data('size'); + var newSize = oldSize + newFile.data('size'); + oldFile.data('size', newSize); + oldFile.find('td.filesize').text(OC.Util.humanFileSize(newSize)); + + // TODO: also update entry in FileList.files + self.remove(fileName); + } + }) + .fail(function(status) { + if (status === 412) { + // TODO: some day here we should invoke the conflict dialog + OC.Notification.showTemporary( + t('files', 'Could not move "{file}", target exists', {file: fileName}) + ); } else { - OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); + OC.Notification.showTemporary( + t('files', 'Could not move "{file}"', {file: fileName}) + ); } + }) + .always(function() { self.showFileBusyState($tr, false); - } - ); + }); }); }, @@ -1700,16 +1764,16 @@ * Triggers file rename input field for the given file name. * If the user enters a new name, the file will be renamed. * - * @param oldname file name of the file to rename + * @param oldName file name of the file to rename */ - rename: function(oldname) { + rename: function(oldName) { var self = this; var tr, td, input, form; - tr = this.findFileEl(oldname); + tr = this.findFileEl(oldName); var oldFileInfo = this.files[tr.index()]; tr.data('renaming',true); td = tr.children('td.filename'); - input = $('').val(oldname); + input = $('').val(oldName); form = $('
'); form.append(input); td.children('a.name').hide(); @@ -1724,11 +1788,11 @@ input.selectRange(0, len); var checkInput = function () { var filename = input.val(); - if (filename !== oldname) { + if (filename !== oldName) { // Files.isFileNameValid(filename) throws an exception itself OCA.Files.Files.isFileNameValid(filename); if (self.inList(filename)) { - throw t('files', '{new_name} already exists', {new_name: filename}); + throw t('files', '{newName} already exists', {newName: filename}); } } return true; @@ -1741,6 +1805,12 @@ td.children('a.name').show(); } + function updateInList(fileInfo) { + self.updateRow(tr, fileInfo); + self._updateDetailsView(fileInfo.name, false); + } + + // TODO: too many nested blocks, move parts into functions form.submit(function(event) { event.stopPropagation(); event.preventDefault(); @@ -1753,7 +1823,7 @@ input.tooltip('hide'); form.remove(); - if (newName !== oldname) { + if (newName !== oldName) { checkInput(); // mark as loading (temp element) self.showFileBusyState(tr, true); @@ -1765,34 +1835,45 @@ td.find('a.name span.nametext').text(basename); td.children('a.name').show(); - $.ajax({ - url: OC.filePath('files','ajax','rename.php'), - data: { - dir : tr.attr('data-path') || self.getCurrentDirectory(), - newname: newName, - file: oldname - }, - success: function(result) { - var fileInfo; - if (!result || result.status === 'error') { - OC.dialogs.alert(result.data.message, t('files', 'Could not rename file')); - fileInfo = oldFileInfo; - if (result.data.code === 'sourcenotfound') { - self.remove(result.data.newname, {updateSummary: true}); - return; - } - } - else { - fileInfo = result.data; + var path = tr.attr('data-path') || self.getCurrentDirectory(); + self.filesClient.move(OC.joinPaths(path, oldName), OC.joinPaths(path, newName)) + .done(function() { + oldFileInfo.name = newName; + updateInList(oldFileInfo); + }) + .fail(function(status) { + // TODO: 409 means current folder does not exist, redirect ? + if (status === 404) { + // source not found, so remove it from the list + OC.Notification.showTemporary( + t( + 'files', + 'Could not rename "{fileName}", it does not exist any more', + {fileName: oldName} + ) + ); + self.remove(newName, {updateSummary: true}); + return; + } else if (status === 412) { + // target exists + OC.Notification.showTemporary( + t( + 'files', + 'The name "{targetName}" is already used in the folder "{dir}". Please choose a different name.', + { + targetName: newName, + dir: self.getCurrentDirectory() + } + ) + ); + } else { + // restore the item to its previous state + OC.Notification.showTemporary( + t('files', 'Could not rename "{fileName}"', {fileName: oldName}) + ); } - // reinsert row - self.files.splice(tr.index(), 1); - tr.remove(); - tr = self.add(fileInfo, {updateSummary: false, silent: true}); - self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)})); - self._updateDetailsView(fileInfo.name, false); - } - }); + updateInList(oldFileInfo); + }); } else { // add back the old file info when cancelled self.files.splice(tr.index(), 1); @@ -1849,32 +1930,48 @@ var promise = deferred.promise(); OCA.Files.Files.isFileNameValid(name); - name = this.getUniqueName(name); if (this.lastAction) { this.lastAction(); } - $.post( - OC.generateUrl('/apps/files/ajax/newfile.php'), - { - dir: this.getCurrentDirectory(), - filename: name - }, - function(result) { - if (result.status === 'success') { - self.add(result.data, {animate: true, scrollTo: true}); - deferred.resolve(result.status, result.data); + name = this.getUniqueName(name); + var targetPath = this.getCurrentDirectory() + '/' + name; + + self.filesClient.putFileContents( + targetPath, + '', + { + contentType: 'text/plain', + overwrite: true + } + ) + .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); + }); + }) + .fail(function(status) { + if (status === 412) { + OC.Notification.showTemporary( + t('files', 'Could not create file "{file}" because it already exists', {file: name}) + ); } else { - if (result.data && result.data.message) { - OC.Notification.showTemporary(result.data.message); - } else { - OC.Notification.showTemporary(t('core', 'Could not create file')); - } - deferred.reject(result.status, result.data); + OC.Notification.showTemporary(t('files', 'Could not create file "{file}"', {file: name})); } - } - ); + deferred.reject(status); + }); return promise; }, @@ -1895,32 +1992,58 @@ var promise = deferred.promise(); OCA.Files.Files.isFileNameValid(name); - name = this.getUniqueName(name); if (this.lastAction) { this.lastAction(); } - $.post( - OC.generateUrl('/apps/files/ajax/newfolder.php'), - { - dir: this.getCurrentDirectory(), - foldername: name - }, - function(result) { - if (result.status === 'success') { - self.add(result.data, {animate: true, scrollTo: true}); - deferred.resolve(result.status, result.data); + name = this.getUniqueName(name); + 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); + }); + }) + .fail(function(createStatus) { + // method not allowed, folder might exist already + if (createStatus === 405) { + self.filesClient.getFileInfo( + targetPath, { + properties: self._getWebdavProperties() + } + ) + .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}) + ); + // still consider a failure + deferred.reject(createStatus, data); + }) + .fail(function() { + OC.Notification.showTemporary( + t('files', 'Could not create folder "{dir}"', {dir: name}) + ); + deferred.reject(status); + }); } else { - if (result.data && result.data.message) { - OC.Notification.showTemporary(result.data.message); - } else { - OC.Notification.showTemporary(t('core', 'Could not create folder')); - } - deferred.reject(result.status); + OC.Notification.showTemporary(t('files', 'Could not create folder "{dir}"', {dir: name})); + deferred.reject(createStatus); } - } - ); + }); return promise; }, @@ -1981,76 +2104,59 @@ */ do_delete:function(files, dir) { var self = this; - var params; if (files && files.substr) { files=[files]; } + if (!files) { + // delete all files in directory + files = _.pluck(this.files, 'name'); + } if (files) { this.showFileBusyState(files, true); - for (var i=0; iview = $view; - $this->l10n = $l10n; - } - /** * Returns the app's navigation manager * * @return \OCP\INavigationManager */ public static function getNavigationManager() { + // TODO: move this into a service in the Application class if (self::$navigationManager === null) { self::$navigationManager = new \OC\NavigationManager(); } return self::$navigationManager; } - /** - * rename a file - * - * @param string $dir - * @param string $oldname - * @param string $newname - * @return array - */ - public function rename($dir, $oldname, $newname) { - $result = array( - 'success' => false, - 'data' => NULL - ); - - try { - // check if the new name is conform to file name restrictions - $this->view->verifyPath($dir, $newname); - } catch (\OCP\Files\InvalidPathException $ex) { - $result['data'] = array( - 'message' => $this->l10n->t($ex->getMessage()), - 'code' => 'invalidname', - ); - return $result; - } - - $normalizedOldPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $oldname); - $normalizedNewPath = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); - - // rename to non-existing folder is denied - if (!$this->view->file_exists($normalizedOldPath)) { - $result['data'] = array( - 'message' => $this->l10n->t('%s could not be renamed as it has been deleted', array($oldname)), - 'code' => 'sourcenotfound', - 'oldname' => $oldname, - 'newname' => $newname, - ); - }else if (!$this->view->file_exists($dir)) { - $result['data'] = array('message' => (string)$this->l10n->t( - 'The target folder has been moved or deleted.', - array($dir)), - 'code' => 'targetnotfound' - ); - // rename to existing file is denied - } else if ($this->view->file_exists($normalizedNewPath)) { - - $result['data'] = array( - 'message' => $this->l10n->t( - "The name %s is already used in the folder %s. Please choose a different name.", - array($newname, $dir)) - ); - } else if ( - // rename to "." is denied - $newname !== '.' and - // THEN try to rename - $this->view->rename($normalizedOldPath, $normalizedNewPath) - ) { - // successful rename - $meta = $this->view->getFileInfo($normalizedNewPath); - $meta = \OCA\Files\Helper::populateTags(array($meta)); - $fileInfo = \OCA\Files\Helper::formatFileInfo(current($meta)); - $fileInfo['path'] = dirname($normalizedNewPath); - $result['success'] = true; - $result['data'] = $fileInfo; - } else { - // rename failed - $result['data'] = array( - 'message' => $this->l10n->t('%s could not be renamed', array($oldname)) - ); - } - return $result; - } - } diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index fb14cea731f3..9a4e8d59786a 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -139,9 +139,6 @@ public static function formatFileInfo(FileInfo $i) { $entry['parentId'] = $i['parent']; $entry['mtime'] = $i['mtime'] * 1000; // only pick out the needed attributes - if (\OC::$server->getPreviewManager()->isAvailable($i)) { - $entry['isPreviewAvailable'] = true; - } $entry['name'] = $i->getName(); $entry['permissions'] = $i['permissions']; $entry['mimetype'] = $i['mimetype']; diff --git a/apps/files/templates/list.php b/apps/files/templates/list.php index 7ebf80ee8b2f..04550f945b6e 100644 --- a/apps/files/templates/list.php +++ b/apps/files/templates/list.php @@ -1,16 +1,5 @@