diff options
Diffstat (limited to 'apps')
27 files changed, 423 insertions, 56 deletions
diff --git a/apps/comments/activity/extension.php b/apps/comments/activity/extension.php index 6bf7cae5882..ef800e88abc 100644 --- a/apps/comments/activity/extension.php +++ b/apps/comments/activity/extension.php @@ -80,7 +80,10 @@ class Extension implements IExtension { $l = $this->getL10N($languageCode); return array( - self::APP_NAME => (string) $l->t('<strong>Comments</strong> for files'), + self::APP_NAME => [ + 'desc' => (string) $l->t('<strong>Comments</strong> for files <em>(always listed in stream)</em>'), + 'methods' => [self::METHOD_MAIL], // self::METHOD_STREAM is forced true by the default value + ], ); } @@ -274,7 +277,11 @@ class Extension implements IExtension { */ public function filterNotificationTypes($types, $filter) { if ($filter === self::APP_NAME) { - return array_intersect($types, [self::APP_NAME]); + return [self::APP_NAME]; + } + if (in_array($filter, ['all', 'by', 'self', 'filter'])) { + $types[] = self::APP_NAME; + return $types; } return false; } diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index 686d0863f91..dd4670da5fa 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -39,6 +39,7 @@ use Sabre\DAV\Tree; use \Sabre\HTTP\RequestInterface; use \Sabre\HTTP\ResponseInterface; use OCP\Files\StorageNotAvailableException; +use OCP\IConfig; class FilesPlugin extends ServerPlugin { @@ -55,6 +56,7 @@ class FilesPlugin extends ServerPlugin { const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id'; const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name'; const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums'; + const DATA_FINGERPRINT_PROPERTYNAME = '{http://owncloud.org/ns}data-fingerprint'; /** * Reference to main server object @@ -87,6 +89,11 @@ class FilesPlugin extends ServerPlugin { private $downloadAttachment; /** + * @var IConfig + */ + private $config; + + /** * @param Tree $tree * @param View $view * @param bool $isPublic @@ -94,10 +101,12 @@ class FilesPlugin extends ServerPlugin { */ public function __construct(Tree $tree, View $view, + IConfig $config, $isPublic = false, $downloadAttachment = true) { $this->tree = $tree; $this->fileView = $view; + $this->config = $config; $this->isPublic = $isPublic; $this->downloadAttachment = $downloadAttachment; } @@ -125,6 +134,7 @@ class FilesPlugin extends ServerPlugin { $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME; $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME; $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME; + $server->protectedProperties[] = self::DATA_FINGERPRINT_PROPERTYNAME; // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH $allowedProperties = ['{DAV:}getetag']; @@ -272,6 +282,18 @@ class FilesPlugin extends ServerPlugin { $displayName = $owner->getDisplayName(); return $displayName; }); + + $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { + if ($node->getPath() === '/') { + return $this->config->getSystemValue('data-fingerprint', ''); + } + }); + } + + if ($node instanceof \OCA\DAV\Files\FilesHome) { + $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { + return $this->config->getSystemValue('data-fingerprint', ''); + }); } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php index cab7a85d19f..5853370778d 100644 --- a/apps/dav/lib/connector/sabre/serverfactory.php +++ b/apps/dav/lib/connector/sabre/serverfactory.php @@ -137,8 +137,15 @@ class ServerFactory { } $objectTree->init($root, $view, $this->mountManager); - $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($objectTree, $view, false, - !$this->config->getSystemValue('debug', false))); + $server->addPlugin( + new \OCA\DAV\Connector\Sabre\FilesPlugin( + $objectTree, + $view, + $this->config, + false, + !$this->config->getSystemValue('debug', false) + ) + ); $server->addPlugin(new \OCA\DAV\Connector\Sabre\QuotaPlugin($view)); if($this->userSession->isLoggedIn()) { diff --git a/apps/dav/lib/hookmanager.php b/apps/dav/lib/hookmanager.php index 4a4704ff2a2..687e6718db3 100644 --- a/apps/dav/lib/hookmanager.php +++ b/apps/dav/lib/hookmanager.php @@ -100,26 +100,26 @@ class HookManager { public function postLogin($params) { $user = $this->userManager->get($params['uid']); - - $principal = 'principals/users/' . $user->getUID(); - $calendars = $this->calDav->getCalendarsForUser($principal); - if (empty($calendars)) { - try { - $this->calDav->createCalendar($principal, 'personal', [ - 'displayname' => 'Personal']); - } catch (\Exception $ex) { - \OC::$server->getLogger()->logException($ex); + if (!is_null($user)) { + $principal = 'principals/users/' . $user->getUID(); + $calendars = $this->calDav->getCalendarsForUser($principal); + if (empty($calendars)) { + try { + $this->calDav->createCalendar($principal, 'personal', [ + '{DAV:}displayname' => 'Personal']); + } catch (\Exception $ex) { + \OC::$server->getLogger()->logException($ex); + } } - } - $books = $this->cardDav->getAddressBooksForUser($principal); - if (empty($books)) { - try { - $this->cardDav->createAddressBook($principal, 'contacts', [ - 'displayname' => 'Contacts']); - } catch (\Exception $ex) { - \OC::$server->getLogger()->logException($ex); + $books = $this->cardDav->getAddressBooksForUser($principal); + if (empty($books)) { + try { + $this->cardDav->createAddressBook($principal, 'contacts', [ + '{DAV:}displayname' => 'Contacts']); + } catch (\Exception $ex) { + \OC::$server->getLogger()->logException($ex); + } } } - } } diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index e6668556448..73e24c9a292 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -132,8 +132,15 @@ class Server { $user = \OC::$server->getUserSession()->getUser(); if (!is_null($user)) { $view = \OC\Files\Filesystem::getView(); - $this->server->addPlugin(new FilesPlugin($this->server->tree, $view, false, - !\OC::$server->getConfig()->getSystemValue('debug', false))); + $this->server->addPlugin( + new FilesPlugin( + $this->server->tree, + $view, + \OC::$server->getConfig(), + false, + !\OC::$server->getConfig()->getSystemValue('debug', false) + ) + ); $this->server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( diff --git a/apps/dav/tests/unit/connector/sabre/filesplugin.php b/apps/dav/tests/unit/connector/sabre/filesplugin.php index 63ee5a53c17..fb5d658b39c 100644 --- a/apps/dav/tests/unit/connector/sabre/filesplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesplugin.php @@ -43,6 +43,7 @@ class FilesPlugin extends TestCase { const DOWNLOADURL_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME; const OWNER_ID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_ID_PROPERTYNAME; const OWNER_DISPLAY_NAME_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; + const DATA_FINGERPRINT_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; /** * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject @@ -64,6 +65,11 @@ class FilesPlugin extends TestCase { */ private $view; + /** + * @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject + */ + private $config; + public function setUp() { parent::setUp(); $this->server = $this->getMockBuilder('\Sabre\DAV\Server') @@ -75,8 +81,16 @@ class FilesPlugin extends TestCase { $this->view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); - - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view); + $this->config = $this->getMock('\OCP\IConfig'); + $this->config->method('getSystemValue') + ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) + ->willReturn('my_fingerprint'); + + $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $this->config + ); $this->plugin->initialize($this->server); } @@ -128,7 +142,8 @@ class FilesPlugin extends TestCase { self::PERMISSIONS_PROPERTYNAME, self::DOWNLOADURL_PROPERTYNAME, self::OWNER_ID_PROPERTYNAME, - self::OWNER_DISPLAY_NAME_PROPERTYNAME + self::OWNER_DISPLAY_NAME_PROPERTYNAME, + self::DATA_FINGERPRINT_PROPERTYNAME, ), 0 ); @@ -166,7 +181,7 @@ class FilesPlugin extends TestCase { $this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); $this->assertEquals('foo', $propFind->get(self::OWNER_ID_PROPERTYNAME)); $this->assertEquals('M. Foo', $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME)); - $this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties()); + $this->assertEquals([self::SIZE_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties()); } public function testGetPropertiesForFileHome() { @@ -185,7 +200,8 @@ class FilesPlugin extends TestCase { self::PERMISSIONS_PROPERTYNAME, self::DOWNLOADURL_PROPERTYNAME, self::OWNER_ID_PROPERTYNAME, - self::OWNER_DISPLAY_NAME_PROPERTYNAME + self::OWNER_DISPLAY_NAME_PROPERTYNAME, + self::DATA_FINGERPRINT_PROPERTYNAME, ), 0 ); @@ -217,6 +233,7 @@ class FilesPlugin extends TestCase { '{http://owncloud.org/ns}owner-id', '{http://owncloud.org/ns}owner-display-name' ], $propFind->get404Properties()); + $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); } public function testGetPropertiesStorageNotAvailable() { @@ -244,7 +261,11 @@ class FilesPlugin extends TestCase { } public function testGetPublicPermissions() { - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view, true); + $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $this->config, + true); $this->plugin->initialize($this->server); $propFind = new PropFind( @@ -281,6 +302,7 @@ class FilesPlugin extends TestCase { self::SIZE_PROPERTYNAME, self::PERMISSIONS_PROPERTYNAME, self::DOWNLOADURL_PROPERTYNAME, + self::DATA_FINGERPRINT_PROPERTYNAME, ), 0 ); @@ -299,7 +321,30 @@ class FilesPlugin extends TestCase { $this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME)); $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals(array(self::DOWNLOADURL_PROPERTYNAME), $propFind->get404Properties()); + $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties()); + } + + public function testGetPropertiesForRootDirectory() { + /** @var \OCA\DAV\Connector\Sabre\Directory | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $node->method('getPath')->willReturn('/'); + + $propFind = new PropFind( + '/', + [ + self::DATA_FINGERPRINT_PROPERTYNAME, + ], + 0 + ); + + $this->plugin->handleGetProperties( + $propFind, + $node + ); + + $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); } public function testUpdateProps() { diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php index 87973ef0071..ffe1a19ee56 100644 --- a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php @@ -336,7 +336,15 @@ class FilesReportPlugin extends \Test\TestCase { ->method('getSize') ->will($this->returnValue(1024)); - $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view)); + $config = $this->getMock('\OCP\IConfig'); + + $this->server->addPlugin( + new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $config + ) + ); $this->plugin->initialize($this->server); $responses = $this->plugin->prepareResponses($requestedProps, [$node1, $node2]); diff --git a/apps/dav/tests/unit/dav/HookManagerTest.php b/apps/dav/tests/unit/dav/HookManagerTest.php new file mode 100644 index 00000000000..bec4c240ce8 --- /dev/null +++ b/apps/dav/tests/unit/dav/HookManagerTest.php @@ -0,0 +1,71 @@ +<?php + +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, 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 <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\Unit\DAV; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\CardDAV\SyncService; +use OCA\DAV\HookManager; +use OCP\IUserManager; +use Test\TestCase; + +class HookManagerTest extends TestCase { + public function test() { + $user = $this->getMockBuilder('\OCP\IUser') + ->disableOriginalConstructor() + ->getMock(); + $user->expects($this->once())->method('getUID')->willReturn('newUser'); + + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject $userManager */ + $userManager = $this->getMockBuilder('\OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + $userManager->expects($this->once())->method('get')->willReturn($user); + + /** @var SyncService | \PHPUnit_Framework_MockObject_MockObject $syncService */ + $syncService = $this->getMockBuilder('OCA\DAV\CardDAV\SyncService') + ->disableOriginalConstructor() + ->getMock(); + + /** @var CalDavBackend | \PHPUnit_Framework_MockObject_MockObject $cal */ + $cal = $this->getMockBuilder('OCA\DAV\CalDAV\CalDavBackend') + ->disableOriginalConstructor() + ->getMock(); + $cal->expects($this->once())->method('getCalendarsForUser')->willReturn([]); + $cal->expects($this->once())->method('createCalendar')->with( + 'principals/users/newUser', + 'personal', ['{DAV:}displayname' => 'Personal']); + + /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $card */ + $card = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') + ->disableOriginalConstructor() + ->getMock(); + $card->expects($this->once())->method('getAddressBooksForUser')->willReturn([]); + $card->expects($this->once())->method('createAddressBook')->with( + 'principals/users/newUser', + 'contacts', ['{DAV:}displayname' => 'Contacts']); + + $hm = new HookManager($userManager, $syncService, $cal, $card); + $hm->postLogin(['uid' => 'newUser']); + } +} diff --git a/apps/encryption/hooks/userhooks.php b/apps/encryption/hooks/userhooks.php index 62acd168909..bde4d5869b4 100644 --- a/apps/encryption/hooks/userhooks.php +++ b/apps/encryption/hooks/userhooks.php @@ -24,6 +24,7 @@ namespace OCA\Encryption\Hooks; +use OC\Files\Filesystem; use OCP\IUserManager; use OCP\Util as OCUtil; use OCA\Encryption\Hooks\Contracts\IHook; @@ -243,6 +244,7 @@ class UserHooks implements IHook { // used to decrypt it has changed } else { // admin changed the password for a different user, create new keys and re-encrypt file keys $user = $params['uid']; + $this->initMountPoints($user); $recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null; // we generate new keys if... @@ -281,6 +283,15 @@ class UserHooks implements IHook { } } + /** + * init mount points for given user + * + * @param string $user + * @throws \OC\User\NoUserException + */ + protected function initMountPoints($user) { + Filesystem::initMountPoints($user); + } /** diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php index 907a6437f5b..6eff66e72be 100644 --- a/apps/encryption/lib/crypto/encryption.php +++ b/apps/encryption/lib/crypto/encryption.php @@ -547,4 +547,17 @@ class Encryption implements IEncryptionModule { return $path; } + /** + * Check if the module is ready to be used by that specific user. + * In case a module is not ready - because e.g. key pairs have not been generated + * upon login this method can return false before any operation starts and might + * cause issues during operations. + * + * @param string $user + * @return boolean + * @since 9.1.0 + */ + public function isReadyForUser($user) { + return $this->keyManager->userHasKeys($user); + } } diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php index 12fa5f92bd5..0accfb7900a 100644 --- a/apps/encryption/lib/keymanager.php +++ b/apps/encryption/lib/keymanager.php @@ -493,6 +493,7 @@ class KeyManager { */ public function userHasKeys($userId) { $privateKey = $publicKey = true; + $exception = null; try { $this->getPrivateKey($userId); diff --git a/apps/encryption/tests/hooks/UserHooksTest.php b/apps/encryption/tests/hooks/UserHooksTest.php index 08d1981266c..d2a7d4f2d04 100644 --- a/apps/encryption/tests/hooks/UserHooksTest.php +++ b/apps/encryption/tests/hooks/UserHooksTest.php @@ -29,6 +29,12 @@ use OCA\Encryption\Crypto\Crypt; use OCA\Encryption\Hooks\UserHooks; use Test\TestCase; +/** + * Class UserHooksTest + * + * @group DB + * @package OCA\Encryption\Tests\Hooks + */ class UserHooksTest extends TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject @@ -190,6 +196,23 @@ class UserHooksTest extends TestCase { ->willReturnOnConsecutiveCalls(true, false); + $this->instance = $this->getMockBuilder('OCA\Encryption\Hooks\UserHooks') + ->setConstructorArgs( + [ + $this->keyManagerMock, + $this->userManagerMock, + $this->loggerMock, + $this->userSetupMock, + $this->userSessionMock, + $this->utilMock, + $this->sessionMock, + $this->cryptMock, + $this->recoveryMock + ] + )->setMethods(['initMountPoints'])->getMock(); + + $this->instance->expects($this->exactly(3))->method('initMountPoints'); + // Test first if statement $this->assertNull($this->instance->setPassphrase($this->params)); @@ -236,16 +259,20 @@ class UserHooksTest extends TestCase { ->with('testUser') ->willReturn(false); - $userHooks = new UserHooks($this->keyManagerMock, - $this->userManagerMock, - $this->loggerMock, - $this->userSetupMock, - $userSessionMock, - $this->utilMock, - $this->sessionMock, - $this->cryptMock, - $this->recoveryMock - ); + $userHooks = $this->getMockBuilder('OCA\Encryption\Hooks\UserHooks') + ->setConstructorArgs( + [ + $this->keyManagerMock, + $this->userManagerMock, + $this->loggerMock, + $this->userSetupMock, + $userSessionMock, + $this->utilMock, + $this->sessionMock, + $this->cryptMock, + $this->recoveryMock + ] + )->setMethods(['initMountPoints'])->getMock(); $this->assertNull($userHooks->setPassphrase($this->params)); } diff --git a/apps/files/appinfo/routes.php b/apps/files/appinfo/routes.php index 6ad938101a2..2291b48e3c5 100644 --- a/apps/files/appinfo/routes.php +++ b/apps/files/appinfo/routes.php @@ -54,6 +54,11 @@ $application->registerRoutes( 'url' => '/api/v1/sorting', 'verb' => 'POST' ), + array( + 'name' => 'API#showHiddenFiles', + 'url' => '/api/v1/showhidden', + 'verb' => 'POST' + ), [ 'name' => 'view#index', 'url' => '/', diff --git a/apps/files/command/transferownership.php b/apps/files/command/transferownership.php index 6bf2fae6bdf..1f46efdde0d 100644 --- a/apps/files/command/transferownership.php +++ b/apps/files/command/transferownership.php @@ -97,6 +97,12 @@ class TransferOwnership extends Command { $output->writeln("<error>Unknown destination user $this->destinationUser</error>"); return; } + + // target user has to be ready + if (!\OC::$server->getEncryptionManager()->isReadyForUser($this->destinationUser)) { + $output->writeln("<error>The target user is not ready to accept files. The user has at least to be logged in once.</error>"); + return; + } $date = date('c'); $this->finalTarget = "$this->destinationUser/files/transferred from $this->sourceUser on $date"; diff --git a/apps/files/controller/apicontroller.php b/apps/files/controller/apicontroller.php index 43d426476fe..072498c7b5f 100644 --- a/apps/files/controller/apicontroller.php +++ b/apps/files/controller/apicontroller.php @@ -224,4 +224,16 @@ class ApiController extends Controller { return new Response(); } + /** + * Toggle default for showing/hiding hidden files + * + * @NoAdminRequired + * + * @param bool $show + */ + public function showHiddenFiles($show) { + $this->config->setUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', (int) $show); + return new Response(); + } + } diff --git a/apps/files/controller/viewcontroller.php b/apps/files/controller/viewcontroller.php index 6c5f4c6d2a0..9fe34521332 100644 --- a/apps/files/controller/viewcontroller.php +++ b/apps/files/controller/viewcontroller.php @@ -67,6 +67,7 @@ class ViewController extends Controller { * @param IL10N $l10n * @param IConfig $config * @param EventDispatcherInterface $eventDispatcherInterface + * @param IUserSession $userSession */ public function __construct($appName, IRequest $request, @@ -222,6 +223,8 @@ class ViewController extends Controller { $user = $this->userSession->getUser()->getUID(); $params['defaultFileSorting'] = $this->config->getUserValue($user, 'files', 'file_sorting', 'name'); $params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc'); + $showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false); + $params['showHiddenFiles'] = $showHidden ? 1 : 0; $params['appNavigation'] = $nav; $params['appContents'] = $contentItems; $this->navigationManager->setActiveEntry('files_index'); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index d20ab102ba5..373739071e9 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -2,6 +2,11 @@ This file is licensed under the Affero General Public License version 3 or later. See the COPYING-README file. */ +/* SETTINGS */ +#files-setting-showhidden { + padding-bottom: 8px; +} + /* FILE MENU */ .actions { padding:5px; height:32px; display: inline-block; float: left; } .actions input, .actions button, .actions .button { margin:0; float:left; } diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 4ed805d2681..eac080a009d 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -11,7 +11,7 @@ * */ -/* global dragOptions, folderDropOptions */ +/* global dragOptions, folderDropOptions, OC */ (function() { if (!OCA.Files) { @@ -41,10 +41,22 @@ fileList: null, /** + * Backbone model for storing files preferences + */ + _filesConfig: null, + + /** * Initializes the files app */ initialize: function() { this.navigation = new OCA.Files.Navigation($('#app-navigation')); + this.$showHiddenFiles = $('input#showhiddenfilesToggle'); + var showHidden = $('#showHiddenFiles').val() === "1"; + this.$showHiddenFiles.prop('checked', showHidden); + + this._filesConfig = new OC.Backbone.Model({ + showhidden: showHidden + }); var urlParams = OC.Util.History.parseUrlQuery(); var fileActions = new OCA.Files.FileActions(); @@ -76,7 +88,8 @@ sorting: { mode: $('#defaultFileSorting').val(), direction: $('#defaultFileSortingDirection').val() - } + }, + config: this._filesConfig, } ); this.files.initialize(); @@ -90,6 +103,8 @@ this._setupEvents(); // trigger URL change event handlers this._onPopState(urlParams); + + this._debouncedPersistShowHiddenFilesState = _.debounce(this._persistShowHiddenFilesState, 1200); }, /** @@ -144,6 +159,14 @@ }, /** + * + * @returns {Backbone.Model} + */ + getFilesConfig: function() { + return this._filesConfig; + }, + + /** * Setup events based on URL changes */ _setupEvents: function() { @@ -154,6 +177,30 @@ $('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this)); $('#app-navigation').on('itemChanged', _.bind(this._onNavigationChanged, this)); + this.$showHiddenFiles.on('change', _.bind(this._onShowHiddenFilesChange, this)); + }, + + /** + * Toggle showing hidden files according to the settings checkbox + * + * @returns {undefined} + */ + _onShowHiddenFilesChange: function() { + var show = this.$showHiddenFiles.is(':checked'); + this._filesConfig.set('showhidden', show); + this._debouncedPersistShowHiddenFilesState(); + }, + + /** + * Persist show hidden preference on ther server + * + * @returns {undefined} + */ + _persistShowHiddenFilesState: function() { + var show = this._filesConfig.get('showhidden'); + $.post(OC.generateUrl('/apps/files/api/v1/showhidden'), { + show: show + }); }, /** diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 7de64f8ade3..79dc42da8f1 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -145,6 +145,11 @@ _filter: '', /** + * @type Backbone.Model + */ + _filesConfig: null, + + /** * Sort attribute * @type String */ @@ -198,6 +203,15 @@ return; } + if (options.config) { + this._filesConfig = options.config; + } else { + this._filesConfig = OCA.Files.App.getFilesConfig(); + } + this._filesConfig.on('change:showhidden', function() { + self.setFiles(self.files); + }); + if (options.dragOptions) { this._dragOptions = options.dragOptions; } @@ -847,6 +861,10 @@ * @return array of DOM elements of the newly added files */ _nextPage: function(animate) { + // Save full files list while rendering + var allFiles = this.files; + this.files = this._filterHiddenFiles(this.files); + var index = this.$fileList.children().length, count = this.pageSize(), hidden, @@ -893,6 +911,10 @@ } }, 0); } + + // Restore full files list after rendering + this.files = allFiles; + return newTrs; }, @@ -930,18 +952,25 @@ // clear "Select all" checkbox this.$el.find('.select-all').prop('checked', false); + // Save full files list while rendering + var allFiles = this.files; + this.files = this._filterHiddenFiles(this.files); + this.isEmpty = this.files.length === 0; this._nextPage(); this.updateEmptyContent(); - this.fileSummary.calculate(filesArray); + this.fileSummary.calculate(this.files); this._selectedFiles = {}; this._selectionSummary.clear(); this.updateSelectionSummary(); $(window).scrollTop(0); + // Restore full files list after rendering + this.files = allFiles; + this.$fileList.trigger(jQuery.Event('updated')); _.defer(function() { self.$el.closest('#app-content').trigger(jQuery.Event('apprendered')); @@ -949,6 +978,21 @@ }, /** + * Filter hidden files of the given filesArray (dot-files) + * + * @param filesArray files to be filtered + * @returns {array} + */ + _filterHiddenFiles: function(files) { + if (this._filesConfig.get('showhidden')) { + return files; + } + return _.filter(files, function(file) { + return file.name.indexOf('.') !== 0; + }); + }, + + /** * Returns the icon URL matching the given file info * * @param {OC.Files.FileInfo} fileInfo file info diff --git a/apps/files/templates/appnavigation.php b/apps/files/templates/appnavigation.php index 39a5875f3d0..9c79f806713 100644 --- a/apps/files/templates/appnavigation.php +++ b/apps/files/templates/appnavigation.php @@ -16,9 +16,13 @@ </button> </div> <div id="app-settings-content"> - <label for="webdavurl"><?php p($l->t('WebDAV'));?></label> - <input id="webdavurl" type="text" readonly="readonly" value="<?php p(\OCP\Util::linkToRemote('webdav')); ?>" /> - <em><?php print_unescaped($l->t('Use this address to <a href="%s" target="_blank" rel="noreferrer">access your Files via WebDAV</a>', array(link_to_docs('user-webdav'))));?></em> + <div id="files-setting-showhidden"> + <input class="checkbox" id="showhiddenfilesToggle" checked="checked" type="checkbox"> + <label for="showhiddenfilesToggle"><?php p($l->t('Show hidden files')); ?></label> + </div> + <label for="webdavurl"><?php p($l->t('WebDAV'));?></label> + <input id="webdavurl" type="text" readonly="readonly" value="<?php p(\OCP\Util::linkToRemote('webdav')); ?>" /> + <em><?php print_unescaped($l->t('Use this address to <a href="%s" target="_blank" rel="noreferrer">access your Files via WebDAV</a>', array(link_to_docs('user-webdav'))));?></em> </div> </div> </div> diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index db464ad2eca..7281edd3aec 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -20,4 +20,5 @@ <input type="hidden" name="allowShareWithLink" id="allowShareWithLink" value="<?php p($_['allowShareWithLink']) ?>" /> <input type="hidden" name="defaultFileSorting" id="defaultFileSorting" value="<?php p($_['defaultFileSorting']) ?>" /> <input type="hidden" name="defaultFileSortingDirection" id="defaultFileSortingDirection" value="<?php p($_['defaultFileSortingDirection']) ?>" /> +<input type="hidden" name="showHiddenFiles" id="showHiddenFiles" value="<?php p($_['showHiddenFiles']); ?>" /> <?php endif; diff --git a/apps/files/tests/controller/ViewControllerTest.php b/apps/files/tests/controller/ViewControllerTest.php index 0446cc8982c..420e635b4b9 100644 --- a/apps/files/tests/controller/ViewControllerTest.php +++ b/apps/files/tests/controller/ViewControllerTest.php @@ -155,11 +155,12 @@ class ViewControllerTest extends TestCase { 'owner' => 'MyName', 'ownerDisplayName' => 'MyDisplayName', ])); - $this->config->expects($this->exactly(2)) + $this->config->expects($this->exactly(3)) ->method('getUserValue') ->will($this->returnValueMap([ [$this->user->getUID(), 'files', 'file_sorting', 'name', 'name'], - [$this->user->getUID(), 'files', 'file_sorting_direction', 'asc', 'asc'] + [$this->user->getUID(), 'files', 'file_sorting_direction', 'asc', 'asc'], + [$this->user->getUID(), 'files', 'show_hidden', false, false], ])); $this->config @@ -244,6 +245,7 @@ class ViewControllerTest extends TestCase { 'isPublic' => false, 'defaultFileSorting' => 'name', 'defaultFileSortingDirection' => 'asc', + 'showHiddenFiles' => false, 'mailNotificationEnabled' => 'no', 'mailPublicNotificationEnabled' => 'no', 'allowShareWithLink' => 'yes', diff --git a/apps/files/tests/controller/apicontrollertest.php b/apps/files/tests/controller/apicontrollertest.php index 59f53e8ee81..2eba7d62feb 100644 --- a/apps/files/tests/controller/apicontrollertest.php +++ b/apps/files/tests/controller/apicontrollertest.php @@ -1,4 +1,5 @@ <?php + /** * @author Christoph Wurst <christoph@winzerhof-wurst.at> * @author Lukas Reschke <lukas@owncloud.com> @@ -382,4 +383,17 @@ class ApiControllerTest extends TestCase { $this->assertEquals($expected, $result); } + public function testShowHiddenFiles() { + $show = false; + + $this->config->expects($this->once()) + ->method('setUserValue') + ->with($this->user->getUID(), 'files', 'show_hidden', $show); + + $expected = new Http\Response(); + $actual = $this->apiController->showHiddenFiles($show); + + $this->assertEquals($expected, $actual); + } + } diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index af198208de2..6f12b3d4456 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -33,7 +33,8 @@ OCA.Sharing.App = { id: 'shares.self', scrollContainer: $('#app-content'), sharedWithUser: true, - fileActions: this._createFileActions() + fileActions: this._createFileActions(), + config: OCA.Files.App.getFilesConfig() } ); @@ -55,7 +56,8 @@ OCA.Sharing.App = { id: 'shares.others', scrollContainer: $('#app-content'), sharedWithUser: false, - fileActions: this._createFileActions() + fileActions: this._createFileActions(), + config: OCA.Files.App.getFilesConfig() } ); @@ -77,7 +79,8 @@ OCA.Sharing.App = { id: 'shares.link', scrollContainer: $('#app-content'), linksOnly: true, - fileActions: this._createFileActions() + fileActions: this._createFileActions(), + config: OCA.Files.App.getFilesConfig() } ); diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php index d30782e5074..0950c2a62f5 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -87,7 +87,7 @@ abstract class TestCase extends \Test\TestCase { $backend->createUser(self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER4); // create group - $groupBackend = new \OC_Group_Dummy(); + $groupBackend = new \Test\Util\Group\Dummy(); $groupBackend->createGroup(self::TEST_FILES_SHARING_API_GROUP1); $groupBackend->createGroup('group'); $groupBackend->createGroup('group1'); diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js index 771ea90bc16..fd3d5db32ff 100644 --- a/apps/files_trashbin/js/app.js +++ b/apps/files_trashbin/js/app.js @@ -29,7 +29,8 @@ OCA.Trashbin.App = { scrollContainer: $('#app-content'), fileActions: this._createFileActions(), detailsViewEnabled: false, - scrollTo: urlParams.scrollto + scrollTo: urlParams.scrollto, + config: OCA.Files.App.getFilesConfig() } ); }, diff --git a/apps/systemtags/js/app.js b/apps/systemtags/js/app.js index d28514358c1..e027c0be123 100644 --- a/apps/systemtags/js/app.js +++ b/apps/systemtags/js/app.js @@ -28,7 +28,8 @@ { id: 'systemtags', scrollContainer: $('#app-content'), - fileActions: this._createFileActions() + fileActions: this._createFileActions(), + config: OCA.Files.App.getFilesConfig() } ); |