From 25ed6714c78f811e6ed6ae611df0c89d9e9278f9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 24 Aug 2016 14:44:33 +0200 Subject: [PATCH 01/22] dont update the auth token twice Signed-off-by: Robin Appelman --- lib/private/User/Session.php | 2 -- tests/lib/User/SessionTest.php | 3 --- 2 files changed, 5 deletions(-) diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index dec959820f..4b56609ccf 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -597,7 +597,6 @@ class Session implements IUserSession, Emitter { } $dbToken->setLastCheck($now); - $this->tokenProvider->updateToken($dbToken); return true; } @@ -608,7 +607,6 @@ class Session implements IUserSession, Emitter { return false; } $dbToken->setLastCheck($now); - $this->tokenProvider->updateToken($dbToken); return true; } diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 2cd6b9b3be..37eb53ad16 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -890,9 +890,6 @@ class SessionTest extends \Test\TestCase { ->method('getPassword') ->with($token, 'APP-PASSWORD') ->will($this->throwException(new \OC\Authentication\Exceptions\PasswordlessTokenException())); - $tokenProvider->expects($this->once()) - ->method('updateToken') - ->with($token); $this->invokePrivate($userSession, 'validateSession', [$user]); From 90db361827b6d4a6cf8728577688ece219cd0e71 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 10 Oct 2016 13:59:55 +0200 Subject: [PATCH 02/22] Add test to ensure token times are updated Signed-off-by: Robin Appelman --- tests/lib/User/SessionTest.php | 152 +++++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 24 deletions(-) diff --git a/tests/lib/User/SessionTest.php b/tests/lib/User/SessionTest.php index 37eb53ad16..21ac1b655b 100644 --- a/tests/lib/User/SessionTest.php +++ b/tests/lib/User/SessionTest.php @@ -9,6 +9,8 @@ namespace Test\User; +use OC\Authentication\Token\DefaultTokenMapper; +use OC\Authentication\Token\DefaultTokenProvider; use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; use OC\Security\Bruteforce\Throttler; @@ -18,10 +20,12 @@ use OC\User\Session; use OC\User\User; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; +use OCP\ILogger; use OCP\IRequest; use OCP\ISession; use OCP\IUser; use OCP\IUserManager; +use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; /** @@ -167,16 +171,16 @@ class SessionTest extends \Test\TestCase { $session->expects($this->exactly(2)) ->method('set') ->with($this->callback(function ($key) { - switch ($key) { - case 'user_id': - case 'loginname': - return true; - break; - default: - return false; - break; - } - }, 'foo')); + switch ($key) { + case 'user_id': + case 'loginname': + return true; + break; + default: + return false; + break; + } + }, 'foo')); $managerMethods = get_class_methods('\OC\User\Manager'); //keep following methods intact in order to ensure hooks are @@ -490,13 +494,13 @@ class SessionTest extends \Test\TestCase { $session->expects($this->exactly(1)) ->method('set') ->with($this->callback(function ($key) { - switch ($key) { - case 'user_id': - return true; - default: - return false; - } - }, 'foo')); + switch ($key) { + case 'user_id': + return true; + default: + return false; + } + }, 'foo')); $session->expects($this->once()) ->method('regenerateId'); @@ -643,8 +647,8 @@ class SessionTest extends \Test\TestCase { $manager->expects($this->any()) ->method('get') ->will($this->returnCallback(function ($uid) use ($users) { - return $users[$uid]; - })); + return $users[$uid]; + })); $session = new Memory(''); $session->set('user_id', 'foo'); @@ -699,7 +703,7 @@ class SessionTest extends \Test\TestCase { ->method('getToken') ->with($password) ->will($this->throwException(new \OC\Authentication\Exceptions\InvalidTokenException())); - + $this->tokenProvider->expects($this->once()) ->method('generateToken') ->with($sessionId, $uid, $loginName, $password, 'Firefox'); @@ -748,7 +752,7 @@ class SessionTest extends \Test\TestCase { ->method('getPassword') ->with($token, $password) ->will($this->returnValue($realPassword)); - + $this->tokenProvider->expects($this->once()) ->method('generateToken') ->with($sessionId, $uid, $loginName, $realPassword, 'Firefox'); @@ -772,7 +776,7 @@ class SessionTest extends \Test\TestCase { ->method('get') ->with($uid) ->will($this->returnValue(null)); - + $this->assertFalse($userSession->createSessionToken($request, $uid, $loginName, $password)); } @@ -904,7 +908,7 @@ class SessionTest extends \Test\TestCase { $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config); $password = '123456'; - $sessionId ='session1234'; + $sessionId = 'session1234'; $token = new \OC\Authentication\Token\DefaultToken(); $session->expects($this->once()) @@ -943,7 +947,7 @@ class SessionTest extends \Test\TestCase { $userSession = new \OC\User\Session($userManager, $session, $timeFactory, $tokenProvider, $this->config); $password = '123456'; - $sessionId ='session1234'; + $sessionId = 'session1234'; $token = new \OC\Authentication\Token\DefaultToken(); $session->expects($this->once()) @@ -961,4 +965,104 @@ class SessionTest extends \Test\TestCase { $userSession->updateSessionTokenPassword($password); } + public function testUpdateAuthTokenLastCheck() { + $manager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $session = $this->createMock(ISession::class); + $request = $this->createMock(IRequest::class); + + $token = new \OC\Authentication\Token\DefaultToken(); + $token->setUid('john'); + $token->setLoginName('john'); + $token->setLastActivity(100); + $token->setLastCheck(100); + + $mapper = $this->getMockBuilder(DefaultTokenMapper::class) + ->disableOriginalConstructor() + ->getMock(); + $crypto = $this->getMock(ICrypto::class); + $logger = $this->getMock(ILogger::class); + $tokenProvider = new DefaultTokenProvider($mapper, $crypto, $this->config, $logger, $this->timeFactory); + + /** @var \OC\User\Session $userSession */ + $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config); + + $mapper->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($token)); + $mapper->expects($this->once()) + ->method('update'); + $request + ->expects($this->any()) + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->throttler + ->expects($this->once()) + ->method('sleepDelay') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->any()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(0); + $this->timeFactory + ->expects($this->any()) + ->method('getTime') + ->will($this->returnValue(100)); + + $userSession->logClientIn('john', 'doe', $request, $this->throttler); + + $this->assertEquals(10000, $token->getLastActivity()); + $this->assertEquals(10000, $token->getLastCheck()); + } + + public function testNoUpdateAuthTokenLastCheckRecent() { + $manager = $this->getMockBuilder('\OC\User\Manager') + ->disableOriginalConstructor() + ->getMock(); + $session = $this->createMock(ISession::class); + $request = $this->createMock(IRequest::class); + + $token = new \OC\Authentication\Token\DefaultToken(); + $token->setUid('john'); + $token->setLoginName('john'); + $token->setLastActivity(10000); + $token->setLastCheck(100); + + $mapper = $this->getMockBuilder(DefaultTokenMapper::class) + ->disableOriginalConstructor() + ->getMock(); + $crypto = $this->getMock(ICrypto::class); + $logger = $this->getMock(ILogger::class); + $tokenProvider = new DefaultTokenProvider($mapper, $crypto, $this->config, $logger, $this->timeFactory); + + /** @var \OC\User\Session $userSession */ + $userSession = new Session($manager, $session, $this->timeFactory, $tokenProvider, $this->config); + + $mapper->expects($this->any()) + ->method('getToken') + ->will($this->returnValue($token)); + $mapper->expects($this->never()) + ->method('update'); + $request + ->expects($this->any()) + ->method('getRemoteAddress') + ->willReturn('192.168.0.1'); + $this->throttler + ->expects($this->once()) + ->method('sleepDelay') + ->with('192.168.0.1'); + $this->throttler + ->expects($this->any()) + ->method('getDelay') + ->with('192.168.0.1') + ->willReturn(0); + $this->timeFactory + ->expects($this->any()) + ->method('getTime') + ->will($this->returnValue(100)); + + $userSession->logClientIn('john', 'doe', $request, $this->throttler); + } } From e378cadb56a62463c13ff8d1721d807f51758652 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 11 Oct 2016 12:41:19 +0200 Subject: [PATCH 03/22] tidy up adding user to list a little bit Signed-off-by: Arthur Schiwon --- settings/js/users/users.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 271188875d..d25f9d92db 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -48,10 +48,8 @@ var UserList = { * 'email': 'username@example.org' * 'isRestoreDisabled':false * } - * @param sort - * @returns table row created for this user */ - add: function (user, sort) { + add: function (user) { if (this.currentGid && this.currentGid !== '_everyone' && _.indexOf(user.groups, this.currentGid) < 0) { return; } @@ -170,20 +168,12 @@ var UserList = { UserList.checkUsersToLoad(); } - /** - * sort list - */ - if (sort) { - UserList.doSort(); - } - $quotaSelect.on('change', UserList.onQuotaSelect); // defer init so the user first sees the list appear more quickly window.setTimeout(function(){ $quotaSelect.singleSelect(); }, 0); - return $tr; }, // From http://my.opera.com/GreyWyvern/blog/show.dml/1671288 alphanum: function(a, b) { @@ -389,8 +379,6 @@ var UserList = { OC.generateUrl('/settings/users/users'), { offset: UserList.offset, limit: limit, gid: gid, pattern: pattern }, function (result) { - var loadedUsers = 0; - var trs = []; //The offset does not mirror the amount of users available, //because it is backend-dependent. For correct retrieval, //always the limit(requested amount of users) needs to be added. @@ -398,9 +386,7 @@ var UserList = { if(UserList.has(user.name)) { return true; } - var $tr = UserList.add(user, false); - trs.push($tr); - loadedUsers++; + UserList.add(user); }); if (result.length > 0) { UserList.doSort(); @@ -867,7 +853,8 @@ $(document).ready(function () { } } if(!UserList.has(username)) { - UserList.add(result, true); + UserList.add(result); + UserList.doSort(); } $('#newusername').focus(); GroupList.incEveryoneCount(); From 28708e1b19faf2d2d1293f90f6328bfe101d9ad7 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Wed, 12 Oct 2016 11:55:56 +0200 Subject: [PATCH 04/22] [3rdparty] Update to master again --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 106b98063b..8575a38d0d 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 106b98063b183a721c2863daea519d55a5da1d0c +Subproject commit 8575a38d0dbbc59166f2d7ceddea50d0d120fc49 From 96c2f42ac5aba9e2ba6d893bbd91b3c49a3edb38 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Fri, 7 Oct 2016 11:24:24 +0200 Subject: [PATCH 05/22] Add .bower.json to version control We need .bower.json to have bower functioning correctly Signed-off-by: Roeland Jago Douma --- core/vendor/bootstrap/.bower.json | 44 ++++++++++++++++++++++++++++ core/vendor/clipboard/.bower.json | 32 ++++++++++++++++++++ core/vendor/davclient.js/.bower.json | 32 ++++++++++++++++++++ core/vendor/es6-promise/.bower.json | 39 ++++++++++++++++++++++++ core/vendor/jquery-ui/.bower.json | 18 ++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 core/vendor/bootstrap/.bower.json create mode 100644 core/vendor/clipboard/.bower.json create mode 100644 core/vendor/davclient.js/.bower.json create mode 100644 core/vendor/es6-promise/.bower.json create mode 100644 core/vendor/jquery-ui/.bower.json diff --git a/core/vendor/bootstrap/.bower.json b/core/vendor/bootstrap/.bower.json new file mode 100644 index 0000000000..712f89a908 --- /dev/null +++ b/core/vendor/bootstrap/.bower.json @@ -0,0 +1,44 @@ +{ + "name": "bootstrap", + "description": "The most popular front-end framework for developing responsive, mobile first projects on the web.", + "keywords": [ + "css", + "js", + "less", + "mobile-first", + "responsive", + "front-end", + "framework", + "web" + ], + "homepage": "http://getbootstrap.com", + "license": "MIT", + "moduleType": "globals", + "main": [ + "less/bootstrap.less", + "dist/js/bootstrap.js" + ], + "ignore": [ + "/.*", + "_config.yml", + "CNAME", + "composer.json", + "CONTRIBUTING.md", + "docs", + "js/tests", + "test-infra" + ], + "dependencies": { + "jquery": "1.9.1 - 3" + }, + "version": "3.3.7", + "_release": "3.3.7", + "_resolution": { + "type": "version", + "tag": "v3.3.7", + "commit": "0b9c4a4007c44201dce9a6cc1a38407005c26c86" + }, + "_source": "https://github.com/twbs/bootstrap.git", + "_target": "~3.3.6", + "_originalSource": "bootstrap" +} \ No newline at end of file diff --git a/core/vendor/clipboard/.bower.json b/core/vendor/clipboard/.bower.json new file mode 100644 index 0000000000..28d153baa3 --- /dev/null +++ b/core/vendor/clipboard/.bower.json @@ -0,0 +1,32 @@ +{ + "name": "clipboard", + "version": "1.5.12", + "description": "Modern copy to clipboard. No Flash. Just 2kb", + "license": "MIT", + "main": "dist/clipboard.js", + "ignore": [ + "/.*/", + "/demo/", + "/test/", + "/.*", + "/bower.json", + "/karma.conf.js", + "/src", + "/lib" + ], + "keywords": [ + "clipboard", + "copy", + "cut" + ], + "homepage": "https://github.com/zenorocha/clipboard.js", + "_release": "1.5.12", + "_resolution": { + "type": "version", + "tag": "v1.5.12", + "commit": "eb7418b51bb3c963893d83954c5cfa4de3f7f527" + }, + "_source": "https://github.com/zenorocha/clipboard.js.git", + "_target": "^1.5.12", + "_originalSource": "clipboard" +} \ No newline at end of file diff --git a/core/vendor/davclient.js/.bower.json b/core/vendor/davclient.js/.bower.json new file mode 100644 index 0000000000..5ae36dac53 --- /dev/null +++ b/core/vendor/davclient.js/.bower.json @@ -0,0 +1,32 @@ +{ + "name": "davclient.js", + "version": "0.0.1", + "authors": [ + "Evert Pot " + ], + "description": "A WebDAV, CalDAV and CardDAV client for browsers.", + "main": "lib/client.js", + "keywords": [ + "webdav", + "caldav", + "carddav" + ], + "license": "MIT", + "homepage": "http://sabre.io/", + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "_release": "0.0.1", + "_resolution": { + "type": "version", + "tag": "0.0.1", + "commit": "2d054c63ba5bf7f7d40de904a742f3ad9c71e63c" + }, + "_source": "https://github.com/evert/davclient.js.git", + "_target": "*", + "_originalSource": "https://github.com/evert/davclient.js.git" +} \ No newline at end of file diff --git a/core/vendor/es6-promise/.bower.json b/core/vendor/es6-promise/.bower.json new file mode 100644 index 0000000000..04a9e9cf93 --- /dev/null +++ b/core/vendor/es6-promise/.bower.json @@ -0,0 +1,39 @@ +{ + "name": "es6-promise", + "namespace": "Promise", + "version": "2.3.0", + "description": "A polyfill for ES6-style Promises, tracking rsvp", + "authors": [ + "Stefan Penner " + ], + "main": "dist/es6-promise.js", + "keywords": [ + "promise" + ], + "repository": { + "type": "git", + "url": "git://github.com/jakearchibald/ES6-Promises.git" + }, + "bugs": { + "url": "https://github.com/jakearchibald/ES6-Promises/issues" + }, + "license": "MIT", + "ignore": [ + "node_modules", + "bower_components", + "test", + "tests", + "vendor", + "tasks" + ], + "homepage": "https://github.com/jakearchibald/es6-promise", + "_release": "2.3.0", + "_resolution": { + "type": "version", + "tag": "2.3.0", + "commit": "fcbab11a1a981eb2290bfff89017cb764335a2a5" + }, + "_source": "https://github.com/jakearchibald/es6-promise.git", + "_target": "~2.3.0", + "_originalSource": "https://github.com/jakearchibald/es6-promise.git" +} \ No newline at end of file diff --git a/core/vendor/jquery-ui/.bower.json b/core/vendor/jquery-ui/.bower.json new file mode 100644 index 0000000000..200b000add --- /dev/null +++ b/core/vendor/jquery-ui/.bower.json @@ -0,0 +1,18 @@ +{ + "name": "jquery-ui", + "version": "1.10.0", + "main": "./ui/jquery-ui.js", + "dependencies": { + "jquery": ">= 1.8" + }, + "homepage": "https://github.com/components/jqueryui", + "_release": "1.10.0", + "_resolution": { + "type": "version", + "tag": "1.10.0", + "commit": "4c0bac635cc97c8cd5087bb244d497100429fea2" + }, + "_source": "https://github.com/components/jqueryui.git", + "_target": "1.10.0", + "_originalSource": "jquery-ui" +} \ No newline at end of file From 38eae3177d0fc7d753124f2cf9438e3bd7c9c7aa Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Wed, 12 Oct 2016 13:34:10 +0200 Subject: [PATCH 06/22] add proper .gitignore rules Signed-off-by: Morris Jobke --- core/vendor/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/vendor/.gitignore b/core/vendor/.gitignore index 7982f7de93..057499af3b 100644 --- a/core/vendor/.gitignore +++ b/core/vendor/.gitignore @@ -71,6 +71,7 @@ jquery-ui/themes/vader jquery-ui/ui/** !jquery-ui/ui/jquery-ui.custom.js jquery-ui/*.json +!jquery-ui/.bower.json jquery-ui/AUTHORS.txt jquery-ui/MANIFEST jquery-ui/README.md @@ -115,6 +116,7 @@ snapjs/scripts # bootstrap - only include the tooltip plugin bootstrap/** +!bootstrap/.bower.json !bootstrap/js bootstrap/js/* !bootstrap/js/tooltip.js @@ -124,12 +126,14 @@ backbone/backbone-min* # davclient.js davclient.js/** +!davclient.js/.bower.json !davclient.js/lib !davclient.js/lib/client.js !davclient.js/LICENSE # es6-promise es6-promise/** +!es6-promise/.bower.json !es6-promise/LICENSE !es6-promise/dist es6-promise/dist/* @@ -140,6 +144,7 @@ base64/*min.js # clipboard clipboard/** +!clipboard/.bower.json !clipboard/dist !clipboard/dist/clipboard.js From 0d842e0550a15b7b6c501dae2ec24a61b72ef8c9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 18 Sep 2016 18:36:53 +0200 Subject: [PATCH 07/22] optimize Folder::getById to use less queries Signed-off-by: Robin Appelman --- lib/private/Files/Config/CachedMountInfo.php | 18 ++- .../Files/Config/LazyStorageMountInfo.php | 9 ++ lib/private/Files/Config/UserMountCache.php | 30 ++-- lib/private/Files/Node/Folder.php | 59 ++++--- lib/private/Files/Node/Root.php | 14 +- lib/private/Server.php | 4 +- lib/public/Files/Config/ICachedMountInfo.php | 8 + tests/lib/Files/Config/UserMountCacheTest.php | 111 ++++++++----- tests/lib/Files/Node/FileTest.php | 48 +++--- tests/lib/Files/Node/FolderTest.php | 153 ++++++++++++------ tests/lib/Files/Node/HookConnectorTest.php | 7 +- tests/lib/Files/Node/IntegrationTest.php | 2 +- tests/lib/Files/Node/NodeTest.php | 9 +- tests/lib/Files/Node/RootTest.php | 13 +- 14 files changed, 328 insertions(+), 157 deletions(-) diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php index 74812b3ed8..c4132a3443 100644 --- a/lib/private/Files/Config/CachedMountInfo.php +++ b/lib/private/Files/Config/CachedMountInfo.php @@ -53,6 +53,11 @@ class CachedMountInfo implements ICachedMountInfo { */ protected $mountId; + /** + * @var string + */ + protected $rootInternalPath; + /** * CachedMountInfo constructor. * @@ -61,13 +66,15 @@ class CachedMountInfo implements ICachedMountInfo { * @param int $rootId * @param string $mountPoint * @param int|null $mountId + * @param string $rootInternalPath */ - public function __construct(IUser $user, $storageId, $rootId, $mountPoint, $mountId = null) { + public function __construct(IUser $user, $storageId, $rootId, $mountPoint, $mountId = null, $rootInternalPath = '') { $this->user = $user; $this->storageId = $storageId; $this->rootId = $rootId; $this->mountPoint = $mountPoint; $this->mountId = $mountId; + $this->rootInternalPath = $rootInternalPath; } /** @@ -122,4 +129,13 @@ class CachedMountInfo implements ICachedMountInfo { public function getMountId() { return $this->mountId; } + + /** + * Get the internal path (within the storage) of the root of the mount + * + * @return string + */ + public function getRootInternalPath() { + return $this->rootInternalPath; + } } diff --git a/lib/private/Files/Config/LazyStorageMountInfo.php b/lib/private/Files/Config/LazyStorageMountInfo.php index 4df813d57d..40d463c510 100644 --- a/lib/private/Files/Config/LazyStorageMountInfo.php +++ b/lib/private/Files/Config/LazyStorageMountInfo.php @@ -76,4 +76,13 @@ class LazyStorageMountInfo extends CachedMountInfo { public function getMountId() { return $this->mount->getMountId(); } + + /** + * Get the internal path (within the storage) of the root of the mount + * + * @return string + */ + public function getRootInternalPath() { + return $this->mount->getInternalPath($this->mount->getMountPoint()); + } } diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index bd8343fa44..e9e142d0d4 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -31,6 +31,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Config\ICachedMountInfo; use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountPoint; +use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\ICache; use OCP\IDBConnection; @@ -187,7 +188,7 @@ class UserMountCache implements IUserMountCache { private function dbRowToMountInfo(array $row) { $user = $this->userManager->get($row['user_id']); - return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point'], $row['mount_id']); + return new CachedMountInfo($user, (int)$row['storage_id'], (int)$row['root_id'], $row['mount_point'], $row['mount_id'], isset($row['path'])? $row['path']:''); } /** @@ -197,8 +198,9 @@ class UserMountCache implements IUserMountCache { public function getMountsForUser(IUser $user) { if (!isset($this->mountsForUsers[$user->getUID()])) { $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id') - ->from('mounts') + $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') + ->from('mounts', 'm') + ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('user_id', $builder->createPositionalParameter($user->getUID()))); $rows = $query->execute()->fetchAll(); @@ -214,8 +216,9 @@ class UserMountCache implements IUserMountCache { */ public function getMountsForStorageId($numericStorageId) { $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id') - ->from('mounts') + $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') + ->from('mounts', 'm') + ->innerJoin('m', 'filecache', 'f' , $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('storage_id', $builder->createPositionalParameter($numericStorageId, IQueryBuilder::PARAM_INT))); $rows = $query->execute()->fetchAll(); @@ -229,8 +232,9 @@ class UserMountCache implements IUserMountCache { */ public function getMountsForRootId($rootFileId) { $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id') - ->from('mounts') + $query = $builder->select('storage_id', 'root_id', 'user_id', 'mount_point', 'mount_id', 'f.path') + ->from('mounts', 'm') + ->innerJoin('m', 'filecache', 'f', $builder->expr()->eq('m.root_id', 'f.fileid')) ->where($builder->expr()->eq('root_id', $builder->createPositionalParameter($rootFileId, IQueryBuilder::PARAM_INT))); $rows = $query->execute()->fetchAll(); @@ -246,7 +250,7 @@ class UserMountCache implements IUserMountCache { private function getCacheInfoFromFileId($fileId) { if (!isset($this->cacheInfoCache[$fileId])) { $builder = $this->connection->getQueryBuilder(); - $query = $builder->select('storage', 'path') + $query = $builder->select('storage', 'path', 'mimetype') ->from('filecache') ->where($builder->expr()->eq('fileid', $builder->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); @@ -254,7 +258,8 @@ class UserMountCache implements IUserMountCache { if (is_array($row)) { $this->cacheInfoCache[$fileId] = [ (int)$row['storage'], - $row['path'] + $row['path'], + (int)$row['mimetype'] ]; } else { throw new NotFoundException('File with id "' . $fileId . '" not found'); @@ -281,15 +286,10 @@ class UserMountCache implements IUserMountCache { if ($fileId === $mount->getRootId()) { return true; } - try { - list(, $internalMountPath) = $this->getCacheInfoFromFileId($mount->getRootId()); - } catch (NotFoundException $e) { - return false; - } + $internalMountPath = $mount->getRootInternalPath(); return $internalMountPath === '' || substr($internalPath, 0, strlen($internalMountPath) + 1) === $internalMountPath . '/'; }); - } /** diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index e67e4817e2..b12ded8e79 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -28,6 +28,7 @@ namespace OC\Files\Node; use OC\DB\QueryBuilder\Literal; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Config\ICachedMountInfo; use OCP\Files\FileInfo; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotFoundException; @@ -83,7 +84,7 @@ class Folder extends Node implements \OCP\Files\Folder { public function getDirectoryListing() { $folderContent = $this->view->getDirectoryContent($this->path); - return array_map(function(FileInfo $info) { + return array_map(function (FileInfo $info) { if ($info->getMimetype() === 'httpd/unix-directory') { return new Folder($this->root, $this->view, $info->getPath(), $info); } else { @@ -253,7 +254,7 @@ class Folder extends Node implements \OCP\Files\Folder { } } - return array_map(function(FileInfo $file) { + return array_map(function (FileInfo $file) { return $this->createNode($file->getPath(), $file); }, $files); } @@ -263,29 +264,45 @@ class Folder extends Node implements \OCP\Files\Folder { * @return \OC\Files\Node\Node[] */ public function getById($id) { + $mountCache = $this->root->getUserMountCache(); + $mountsContainingFile = $mountCache->getMountsForFileId($id); $mounts = $this->root->getMountsIn($this->path); $mounts[] = $this->root->getMount($this->path); - // reverse the array so we start with the storage this view is in - // which is the most likely to contain the file we're looking for - $mounts = array_reverse($mounts); + /** @var IMountPoint[] $folderMounts */ + $folderMounts = array_combine(array_map(function (IMountPoint $mountPoint) { + return $mountPoint->getMountPoint(); + }, $mounts), $mounts); - $nodes = array(); - foreach ($mounts as $mount) { - /** - * @var \OC\Files\Mount\MountPoint $mount - */ - if ($mount->getStorage()) { - $cache = $mount->getStorage()->getCache(); - $internalPath = $cache->getPathById($id); - if (is_string($internalPath)) { - $fullPath = $mount->getMountPoint() . $internalPath; - if (!is_null($path = $this->getRelativePath($fullPath))) { - $nodes[] = $this->get($path); - } - } - } + /** @var ICachedMountInfo[] $mountsContainingFile */ + $mountsContainingFile = array_values(array_filter($mountsContainingFile, function (ICachedMountInfo $cachedMountInfo) use ($folderMounts) { + return isset($folderMounts[$cachedMountInfo->getMountPoint()]); + })); + + if (count($mountsContainingFile) === 0) { + return []; } - return $nodes; + + // we only need to get the cache info once, since all mounts we found point to the same storage + + $mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()]; + $cacheEntry = $mount->getStorage()->getCache()->get($id); + // cache jails will hide the "true" internal path + $internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/'); + + $nodes = array_map(function (ICachedMountInfo $cachedMountInfo) use ($cacheEntry, $folderMounts, $internalPath) { + $mount = $folderMounts[$cachedMountInfo->getMountPoint()]; + $pathRelativeToMount = substr($internalPath, strlen($cachedMountInfo->getRootInternalPath())); + $pathRelativeToMount = ltrim($pathRelativeToMount, '/'); + $absolutePath = $cachedMountInfo->getMountPoint() . $pathRelativeToMount; + return $this->root->createNode($absolutePath, new \OC\Files\FileInfo( + $absolutePath, $mount->getStorage(), $cacheEntry->getPath(), $cacheEntry, $mount, + \OC::$server->getUserManager()->get($mount->getStorage()->getOwner($pathRelativeToMount)) + )); + }, $mountsContainingFile); + + return array_filter($nodes, function (Node $node) { + return $this->getRelativePath($node->getPath()); + }); } public function getFreeSpace() { diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 007847fb51..0cda2c8b82 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -31,6 +31,7 @@ namespace OC\Files\Node; use OC\Cache\CappedMemoryCache; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; +use OCP\Files\Config\IUserMountCache; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OC\Hooks\PublicEmitter; @@ -74,17 +75,24 @@ class Root extends Folder implements IRootFolder { private $userFolderCache; + /** + * @var IUserMountCache + */ + private $userMountCache; + /** * @param \OC\Files\Mount\Manager $manager * @param \OC\Files\View $view * @param \OC\User\User|null $user + * @param IUserMountCache $userMountCache */ - public function __construct($manager, $view, $user) { + public function __construct($manager, $view, $user, IUserMountCache $userMountCache) { parent::__construct($this, $view, ''); $this->mountManager = $manager; $this->user = $user; $this->emitter = new PublicEmitter(); $this->userFolderCache = new CappedMemoryCache(); + $this->userMountCache = $userMountCache; } /** @@ -361,4 +369,8 @@ class Root extends Folder implements IRootFolder { public function clearCache() { $this->userFolderCache = new CappedMemoryCache(); } + + public function getUserMountCache() { + return $this->userMountCache; + } } diff --git a/lib/private/Server.php b/lib/private/Server.php index b49e94b554..291714b23d 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -175,10 +175,10 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('SystemTagObjectMapper', function (Server $c) { return $c->query('SystemTagManagerFactory')->getObjectMapper(); }); - $this->registerService('RootFolder', function () { + $this->registerService('RootFolder', function (Server $c) { $manager = \OC\Files\Filesystem::getMountManager(null); $view = new View(); - $root = new Root($manager, $view, null); + $root = new Root($manager, $view, null, $c->getUserMountCache()); $connector = new HookConnector($root, $view); $connector->viewToNode(); return $root; diff --git a/lib/public/Files/Config/ICachedMountInfo.php b/lib/public/Files/Config/ICachedMountInfo.php index 2986f1d98c..f5c5bb87ea 100644 --- a/lib/public/Files/Config/ICachedMountInfo.php +++ b/lib/public/Files/Config/ICachedMountInfo.php @@ -68,4 +68,12 @@ interface ICachedMountInfo { * @since 9.1.0 */ public function getMountId(); + + /** + * Get the internal path (within the storage) of the root of the mount + * + * @return string + * @since 9.2.0 + */ + public function getRootInternalPath(); } diff --git a/tests/lib/Files/Config/UserMountCacheTest.php b/tests/lib/Files/Config/UserMountCacheTest.php index 85bc4146a2..51d488e6de 100644 --- a/tests/lib/Files/Config/UserMountCacheTest.php +++ b/tests/lib/Files/Config/UserMountCacheTest.php @@ -64,7 +64,9 @@ class UserMountCacheTest extends TestCase { } } - private function getStorage($storageId, $rootId) { + private function getStorage($storageId) { + $rootId = $this->createCacheEntry('', $storageId); + $storageCache = $this->getMockBuilder('\OC\Files\Cache\Storage') ->disableOriginalConstructor() ->getMock(); @@ -89,7 +91,7 @@ class UserMountCacheTest extends TestCase { ->method('getCache') ->will($this->returnValue($cache)); - return $storage; + return [$storage, $rootId]; } private function clearCache() { @@ -99,7 +101,7 @@ class UserMountCacheTest extends TestCase { public function testNewMounts() { $user = $this->userManager->get('u1'); - $storage = $this->getStorage(10, 20); + list($storage) = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); @@ -119,7 +121,7 @@ class UserMountCacheTest extends TestCase { public function testSameMounts() { $user = $this->userManager->get('u1'); - $storage = $this->getStorage(10, 20); + list($storage) = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); @@ -143,7 +145,7 @@ class UserMountCacheTest extends TestCase { public function testRemoveMounts() { $user = $this->userManager->get('u1'); - $storage = $this->getStorage(10, 20); + list($storage) = $this->getStorage(10); $mount = new MountPoint($storage, '/asd/'); $this->cache->registerMounts($user, [$mount]); @@ -162,7 +164,7 @@ class UserMountCacheTest extends TestCase { public function testChangeMounts() { $user = $this->userManager->get('u1'); - $storage = $this->getStorage(10, 20); + list($storage) = $this->getStorage(10); $mount = new MountPoint($storage, '/bar/'); $this->cache->registerMounts($user, [$mount]); @@ -185,7 +187,7 @@ class UserMountCacheTest extends TestCase { public function testChangeMountId() { $user = $this->userManager->get('u1'); - $storage = $this->getStorage(10, 20); + list($storage) = $this->getStorage(10); $mount = new MountPoint($storage, '/foo/', null, null, null, null); $this->cache->registerMounts($user, [$mount]); @@ -209,8 +211,10 @@ class UserMountCacheTest extends TestCase { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); - $mount1 = new MountPoint($this->getStorage(1, 2), '/foo/'); - $mount2 = new MountPoint($this->getStorage(3, 4), '/bar/'); + list($storage1, $id1) = $this->getStorage(1); + list($storage2, $id2) = $this->getStorage(2); + $mount1 = new MountPoint($storage1, '/foo/'); + $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); @@ -222,69 +226,73 @@ class UserMountCacheTest extends TestCase { $this->assertCount(2, $cachedMounts); $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); - $this->assertEquals(2, $cachedMounts[0]->getRootId()); + $this->assertEquals($id1, $cachedMounts[0]->getRootId()); $this->assertEquals(1, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[1]->getUser()); - $this->assertEquals(4, $cachedMounts[1]->getRootId()); - $this->assertEquals(3, $cachedMounts[1]->getStorageId()); + $this->assertEquals($id2, $cachedMounts[1]->getRootId()); + $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } public function testGetMountsByStorageId() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); - $mount1 = new MountPoint($this->getStorage(1, 2), '/foo/'); - $mount2 = new MountPoint($this->getStorage(3, 4), '/bar/'); + list($storage1, $id1) = $this->getStorage(1); + list($storage2, $id2) = $this->getStorage(2); + $mount1 = new MountPoint($storage1, '/foo/'); + $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); $this->clearCache(); - $cachedMounts = $this->cache->getMountsForStorageId(3); + $cachedMounts = $this->cache->getMountsForStorageId(2); $this->sortMounts($cachedMounts); $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); - $this->assertEquals(4, $cachedMounts[0]->getRootId()); - $this->assertEquals(3, $cachedMounts[0]->getStorageId()); + $this->assertEquals($id2, $cachedMounts[0]->getRootId()); + $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); $this->assertEquals($user2, $cachedMounts[1]->getUser()); - $this->assertEquals(4, $cachedMounts[1]->getRootId()); - $this->assertEquals(3, $cachedMounts[1]->getStorageId()); + $this->assertEquals($id2, $cachedMounts[1]->getRootId()); + $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } public function testGetMountsByRootId() { $user1 = $this->userManager->get('u1'); $user2 = $this->userManager->get('u2'); - $mount1 = new MountPoint($this->getStorage(1, 2), '/foo/'); - $mount2 = new MountPoint($this->getStorage(3, 4), '/bar/'); + list($storage1, $id1) = $this->getStorage(1); + list($storage2, $id2) = $this->getStorage(2); + $mount1 = new MountPoint($storage1, '/foo/'); + $mount2 = new MountPoint($storage2, '/bar/'); $this->cache->registerMounts($user1, [$mount1, $mount2]); $this->cache->registerMounts($user2, [$mount2]); $this->clearCache(); - $cachedMounts = $this->cache->getMountsForRootId(4); + $cachedMounts = $this->cache->getMountsForRootId($id2); $this->sortMounts($cachedMounts); $this->assertCount(2, $cachedMounts); $this->assertEquals('/bar/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); - $this->assertEquals(4, $cachedMounts[0]->getRootId()); - $this->assertEquals(3, $cachedMounts[0]->getStorageId()); + $this->assertEquals($id2, $cachedMounts[0]->getRootId()); + $this->assertEquals(2, $cachedMounts[0]->getStorageId()); $this->assertEquals('/bar/', $cachedMounts[1]->getMountPoint()); $this->assertEquals($user2, $cachedMounts[1]->getUser()); - $this->assertEquals(4, $cachedMounts[1]->getRootId()); - $this->assertEquals(3, $cachedMounts[1]->getStorageId()); + $this->assertEquals($id2, $cachedMounts[1]->getRootId()); + $this->assertEquals(2, $cachedMounts[1]->getStorageId()); } private function sortMounts(&$mounts) { @@ -294,7 +302,8 @@ class UserMountCacheTest extends TestCase { } private function createCacheEntry($internalPath, $storageId) { - $this->connection->insertIfNotExist('*PREFIX*filecache', [ + $internalPath = trim($internalPath, '/'); + $inserted = $this->connection->insertIfNotExist('*PREFIX*filecache', [ 'storage' => $storageId, 'path' => $internalPath, 'path_hash' => md5($internalPath), @@ -309,17 +318,23 @@ class UserMountCacheTest extends TestCase { 'etag' => '', 'permissions' => 31 ], ['storage', 'path_hash']); - $id = (int)$this->connection->lastInsertId('*PREFIX*filecache'); - $this->fileIds[] = $id; + if ($inserted) { + $id = (int)$this->connection->lastInsertId('*PREFIX*filecache'); + $this->fileIds[] = $id; + } else { + $sql = 'SELECT fileid FROM *PREFIX*filecache WHERE `storage` = ? AND `path_hash` =?'; + $query = $this->connection->prepare($sql); + $query->execute([$storageId, md5($internalPath)]); + return (int)$query->fetchColumn(); + } return $id; } public function testGetMountsForFileIdRootId() { $user1 = $this->userManager->get('u1'); - $rootId = $this->createCacheEntry('', 2); - - $mount1 = new MountPoint($this->getStorage(2, $rootId), '/foo/'); + list($storage1, $rootId) = $this->getStorage(2); + $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); @@ -338,10 +353,10 @@ class UserMountCacheTest extends TestCase { public function testGetMountsForFileIdSubFolder() { $user1 = $this->userManager->get('u1'); - $rootId = $this->createCacheEntry('', 2); $fileId = $this->createCacheEntry('/foo/bar', 2); - $mount1 = new MountPoint($this->getStorage(2, $rootId), '/foo/'); + list($storage1, $rootId) = $this->getStorage(2); + $mount1 = new MountPoint($storage1, '/foo/'); $this->cache->registerMounts($user1, [$mount1]); @@ -360,11 +375,19 @@ class UserMountCacheTest extends TestCase { public function testGetMountsForFileIdSubFolderMount() { $user1 = $this->userManager->get('u1'); - $this->createCacheEntry('', 2); + list($storage1, $rootId) = $this->getStorage(2); $folderId = $this->createCacheEntry('/foo', 2); $fileId = $this->createCacheEntry('/foo/bar', 2); - $mount1 = new MountPoint($this->getStorage(2, $folderId), '/foo/'); + + $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') + ->setConstructorArgs([$storage1, '/']) + ->setMethods(['getStorageRootId']) + ->getMock(); + + $mount1->expects($this->any()) + ->method('getStorageRootId') + ->will($this->returnValue($folderId)); $this->cache->registerMounts($user1, [$mount1]); @@ -374,20 +397,30 @@ class UserMountCacheTest extends TestCase { $this->assertCount(1, $cachedMounts); - $this->assertEquals('/foo/', $cachedMounts[0]->getMountPoint()); + $this->assertEquals('/', $cachedMounts[0]->getMountPoint()); $this->assertEquals($user1, $cachedMounts[0]->getUser()); $this->assertEquals($folderId, $cachedMounts[0]->getRootId()); $this->assertEquals(2, $cachedMounts[0]->getStorageId()); + $this->assertEquals('foo', $cachedMounts[0]->getRootInternalPath()); } public function testGetMountsForFileIdSubFolderMountOutside() { $user1 = $this->userManager->get('u1'); - $this->createCacheEntry('', 2); + list($storage1, $rootId) = $this->getStorage(2); $folderId = $this->createCacheEntry('/foo', 2); $fileId = $this->createCacheEntry('/bar/asd', 2); - $mount1 = new MountPoint($this->getStorage(2, $folderId), '/foo/'); + $mount1 = $this->getMockBuilder('\OC\Files\Mount\MountPoint') + ->setConstructorArgs([$storage1, '/foo/']) + ->setMethods(['getStorageRootId']) + ->getMock(); + + $mount1->expects($this->any()) + ->method('getStorageRootId') + ->will($this->returnValue($folderId)); + + $this->cache->registerMounts($user1, [$mount1]); $this->cache->registerMounts($user1, [$mount1]); diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index 1b665bd4c7..b7a2fa2a1a 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -21,6 +21,9 @@ class FileTest extends \Test\TestCase { /** @var \OC\Files\View|\PHPUnit_Framework_MockObject_MockObject */ private $view; + /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit_Framework_MockObject_MockObject */ + private $userMountCache; + protected function setUp() { parent::setUp(); $config = $this->getMockBuilder('\OCP\IConfig') @@ -34,6 +37,9 @@ class FileTest extends \Test\TestCase { $this->view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); } protected function getMockStorage() { @@ -52,7 +58,7 @@ class FileTest extends \Test\TestCase { public function testDelete() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->exactly(2)) @@ -102,7 +108,7 @@ class FileTest extends \Test\TestCase { $hooksRun++; }; - $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user, $this->userMountCache); $root->listen('\OC\Files', 'preDelete', $preListener); $root->listen('\OC\Files', 'postDelete', $postListener); @@ -132,7 +138,7 @@ class FileTest extends \Test\TestCase { public function testDeleteNotPermitted() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->any()) @@ -151,7 +157,7 @@ class FileTest extends \Test\TestCase { public function testGetContent() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $hook = function ($file) { @@ -181,7 +187,7 @@ class FileTest extends \Test\TestCase { public function testGetContentNotPermitted() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->any()) @@ -200,7 +206,7 @@ class FileTest extends \Test\TestCase { public function testPutContent() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->any()) @@ -227,7 +233,7 @@ class FileTest extends \Test\TestCase { public function testPutContentNotPermitted() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->once()) @@ -242,7 +248,7 @@ class FileTest extends \Test\TestCase { public function testGetMimeType() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->once()) @@ -259,7 +265,7 @@ class FileTest extends \Test\TestCase { fwrite($stream, 'bar'); rewind($stream); - $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user, $this->userMountCache); $hook = function ($file) { throw new \Exception('Hooks are not supposed to be called'); @@ -287,7 +293,7 @@ class FileTest extends \Test\TestCase { public function testFOpenWrite() { $stream = fopen('php://memory', 'w+'); - $root = new \OC\Files\Node\Root($this->manager, new $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, new $this->view, $this->user, $this->userMountCache); $hooksCalled = 0; $hook = function ($file) use (&$hooksCalled) { @@ -320,7 +326,7 @@ class FileTest extends \Test\TestCase { * @expectedException \OCP\Files\NotPermittedException */ public function testFOpenReadNotPermitted() { - $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user, $this->userMountCache); $hook = function ($file) { throw new \Exception('Hooks are not supposed to be called'); @@ -339,7 +345,7 @@ class FileTest extends \Test\TestCase { * @expectedException \OCP\Files\NotPermittedException */ public function testFOpenReadWriteNoReadPermissions() { - $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user, $this->userMountCache); $hook = function () { throw new \Exception('Hooks are not supposed to be called'); @@ -358,7 +364,7 @@ class FileTest extends \Test\TestCase { * @expectedException \OCP\Files\NotPermittedException */ public function testFOpenReadWriteNoWritePermissions() { - $root = new \OC\Files\Node\Root($this->manager, new $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, new $this->view, $this->user, $this->userMountCache); $hook = function () { throw new \Exception('Hooks are not supposed to be called'); @@ -376,7 +382,7 @@ class FileTest extends \Test\TestCase { public function testCopySameStorage() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->any()) @@ -409,7 +415,7 @@ class FileTest extends \Test\TestCase { public function testCopyNotPermitted() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); /** @@ -447,7 +453,7 @@ class FileTest extends \Test\TestCase { public function testCopyNoParent() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->never()) @@ -469,7 +475,7 @@ class FileTest extends \Test\TestCase { public function testCopyParentIsFile() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->never()) @@ -490,7 +496,7 @@ class FileTest extends \Test\TestCase { public function testMoveSameStorage() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->any()) @@ -520,7 +526,7 @@ class FileTest extends \Test\TestCase { public function testMoveNotPermitted() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->any()) @@ -547,7 +553,7 @@ class FileTest extends \Test\TestCase { public function testMoveNoParent() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); /** @@ -577,7 +583,7 @@ class FileTest extends \Test\TestCase { public function testMoveParentIsFile() { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject $root */ $root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); $this->view->expects($this->never()) diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index 17cb2f2caa..e592013eb6 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -9,6 +9,8 @@ namespace Test\Files\Node; use OC\Files\Cache\Cache; +use OC\Files\Cache\CacheEntry; +use OC\Files\Config\CachedMountInfo; use OC\Files\FileInfo; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; @@ -32,9 +34,15 @@ use OCP\Files\Storage; class FolderTest extends \Test\TestCase { private $user; + /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit_Framework_MockObject_MockObject */ + private $userMountCache; + protected function setUp() { parent::setUp(); $this->user = new \OC\User\User('', new \Test\Util\User\Dummy); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); } protected function getMockStorage() { @@ -56,7 +64,7 @@ class FolderTest extends \Test\TestCase { */ $view = $this->createMock(View::class); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -110,7 +118,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = new \OC\Files\Node\Root($manager, $view, $this->user); + $root = new \OC\Files\Node\Root($manager, $view, $this->user, $this->userMountCache); $root->listen('\OC\Files', 'preDelete', $preListener); $root->listen('\OC\Files', 'postDelete', $postListener); @@ -142,7 +150,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -163,7 +171,7 @@ class FolderTest extends \Test\TestCase { */ $view = $this->createMock(View::class); $root = $this->getMockBuilder(Root::class) - ->setConstructorArgs([$manager, $view, $this->user]) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache]) ->getMock(); $root->expects($this->any()) ->method('getUser') @@ -194,7 +202,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -213,7 +221,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -235,7 +243,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -255,7 +263,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -285,7 +293,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -305,7 +313,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -335,7 +343,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -355,7 +363,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -375,7 +383,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -423,7 +431,8 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -471,7 +480,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -519,7 +528,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -567,7 +576,7 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $root->expects($this->any()) ->method('getUser') ->will($this->returnValue($this->user)); @@ -647,26 +656,34 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); - $root->expects($this->any()) - ->method('getUser') - ->will($this->returnValue($this->user)); + $root = $this->getMockBuilder(Root::class)->setMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount = new MountPoint($storage, '/bar'); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([''])->getMock(); - $view->expects($this->once()) - ->method('getFileInfo') - ->will($this->returnValue(new FileInfo('/bar/foo/qwerty', null, 'qwerty', ['mimetype' => 'text/plain'], null))); + $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null); $storage->expects($this->once()) ->method('getCache') ->will($this->returnValue($cache)); + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->will($this->returnValue([new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 1, + '' + )])); + $cache->expects($this->once()) - ->method('getPathById') - ->with('1') - ->will($this->returnValue('foo/qwerty')); + ->method('get') + ->with(1) + ->will($this->returnValue($fileInfo)); $root->expects($this->once()) ->method('getMountsIn') @@ -690,22 +707,34 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); - $root->expects($this->any()) - ->method('getUser') - ->will($this->returnValue($this->user)); + $root = $this->getMockBuilder(Root::class)->setMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount = new MountPoint($storage, '/bar'); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([''])->getMock(); + $fileInfo = new CacheEntry(['path' => 'foobar', 'mimetype' => 'text/plain'], null); + $storage->expects($this->once()) ->method('getCache') ->will($this->returnValue($cache)); + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->will($this->returnValue([new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 1, + '' + )])); + $cache->expects($this->once()) - ->method('getPathById') - ->with('1') - ->will($this->returnValue('foobar')); + ->method('get') + ->with(1) + ->will($this->returnValue($fileInfo)); $root->expects($this->once()) ->method('getMountsIn') @@ -719,7 +748,7 @@ class FolderTest extends \Test\TestCase { $node = new \OC\Files\Node\Folder($root, $view, '/bar/foo'); $result = $node->getById(1); - $this->assertCount(0, $result); + $this->assertEquals(0, count($result)); } public function testGetByIdMultipleStorages() { @@ -728,27 +757,49 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); - $root->expects($this->any()) - ->method('getUser') - ->will($this->returnValue($this->user)); + $root = $this->getMockBuilder(Root::class)->setMethods(['getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $storage = $this->createMock(\OC\Files\Storage\Storage::class); $mount1 = new MountPoint($storage, '/bar'); $mount2 = new MountPoint($storage, '/bar/foo/asd'); $cache = $this->getMockBuilder(Cache::class)->setConstructorArgs([''])->getMock(); - $view->expects($this->any()) - ->method('getFileInfo') - ->will($this->returnValue(new FileInfo('/bar/foo/qwerty', null, 'qwerty', ['mimetype' => 'plain'], null))); + $fileInfo = new CacheEntry(['path' => 'foo/qwerty', 'mimetype' => 'text/plain'], null); + + $storage->expects($this->once()) + ->method('getCache') + ->will($this->returnValue($cache)); + + $this->userMountCache->expects($this->any()) + ->method('getMountsForFileId') + ->with(1) + ->will($this->returnValue([ + new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/', + 1, + '' + ), + new CachedMountInfo( + $this->user, + 1, + 0, + '/bar/foo/asd/', + 1, + '' + ) + ])); $storage->expects($this->any()) ->method('getCache') ->will($this->returnValue($cache)); $cache->expects($this->any()) - ->method('getPathById') - ->with('1') - ->will($this->returnValue('foo/qwerty')); + ->method('get') + ->with(1) + ->will($this->returnValue($fileInfo)); $root->expects($this->any()) ->method('getMountsIn') @@ -786,7 +837,8 @@ class FolderTest extends \Test\TestCase { * @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view */ $view = $this->createMock(View::class); - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); $view->expects($this->any()) ->method('file_exists') @@ -811,7 +863,8 @@ class FolderTest extends \Test\TestCase { */ $view = $this->createMock(View::class); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */ - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder('\OC\Files\FileInfo') ->disableOriginalConstructor()->getMock(); @@ -869,7 +922,8 @@ class FolderTest extends \Test\TestCase { */ $view = $this->createMock(View::class); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */ - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder('\OC\Files\FileInfo') ->disableOriginalConstructor()->getMock(); @@ -925,7 +979,8 @@ class FolderTest extends \Test\TestCase { */ $view = $this->createMock(View::class); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */ - $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount'])->setConstructorArgs([$manager, $view, $this->user])->getMock(); + $root = $this->getMockBuilder(Root::class)->setMethods(['getUser', 'getMountsIn', 'getMount']) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache])->getMock(); /** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */ $folderInfo = $this->getMockBuilder('\OC\Files\FileInfo') ->disableOriginalConstructor()->getMock(); diff --git a/tests/lib/Files/Node/HookConnectorTest.php b/tests/lib/Files/Node/HookConnectorTest.php index 7245dd3759..013fcabf22 100644 --- a/tests/lib/Files/Node/HookConnectorTest.php +++ b/tests/lib/Files/Node/HookConnectorTest.php @@ -50,7 +50,12 @@ class HookConnectorTest extends TestCase { $this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/'); \OC_Util::setupFS($this->userId); $this->view = new View(); - $this->root = new Root(Filesystem::getMountManager(), $this->view, \OC::$server->getUserManager()->get($this->userId)); + $this->root = new Root( + Filesystem::getMountManager(), + $this->view, + \OC::$server->getUserManager()->get($this->userId), + \OC::$server->getUserMountCache() + ); } public function tearDown() { diff --git a/tests/lib/Files/Node/IntegrationTest.php b/tests/lib/Files/Node/IntegrationTest.php index f52e0623e1..da753f833c 100644 --- a/tests/lib/Files/Node/IntegrationTest.php +++ b/tests/lib/Files/Node/IntegrationTest.php @@ -47,7 +47,7 @@ class IntegrationTest extends \Test\TestCase { $this->loginAsUser($user->getUID()); $this->view = new View(); - $this->root = new Root($manager, $this->view, $user); + $this->root = new Root($manager, $this->view, $user, \OC::$server->getUserMountCache()); $storage = new Temporary(array()); $subStorage = new Temporary(array()); $this->storages[] = $storage; diff --git a/tests/lib/Files/Node/NodeTest.php b/tests/lib/Files/Node/NodeTest.php index 2fd47680ba..b7e2f336a6 100644 --- a/tests/lib/Files/Node/NodeTest.php +++ b/tests/lib/Files/Node/NodeTest.php @@ -22,6 +22,8 @@ class NodeTest extends \Test\TestCase { /** @var \OC\Files\Node\Root|\PHPUnit_Framework_MockObject_MockObject */ private $root; + /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit_Framework_MockObject_MockObject */ + private $userMountCache; protected function setUp() { parent::setUp(); @@ -41,8 +43,11 @@ class NodeTest extends \Test\TestCase { $this->view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); $this->root = $this->getMockBuilder('\OC\Files\Node\Root') - ->setConstructorArgs([$this->manager, $this->view, $this->user]) + ->setConstructorArgs([$this->manager, $this->view, $this->user, $this->userMountCache]) ->getMock(); } @@ -268,7 +273,7 @@ class NodeTest extends \Test\TestCase { $hooksRun++; }; - $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $this->view, $this->user, $this->userMountCache); $root->listen('\OC\Files', 'preTouch', $preListener); $root->listen('\OC\Files', 'postTouch', $postListener); diff --git a/tests/lib/Files/Node/RootTest.php b/tests/lib/Files/Node/RootTest.php index 92aebcddab..f42a42c42b 100644 --- a/tests/lib/Files/Node/RootTest.php +++ b/tests/lib/Files/Node/RootTest.php @@ -16,6 +16,8 @@ class RootTest extends \Test\TestCase { /** @var \OC\Files\Mount\Manager */ private $manager; + /** @var \OCP\Files\Config\IUserMountCache|\PHPUnit_Framework_MockObject_MockObject */ + private $userMountCache; protected function setUp() { parent::setUp(); @@ -32,6 +34,9 @@ class RootTest extends \Test\TestCase { $this->manager = $this->getMockBuilder('\OC\Files\Mount\Manager') ->disableOriginalConstructor() ->getMock(); + $this->userMountCache = $this->getMockBuilder('\OCP\Files\Config\IUserMountCache') + ->disableOriginalConstructor() + ->getMock(); } protected function getFileInfo($data) { @@ -51,7 +56,7 @@ class RootTest extends \Test\TestCase { $view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); - $root = new \OC\Files\Node\Root($this->manager, $view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $view, $this->user, $this->userMountCache); $view->expects($this->once()) ->method('getFileInfo') @@ -80,7 +85,7 @@ class RootTest extends \Test\TestCase { $view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); - $root = new \OC\Files\Node\Root($this->manager, $view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $view, $this->user, $this->userMountCache); $view->expects($this->once()) ->method('getFileInfo') @@ -101,7 +106,7 @@ class RootTest extends \Test\TestCase { $view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); - $root = new \OC\Files\Node\Root($this->manager, $view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $view, $this->user, $this->userMountCache); $root->get('/../foo'); } @@ -116,7 +121,7 @@ class RootTest extends \Test\TestCase { $view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); - $root = new \OC\Files\Node\Root($this->manager, $view, $this->user); + $root = new \OC\Files\Node\Root($this->manager, $view, $this->user, $this->userMountCache); $root->get('/bar/foo'); } From 37eded7e7cda2ab421b573d3b6d9165a4fb9dc9a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 11:29:12 +0200 Subject: [PATCH 08/22] Always unlock node after trying to create a share Signed-off-by: Robin Appelman --- apps/files_sharing/lib/API/Share20OCS.php | 4 +++- lib/private/Share20/Share.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index 34f73c7ac0..5adfa640c0 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -402,8 +402,10 @@ class Share20OCS extends OCSController { } catch (GenericShareException $e) { $code = $e->getCode() === 0 ? 403 : $e->getCode(); throw new OCSException($e->getHint(), $code); - }catch (\Exception $e) { + } catch (\Exception $e) { throw new OCSForbiddenException($e->getMessage()); + } finally { + $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); } $output = $this->formatShare($share); diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index c565809c07..e3e8482f4e 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -160,7 +160,7 @@ class Share implements \OCP\Share\IShare { $nodes = $userFolder->getById($this->fileId); if (empty($nodes)) { - throw new NotFoundException(); + throw new NotFoundException('Node for share not found, fileid: ' . $this->fileId); } $this->node = $nodes[0]; From 7ce68ce6ac03f85532e312a68e61aaa0e09037ba Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 11:45:06 +0200 Subject: [PATCH 09/22] phpunit is weird Signed-off-by: Robin Appelman --- apps/files_sharing/tests/ApiTest.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index a62c29c5ca..19f3ad3b36 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -29,6 +29,7 @@ namespace OCA\Files_Sharing\Tests; +use OC\Files\Cache\Scanner; use OCP\AppFramework\OCS\OCSBadRequestException; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCS\OCSForbiddenException; @@ -72,6 +73,8 @@ class ApiTest extends TestCase { $this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder); $this->view->file_put_contents($this->folder.$this->filename, $this->data); $this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data); + $mount = $this->view->getMount($this->filename); + $mount->getStorage()->getScanner()->scan('', Scanner::SCAN_RECURSIVE); $this->userFolder = \OC::$server->getUserFolder(self::TEST_FILES_SHARING_API_USER1); } @@ -113,10 +116,8 @@ class ApiTest extends TestCase { ); } - /** - * @medium - */ function testCreateShareUserFile() { + $this->setUp(); // for some reasons phpunit refuses to do this for us only for this test $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1); $result = $ocs->createShare($this->filename, \OCP\Constants::PERMISSION_ALL, \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2); $ocs->cleanup(); From 3845d4ec9a7a84cda4f2fe5fbb4a65e90dfe21ff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 15:27:40 +0200 Subject: [PATCH 10/22] more robust test setup Signed-off-by: Robin Appelman --- apps/files_sharing/tests/ApiTest.php | 11 ++++------- apps/files_sharing/tests/External/ManagerTest.php | 6 +++--- apps/files_sharing/tests/TestCase.php | 3 +++ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index 19f3ad3b36..0a8b4b119c 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -1194,14 +1194,11 @@ class ApiTest extends TestCase { * Tests mounting a folder that is an external storage mount point. */ public function testShareStorageMountPoint() { - self::$tempStorage = new \OC\Files\Storage\Temporary(array()); - self::$tempStorage->file_put_contents('test.txt', 'abcdef'); - self::$tempStorage->getScanner()->scan(''); + $tempStorage = new \OC\Files\Storage\Temporary(array()); + $tempStorage->file_put_contents('test.txt', 'abcdef'); + $tempStorage->getScanner()->scan(''); - // needed because the sharing code sometimes switches the user internally and mounts the user's - // storages. In our case the temp storage isn't mounted automatically, so doing it in the post hook - // (similar to how ext storage works) - \OCP\Util::connectHook('OC_Filesystem', 'post_initMountPoints', '\OCA\Files_Sharing\Tests\ApiTest', 'initTestMountPointsHook'); + $this->registerMount(self::TEST_FILES_SHARING_API_USER1, $tempStorage, self::TEST_FILES_SHARING_API_USER1 . '/files' . self::TEST_FOLDER_NAME); // logging in will auto-mount the temp storage for user1 as well self::loginHelper(self::TEST_FILES_SHARING_API_USER1); diff --git a/apps/files_sharing/tests/External/ManagerTest.php b/apps/files_sharing/tests/External/ManagerTest.php index 096bbe8577..48476888bd 100644 --- a/apps/files_sharing/tests/External/ManagerTest.php +++ b/apps/files_sharing/tests/External/ManagerTest.php @@ -58,7 +58,7 @@ class ManagerTest extends TestCase { * @var \OCP\IUser */ private $user; - private $mountProvider; + private $testMountProvider; protected function setUp() { parent::setUp(); @@ -82,13 +82,13 @@ class ManagerTest extends TestCase { $discoveryManager, $this->uid ); - $this->mountProvider = new MountProvider(\OC::$server->getDatabaseConnection(), function() { + $this->testMountProvider = new MountProvider(\OC::$server->getDatabaseConnection(), function() { return $this->manager; }); } private function setupMounts() { - $mounts = $this->mountProvider->getMountsForUser($this->user, new StorageFactory()); + $mounts = $this->testMountProvider->getMountsForUser($this->user, new StorageFactory()); foreach ($mounts as $mount) { $this->mountManager->addMount($mount); } diff --git a/apps/files_sharing/tests/TestCase.php b/apps/files_sharing/tests/TestCase.php index 0ad58151db..1777eb5bad 100644 --- a/apps/files_sharing/tests/TestCase.php +++ b/apps/files_sharing/tests/TestCase.php @@ -31,8 +31,10 @@ namespace OCA\Files_Sharing\Tests; +use OC\Files\Cache\Scanner; use OC\Files\Filesystem; use OCA\Files_Sharing\AppInfo\Application; +use Test\Traits\MountProviderTrait; /** * Class TestCase @@ -42,6 +44,7 @@ use OCA\Files_Sharing\AppInfo\Application; * Base class for sharing tests. */ abstract class TestCase extends \Test\TestCase { + use MountProviderTrait; const TEST_FILES_SHARING_API_USER1 = "test-share-user1"; const TEST_FILES_SHARING_API_USER2 = "test-share-user2"; From 1b3b816a0a2ffb80d2495cd7394988e689351c47 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 17:59:04 +0200 Subject: [PATCH 11/22] re-use the share node while formating if we already have it Signed-off-by: Robin Appelman --- apps/files_sharing/lib/API/Share20OCS.php | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index 5adfa640c0..4ec4a006be 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -29,6 +29,7 @@ use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCS\OCSForbiddenException; use OCP\AppFramework\OCS\OCSNotFoundException; use OCP\AppFramework\OCSController; +use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\IGroupManager; use OCP\IL10N; @@ -110,10 +111,11 @@ class Share20OCS extends OCSController { * Convert an IShare to an array for OCS output * * @param \OCP\Share\IShare $share + * @param Node|null $recipientNode * @return array * @throws NotFoundException In case the node can't be resolved. */ - protected function formatShare(\OCP\Share\IShare $share) { + protected function formatShare(\OCP\Share\IShare $share, Node $recipientNode = null) { $sharedBy = $this->userManager->get($share->getSharedBy()); $shareOwner = $this->userManager->get($share->getShareOwner()); @@ -132,14 +134,18 @@ class Share20OCS extends OCSController { ]; $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $nodes = $userFolder->getById($share->getNodeId()); + if ($recipientNode) { + $node = $recipientNode; + } else { + $nodes = $userFolder->getById($share->getNodeId()); - if (empty($nodes)) { - throw new NotFoundException(); + if (empty($nodes)) { + throw new NotFoundException(); + } + + $node = $nodes[0]; } - $node = $nodes[0]; - $result['path'] = $userFolder->getRelativePath($node->getPath()); if ($node instanceOf \OCP\Files\Folder) { $result['item_type'] = 'folder'; @@ -543,7 +549,7 @@ class Share20OCS extends OCSController { $formatted = []; foreach ($shares as $share) { try { - $formatted[] = $this->formatShare($share); + $formatted[] = $this->formatShare($share, $path); } catch (NotFoundException $e) { //Ignore share } From 7d1e800c153d362569a1e6f462109212c03f3b0a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 17:59:24 +0200 Subject: [PATCH 12/22] fix share tests not passing path Signed-off-by: Robin Appelman --- apps/files_sharing/tests/ApiTest.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index 0a8b4b119c..aff3aadd7e 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -628,7 +628,7 @@ class ApiTest extends TestCase { ); foreach ($testValues as $value) { - $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2); + $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2); $result = $ocs->getShares('false', 'false', 'true', $value['query']); $ocs->cleanup(); @@ -765,6 +765,7 @@ class ApiTest extends TestCase { * @medium */ function testGetShareMultipleSharedFolder() { + $this->setUp(); $node1 = $this->userFolder->get($this->folder . $this->subfolder); $share1 = $this->shareManager->newShare(); $share1->setNode($node1) @@ -790,8 +791,9 @@ class ApiTest extends TestCase { ->setPermissions(1); $share3 = $this->shareManager->createShare($share3); + // $request = $this->createRequest(['path' => $this->subfolder]); $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2); - $result1 = $ocs->getShares(); + $result1 = $ocs->getShares('false','false','false', $this->subfolder); $ocs->cleanup(); // test should return one share within $this->folder @@ -799,8 +801,9 @@ class ApiTest extends TestCase { $this->assertCount(1, $data1); $s1 = reset($data1); + //$request = $this->createRequest(['path' => $this->folder.$this->subfolder]); $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER2); - $result2 = $ocs->getShares(); + $result2 = $ocs->getShares('false', 'false', 'false', $this->folder . $this->subfolder); $ocs->cleanup(); // test should return one share within $this->folder @@ -808,7 +811,7 @@ class ApiTest extends TestCase { $this->assertCount(1, $data2); $s2 = reset($data2); - $this->assertEquals($this->folder.$this->subfolder, $s1['path']); + $this->assertEquals($this->subfolder, $s1['path']); $this->assertEquals($this->folder.$this->subfolder, $s2['path']); $this->shareManager->deleteShare($share1); From 240538d9e6b12327a6affacbfc744ba51cb70be8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 20 Sep 2016 18:00:20 +0200 Subject: [PATCH 13/22] reuse share node when creating a share Signed-off-by: Robin Appelman --- lib/private/Share20/Manager.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index b4fe69a83e..22cf5a3f65 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -614,8 +614,11 @@ class Manager implements IManager { throw new \Exception($error); } + $oldShare = $share; $provider = $this->factory->getProviderForType($share->getShareType()); $share = $provider->create($share); + //reuse the node we already have + $share->setNode($oldShare->getNode()); // Post share hook $postHookData = [ From 9754724c8b3afca625d9f6f4bbd3442c50c8ad33 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 21 Sep 2016 17:35:09 +0200 Subject: [PATCH 14/22] fix getting recipient share in ocs api Signed-off-by: Robin Appelman --- apps/files_sharing/lib/API/Share20OCS.php | 32 +++++++++++++---------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index 4ec4a006be..d5a6d8bc9d 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -85,15 +85,15 @@ class Share20OCS extends OCSController { * @param IL10N $l10n */ public function __construct( - $appName, - IRequest $request, - IManager $shareManager, - IGroupManager $groupManager, - IUserManager $userManager, - IRootFolder $rootFolder, - IURLGenerator $urlGenerator, - IUser $currentUser, - IL10N $l10n + $appName, + IRequest $request, + IManager $shareManager, + IGroupManager $groupManager, + IUserManager $userManager, + IRootFolder $rootFolder, + IURLGenerator $urlGenerator, + IUser $currentUser, + IL10N $l10n ) { parent::__construct($appName, $request); @@ -405,6 +405,8 @@ class Share20OCS extends OCSController { try { $share = $this->shareManager->createShare($share); + $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); + $recipientNode = $userFolder->get($share->getTarget()); } catch (GenericShareException $e) { $code = $e->getCode() === 0 ? 403 : $e->getCode(); throw new OCSException($e->getHint(), $code); @@ -414,7 +416,7 @@ class Share20OCS extends OCSController { $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); } - $output = $this->formatShare($share); + $output = $this->formatShare($share, $recipientNode); return new DataResponse($output); } @@ -429,7 +431,7 @@ class Share20OCS extends OCSController { $shares = array_merge($userShares, $groupShares); - $shares = array_filter($shares, function(IShare $share) { + $shares = array_filter($shares, function (IShare $share) { return $share->getShareOwner() !== $this->currentUser->getUID(); }); @@ -716,7 +718,8 @@ class Share20OCS extends OCSController { // If the share is shared with you (or a group you are a member of) if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && - $share->getSharedWith() === $this->currentUser->getUID()) { + $share->getSharedWith() === $this->currentUser->getUID() + ) { return true; } @@ -751,7 +754,7 @@ class Share20OCS extends OCSController { throw new \Exception('Invalid date. Format must be YYYY-MM-DD'); } - $date->setTime(0,0,0); + $date->setTime(0, 0, 0); return $date; } @@ -769,7 +772,7 @@ class Share20OCS extends OCSController { // First check if it is an internal share. try { - $share = $this->shareManager->getShareById('ocinternal:'.$id); + $share = $this->shareManager->getShareById('ocinternal:' . $id); } catch (ShareNotFound $e) { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { throw new ShareNotFound(); @@ -783,6 +786,7 @@ class Share20OCS extends OCSController { /** * Lock a Node + * * @param \OCP\Files\Node $node */ private function lock(\OCP\Files\Node $node) { From f63f0aa1ae338b6599a591faf9d35d82e9f78457 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 21 Sep 2016 18:55:58 +0200 Subject: [PATCH 15/22] get node by path as fallback Signed-off-by: Robin Appelman --- apps/files_sharing/lib/API/Share20OCS.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index d5a6d8bc9d..ac9ddc1fff 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -140,10 +140,14 @@ class Share20OCS extends OCSController { $nodes = $userFolder->getById($share->getNodeId()); if (empty($nodes)) { - throw new NotFoundException(); + // fallback to guessing the path + $node = $userFolder->get($share->getTarget()); + if ($node === null) { + throw new NotFoundException(); + } + } else { + $node = $nodes[0]; } - - $node = $nodes[0]; } $result['path'] = $userFolder->getRelativePath($node->getPath()); @@ -405,8 +409,6 @@ class Share20OCS extends OCSController { try { $share = $this->shareManager->createShare($share); - $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $recipientNode = $userFolder->get($share->getTarget()); } catch (GenericShareException $e) { $code = $e->getCode() === 0 ? 403 : $e->getCode(); throw new OCSException($e->getHint(), $code); @@ -416,7 +418,7 @@ class Share20OCS extends OCSController { $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); } - $output = $this->formatShare($share, $recipientNode); + $output = $this->formatShare($share); return new DataResponse($output); } From ab10a548366a030bd58b97a5ea88588b8138aee7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 22 Sep 2016 10:36:31 +0200 Subject: [PATCH 16/22] remove duplicate trait Signed-off-by: Robin Appelman --- apps/files_sharing/tests/SizePropagationTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/files_sharing/tests/SizePropagationTest.php b/apps/files_sharing/tests/SizePropagationTest.php index 3655977fd9..04db505e8a 100644 --- a/apps/files_sharing/tests/SizePropagationTest.php +++ b/apps/files_sharing/tests/SizePropagationTest.php @@ -38,7 +38,6 @@ use Test\Traits\UserTrait; */ class SizePropagationTest extends TestCase { use UserTrait; - use MountProviderTrait; protected function setupUser($name, $password = '') { $this->createUser($name, $password); From 1484d01ff623317e6c14c02ad1053b68f30f2622 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 10 Oct 2016 14:16:25 +0200 Subject: [PATCH 17/22] explicitly cast to int Signed-off-by: Robin Appelman --- lib/private/Files/Node/Folder.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index b12ded8e79..353b89068c 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -265,7 +265,7 @@ class Folder extends Node implements \OCP\Files\Folder { */ public function getById($id) { $mountCache = $this->root->getUserMountCache(); - $mountsContainingFile = $mountCache->getMountsForFileId($id); + $mountsContainingFile = $mountCache->getMountsForFileId((int)$id); $mounts = $this->root->getMountsIn($this->path); $mounts[] = $this->root->getMount($this->path); /** @var IMountPoint[] $folderMounts */ @@ -285,7 +285,10 @@ class Folder extends Node implements \OCP\Files\Folder { // we only need to get the cache info once, since all mounts we found point to the same storage $mount = $folderMounts[$mountsContainingFile[0]->getMountPoint()]; - $cacheEntry = $mount->getStorage()->getCache()->get($id); + $cacheEntry = $mount->getStorage()->getCache()->get((int)$id); + if (!$cacheEntry) { + return []; + } // cache jails will hide the "true" internal path $internalPath = ltrim($mountsContainingFile[0]->getRootInternalPath() . '/' . $cacheEntry->getPath(), '/'); From 2de59d8a2a32427654a4ae70146464b7a338c308 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Oct 2016 16:12:01 +0200 Subject: [PATCH 18/22] remove unneeded unlock Signed-off-by: Robin Appelman --- apps/files_sharing/lib/API/Share20OCS.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/files_sharing/lib/API/Share20OCS.php b/apps/files_sharing/lib/API/Share20OCS.php index ac9ddc1fff..8d00d17667 100644 --- a/apps/files_sharing/lib/API/Share20OCS.php +++ b/apps/files_sharing/lib/API/Share20OCS.php @@ -414,8 +414,6 @@ class Share20OCS extends OCSController { throw new OCSException($e->getHint(), $code); } catch (\Exception $e) { throw new OCSForbiddenException($e->getMessage()); - } finally { - $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); } $output = $this->formatShare($share); From 9e817e9e0b5d6c0b34954bfef70510b64ced11f4 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 12 Oct 2016 23:47:07 +0200 Subject: [PATCH 19/22] symfony replaced table helper by class, fixes two broken LDAP occ commands Signed-off-by: Arthur Schiwon --- apps/user_ldap/lib/Access.php | 1 - apps/user_ldap/lib/Command/ShowConfig.php | 3 ++- apps/user_ldap/lib/Command/ShowRemnants.php | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/user_ldap/lib/Access.php b/apps/user_ldap/lib/Access.php index 96b6bae64b..40bae8d7b4 100644 --- a/apps/user_ldap/lib/Access.php +++ b/apps/user_ldap/lib/Access.php @@ -304,7 +304,6 @@ class Access extends LDAPUtility implements IUserTools { } /** - public function ocname2dn($name, $isUser) { * returns the internal ownCloud name for the given LDAP DN of the group, false on DN outside of search DN or failure * @param string $fdn the dn of the group object * @param string $ldapName optional, the display name of the object diff --git a/apps/user_ldap/lib/Command/ShowConfig.php b/apps/user_ldap/lib/Command/ShowConfig.php index ac70468b1c..7a24889eb0 100644 --- a/apps/user_ldap/lib/Command/ShowConfig.php +++ b/apps/user_ldap/lib/Command/ShowConfig.php @@ -26,6 +26,7 @@ namespace OCA\User_LDAP\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -91,7 +92,7 @@ class ShowConfig extends Command { $configuration = $configHolder->getConfiguration(); ksort($configuration); - $table = $this->getHelperSet()->get('table'); + $table = new Table($output); $table->setHeaders(array('Configuration', $id)); $rows = array(); foreach($configuration as $key => $value) { diff --git a/apps/user_ldap/lib/Command/ShowRemnants.php b/apps/user_ldap/lib/Command/ShowRemnants.php index df38d6f650..0e6aea6a5a 100644 --- a/apps/user_ldap/lib/Command/ShowRemnants.php +++ b/apps/user_ldap/lib/Command/ShowRemnants.php @@ -26,6 +26,7 @@ namespace OCA\User_LDAP\Command; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -64,7 +65,7 @@ class ShowRemnants extends Command { */ protected function execute(InputInterface $input, OutputInterface $output) { /** @var \Symfony\Component\Console\Helper\Table $table */ - $table = $this->getHelperSet()->get('table'); + $table = new Table($output); $table->setHeaders(array( 'ownCloud name', 'Display Name', 'LDAP UID', 'LDAP DN', 'Last Login', 'Dir', 'Sharer')); From 9aae4a0d020d5ca07d3840aed322ba646e6af15f Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 13 Oct 2016 00:07:02 +0000 Subject: [PATCH 20/22] [tx-robot] updated from transifex --- apps/files/l10n/ru.js | 4 ++++ apps/files/l10n/ru.json | 4 ++++ apps/twofactor_backupcodes/l10n/fr.js | 5 ++++- apps/twofactor_backupcodes/l10n/fr.json | 5 ++++- core/l10n/de.js | 1 + core/l10n/de.json | 1 + core/l10n/de_DE.js | 1 + core/l10n/de_DE.json | 1 + core/l10n/hu_HU.js | 1 + core/l10n/hu_HU.json | 1 + core/l10n/it.js | 2 ++ core/l10n/it.json | 2 ++ lib/l10n/ja.js | 14 +++++++++++--- lib/l10n/ja.json | 14 +++++++++++--- settings/l10n/ja.js | 10 +++++++++- settings/l10n/ja.json | 10 +++++++++- 16 files changed, 66 insertions(+), 10 deletions(-) diff --git a/apps/files/l10n/ru.js b/apps/files/l10n/ru.js index e69a8321c1..449b4c486d 100644 --- a/apps/files/l10n/ru.js +++ b/apps/files/l10n/ru.js @@ -109,9 +109,13 @@ OC.L10N.register( "%2$s deleted %1$s" : "%2$s удалил %1$s", "You restored %1$s" : "Вы восстановили %1$s", "%2$s restored %1$s" : "%2$s восстановил %1$s", + "You renamed %2$s to %1$s" : "Вы переименовали %2$s в %1$s", + "You moved %2$s to %1$s" : "Вы переместили %2$s в %1$s", "Changed by %2$s" : "Изменено %2$s", "Deleted by %2$s" : "Удалено %2$s", "Restored by %2$s" : "Восстановлено %2$s", + "Renamed by %2$s" : "Переименовано %2$s", + "Moved by %2$s" : "Перемещено %2$s", "Upload (max. %s)" : "Загрузка (максимум %s)", "File handling" : "Управление файлами", "Maximum upload size" : "Максимальный размер загружаемого файла", diff --git a/apps/files/l10n/ru.json b/apps/files/l10n/ru.json index 599f0d4d21..cbf4b95a39 100644 --- a/apps/files/l10n/ru.json +++ b/apps/files/l10n/ru.json @@ -107,9 +107,13 @@ "%2$s deleted %1$s" : "%2$s удалил %1$s", "You restored %1$s" : "Вы восстановили %1$s", "%2$s restored %1$s" : "%2$s восстановил %1$s", + "You renamed %2$s to %1$s" : "Вы переименовали %2$s в %1$s", + "You moved %2$s to %1$s" : "Вы переместили %2$s в %1$s", "Changed by %2$s" : "Изменено %2$s", "Deleted by %2$s" : "Удалено %2$s", "Restored by %2$s" : "Восстановлено %2$s", + "Renamed by %2$s" : "Переименовано %2$s", + "Moved by %2$s" : "Перемещено %2$s", "Upload (max. %s)" : "Загрузка (максимум %s)", "File handling" : "Управление файлами", "Maximum upload size" : "Максимальный размер загружаемого файла", diff --git a/apps/twofactor_backupcodes/l10n/fr.js b/apps/twofactor_backupcodes/l10n/fr.js index 89c7420b86..56b14ae0f6 100644 --- a/apps/twofactor_backupcodes/l10n/fr.js +++ b/apps/twofactor_backupcodes/l10n/fr.js @@ -3,12 +3,15 @@ OC.L10N.register( { "Generate backup codes" : "Générer des codes de récupération", "Backup codes have been generated. {{used}} of {{total}} codes have been used." : "Les codes de récupération ont été générés. {{used}} codes sur {{total}} ont été utilisés.", + "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "Voici vos codes de récupération. Veuillez les sauvegarder et/ou les imprimer car vous ne pouvez plus y avoir accès ultérieurement", "Save backup codes" : "Sauvegarder les codes de récupération", "Print backup codes" : "Imprimer les codes de récupération", "Regenerate backup codes" : "Régénérer les codes de récupération", + "If you regenerate backup codes, you automatically invalidate old codes." : "Si vous régénérez les codes de récupération, vous invalidez automatiquement les anciens codes.", "An error occurred while generating your backup codes" : "Une erreur est survenue lors de la génération de vos codes de récupération", "Nextcloud backup codes" : "Codes de récupération Nextcloud", "Backup code" : "Code de récupération", - "Use backup code" : "Utiliser un code de récupération" + "Use backup code" : "Utiliser un code de récupération", + "Second-factor backup codes" : "Codes de récupération pour l'authentification en deux étapes" }, "nplurals=2; plural=(n > 1);"); diff --git a/apps/twofactor_backupcodes/l10n/fr.json b/apps/twofactor_backupcodes/l10n/fr.json index 9d7dbf03e5..275fbb7e2f 100644 --- a/apps/twofactor_backupcodes/l10n/fr.json +++ b/apps/twofactor_backupcodes/l10n/fr.json @@ -1,12 +1,15 @@ { "translations": { "Generate backup codes" : "Générer des codes de récupération", "Backup codes have been generated. {{used}} of {{total}} codes have been used." : "Les codes de récupération ont été générés. {{used}} codes sur {{total}} ont été utilisés.", + "These are your backup codes. Please save and/or print them as you will not be able to read the codes again later" : "Voici vos codes de récupération. Veuillez les sauvegarder et/ou les imprimer car vous ne pouvez plus y avoir accès ultérieurement", "Save backup codes" : "Sauvegarder les codes de récupération", "Print backup codes" : "Imprimer les codes de récupération", "Regenerate backup codes" : "Régénérer les codes de récupération", + "If you regenerate backup codes, you automatically invalidate old codes." : "Si vous régénérez les codes de récupération, vous invalidez automatiquement les anciens codes.", "An error occurred while generating your backup codes" : "Une erreur est survenue lors de la génération de vos codes de récupération", "Nextcloud backup codes" : "Codes de récupération Nextcloud", "Backup code" : "Code de récupération", - "Use backup code" : "Utiliser un code de récupération" + "Use backup code" : "Utiliser un code de récupération", + "Second-factor backup codes" : "Codes de récupération pour l'authentification en deux étapes" },"pluralForm" :"nplurals=2; plural=(n > 1);" } \ No newline at end of file diff --git a/core/l10n/de.js b/core/l10n/de.js index 67c893af4b..62b7d5ca61 100644 --- a/core/l10n/de.js +++ b/core/l10n/de.js @@ -185,6 +185,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} (auf {server})", "{sharee} (remote)" : "{sharee} (remote)", "Share" : "Teilen", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Teile es mit Nutzern in anderen Nextclouds. Die Syntax hierfür lautet: username@beispiel.de/nextcloud", "Share with users…" : "Mit Benutzern teilen…", "Share with users, groups or remote users…" : "Mit Benutzern, Gruppen oder externen Benutzern teilen…", "Share with users or groups…" : "Mit Benutzern oder Gruppen teilen…", diff --git a/core/l10n/de.json b/core/l10n/de.json index c0873f3557..8b6cc64b22 100644 --- a/core/l10n/de.json +++ b/core/l10n/de.json @@ -183,6 +183,7 @@ "{sharee} (at {server})" : "{sharee} (auf {server})", "{sharee} (remote)" : "{sharee} (remote)", "Share" : "Teilen", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Teile es mit Nutzern in anderen Nextclouds. Die Syntax hierfür lautet: username@beispiel.de/nextcloud", "Share with users…" : "Mit Benutzern teilen…", "Share with users, groups or remote users…" : "Mit Benutzern, Gruppen oder externen Benutzern teilen…", "Share with users or groups…" : "Mit Benutzern oder Gruppen teilen…", diff --git a/core/l10n/de_DE.js b/core/l10n/de_DE.js index 8cbc127e80..cb20599fe1 100644 --- a/core/l10n/de_DE.js +++ b/core/l10n/de_DE.js @@ -185,6 +185,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} (auf {server})", "{sharee} (remote)" : "{sharee} (entfernt)", "Share" : "Teilen", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Teilen Sie es mit Nutzern in anderen Nextclouds. Die Syntax hierfür lautet: username@beispiel.de/nextcloud", "Share with users…" : "Mit Benutzern teilen…", "Share with users, groups or remote users…" : "Mit Benutzern, Gruppen oder externen Benutzern teilen…", "Share with users or groups…" : "Mit Benutzern oder Gruppen teilen…", diff --git a/core/l10n/de_DE.json b/core/l10n/de_DE.json index 8603b2960d..c016fdf64c 100644 --- a/core/l10n/de_DE.json +++ b/core/l10n/de_DE.json @@ -183,6 +183,7 @@ "{sharee} (at {server})" : "{sharee} (auf {server})", "{sharee} (remote)" : "{sharee} (entfernt)", "Share" : "Teilen", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Teilen Sie es mit Nutzern in anderen Nextclouds. Die Syntax hierfür lautet: username@beispiel.de/nextcloud", "Share with users…" : "Mit Benutzern teilen…", "Share with users, groups or remote users…" : "Mit Benutzern, Gruppen oder externen Benutzern teilen…", "Share with users or groups…" : "Mit Benutzern oder Gruppen teilen…", diff --git a/core/l10n/hu_HU.js b/core/l10n/hu_HU.js index 6e8b7c8b15..eeed2bbd2d 100644 --- a/core/l10n/hu_HU.js +++ b/core/l10n/hu_HU.js @@ -185,6 +185,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} ({server} szerveren)", "{sharee} (remote)" : "{sharee} (távoli)", "Share" : "Megosztás", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Megosztás más Nextcloud-ot használó emberekkel, a következő szintaxissal: felhasznalo@pelda.hu/nextcloud", "Share with users…" : "Megosztás felhasználókkal", "Share with users, groups or remote users…" : "Megosztás felhasználókkal, csoportokkal vagy távoli felhasználókkal...", "Share with users or groups…" : "Megosztás felhasználókkal vagy csoportokkal...", diff --git a/core/l10n/hu_HU.json b/core/l10n/hu_HU.json index 7801a7987f..6bba1e09c6 100644 --- a/core/l10n/hu_HU.json +++ b/core/l10n/hu_HU.json @@ -183,6 +183,7 @@ "{sharee} (at {server})" : "{sharee} ({server} szerveren)", "{sharee} (remote)" : "{sharee} (távoli)", "Share" : "Megosztás", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Megosztás más Nextcloud-ot használó emberekkel, a következő szintaxissal: felhasznalo@pelda.hu/nextcloud", "Share with users…" : "Megosztás felhasználókkal", "Share with users, groups or remote users…" : "Megosztás felhasználókkal, csoportokkal vagy távoli felhasználókkal...", "Share with users or groups…" : "Megosztás felhasználókkal vagy csoportokkal...", diff --git a/core/l10n/it.js b/core/l10n/it.js index 26a356d937..9add07a2b2 100644 --- a/core/l10n/it.js +++ b/core/l10n/it.js @@ -165,6 +165,7 @@ OC.L10N.register( "Send link via email" : "Invia collegamento tramite email", "Shared with you and the group {group} by {owner}" : "Condiviso con te e con il gruppo {group} da {owner}", "Shared with you by {owner}" : "Condiviso con te da {owner}", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} ha condiviso tramite collegamento", "group" : "gruppo", "remote" : "remota", "notify by email" : "notifica tramite email", @@ -184,6 +185,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} (su {server})", "{sharee} (remote)" : "{sharee} (remote)", "Share" : "Condividi", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Condividi con persone su altri Nextcloud utilizzando la sintassi nomeutente@esempio.com/nextcloud", "Share with users…" : "Condividi con utenti...", "Share with users, groups or remote users…" : "Condividi con utenti, gruppi o utenti remoti...", "Share with users or groups…" : "Condividi con utenti o gruppi...", diff --git a/core/l10n/it.json b/core/l10n/it.json index b0e3fcf302..1e86981f42 100644 --- a/core/l10n/it.json +++ b/core/l10n/it.json @@ -163,6 +163,7 @@ "Send link via email" : "Invia collegamento tramite email", "Shared with you and the group {group} by {owner}" : "Condiviso con te e con il gruppo {group} da {owner}", "Shared with you by {owner}" : "Condiviso con te da {owner}", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} ha condiviso tramite collegamento", "group" : "gruppo", "remote" : "remota", "notify by email" : "notifica tramite email", @@ -182,6 +183,7 @@ "{sharee} (at {server})" : "{sharee} (su {server})", "{sharee} (remote)" : "{sharee} (remote)", "Share" : "Condividi", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Condividi con persone su altri Nextcloud utilizzando la sintassi nomeutente@esempio.com/nextcloud", "Share with users…" : "Condividi con utenti...", "Share with users, groups or remote users…" : "Condividi con utenti, gruppi o utenti remoti...", "Share with users or groups…" : "Condividi con utenti o gruppi...", diff --git a/lib/l10n/ja.js b/lib/l10n/ja.js index 1ed9d5d513..357637e40a 100644 --- a/lib/l10n/ja.js +++ b/lib/l10n/ja.js @@ -52,6 +52,11 @@ OC.L10N.register( "App can't be installed because it is not compatible with this version of the server" : "アプリは、このバージョンのサーバーと互換性がないためインストールされませんでした。", "App can't be installed because it contains the true tag which is not allowed for non shipped apps" : "非shippedアプリには許可されないtrueタグが含まれているためにアプリをインストールできません。", "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "info.xmlのバージョンがアプリストアのバージョンと合っていないため、アプリはインストールされません", + "APCu" : "APCu", + "Redis" : "Redis", + "Server settings" : "サーバー設定", + "Logging" : "ロギング", + "Additional settings" : "追加設定", "%s enter the database username and name." : "%s データベース名とデータベースのユーザー名を入力してください。", "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", @@ -115,6 +120,7 @@ OC.L10N.register( "The username is already being used" : "ユーザー名はすでに使われています", "Login canceled by app" : "アプリによりログインが中止されました", "User disabled" : "ユーザーは無効です", + "Help" : "ヘルプ", "Personal" : "個人", "Users" : "ユーザー", "Admin" : "管理", @@ -131,8 +137,8 @@ OC.L10N.register( "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", "Unknown user" : "不明なユーザー", "No database drivers (sqlite, mysql, or postgresql) installed." : "データベースドライバー (sqlite, mysql, postgresql) がインストールされていません。", - "Microsoft Windows Platform is not supported" : "Microsoft Windows サーバーはサポートしていません。", - "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "マイクロソフト Windowsサーバー上で Nextcloudを動かすのをサポートしていません。サーバー丸ごと移行する方式をとれない場合は仮想マシンで Linux を動かすことをお勧めします。", + "Microsoft Windows Platform is not supported" : "Microsoft Windows プラットフォームはサポートしていません。", + "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "マイクロソフト Windowsプラットフォーム上での Nextcloudの動作をサポートしていません。サーバー丸ごと移行する方式をとれない場合は仮想マシンで Linux を動かすことをお勧めします。", "Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません", "Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "多くの場合、これは %s Webサーバーにappsディレクトリ %s への書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで解決できます。", @@ -166,6 +172,8 @@ OC.L10N.register( "Storage incomplete configuration. %s" : "設定が未完了のストレージです。 %s", "Storage connection error. %s" : "ストレージへの接続エラー。 %s", "Storage not available" : "ストレージが利用できません", - "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s" + "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s", + "ownCloud %s or higher is required." : "ownCloud %s 以上が必要です", + "ownCloud %s or lower is required." : "ownCloud %s 以下が必要です" }, "nplurals=1; plural=0;"); diff --git a/lib/l10n/ja.json b/lib/l10n/ja.json index 9ba57f9d10..5fa2bcffe9 100644 --- a/lib/l10n/ja.json +++ b/lib/l10n/ja.json @@ -50,6 +50,11 @@ "App can't be installed because it is not compatible with this version of the server" : "アプリは、このバージョンのサーバーと互換性がないためインストールされませんでした。", "App can't be installed because it contains the true tag which is not allowed for non shipped apps" : "非shippedアプリには許可されないtrueタグが含まれているためにアプリをインストールできません。", "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "info.xmlのバージョンがアプリストアのバージョンと合っていないため、アプリはインストールされません", + "APCu" : "APCu", + "Redis" : "Redis", + "Server settings" : "サーバー設定", + "Logging" : "ロギング", + "Additional settings" : "追加設定", "%s enter the database username and name." : "%s データベース名とデータベースのユーザー名を入力してください。", "%s enter the database username." : "%s のデータベースのユーザー名を入力してください。", "%s enter the database name." : "%s のデータベース名を入力してください。", @@ -113,6 +118,7 @@ "The username is already being used" : "ユーザー名はすでに使われています", "Login canceled by app" : "アプリによりログインが中止されました", "User disabled" : "ユーザーは無効です", + "Help" : "ヘルプ", "Personal" : "個人", "Users" : "ユーザー", "Admin" : "管理", @@ -129,8 +135,8 @@ "Token expired. Please reload page." : "トークンが無効になりました。ページを再読込してください。", "Unknown user" : "不明なユーザー", "No database drivers (sqlite, mysql, or postgresql) installed." : "データベースドライバー (sqlite, mysql, postgresql) がインストールされていません。", - "Microsoft Windows Platform is not supported" : "Microsoft Windows サーバーはサポートしていません。", - "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "マイクロソフト Windowsサーバー上で Nextcloudを動かすのをサポートしていません。サーバー丸ごと移行する方式をとれない場合は仮想マシンで Linux を動かすことをお勧めします。", + "Microsoft Windows Platform is not supported" : "Microsoft Windows プラットフォームはサポートしていません。", + "Running Nextcloud Server on the Microsoft Windows platform is not supported. We suggest you use a Linux server in a virtual machine if you have no option for migrating the server itself." : "マイクロソフト Windowsプラットフォーム上での Nextcloudの動作をサポートしていません。サーバー丸ごと移行する方式をとれない場合は仮想マシンで Linux を動かすことをお勧めします。", "Cannot write into \"config\" directory" : "\"config\" ディレクトリに書き込みができません", "Cannot write into \"apps\" directory" : "\"apps\" ディレクトリに書き込みができません", "This can usually be fixed by %sgiving the webserver write access to the apps directory%s or disabling the appstore in the config file." : "多くの場合、これは %s Webサーバーにappsディレクトリ %s への書き込み権限を与えるか、設定ファイルでアプリストアを無効化することで解決できます。", @@ -164,6 +170,8 @@ "Storage incomplete configuration. %s" : "設定が未完了のストレージです。 %s", "Storage connection error. %s" : "ストレージへの接続エラー。 %s", "Storage not available" : "ストレージが利用できません", - "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s" + "Storage connection timeout. %s" : "ストレージへの接続がタイムアウト。 %s", + "ownCloud %s or higher is required." : "ownCloud %s 以上が必要です", + "ownCloud %s or lower is required." : "ownCloud %s 以下が必要です" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/settings/l10n/ja.js b/settings/l10n/ja.js index 774938dfde..3ca023b5ef 100644 --- a/settings/l10n/ja.js +++ b/settings/l10n/ja.js @@ -60,7 +60,7 @@ OC.L10N.register( "_You have %n app update pending_::_You have %n app updates pending_" : ["%n 個のアプリのアップデートを保留中"], "Please wait...." : "しばらくお待ちください...", "Error while disabling app" : "アプリ無効化中にエラーが発生", - "Disable" : "無効", + "Disable" : "無効にする", "Enable" : "有効にする", "Error while enabling app" : "アプリを有効にする際にエラーが発生", "Error: this app cannot be enabled because it makes the server unstable" : "エラー:このアプリは、サーバーを不安定にするため、有効にすることができません。", @@ -76,6 +76,12 @@ OC.L10N.register( "App update" : "アプリのアップデート", "No apps found for {query}" : "{query} に対応するアプリはありません", "Disconnect" : "切断", + "Internet Explorer" : "Internet Explorer", + "Edge" : "Edge", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "iPhone" : "iPhone", "Error while loading browser sessions and device tokens" : "ブラウザセッションとデバイストークンの読み込みにおけるエラー", "Error while creating device token" : "デバイストークンの作成におけるエラー", "Error while deleting the token" : "トークンの削除におけるエラー", @@ -99,6 +105,7 @@ OC.L10N.register( "deleted {userName}" : "{userName} を削除しました", "Invalid quota value \"{val}\"" : "クオータ値 \"{val}\" は不正な値です。", "Changing the password will result in data loss, because data recovery is not available for this user" : "このユーザーのデータ復旧が無効になっていますので、パスワードを変更するとユーザーはデータに二度とアクセスできません。", + "Password successfully changed" : "パスワードは変更されました", "A valid username must be provided" : "有効なユーザー名を指定する必要があります", "Error creating user: {message}" : "ユーザ作成エラー {message}", "A valid password must be provided" : "有効なパスワードを指定する必要があります", @@ -288,6 +295,7 @@ OC.L10N.register( "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "ストレージのクォータを入力してください (例: \"512MB\" や \"12 GB\")", "Other" : "その他", "Quota" : "クオータ", + "Last login" : "最終ログイン", "change full name" : "名前を変更", "set new password" : "新しいパスワードを設定", "change email address" : "メールアドレスを変更", diff --git a/settings/l10n/ja.json b/settings/l10n/ja.json index 2e78eac1ed..b0249deb32 100644 --- a/settings/l10n/ja.json +++ b/settings/l10n/ja.json @@ -58,7 +58,7 @@ "_You have %n app update pending_::_You have %n app updates pending_" : ["%n 個のアプリのアップデートを保留中"], "Please wait...." : "しばらくお待ちください...", "Error while disabling app" : "アプリ無効化中にエラーが発生", - "Disable" : "無効", + "Disable" : "無効にする", "Enable" : "有効にする", "Error while enabling app" : "アプリを有効にする際にエラーが発生", "Error: this app cannot be enabled because it makes the server unstable" : "エラー:このアプリは、サーバーを不安定にするため、有効にすることができません。", @@ -74,6 +74,12 @@ "App update" : "アプリのアップデート", "No apps found for {query}" : "{query} に対応するアプリはありません", "Disconnect" : "切断", + "Internet Explorer" : "Internet Explorer", + "Edge" : "Edge", + "Firefox" : "Firefox", + "Google Chrome" : "Google Chrome", + "Safari" : "Safari", + "iPhone" : "iPhone", "Error while loading browser sessions and device tokens" : "ブラウザセッションとデバイストークンの読み込みにおけるエラー", "Error while creating device token" : "デバイストークンの作成におけるエラー", "Error while deleting the token" : "トークンの削除におけるエラー", @@ -97,6 +103,7 @@ "deleted {userName}" : "{userName} を削除しました", "Invalid quota value \"{val}\"" : "クオータ値 \"{val}\" は不正な値です。", "Changing the password will result in data loss, because data recovery is not available for this user" : "このユーザーのデータ復旧が無効になっていますので、パスワードを変更するとユーザーはデータに二度とアクセスできません。", + "Password successfully changed" : "パスワードは変更されました", "A valid username must be provided" : "有効なユーザー名を指定する必要があります", "Error creating user: {message}" : "ユーザ作成エラー {message}", "A valid password must be provided" : "有効なパスワードを指定する必要があります", @@ -286,6 +293,7 @@ "Please enter storage quota (ex: \"512 MB\" or \"12 GB\")" : "ストレージのクォータを入力してください (例: \"512MB\" や \"12 GB\")", "Other" : "その他", "Quota" : "クオータ", + "Last login" : "最終ログイン", "change full name" : "名前を変更", "set new password" : "新しいパスワードを設定", "change email address" : "メールアドレスを変更", From 92c24a91fac434f0de188d3f39d8f6cf1012f527 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 14 Oct 2016 00:07:00 +0000 Subject: [PATCH 21/22] [tx-robot] updated from transifex --- apps/files/l10n/ru.js | 2 ++ apps/files/l10n/ru.json | 2 ++ apps/files/l10n/zh_TW.js | 3 +++ apps/files/l10n/zh_TW.json | 3 +++ apps/updatenotification/l10n/fr.js | 1 + apps/updatenotification/l10n/fr.json | 1 + apps/updatenotification/l10n/zh_TW.js | 17 +++++++++++++++-- apps/updatenotification/l10n/zh_TW.json | 17 +++++++++++++++-- core/l10n/ru.js | 4 ++++ core/l10n/ru.json | 4 ++++ core/l10n/zh_TW.js | 12 ++++++++++++ core/l10n/zh_TW.json | 12 ++++++++++++ 12 files changed, 74 insertions(+), 4 deletions(-) diff --git a/apps/files/l10n/ru.js b/apps/files/l10n/ru.js index 449b4c486d..d8c3f6703e 100644 --- a/apps/files/l10n/ru.js +++ b/apps/files/l10n/ru.js @@ -110,7 +110,9 @@ OC.L10N.register( "You restored %1$s" : "Вы восстановили %1$s", "%2$s restored %1$s" : "%2$s восстановил %1$s", "You renamed %2$s to %1$s" : "Вы переименовали %2$s в %1$s", + "%2$s renamed %3$s to %1$s" : "%2$s переименовал %3$s в %1$s", "You moved %2$s to %1$s" : "Вы переместили %2$s в %1$s", + "%2$s moved %3$s to %1$s" : "%2$s переместил %3$s в %1$s", "Changed by %2$s" : "Изменено %2$s", "Deleted by %2$s" : "Удалено %2$s", "Restored by %2$s" : "Восстановлено %2$s", diff --git a/apps/files/l10n/ru.json b/apps/files/l10n/ru.json index cbf4b95a39..10396ad3cf 100644 --- a/apps/files/l10n/ru.json +++ b/apps/files/l10n/ru.json @@ -108,7 +108,9 @@ "You restored %1$s" : "Вы восстановили %1$s", "%2$s restored %1$s" : "%2$s восстановил %1$s", "You renamed %2$s to %1$s" : "Вы переименовали %2$s в %1$s", + "%2$s renamed %3$s to %1$s" : "%2$s переименовал %3$s в %1$s", "You moved %2$s to %1$s" : "Вы переместили %2$s в %1$s", + "%2$s moved %3$s to %1$s" : "%2$s переместил %3$s в %1$s", "Changed by %2$s" : "Изменено %2$s", "Deleted by %2$s" : "Удалено %2$s", "Restored by %2$s" : "Восстановлено %2$s", diff --git a/apps/files/l10n/zh_TW.js b/apps/files/l10n/zh_TW.js index 6cafc5e5c2..23dc1c23c0 100644 --- a/apps/files/l10n/zh_TW.js +++ b/apps/files/l10n/zh_TW.js @@ -42,6 +42,7 @@ OC.L10N.register( "{seconds}s" : "{seconds} 秒", "Any moment now..." : "即將完成…", "Soon..." : "即將完成…", + "{loadedSize} of {totalSize} ({bitrate})" : "{totalSize} 中的 {loadedSize} ({bitrate})", "File upload is in progress. Leaving the page now will cancel the upload." : "檔案上傳中,離開此頁面將會取消上傳", "Actions" : "動作", "Download" : "下載", @@ -110,6 +111,8 @@ OC.L10N.register( "Changed by %2$s" : "由 %2$s 改動", "Deleted by %2$s" : "由 %2$s 刪除", "Restored by %2$s" : "由 %2$s 還原", + "Renamed by %2$s" : "由 %2$s 重新命名", + "Moved by %2$s" : "由 %2$s 移動", "Upload (max. %s)" : "上傳(至多 %s)", "File handling" : "檔案處理", "Maximum upload size" : "上傳限制", diff --git a/apps/files/l10n/zh_TW.json b/apps/files/l10n/zh_TW.json index 5e9f270e53..94e8bbe4bf 100644 --- a/apps/files/l10n/zh_TW.json +++ b/apps/files/l10n/zh_TW.json @@ -40,6 +40,7 @@ "{seconds}s" : "{seconds} 秒", "Any moment now..." : "即將完成…", "Soon..." : "即將完成…", + "{loadedSize} of {totalSize} ({bitrate})" : "{totalSize} 中的 {loadedSize} ({bitrate})", "File upload is in progress. Leaving the page now will cancel the upload." : "檔案上傳中,離開此頁面將會取消上傳", "Actions" : "動作", "Download" : "下載", @@ -108,6 +109,8 @@ "Changed by %2$s" : "由 %2$s 改動", "Deleted by %2$s" : "由 %2$s 刪除", "Restored by %2$s" : "由 %2$s 還原", + "Renamed by %2$s" : "由 %2$s 重新命名", + "Moved by %2$s" : "由 %2$s 移動", "Upload (max. %s)" : "上傳(至多 %s)", "File handling" : "檔案處理", "Maximum upload size" : "上傳限制", diff --git a/apps/updatenotification/l10n/fr.js b/apps/updatenotification/l10n/fr.js index 7f7cf75dda..7d646f36ff 100644 --- a/apps/updatenotification/l10n/fr.js +++ b/apps/updatenotification/l10n/fr.js @@ -5,6 +5,7 @@ OC.L10N.register( "Could not start updater, please try the manual update" : "Impossible de démarrer le système de mise à jour, s'il vous plaît essayer la mise à jour manuelle", "{version} is available. Get more information on how to update." : "La version {version} est disponible. Cliquez ici pour plus d'informations sur comment mettre à jour.", "Channel updated" : "Canal de mise à jour modifié", + "Update to %1$s is available." : "Une mise à jour vers %1$s est disponible", "Update for %1$s to version %2$s is available." : "Une mise à jour de %1$s vers la version %2$s est disponible.", "A new version is available: %s" : "Une nouvelle version est disponible : %s", "Open updater" : "Ouvrir le système de mise à jour", diff --git a/apps/updatenotification/l10n/fr.json b/apps/updatenotification/l10n/fr.json index a4e7ba64bf..057cb2c7b3 100644 --- a/apps/updatenotification/l10n/fr.json +++ b/apps/updatenotification/l10n/fr.json @@ -3,6 +3,7 @@ "Could not start updater, please try the manual update" : "Impossible de démarrer le système de mise à jour, s'il vous plaît essayer la mise à jour manuelle", "{version} is available. Get more information on how to update." : "La version {version} est disponible. Cliquez ici pour plus d'informations sur comment mettre à jour.", "Channel updated" : "Canal de mise à jour modifié", + "Update to %1$s is available." : "Une mise à jour vers %1$s est disponible", "Update for %1$s to version %2$s is available." : "Une mise à jour de %1$s vers la version %2$s est disponible.", "A new version is available: %s" : "Une nouvelle version est disponible : %s", "Open updater" : "Ouvrir le système de mise à jour", diff --git a/apps/updatenotification/l10n/zh_TW.js b/apps/updatenotification/l10n/zh_TW.js index db8798e4a9..64a0e002d5 100644 --- a/apps/updatenotification/l10n/zh_TW.js +++ b/apps/updatenotification/l10n/zh_TW.js @@ -1,9 +1,22 @@ OC.L10N.register( "updatenotification", { + "Update notifications" : "更新通知", + "Could not start updater, please try the manual update" : "無法啟動更新程式,請嘗試手動更新", "{version} is available. Get more information on how to update." : "{version} 釋出了,可以更新", - "Updater" : "更新者", + "Channel updated" : "頻道已更新", + "Update to %1$s is available." : "更新版 %1$s 已經釋出", + "Update for %1$s to version %2$s is available." : "%1$s 到 %2$s 的更新已經釋出", + "A new version is available: %s" : "新版本可用:%s", + "Open updater" : "打開更新程式", + "Download now" : "現在下載", + "Your version is up to date." : "您的版本是最新版", + "Checked on %s" : "於 %s 檢查過", "Update channel:" : "更新通道:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以隨時更新至較新的版本 / 實驗通道,但您不能降版至更穩定的通道。" + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以隨時更新至較新的版本 / 實驗通道,但您不能降版至更穩定的通道。", + "Notify members of the following groups about available updates:" : "有可用更新時通知這些群組:", + "Only notification for app updates are available." : "僅提供應用程式更新的通知", + "The selected update channel does not support updates of the server." : "所選的更新頻道不提供伺服器軟體的更新", + "Updater" : "更新者" }, "nplurals=1; plural=0;"); diff --git a/apps/updatenotification/l10n/zh_TW.json b/apps/updatenotification/l10n/zh_TW.json index 2b0baa5eac..7e8edb9820 100644 --- a/apps/updatenotification/l10n/zh_TW.json +++ b/apps/updatenotification/l10n/zh_TW.json @@ -1,7 +1,20 @@ { "translations": { + "Update notifications" : "更新通知", + "Could not start updater, please try the manual update" : "無法啟動更新程式,請嘗試手動更新", "{version} is available. Get more information on how to update." : "{version} 釋出了,可以更新", - "Updater" : "更新者", + "Channel updated" : "頻道已更新", + "Update to %1$s is available." : "更新版 %1$s 已經釋出", + "Update for %1$s to version %2$s is available." : "%1$s 到 %2$s 的更新已經釋出", + "A new version is available: %s" : "新版本可用:%s", + "Open updater" : "打開更新程式", + "Download now" : "現在下載", + "Your version is up to date." : "您的版本是最新版", + "Checked on %s" : "於 %s 檢查過", "Update channel:" : "更新通道:", - "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以隨時更新至較新的版本 / 實驗通道,但您不能降版至更穩定的通道。" + "You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel." : "您可以隨時更新至較新的版本 / 實驗通道,但您不能降版至更穩定的通道。", + "Notify members of the following groups about available updates:" : "有可用更新時通知這些群組:", + "Only notification for app updates are available." : "僅提供應用程式更新的通知", + "The selected update channel does not support updates of the server." : "所選的更新頻道不提供伺服器軟體的更新", + "Updater" : "更新者" },"pluralForm" :"nplurals=1; plural=0;" } \ No newline at end of file diff --git a/core/l10n/ru.js b/core/l10n/ru.js index 3d3206a11b..81eec815e1 100644 --- a/core/l10n/ru.js +++ b/core/l10n/ru.js @@ -90,6 +90,7 @@ OC.L10N.register( "Dec." : "Дек.", "There were problems with the code integrity check. More information…" : " Были обнаружены проблемы с проверкой целостности кода. Подробнее ...", "Settings" : "Настройки", + "Connection to server lost" : "Подключение к серверу потеряно", "Problem loading page, reloading in 5 seconds" : "Возникла проблема при загрузке страницы, повторная попытка через 5 секунд", "Saving..." : "Сохранение...", "Dismiss" : "Закрыть", @@ -164,10 +165,12 @@ OC.L10N.register( "Send link via email" : "Отправить ссылку по электронной почте", "Shared with you and the group {group} by {owner}" : "{owner} поделился с вами и группой {group} ", "Shared with you by {owner}" : "С вами поделился {owner} ", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} поделился через ссылку", "group" : "группа", "remote" : "удаленный", "notify by email" : "уведомить по почте", "Unshare" : "Закрыть доступ", + "can reshare" : "можно опубликовать", "can edit" : "может редактировать", "create" : "создать", "change" : "изменить", @@ -182,6 +185,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} (на {server})", "{sharee} (remote)" : "{sharee} (удалённо)", "Share" : "Поделиться", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Поделиться с людьми на других серверах Nextcloud используя формат username@example.com/nextcloud", "Share with users…" : "Поделиться с пользователями...", "Share with users, groups or remote users…" : "Поделиться с пользователями, группами или удаленными пользователями...", "Share with users or groups…" : "Поделиться с пользователями или группами...", diff --git a/core/l10n/ru.json b/core/l10n/ru.json index 6e7b5b97ab..6d78be837c 100644 --- a/core/l10n/ru.json +++ b/core/l10n/ru.json @@ -88,6 +88,7 @@ "Dec." : "Дек.", "There were problems with the code integrity check. More information…" : " Были обнаружены проблемы с проверкой целостности кода. Подробнее ...", "Settings" : "Настройки", + "Connection to server lost" : "Подключение к серверу потеряно", "Problem loading page, reloading in 5 seconds" : "Возникла проблема при загрузке страницы, повторная попытка через 5 секунд", "Saving..." : "Сохранение...", "Dismiss" : "Закрыть", @@ -162,10 +163,12 @@ "Send link via email" : "Отправить ссылку по электронной почте", "Shared with you and the group {group} by {owner}" : "{owner} поделился с вами и группой {group} ", "Shared with you by {owner}" : "С вами поделился {owner} ", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} поделился через ссылку", "group" : "группа", "remote" : "удаленный", "notify by email" : "уведомить по почте", "Unshare" : "Закрыть доступ", + "can reshare" : "можно опубликовать", "can edit" : "может редактировать", "create" : "создать", "change" : "изменить", @@ -180,6 +183,7 @@ "{sharee} (at {server})" : "{sharee} (на {server})", "{sharee} (remote)" : "{sharee} (удалённо)", "Share" : "Поделиться", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "Поделиться с людьми на других серверах Nextcloud используя формат username@example.com/nextcloud", "Share with users…" : "Поделиться с пользователями...", "Share with users, groups or remote users…" : "Поделиться с пользователями, группами или удаленными пользователями...", "Share with users or groups…" : "Поделиться с пользователями или группами...", diff --git a/core/l10n/zh_TW.js b/core/l10n/zh_TW.js index 52ab53eafd..24adfb2a05 100644 --- a/core/l10n/zh_TW.js +++ b/core/l10n/zh_TW.js @@ -90,6 +90,7 @@ OC.L10N.register( "Dec." : "十二月", "There were problems with the code integrity check. More information…" : "執行程式碼完整性檢查時發生問題。更多資訊…", "Settings" : "設定", + "Connection to server lost" : "伺服器連線中斷", "Problem loading page, reloading in 5 seconds" : "載入頁面出錯,5 秒後重新整理", "Saving..." : "儲存中...", "Dismiss" : "知道了", @@ -122,6 +123,7 @@ OC.L10N.register( "Good password" : "密碼強度佳", "Strong password" : "密碼強度極佳", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "您的網頁伺服器無法提供檔案同步功能,因為 WebDAV 界面有問題", + "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our documentation." : "您的網頁伺服器並未正確設定來解析 \"{url}\" ,請查看我們的說明文件以瞭解更多", "Error occurred while checking server setup" : "檢查伺服器設定時發生錯誤", "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "您的資料目錄和檔案看來可以被公開存取,這表示 .htaccess 檔案並未生效,我們強烈建議您設定您的網頁伺服器,拒絕資料目錄的公開存取,或者將您的資料目錄移出網頁伺服器根目錄。", "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "\"{header}\" HTTP 標頭配置與 \"{expected}\"不一樣,這是一個潛在安全性或者隱私上的風險,因此我們建議您調整此設定", @@ -155,10 +157,12 @@ OC.L10N.register( "Send link via email" : "透過 email 寄送連結", "Shared with you and the group {group} by {owner}" : "由 {owner} 分享給您和 {group}", "Shared with you by {owner}" : "{owner} 已經和您分享", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} 分享了連結", "group" : "群組", "remote" : "遠端", "notify by email" : "以 email 通知", "Unshare" : "取消分享", + "can reshare" : "允許轉分享", "can edit" : "可編輯", "create" : "建立", "change" : "更動", @@ -173,6 +177,7 @@ OC.L10N.register( "{sharee} (at {server})" : "{sharee} (在 {server})", "{sharee} (remote)" : "{sharee} (遠端)", "Share" : "分享", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "要與在其他 Nextcloud 上的人分享,請使用此格式 username@example.com/nextcloud", "Share with users…" : "分享給其他使用者…", "Share with users, groups or remote users…" : "分享給其他使用者、群組或遠端使用者…", "Share with users or groups…" : "分享給群組或使用者…", @@ -281,6 +286,9 @@ OC.L10N.register( "Contact your system administrator if this message persists or appeared unexpectedly." : "若這個訊息持續出現,請聯絡系統管理員", "Thank you for your patience." : "感謝您的耐心", "Two-factor authentication" : "二階段認證", + "Cancel log in" : "取消登入", + "Use backup code" : "使用備用認證碼", + "Error while validating your second factor" : "驗證二階段因子發生錯誤", "You are accessing the server from an untrusted domain." : "你正在從一個未信任的網域存取伺服器", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "請聯絡您的系統管理員,如果您就是系統管理員,請設定 config/config.php 中的 \"trusted_domain\" 選項。範例設定提供於 config/config.sample.php。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "依照設定而定,您身為系統管理員可能也可以使用底下的按鈕來信任這個網域", @@ -293,6 +301,9 @@ OC.L10N.register( "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "在繼續之前,請備份資料庫、config 目錄及資料目錄", "Start update" : "開始升級", "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "在大型安裝上,為了避免升級請求逾時,你也可以在安裝目錄執行下列指令:", + "Detailed logs" : "詳細記錄檔", + "Update needed" : "需要更新", + "Please use the command line updater because you have a big instance." : "請使用命令列更新工具,因為您的服務規模較大", "This %s instance is currently in maintenance mode, which may take a while." : "這個 %s 安裝目前處於維護模式,需要一段時間恢復。", "This page will refresh itself when the %s instance is available again." : "%s 安裝恢復可用之後,本頁會自動重新整理", "Error loading tags" : "載入標籤出錯", @@ -306,6 +317,7 @@ OC.L10N.register( "Allow editing" : "允許編輯", "can share" : "可分享", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "與其他在ownCloud上的人們分享,請使用此格式 username@example.com/owncloud", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "正在更新,在某些狀況下,離開本頁面可能會導致更新中斷", "Updating to {version}" : "正在更新到 {version}", "The update was successful. There were warnings." : "更新成功,有警告訊息", "Two-step verification" : "二階段驗證", diff --git a/core/l10n/zh_TW.json b/core/l10n/zh_TW.json index 43b4436fef..8c36e769b6 100644 --- a/core/l10n/zh_TW.json +++ b/core/l10n/zh_TW.json @@ -88,6 +88,7 @@ "Dec." : "十二月", "There were problems with the code integrity check. More information…" : "執行程式碼完整性檢查時發生問題。更多資訊…", "Settings" : "設定", + "Connection to server lost" : "伺服器連線中斷", "Problem loading page, reloading in 5 seconds" : "載入頁面出錯,5 秒後重新整理", "Saving..." : "儲存中...", "Dismiss" : "知道了", @@ -120,6 +121,7 @@ "Good password" : "密碼強度佳", "Strong password" : "密碼強度極佳", "Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "您的網頁伺服器無法提供檔案同步功能,因為 WebDAV 界面有問題", + "Your web server is not set up properly to resolve \"{url}\". Further information can be found in our documentation." : "您的網頁伺服器並未正確設定來解析 \"{url}\" ,請查看我們的說明文件以瞭解更多", "Error occurred while checking server setup" : "檢查伺服器設定時發生錯誤", "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "您的資料目錄和檔案看來可以被公開存取,這表示 .htaccess 檔案並未生效,我們強烈建議您設定您的網頁伺服器,拒絕資料目錄的公開存取,或者將您的資料目錄移出網頁伺服器根目錄。", "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "\"{header}\" HTTP 標頭配置與 \"{expected}\"不一樣,這是一個潛在安全性或者隱私上的風險,因此我們建議您調整此設定", @@ -153,10 +155,12 @@ "Send link via email" : "透過 email 寄送連結", "Shared with you and the group {group} by {owner}" : "由 {owner} 分享給您和 {group}", "Shared with you by {owner}" : "{owner} 已經和您分享", + "{{shareInitiatorDisplayName}} shared via link" : "{{shareInitiatorDisplayName}} 分享了連結", "group" : "群組", "remote" : "遠端", "notify by email" : "以 email 通知", "Unshare" : "取消分享", + "can reshare" : "允許轉分享", "can edit" : "可編輯", "create" : "建立", "change" : "更動", @@ -171,6 +175,7 @@ "{sharee} (at {server})" : "{sharee} (在 {server})", "{sharee} (remote)" : "{sharee} (遠端)", "Share" : "分享", + "Share with people on other Nextclouds using the syntax username@example.com/nextcloud" : "要與在其他 Nextcloud 上的人分享,請使用此格式 username@example.com/nextcloud", "Share with users…" : "分享給其他使用者…", "Share with users, groups or remote users…" : "分享給其他使用者、群組或遠端使用者…", "Share with users or groups…" : "分享給群組或使用者…", @@ -279,6 +284,9 @@ "Contact your system administrator if this message persists or appeared unexpectedly." : "若這個訊息持續出現,請聯絡系統管理員", "Thank you for your patience." : "感謝您的耐心", "Two-factor authentication" : "二階段認證", + "Cancel log in" : "取消登入", + "Use backup code" : "使用備用認證碼", + "Error while validating your second factor" : "驗證二階段因子發生錯誤", "You are accessing the server from an untrusted domain." : "你正在從一個未信任的網域存取伺服器", "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "請聯絡您的系統管理員,如果您就是系統管理員,請設定 config/config.php 中的 \"trusted_domain\" 選項。範例設定提供於 config/config.sample.php。", "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "依照設定而定,您身為系統管理員可能也可以使用底下的按鈕來信任這個網域", @@ -291,6 +299,9 @@ "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "在繼續之前,請備份資料庫、config 目錄及資料目錄", "Start update" : "開始升級", "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "在大型安裝上,為了避免升級請求逾時,你也可以在安裝目錄執行下列指令:", + "Detailed logs" : "詳細記錄檔", + "Update needed" : "需要更新", + "Please use the command line updater because you have a big instance." : "請使用命令列更新工具,因為您的服務規模較大", "This %s instance is currently in maintenance mode, which may take a while." : "這個 %s 安裝目前處於維護模式,需要一段時間恢復。", "This page will refresh itself when the %s instance is available again." : "%s 安裝恢復可用之後,本頁會自動重新整理", "Error loading tags" : "載入標籤出錯", @@ -304,6 +315,7 @@ "Allow editing" : "允許編輯", "can share" : "可分享", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "與其他在ownCloud上的人們分享,請使用此格式 username@example.com/owncloud", + "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "正在更新,在某些狀況下,離開本頁面可能會導致更新中斷", "Updating to {version}" : "正在更新到 {version}", "The update was successful. There were warnings." : "更新成功,有警告訊息", "Two-step verification" : "二階段驗證", From 3b055b160ea8b78bf077028e15dbf4ce86071ccd Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 14 Oct 2016 11:48:18 +0200 Subject: [PATCH 22/22] fix typo Signed-off-by: Thomas Citharel --- apps/dav/lib/Connector/Sabre/TagsPlugin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dav/lib/Connector/Sabre/TagsPlugin.php b/apps/dav/lib/Connector/Sabre/TagsPlugin.php index b1a6d1fb96..ef6bece58b 100644 --- a/apps/dav/lib/Connector/Sabre/TagsPlugin.php +++ b/apps/dav/lib/Connector/Sabre/TagsPlugin.php @@ -109,7 +109,7 @@ class TagsPlugin extends \Sabre\DAV\ServerPlugin */ public function initialize(\Sabre\DAV\Server $server) { - $server->xml->namespacesMap[self::NS_OWNCLOUD] = 'oc'; + $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; $server->xml->elementMap[self::TAGS_PROPERTYNAME] = 'OCA\\DAV\\Connector\\Sabre\\TagList'; $this->server = $server;