diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php index b491b1d4d3f0..bbc292dc4a1f 100644 --- a/apps/comments/appinfo/app.php +++ b/apps/comments/appinfo/app.php @@ -54,7 +54,7 @@ function () { $eventDispatcher->addListener(\OCP\Comments\CommentsEntityEvent::EVENT_ENTITY, function (\OCP\Comments\CommentsEntityEvent $event) { $event->addEntityCollection('files', function ($name) { - $nodes = \OC::$server->getUserFolder()->getById(\intval($name)); + $nodes = \OC::$server->getUserFolder()->getById((int)$name, true); return !empty($nodes); }); }); diff --git a/apps/comments/lib/Activity/Listener.php b/apps/comments/lib/Activity/Listener.php index a4ca3b4aa463..1781286713d9 100644 --- a/apps/comments/lib/Activity/Listener.php +++ b/apps/comments/lib/Activity/Listener.php @@ -86,10 +86,9 @@ public function commentEvent(CommentsEvent $event) { foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); - $nodes = $ownerFolder->getById($event->getComment()->getObjectId()); - if (!empty($nodes)) { - /** @var Node $node */ - $node = \array_shift($nodes); + $nodes = $ownerFolder->getById($event->getComment()->getObjectId(), true); + $node = $nodes[0] ?? null; + if ($node) { $path = $node->getPath(); if (\strpos($path, '/' . $owner . '/files/') === 0) { $path = \substr($path, \strlen('/' . $owner . '/files')); diff --git a/apps/comments/tests/unit/ActivityListenerTest.php b/apps/comments/tests/unit/ActivityListenerTest.php new file mode 100644 index 000000000000..6f81cc1da7d6 --- /dev/null +++ b/apps/comments/tests/unit/ActivityListenerTest.php @@ -0,0 +1,151 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\Comments\Tests\unit; + +use OCA\Comments\Activity\Listener; +use OCP\Comments\CommentsEntityEvent; +use Test\Traits\UserTrait; +use OCP\IUserSession; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\IRootFolder; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Config\ICachedMountInfo; +use OCP\IUser; +use OCP\Files\Folder; +use OCP\Activity\IEvent; +use OCA\Comments\Activity\Extension; +use OCP\Comments\IComment; +use OCP\Comments\CommentsEvent; + +/** + * Tests for the activity listener + * + * @group DB + */ +class ActivityListenerTest extends \Test\TestCase { + use UserTrait; + + /** + * @var Listener + */ + private $listener; + + /** + * @var IUserMountCache + */ + private $userMountCache; + + /** + * @var IRootFolder + */ + private $rootFolder; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IManager + */ + private $activityManager; + + protected function setUp() { + parent::setUp(); + + $this->activityManager = $this->createMock(IManager::class); + $this->userSession = $this->createMock(IUserSession::class); + $appManager = $this->createMock(IAppManager::class); + $appManager->method('isInstalled')->with('activity')->willReturn(true); + + $this->userMountCache = $this->createMock(IUserMountCache::class); + + $mountProviderCollection = $this->createMock(IMountProviderCollection::class); + $mountProviderCollection->method('getMountCache')->willReturn($this->userMountCache); + + $this->rootFolder = $this->createMock(IRootFolder::class); + + // needed for the one unmockable static method "Share::getUsersSharingFile"... + $this->createUser('user1'); + $this->createUser('actor1'); + + $this->listener = new Listener( + $this->activityManager, + $this->userSession, + $appManager, + $mountProviderCollection, + $this->rootFolder + ); + } + + public function testActivityOnFilesComment() { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + + $actor = $this->createMock(IUser::class); + $actor->method('getUID')->willReturn('actor1'); + + $this->userSession->method('getUser')->willReturn($actor); + + $cachedMountInfo = $this->createMock(ICachedMountInfo::class); + $cachedMountInfo->method('getUser')->willReturn($user); + + $node = $this->createMock(Folder::class); + $node->method('getPath')->willReturn('/user1/files/folder'); + + $ownerFolder = $this->createMock(Folder::class); + $ownerFolder->method('getById') + ->with(123, true) + ->willReturn([$node]); + + $this->rootFolder->method('getUserFolder') + ->with('user1') + ->willReturn($ownerFolder); + + $this->userMountCache->method('getMountsForFileId') + ->with(123) + ->willReturn([$cachedMountInfo]); + + $activityEvent = $this->createMock(IEvent::class); + $activityEvent->expects($this->once())->method('setApp')->with('comments')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setType')->with('comments')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setAuthor')->with('actor1')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setObject')->with('files', 123)->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setMessage')->with(Extension::ADD_COMMENT_MESSAGE, [111])->willReturn($activityEvent); + + $this->activityManager->method('generateEvent') + ->willReturn($activityEvent); + $this->activityManager->expects($this->once()) + ->method('publish') + ->with($activityEvent); + + $comment = $this->createMock(IComment::class); + $comment->method('getObjectType')->willReturn('files'); + $comment->method('getObjectId')->willReturn(123); + $comment->method('getId')->willReturn(111); + + $commentsEvent = new CommentsEvent(CommentsEvent::EVENT_ADD, $comment); + $this->listener->commentEvent($commentsEvent); + } +} diff --git a/apps/comments/tests/unit/AppTest.php b/apps/comments/tests/unit/AppTest.php new file mode 100644 index 000000000000..5cb9f81b04f3 --- /dev/null +++ b/apps/comments/tests/unit/AppTest.php @@ -0,0 +1,53 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\DAV\Tests\unit; + +use OCP\Comments\CommentsEntityEvent; +use Test\Traits\UserTrait; + +/** + * Tests for the logic in app.php + * @group DB + */ +class AppTest extends \Test\TestCase { + use UserTrait; + + protected function setUp() { + parent::setUp(); + + $this->user1 = $this->createUser($this->getUniqueID('dav_comments_user_')); + $this->loginAsUser($this->user1->getUID()); + } + + public function testDefaultCommentsEntityEventHandler() { + $event = new CommentsEntityEvent(CommentsEntityEvent::EVENT_ENTITY); + \OC::$server->getEventDispatcher()->dispatch(CommentsEntityEvent::EVENT_ENTITY, $event); + + $entityCollections = $event->getEntityCollections(); + $this->assertTrue(isset($entityCollections['files'])); + + $folder = \OC::$server->getUserFolder()->newFolder('test'); + + $this->assertTrue($entityCollections['files']($folder->getId())); + $this->assertFalse($entityCollections['files']($folder->getId() + 1)); + } +} diff --git a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php index 3ea18fabfb0d..e6512b376aaf 100644 --- a/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesReportPlugin.php @@ -360,9 +360,9 @@ public function findNodesByFileIds($rootNode, $fileIds) { $results = []; foreach ($fileIds as $fileId) { - $entry = $folder->getById($fileId); + $entries = $folder->getById($fileId, true); + $entry = $entries[0] ?? null; if ($entry) { - $entry = \current($entry); $node = $this->makeSabreNode($entry); if ($node) { $results[] = $node; diff --git a/apps/dav/lib/Connector/Sabre/Principal.php b/apps/dav/lib/Connector/Sabre/Principal.php index 7d50cb4c4245..6bf60f4f7f02 100644 --- a/apps/dav/lib/Connector/Sabre/Principal.php +++ b/apps/dav/lib/Connector/Sabre/Principal.php @@ -3,6 +3,7 @@ * @author Bart Visscher * @author Jakob Sack * @author Jörn Friedrich Dreyer + * @author Julian Müller * @author Lukas Reschke * @author Morris Jobke * @author Thomas Müller @@ -145,7 +146,7 @@ public function getGroupMembership($principal, $needGroups = false) { } if ($this->hasGroups || $needGroups) { - $groups = $this->groupManager->getUserGroups($user); + $groups = $this->groupManager->getUserGroups($user, 'sharing'); $groups = \array_map(function ($group) { /** @var IGroup $group */ return 'principals/groups/' . $group->getGID(); diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index faa8e3fba63d..43eb9b178c03 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -74,7 +74,7 @@ public function __construct() { \OC::$server->getSystemTagObjectMapper(), \OC::$server->getUserSession(), \OC::$server->getGroupManager(), - \OC::$server->getRootFolder() + \OC::$server->getLazyRootFolder() ); $usersCardDavBackend = new CardDavBackend($db, $userPrincipalBackend, $groupPrincipalBackend, $dispatcher); @@ -109,7 +109,7 @@ public function __construct() { $systemTagRelationsCollection, $uploadCollection, $avatarCollection, - new \OCA\DAV\Meta\RootCollection(\OC::$server->getRootFolder()), + new \OCA\DAV\Meta\RootCollection(\OC::$server->getLazyRootFolder()), $queueCollection ]; diff --git a/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php b/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php index fa44c0b2f47b..4ffd4aa20d14 100644 --- a/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php +++ b/apps/dav/lib/SystemTag/SystemTagsObjectTypeCollection.php @@ -139,7 +139,7 @@ public function childExists($name) { if ($this->objectType === 'files') { // make sure the object is reachable for the current user $userId = $this->userSession->getUser()->getUID(); - $nodes = $this->fileRoot->getUserFolder($userId)->getById(\intval($name)); + $nodes = $this->fileRoot->getUserFolder($userId)->getById((int)$name, true); return !empty($nodes); } return true; diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index c6bea791e61a..01606ce5097a 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -886,7 +886,7 @@ private function getNode($userId, $id) { throw new InvalidShare(); } - $nodes = $userFolder->getById($id); + $nodes = $userFolder->getById($id, true); if (empty($nodes)) { throw new InvalidShare(); diff --git a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php index 8b0189c4be0c..ffb0fbd6574c 100644 --- a/apps/federatedfilesharing/tests/FederatedShareProviderTest.php +++ b/apps/federatedfilesharing/tests/FederatedShareProviderTest.php @@ -37,6 +37,8 @@ use OCP\IUserManager; use OCP\Share\IManager; use OCP\Share\IShare; +use OCP\Files\Folder; +use OCP\IUser; /** * Class FederatedShareProviderTest @@ -188,6 +190,90 @@ public function testCreate() { $this->assertEquals('token', $share->getToken()); } + public function testCreateLegacy() { + $share = $this->shareManager->newShare(); + + $node = $this->createMock('\OCP\Files\File'); + $node->method('getId')->willReturn(42); + $node->method('getName')->willReturn('myFile'); + + $share->setSharedWith('user@server.com') + ->setShareOwner('shareOwner') + ->setPermissions(19) + ->setNode($node); + + $this->tokenHandler->method('generateToken')->willReturn('token'); + + $shareWithAddress = new Address('user@server.com'); + $ownerAddress = new Address('shareOwner@http://localhost/'); + $sharedByAddress = new Address('sharedBy@http://localhost/'); + $this->addressHandler->expects($this->any())->method('getLocalUserFederatedAddress') + ->will($this->onConsecutiveCalls($ownerAddress, $sharedByAddress, $ownerAddress)); + + $this->addressHandler->expects($this->any())->method('splitUserRemote') + ->willReturn(['user', 'server.com']); + + $this->notifications->expects($this->once()) + ->method('sendRemoteShare') + ->with( + $this->equalTo($shareWithAddress), + $this->equalTo($ownerAddress), + $this->equalTo($sharedByAddress), + $this->equalTo('token'), + $this->equalTo('myFile'), + $this->anything() + )->willReturn(true); + + $folderOwner = $this->createMock(IUser::class); + $folderOwner->method('getUID')->willReturn('folderOwner'); + $node = $this->createMock(Folder::class); + $node->method('getOwner')->willReturn($folderOwner); + + $userFolder = $this->createMock(Folder::class); + $userFolder->method('getById') + ->with(42, true) + ->willReturn([$node]); + $this->rootFolder->expects($this->once()) + ->method('getUserFolder') + ->with('shareOwner') + ->willReturn($userFolder); + + $share = $this->provider->create($share); + + $qb = $this->connection->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + $expected = [ + 'share_type' => \OCP\Share::SHARE_TYPE_REMOTE, + 'share_with' => 'user@server.com', + 'uid_owner' => 'shareOwner', + 'uid_initiator' => null, + 'item_type' => 'file', + 'item_source' => 42, + 'file_source' => 42, + 'permissions' => 19, + 'accepted' => 0, + 'token' => 'token', + ]; + $this->assertArraySubset($expected, $data); + + $this->assertEquals($data['id'], $share->getId()); + $this->assertEquals(\OCP\Share::SHARE_TYPE_REMOTE, $share->getShareType()); + $this->assertEquals('user@server.com', $share->getSharedWith()); + $this->assertEquals('shareOwner', $share->getSharedBy()); + $this->assertEquals('folderOwner', $share->getShareOwner()); + $this->assertEquals('file', $share->getNodeType()); + $this->assertEquals(42, $share->getNodeId()); + $this->assertEquals(19, $share->getPermissions()); + $this->assertEquals('token', $share->getToken()); + } + public function testCreateCouldNotFindServer() { $share = $this->shareManager->newShare(); diff --git a/apps/files/lib/ActivityHelper.php b/apps/files/lib/ActivityHelper.php index 20ac77cbc75a..c198c10353cb 100644 --- a/apps/files/lib/ActivityHelper.php +++ b/apps/files/lib/ActivityHelper.php @@ -59,10 +59,9 @@ public function getFavoriteFilePaths($user) { $rootFolder = \OC::$server->getUserFolder($user); $folders = $items = []; foreach ($favorites as $favorite) { - $nodes = $rootFolder->getById($favorite); - if (!empty($nodes)) { - /** @var \OCP\Files\Node $node */ - $node = \array_shift($nodes); + $nodes = $rootFolder->getById($favorite, true); + $node = $nodes[0] ?? null; + if ($node) { $path = \substr($node->getPath(), \strlen($user . '/files/')); $items[] = $path; diff --git a/apps/files/tests/ActivityHelperTest.php b/apps/files/tests/ActivityHelperTest.php new file mode 100644 index 000000000000..e2c04ea276e8 --- /dev/null +++ b/apps/files/tests/ActivityHelperTest.php @@ -0,0 +1,137 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +use OCA\Files\ActivityHelper; +use OCP\ITagManager; +use OCP\ITags; +use Test\Traits\UserTrait; + +/** + * Class ActivityHelperTest + * + * @group DB + */ +class ActivityHelperTest extends \Test\TestCase { + use UserTrait; + + /** + * @var ITagManager + */ + private $tagManager; + + /** + * @var ITags + */ + private $tags; + + /** + * @var ActivityHelper + */ + private $helper; + + /** + * @var string + */ + private $user; + + protected function setUp() { + parent::setUp(); + + $this->tags = $this->createMock(ITags::class); + + $this->user = $this->getUniqueID('files_activityhelpertest_user_'); + + // because \OC::$server->getUserFolder() + $this->createUser($this->user); + $this->loginAsUser($this->user); + + $this->tagManager = $this->createMock(ITagManager::class); + $this->tagManager->expects($this->once()) + ->method('load') + ->with('files', [], false, $this->user) + ->willReturn($this->tags); + + $this->helper = new ActivityHelper($this->tagManager); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No favorites + */ + public function testGetFavoriteFilePathsNoFavorites() { + $this->tags->method('getFavorites')->willReturn([]); + $this->helper->getFavoriteFilePaths($this->user); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage Too many favorites + */ + public function testGetFavoriteFilePathsTooManyFavorites() { + $tooManyFavorites = []; + for ($i = 0; $i < ActivityHelper::FAVORITE_LIMIT + 1; $i++) { + $tooManyFavorites[] = []; + } + + $this->tags->method('getFavorites')->willReturn($tooManyFavorites); + + $this->helper->getFavoriteFilePaths($this->user); + } + + public function testGetFavoriteFilePaths() { + $userFolder = \OC::$server->getUserFolder(); + $fav1 = $userFolder->newFolder('fav1'); + $fav2 = $userFolder->newFile('fav2.txt'); + $userFolder->newFolder('nonfav1'); + $userFolder->newFolder('nonfav2'); + + $favorites = [ + $fav1->getId(), + $fav2->getId(), + $fav2->getId() + 999, // non-existing + ]; + + $this->tags->method('getFavorites')->willReturn($favorites); + + $result = $this->helper->getFavoriteFilePaths($this->user); + + $this->assertEquals(['/fav1', '/fav2.txt'], $result['items']); + $this->assertEquals(['/fav1'], $result['folders']); + } + + /** + * @expectedException \RuntimeException + * @expectedExceptionMessage No favorites + */ + public function testGetFavoriteFilePathsMissingFolder() { + $userFolder = \OC::$server->getUserFolder(); + $aFolder = $userFolder->newFolder('x'); + + $favorites = [ + // non-existing + $aFolder->getId() + 999, + ]; + + $this->tags->method('getFavorites')->willReturn($favorites); + + $result = $this->helper->getFavoriteFilePaths($this->user); + } +} diff --git a/apps/files_sharing/lib/Controller/Share20OcsController.php b/apps/files_sharing/lib/Controller/Share20OcsController.php index bde1320991ef..8d1ee1c7645a 100644 --- a/apps/files_sharing/lib/Controller/Share20OcsController.php +++ b/apps/files_sharing/lib/Controller/Share20OcsController.php @@ -166,14 +166,12 @@ protected function formatShare(IShare $share, $received = false) { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); } - $nodes = $userFolder->getById($share->getNodeId()); - - if (empty($nodes)) { + $nodes = $userFolder->getById($share->getNodeId(), true); + $node = $nodes[0] ?? null; + if ($node === null) { throw new NotFoundException(); } - $node = $nodes[0]; - $result['path'] = $userFolder->getRelativePath($node->getPath()); if ($node instanceof \OCP\Files\Folder) { $result['item_type'] = 'folder'; @@ -912,8 +910,8 @@ public function notifyRecipients($itemSource, $shareType, $recipient) { ); $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $nodes = $userFolder->getById($itemSource); - $node = $nodes[0]; + $nodes = $userFolder->getById($itemSource, true); + $node = $nodes[0] ?? null; $result = $mailNotification->sendInternalShareMail($node, $shareType, $recipientList); // if we were able to send to at least one recipient, mark as sent @@ -950,8 +948,8 @@ public function notifyRecipients($itemSource, $shareType, $recipient) { */ public function notifyRecipientsDisabled($itemSource, $shareType, $recipient) { $userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID()); - $nodes = $userFolder->getById($itemSource); - $node = $nodes[0]; + $nodes = $userFolder->getById($itemSource, true); + $node = $nodes[0] ?? null; $items = $this->shareManager->getSharedWith($recipient, $shareType, $node); if (\count($items) > 0) { diff --git a/apps/files_sharing/lib/SharedMount.php b/apps/files_sharing/lib/SharedMount.php index 3923c58da4e1..059687095d97 100644 --- a/apps/files_sharing/lib/SharedMount.php +++ b/apps/files_sharing/lib/SharedMount.php @@ -193,13 +193,13 @@ public function isTargetAllowed($target) { $fileId = (int)$targetStorage->getCache()->getId(\dirname($targetInternalPath)); } - $targetNodes = \OC::$server->getRootFolder()->getById($fileId); - if (empty($targetNodes)) { + $targetNodes = \OC::$server->getRootFolder()->getById($fileId, true); + $targetNode = $targetNodes[0] ?? null; + if ($targetNode === null) { return false; } $shareManager = \OC::$server->getShareManager(); - $targetNode = $targetNodes[0]; // FIXME: make it stop earlier in '/$userId/files' while ($targetNode !== null && $targetNode->getPath() !== '/') { $shares = $shareManager->getSharesByPath($targetNode); diff --git a/apps/systemtags/lib/Activity/Listener.php b/apps/systemtags/lib/Activity/Listener.php index c48b889cbcb7..b7fdf0f58614 100644 --- a/apps/systemtags/lib/Activity/Listener.php +++ b/apps/systemtags/lib/Activity/Listener.php @@ -159,10 +159,9 @@ public function mapperEvent(MapperEvent $event) { foreach ($mounts as $mount) { $owner = $mount->getUser()->getUID(); $ownerFolder = $this->rootFolder->getUserFolder($owner); - $nodes = $ownerFolder->getById($event->getObjectId()); - if (!empty($nodes)) { - /** @var Node $node */ - $node = \array_shift($nodes); + $nodes = $ownerFolder->getById($event->getObjectId(), true); + $node = $nodes[0] ?? null; + if ($node) { $path = $node->getPath(); if (\strpos($path, '/' . $owner . '/files/') === 0) { $path = \substr($path, \strlen('/' . $owner . '/files')); diff --git a/apps/systemtags/tests/unit/ActivityListenerTest.php b/apps/systemtags/tests/unit/ActivityListenerTest.php new file mode 100644 index 000000000000..79c4191c0e72 --- /dev/null +++ b/apps/systemtags/tests/unit/ActivityListenerTest.php @@ -0,0 +1,197 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OCA\SystemTags\Tests\unit; + +use Test\Traits\UserTrait; +use OCA\SystemTags\Activity\Listener; +use OCA\SystemTags\Activity\Extension; +use OCP\IUserSession; +use OCP\Activity\IManager; +use OCP\App\IAppManager; +use OCP\Files\Config\IMountProviderCollection; +use OCP\Files\IRootFolder; +use OCP\Files\Config\IUserMountCache; +use OCP\Files\Config\ICachedMountInfo; +use OCP\IUser; +use OCP\Files\Folder; +use OCP\Activity\IEvent; +use OCP\SystemTag\MapperEvent; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTag; + +/** + * Tests for the activity listener + * + * @group DB + */ +class ActivityListenerTest extends \Test\TestCase { + use UserTrait; + + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var ISystemTagManager + */ + private $tagManager; + + /** + * @var Listener + */ + private $listener; + + /** + * @var IUserMountCache + */ + private $userMountCache; + + /** + * @var IRootFolder + */ + private $rootFolder; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IManager + */ + private $activityManager; + + protected function setUp() { + parent::setUp(); + + $this->groupManager = $this->createMock(IGroupManager::class); + + $this->tagManager = $this->createMock(ISystemTagManager::class); + $this->activityManager = $this->createMock(IManager::class); + $this->userSession = $this->createMock(IUserSession::class); + + $appManager = $this->createMock(IAppManager::class); + $appManager->method('isInstalled')->with('activity')->willReturn(true); + + $this->userMountCache = $this->createMock(IUserMountCache::class); + + $mountProviderCollection = $this->createMock(IMountProviderCollection::class); + $mountProviderCollection->method('getMountCache')->willReturn($this->userMountCache); + + $this->rootFolder = $this->createMock(IRootFolder::class); + + // needed for the one unmockable static method "Share::getUsersSharingFile"... + $this->createUser('user1'); + $this->createUser('actor1'); + + $this->listener = new Listener( + $this->groupManager, + $this->activityManager, + $this->userSession, + $this->tagManager, + $appManager, + $mountProviderCollection, + $this->rootFolder + ); + } + + public function providesEventTypes() { + return [ + [MapperEvent::EVENT_ASSIGN, Extension::ASSIGN_TAG], + [MapperEvent::EVENT_UNASSIGN, Extension::UNASSIGN_TAG], + ]; + } + + /** + * @dataProvider providesEventTypes + */ + public function testActivityOnMapperEvent($eventType, $extensionEvent) { + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + + $actor = $this->createMock(IUser::class); + $actor->method('getUID')->willReturn('actor1'); + + $this->userSession->method('getUser')->willReturn($actor); + + $tag1 = $this->createMock(ISystemTag::class); + $tag1->method('isUserVisible')->willReturn(true); + $tag1->method('isUserAssignable')->willReturn(true); + $tag1->method('getName')->willReturn('tag1'); + + $tag2 = $this->createMock(ISystemTag::class); + $tag2->method('isUserVisible')->willReturn(true); + $tag2->method('isUserAssignable')->willReturn(false); + $tag2->method('getName')->willReturn('tag2'); + + $invisibleTag = $this->createMock(ISystemTag::class); + $invisibleTag->method('isUserVisible')->willReturn(false); + + $this->groupManager->method('isAdmin')->willReturn(false); + + $this->tagManager->method('getTagsByIds') + ->with([111, 222, 333]) + ->willReturn([$tag1, $tag2, $invisibleTag]); + + $cachedMountInfo = $this->createMock(ICachedMountInfo::class); + $cachedMountInfo->method('getUser')->willReturn($user); + + $node = $this->createMock(Folder::class); + $node->method('getPath')->willReturn('/user1/files/folder'); + + $ownerFolder = $this->createMock(Folder::class); + $ownerFolder->method('getById') + ->with(123, true) + ->willReturn([$node]); + + $this->rootFolder->method('getUserFolder') + ->with('user1') + ->willReturn($ownerFolder); + + $this->userMountCache->method('getMountsForFileId') + ->with(123) + ->willReturn([$cachedMountInfo]); + + $activityEvent = $this->createMock(IEvent::class); + $activityEvent->expects($this->once())->method('setApp')->with('systemtags')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setType')->with('systemtags')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setAuthor')->with('actor1')->willReturn($activityEvent); + $activityEvent->expects($this->once())->method('setObject')->with('files', 123)->willReturn($activityEvent); + $activityEvent->expects($this->exactly(2))->method('setSubject') + ->withConsecutive( + [$extensionEvent, ['actor1', '/folder', '{{{tag1|||assignable}}}']], + [$extensionEvent, ['actor1', '/folder', '{{{tag2|||not-assignable}}}']] + ) + ->willReturn($activityEvent); + + $this->activityManager->method('generateEvent') + ->willReturn($activityEvent); + $this->activityManager->expects($this->exactly(2)) + ->method('publish') + ->with($activityEvent); + + $event = new MapperEvent($eventType, 'files', 123, [111, 222, 333]); + $this->listener->mapperEvent($event); + } +} diff --git a/composer.lock b/composer.lock index a47df8c59b53..8178bcf6e5be 100644 --- a/composer.lock +++ b/composer.lock @@ -566,16 +566,16 @@ }, { "name": "egulias/email-validator", - "version": "2.1.6", + "version": "2.1.7", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786" + "reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0578b32b30b22de3e8664f797cf846fc9246f786", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/709f21f92707308cdf8f9bcfa1af4cb26586521e", + "reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e", "shasum": "" }, "require": { @@ -619,7 +619,7 @@ "validation", "validator" ], - "time": "2018-09-25T20:47:26+00:00" + "time": "2018-12-04T22:38:24+00:00" }, { "name": "guzzle/common", @@ -1520,20 +1520,20 @@ }, { "name": "pear/pear-core-minimal", - "version": "v1.10.6", + "version": "v1.10.7", "source": { "type": "git", "url": "https://github.com/pear/pear-core-minimal.git", - "reference": "052868b244d31f822796e7e9981f62557eb256d4" + "reference": "19a3e0fcd50492c4357372f623f55f1b144346da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/052868b244d31f822796e7e9981f62557eb256d4", - "reference": "052868b244d31f822796e7e9981f62557eb256d4", + "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/19a3e0fcd50492c4357372f623f55f1b144346da", + "reference": "19a3e0fcd50492c4357372f623f55f1b144346da", "shasum": "" }, "require": { - "pear/console_getopt": "~1.3", + "pear/console_getopt": "~1.4", "pear/pear_exception": "~1.0" }, "replace": { @@ -1560,7 +1560,7 @@ } ], "description": "Minimal set of PEAR core files to be used as composer dependency", - "time": "2018-08-22T19:28:09+00:00" + "time": "2018-12-05T20:03:52+00:00" }, { "name": "pear/pear_exception", @@ -1857,16 +1857,16 @@ }, { "name": "punic/punic", - "version": "3.3.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/punic/punic.git", - "reference": "d31ec0bec7bad65105876812cc5b0106ae095e34" + "reference": "db3b99397e7ede380eb8b27b08d7f7a95d85d7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/punic/punic/zipball/d31ec0bec7bad65105876812cc5b0106ae095e34", - "reference": "d31ec0bec7bad65105876812cc5b0106ae095e34", + "url": "https://api.github.com/repos/punic/punic/zipball/db3b99397e7ede380eb8b27b08d7f7a95d85d7db", + "reference": "db3b99397e7ede380eb8b27b08d7f7a95d85d7db", "shasum": "" }, "require": { @@ -1927,7 +1927,7 @@ "translations", "unicode" ], - "time": "2018-11-23T10:02:50+00:00" + "time": "2018-12-07T16:55:52+00:00" }, { "name": "rackspace/php-opencloud", @@ -2497,7 +2497,7 @@ }, { "name": "symfony/console", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/console.git", @@ -2566,16 +2566,16 @@ }, { "name": "symfony/debug", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "2016b3eec2e49c127dd02d0ef44a35c53181560d" + "reference": "a2233f555ddf55e5600f386fba7781cea1cb82d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/2016b3eec2e49c127dd02d0ef44a35c53181560d", - "reference": "2016b3eec2e49c127dd02d0ef44a35c53181560d", + "url": "https://api.github.com/repos/symfony/debug/zipball/a2233f555ddf55e5600f386fba7781cea1cb82d3", + "reference": "a2233f555ddf55e5600f386fba7781cea1cb82d3", "shasum": "" }, "require": { @@ -2618,20 +2618,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-11-11T19:48:54+00:00" + "time": "2018-11-27T12:43:10+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "d365fc4416ec4980825873962ea5d1b1bca46f1a" + "reference": "cc35e84adbb15c26ae6868e1efbf705a917be6b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d365fc4416ec4980825873962ea5d1b1bca46f1a", - "reference": "d365fc4416ec4980825873962ea5d1b1bca46f1a", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/cc35e84adbb15c26ae6868e1efbf705a917be6b5", + "reference": "cc35e84adbb15c26ae6868e1efbf705a917be6b5", "shasum": "" }, "require": { @@ -2681,7 +2681,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-11-26T10:17:44+00:00" + "time": "2018-11-30T18:07:24+00:00" }, { "name": "symfony/polyfill-mbstring", @@ -2852,7 +2852,7 @@ }, { "name": "symfony/process", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/process.git", @@ -2901,16 +2901,16 @@ }, { "name": "symfony/routing", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "86eb1a581279b5e40ca280a4f63a15e37d51d16c" + "reference": "94a3dd89bda078bef0c3bf79eb024fe136dd58f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/86eb1a581279b5e40ca280a4f63a15e37d51d16c", - "reference": "86eb1a581279b5e40ca280a4f63a15e37d51d16c", + "url": "https://api.github.com/repos/symfony/routing/zipball/94a3dd89bda078bef0c3bf79eb024fe136dd58f9", + "reference": "94a3dd89bda078bef0c3bf79eb024fe136dd58f9", "shasum": "" }, "require": { @@ -2974,11 +2974,11 @@ "uri", "url" ], - "time": "2018-11-26T08:40:22+00:00" + "time": "2018-12-03T13:20:34+00:00" }, { "name": "symfony/translation", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", @@ -3819,16 +3819,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + "reference": "dc523135366eb68f22268d069ea7749486458562" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/dc523135366eb68f22268d069ea7749486458562", + "reference": "dc523135366eb68f22268d069ea7749486458562", "shasum": "" }, "require": { @@ -3859,7 +3859,7 @@ "Xdebug", "performance" ], - "time": "2018-08-31T19:07:57+00:00" + "time": "2018-11-29T10:59:02+00:00" }, { "name": "doctrine/annotations", @@ -6192,7 +6192,7 @@ }, { "name": "symfony/browser-kit", - "version": "v2.8.48", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", @@ -6249,7 +6249,7 @@ }, { "name": "symfony/class-loader", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", @@ -6305,7 +6305,7 @@ }, { "name": "symfony/config", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/config.git", @@ -6369,7 +6369,7 @@ }, { "name": "symfony/css-selector", - "version": "v2.8.48", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", @@ -6422,16 +6422,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "622b330ced1bdf29d240bd1c364c038f647eb0f5" + "reference": "5be2d762b51076295a972c86604a977fbcc5c12b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/622b330ced1bdf29d240bd1c364c038f647eb0f5", - "reference": "622b330ced1bdf29d240bd1c364c038f647eb0f5", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/5be2d762b51076295a972c86604a977fbcc5c12b", + "reference": "5be2d762b51076295a972c86604a977fbcc5c12b", "shasum": "" }, "require": { @@ -6489,11 +6489,11 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-11-20T16:14:23+00:00" + "time": "2018-12-02T15:50:25+00:00" }, { "name": "symfony/dom-crawler", - "version": "v2.8.48", + "version": "v2.8.49", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", @@ -6550,7 +6550,7 @@ }, { "name": "symfony/filesystem", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", @@ -6600,7 +6600,7 @@ }, { "name": "symfony/finder", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", @@ -6649,7 +6649,7 @@ }, { "name": "symfony/options-resolver", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", @@ -6875,7 +6875,7 @@ }, { "name": "symfony/stopwatch", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", @@ -6924,7 +6924,7 @@ }, { "name": "symfony/yaml", - "version": "v3.4.19", + "version": "v3.4.20", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", diff --git a/core/l10n/pl.js b/core/l10n/pl.js index be650908103b..c0a48cbfd5c2 100644 --- a/core/l10n/pl.js +++ b/core/l10n/pl.js @@ -207,11 +207,13 @@ OC.L10N.register( "Share details could not be loaded for this item." : "Szczegóły udziału nie mogły zostać wczytane dla tego obiektu.", "No users or groups found for {search}" : "Nie znaleziono użytkowników lub grup dla {search}", "No users found for {search}" : "Nie znaleziono użytkowników dla {search}", + "_Please enter at least {chars} character for suggestions_::_Please enter at least {chars} characters for suggestions_" : ["Proszę wprowadź przynajmniej {chars} literę jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię"], "An error occurred. Please try again" : "Wystąpił błąd. Proszę spróbować ponownie.", "{sharee} (group)" : "{sharee} (grupa)", "{sharee} (at {server})" : "{sharee} (na {server})", "User and Groups" : "Użytkownicy i Grupy", "Public Links" : "Linki publiczne", + "Sharing is not allowed" : "Współdzielenie nie jest możliwe", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Współdziel z użytkownikami innych chmur ownCloud używając wzorca uzytkownik@example.com/owncloud", "Share with users…" : "Współdziel z użytkownikami...", "Share with users, groups or remote users…" : "Współdziel z użytkownikami, grupami lub zdalnym użytkownikiem...", @@ -259,9 +261,11 @@ OC.L10N.register( "You can click here to return to %s." : "Możesz kliknąć tutaj aby powrócić do %s.", "Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" : "Witaj,\n\ntylko informuję, że %s współdzieli z Tobą %s.\nZobacz tutaj: %s\n\n", "The share will expire on %s." : "Ten zasób wygaśnie %s", + "Personal note from the sender: \n %s." : "Własna informacja od nadawcy: \n%s.", "Cheers!" : "Pozdrawiam!", "Internal Server Error" : "Błąd wewnętrzny serwera", "The server encountered an internal error and was unable to complete your request." : "Serwer napotkał błąd wewnętrzny i nie był w stanie ukończyć Twojego żądania.", + "Please contact the server administrator if this error reappears multiple times and include the technical details below in your report." : "Jeśli ten błąd się powtarza, skontaktuj się z administratorem serwera i podaj szczegóły techniczne z raportu poniżej.", "More details can be found in the server log." : "Więcej szczegółów można znaleźć w logach serwera.", "Technical details" : "Szczegóły techniczne", "Remote Address: %s" : "Adres zdalny: %s", @@ -273,6 +277,7 @@ OC.L10N.register( "Line: %s" : "Linia: %s", "Trace" : "Ślad", "Imprint" : "Stopka", + "Privacy Policy" : "Polityka prywatności", "Create an admin account" : "Utwórz konta administratora", "Username" : "Nazwa użytkownika", "Storage & database" : "Zasoby dysku & baza danych", @@ -306,6 +311,7 @@ OC.L10N.register( "Please try again or contact your administrator." : "Spróbuj ponownie lub skontaktuj się z administratorem.", "Username or email" : "Nazwa użytkownika lub adres e-mail", "Log in" : "Zaloguj", + "You took too long to login, please try again now" : "Twoje logowanie trwało zbyt długo, spróbuj ponownie teraz", "Wrong password. Reset it?" : "Niepoprawne hasło? Zresetuj je!", "Wrong password." : "Złe hasło", "You are trying to access a private link. Please log in first." : "Próbujesz dostępu do prywatnego linku. Proszę się najpierw zalogować.", @@ -316,6 +322,7 @@ OC.L10N.register( "New password" : "Nowe hasło", "New Password" : "Nowe hasło", "Reset password" : "Zresetuj hasło", + "Personal note from the sender:
%s." : "Własna informacja od nadawcy:
%s.", "This ownCloud instance is currently in single user mode." : "Ta instalacja ownCloud działa obecnie w trybie pojedynczego użytkownika.", "This means only administrators can use the instance." : "To oznacza, że tylko administratorzy mogą w tej chwili używać aplikacji.", "Contact your system administrator if this message persists or appeared unexpectedly." : "Skontaktuj się z administratorem, jeśli ten komunikat pojawił się nieoczekiwanie lub wyświetla się ciągle.", @@ -326,6 +333,7 @@ OC.L10N.register( "Please authenticate using the selected factor." : "Uwierzytelnij przy pomocy wybranego składnika", "An error occurred while verifying the token" : "Wystąpił błąd podczas weryfikacji tokena", "You are accessing the server from an untrusted domain." : "Dostajesz się do serwera z niezaufanej domeny.", + "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. " : "Skontaktuj się z administratorem. Jeśli jesteś administratorem tej instancji, skonfiguruj \"trusted_domains\" w pliku config/config.php.", "App update required" : "Aktualizacja aplikacji wymagana", "%s will be updated to version %s" : "%s zostanie zaktualizowane do wersji %s", "These apps will be updated:" : "Te aplikacje zostaną zaktualizowane:", diff --git a/core/l10n/pl.json b/core/l10n/pl.json index 61af6c0746b0..0d95422ea3cc 100644 --- a/core/l10n/pl.json +++ b/core/l10n/pl.json @@ -205,11 +205,13 @@ "Share details could not be loaded for this item." : "Szczegóły udziału nie mogły zostać wczytane dla tego obiektu.", "No users or groups found for {search}" : "Nie znaleziono użytkowników lub grup dla {search}", "No users found for {search}" : "Nie znaleziono użytkowników dla {search}", + "_Please enter at least {chars} character for suggestions_::_Please enter at least {chars} characters for suggestions_" : ["Proszę wprowadź przynajmniej {chars} literę jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię","Proszę wprowadź przynajmniej {chars} liter jako sugestię"], "An error occurred. Please try again" : "Wystąpił błąd. Proszę spróbować ponownie.", "{sharee} (group)" : "{sharee} (grupa)", "{sharee} (at {server})" : "{sharee} (na {server})", "User and Groups" : "Użytkownicy i Grupy", "Public Links" : "Linki publiczne", + "Sharing is not allowed" : "Współdzielenie nie jest możliwe", "Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Współdziel z użytkownikami innych chmur ownCloud używając wzorca uzytkownik@example.com/owncloud", "Share with users…" : "Współdziel z użytkownikami...", "Share with users, groups or remote users…" : "Współdziel z użytkownikami, grupami lub zdalnym użytkownikiem...", @@ -257,9 +259,11 @@ "You can click here to return to %s." : "Możesz kliknąć tutaj aby powrócić do %s.", "Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n" : "Witaj,\n\ntylko informuję, że %s współdzieli z Tobą %s.\nZobacz tutaj: %s\n\n", "The share will expire on %s." : "Ten zasób wygaśnie %s", + "Personal note from the sender: \n %s." : "Własna informacja od nadawcy: \n%s.", "Cheers!" : "Pozdrawiam!", "Internal Server Error" : "Błąd wewnętrzny serwera", "The server encountered an internal error and was unable to complete your request." : "Serwer napotkał błąd wewnętrzny i nie był w stanie ukończyć Twojego żądania.", + "Please contact the server administrator if this error reappears multiple times and include the technical details below in your report." : "Jeśli ten błąd się powtarza, skontaktuj się z administratorem serwera i podaj szczegóły techniczne z raportu poniżej.", "More details can be found in the server log." : "Więcej szczegółów można znaleźć w logach serwera.", "Technical details" : "Szczegóły techniczne", "Remote Address: %s" : "Adres zdalny: %s", @@ -271,6 +275,7 @@ "Line: %s" : "Linia: %s", "Trace" : "Ślad", "Imprint" : "Stopka", + "Privacy Policy" : "Polityka prywatności", "Create an admin account" : "Utwórz konta administratora", "Username" : "Nazwa użytkownika", "Storage & database" : "Zasoby dysku & baza danych", @@ -304,6 +309,7 @@ "Please try again or contact your administrator." : "Spróbuj ponownie lub skontaktuj się z administratorem.", "Username or email" : "Nazwa użytkownika lub adres e-mail", "Log in" : "Zaloguj", + "You took too long to login, please try again now" : "Twoje logowanie trwało zbyt długo, spróbuj ponownie teraz", "Wrong password. Reset it?" : "Niepoprawne hasło? Zresetuj je!", "Wrong password." : "Złe hasło", "You are trying to access a private link. Please log in first." : "Próbujesz dostępu do prywatnego linku. Proszę się najpierw zalogować.", @@ -314,6 +320,7 @@ "New password" : "Nowe hasło", "New Password" : "Nowe hasło", "Reset password" : "Zresetuj hasło", + "Personal note from the sender:
%s." : "Własna informacja od nadawcy:
%s.", "This ownCloud instance is currently in single user mode." : "Ta instalacja ownCloud działa obecnie w trybie pojedynczego użytkownika.", "This means only administrators can use the instance." : "To oznacza, że tylko administratorzy mogą w tej chwili używać aplikacji.", "Contact your system administrator if this message persists or appeared unexpectedly." : "Skontaktuj się z administratorem, jeśli ten komunikat pojawił się nieoczekiwanie lub wyświetla się ciągle.", @@ -324,6 +331,7 @@ "Please authenticate using the selected factor." : "Uwierzytelnij przy pomocy wybranego składnika", "An error occurred while verifying the token" : "Wystąpił błąd podczas weryfikacji tokena", "You are accessing the server from an untrusted domain." : "Dostajesz się do serwera z niezaufanej domeny.", + "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. " : "Skontaktuj się z administratorem. Jeśli jesteś administratorem tej instancji, skonfiguruj \"trusted_domains\" w pliku config/config.php.", "App update required" : "Aktualizacja aplikacji wymagana", "%s will be updated to version %s" : "%s zostanie zaktualizowane do wersji %s", "These apps will be updated:" : "Te aplikacje zostaną zaktualizowane:", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index d5d52904cec6..35e46f6882f1 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -86,6 +86,7 @@ OC.L10N.register( "Settings" : "Ustawienia", "A safe home for all your data" : "Bezpieczne miejsce dla wszystkich Twoich danych", "Imprint" : "Stopka", + "Privacy Policy" : "Polityka prywatności", "File is currently busy, please try again later" : "Plik jest obecnie niedostępny, proszę spróbować ponownie później", "Application is not enabled" : "Aplikacja nie jest włączona", "Authentication error" : "Błąd uwierzytelniania", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index 6db39802a7bb..e5d1ec28edcf 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -84,6 +84,7 @@ "Settings" : "Ustawienia", "A safe home for all your data" : "Bezpieczne miejsce dla wszystkich Twoich danych", "Imprint" : "Stopka", + "Privacy Policy" : "Polityka prywatności", "File is currently busy, please try again later" : "Plik jest obecnie niedostępny, proszę spróbować ponownie później", "Application is not enabled" : "Aplikacja nie jest włączona", "Authentication error" : "Błąd uwierzytelniania", diff --git a/lib/private/Files/Config/CachedMountInfo.php b/lib/private/Files/Config/CachedMountInfo.php index d2b3c78f393a..cc3074150142 100644 --- a/lib/private/Files/Config/CachedMountInfo.php +++ b/lib/private/Files/Config/CachedMountInfo.php @@ -91,12 +91,8 @@ public function getMountPointNode() { // TODO injection etc Filesystem::initMountPoints($this->getUser()->getUID()); $userNode = \OC::$server->getUserFolder($this->getUser()->getUID()); - $nodes = $userNode->getParent()->getById($this->getRootId()); - if (\count($nodes) > 0) { - return $nodes[0]; - } else { - return null; - } + $nodes = $userNode->getParent()->getById($this->getRootId(), true); + return $nodes[0] ?? null; } /** diff --git a/lib/private/Files/Meta/MetaFileIdNode.php b/lib/private/Files/Meta/MetaFileIdNode.php index b04d344b247e..2f8407ea9fb5 100644 --- a/lib/private/Files/Meta/MetaFileIdNode.php +++ b/lib/private/Files/Meta/MetaFileIdNode.php @@ -34,8 +34,8 @@ */ class MetaFileIdNode extends AbstractFolder { - /** @var int */ - private $fileId; + /** @var \OCP\Files\Node */ + private $node; /** @var MetaRootNode */ private $parentNode; /** @var IRootFolder */ @@ -45,12 +45,13 @@ class MetaFileIdNode extends AbstractFolder { * MetaFileIdNode constructor. * * @param MetaRootNode $parentNode - * @param int $fileId + * @param IRootFolder $root + * @param \OCP\Files\Node $node */ - public function __construct(MetaRootNode $parentNode, IRootFolder $root, $fileId) { + public function __construct(MetaRootNode $parentNode, IRootFolder $root, \OCP\Files\Node $node) { $this->parentNode = $parentNode; - $this->fileId = $fileId; $this->root = $root; + $this->node = $node; } /** @@ -79,7 +80,7 @@ public function isMounted() { */ public function getDirectoryListing() { return [ - new MetaVersionCollection($this->fileId, $this->root) + new MetaVersionCollection($this->root, $this->node) ]; } @@ -90,7 +91,7 @@ public function get($path) { $pieces = \explode('/', $path); if ($pieces[0] === 'v') { \array_shift($pieces); - $node = new MetaVersionCollection($this->fileId, $this->root); + $node = new MetaVersionCollection($this->root, $this->node); if (empty($pieces)) { return $node; } @@ -117,7 +118,7 @@ public function getPath() { * @inheritdoc */ public function getInternalPath() { - return "/meta/{$this->fileId}"; + return "/meta/{$this->node->getId()}"; } /** @@ -166,6 +167,6 @@ public function getParent() { * @inheritdoc */ public function getName() { - return "{$this->fileId}"; + return "{$this->node->getId()}"; } } diff --git a/lib/private/Files/Meta/MetaRootNode.php b/lib/private/Files/Meta/MetaRootNode.php index 6a893652c115..930180f5d714 100644 --- a/lib/private/Files/Meta/MetaRootNode.php +++ b/lib/private/Files/Meta/MetaRootNode.php @@ -76,13 +76,13 @@ public function get($path) { $pieces = \explode('/', $path); $fileId = (int)$pieces[0]; - // check if file exists - if (empty($this->rootFolder->getById($fileId))) { + $nodes = $this->rootFolder->getById($fileId, true); + if (empty($nodes)) { throw new NotFoundException(); } \array_shift($pieces); - $node = new MetaFileIdNode($this, $this->rootFolder, $fileId); + $node = new MetaFileIdNode($this, $this->rootFolder, $nodes[0]); if (empty($pieces)) { return $node; } @@ -92,7 +92,7 @@ public function get($path) { /** * @inheritdoc */ - public function getById($id) { + public function getById($id, $first = false) { return [ $this->get("$id") ]; diff --git a/lib/private/Files/Meta/MetaVersionCollection.php b/lib/private/Files/Meta/MetaVersionCollection.php index 5e63c7d0f43e..fd3a238efa64 100644 --- a/lib/private/Files/Meta/MetaVersionCollection.php +++ b/lib/private/Files/Meta/MetaVersionCollection.php @@ -1,5 +1,6 @@ * @author Thomas Müller * * @copyright Copyright (c) 2018, ownCloud GmbH @@ -24,7 +25,6 @@ use OC\Files\Node\AbstractFolder; use OCP\Files\IRootFolder; use OCP\Files\Storage\IVersionedStorage; -use OC\Files\View; use OCP\Files\NotFoundException; use OCP\Files\Storage; @@ -36,20 +36,20 @@ */ class MetaVersionCollection extends AbstractFolder { - /** @var int */ - private $fileId; /** @var IRootFolder */ private $root; + /** @var \OCP\Files\Node */ + private $node; /** * MetaVersionCollection constructor. * - * @param int $fileId * @param IRootFolder $root + * @param \OCP\Files\Node $node */ - public function __construct($fileId, IRootFolder $root) { - $this->fileId = $fileId; + public function __construct(IRootFolder $root, \OCP\Files\Node $node) { $this->root = $root; + $this->node = $node; } /** @@ -66,22 +66,19 @@ public function isShared() { return false; } - /** - * @inheritdoc - */ public function getDirectoryListing() { - $view = new View(); - $path = $view->getPath($this->fileId, false); - /** @var Storage $storage */ - list($storage, $internalPath) = $view->resolvePath($path); + $node = $this->node; + $storage = $node->getStorage(); + $internalPath = $node->getInternalPath(); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { return []; } /** @var IVersionedStorage | Storage $storage */ $versions = $storage->getVersions($internalPath); - return \array_values(\array_map(function ($version) use ($storage, $internalPath, $view, $path) { + return \array_values(\array_map(function ($version) use ($storage, $node, $internalPath) { if (!isset($version['mimetype'])) { - $version['mimetype'] = $view->getMimeType($path); + $version['mimetype'] = $node->getMimetype(); } return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath); @@ -97,10 +94,10 @@ public function get($path) { throw new NotFoundException(); } $versionId = $pieces[0]; - $view = new View(); - $path = $view->getPath($this->fileId); - /** @var Storage $storage */ - list($storage, $internalPath) = $view->resolvePath($path); + + $storage = $this->node->getStorage(); + $internalPath = $this->node->getInternalPath(); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { throw new NotFoundException(); } @@ -110,7 +107,7 @@ public function get($path) { throw new NotFoundException(); } if (!isset($version['mimetype'])) { - $version['mimetype'] = $view->getMimeType($path); + $version['mimetype'] = $this->node->getMimetype(); } return new MetaFileVersionNode($this, $this->root, $version, $storage, $internalPath); } @@ -119,7 +116,7 @@ public function get($path) { * @inheritdoc */ public function getId() { - return $this->fileId; + return $this->node->getId(); } public function getName() { @@ -127,6 +124,6 @@ public function getName() { } public function getPath() { - return "/meta/{$this->fileId}/v"; + return "/meta/{$this->getId()}/v"; } } diff --git a/lib/private/Files/Node/AbstractFolder.php b/lib/private/Files/Node/AbstractFolder.php index fb6c3feea3a4..d652c026a2f6 100644 --- a/lib/private/Files/Node/AbstractFolder.php +++ b/lib/private/Files/Node/AbstractFolder.php @@ -112,7 +112,7 @@ public function searchByTag($tag, $userId) { /** * @inheritdoc */ - public function getById($id) { + public function getById($id, $first = false) { throw new NotPermittedException(); } diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index adb9e56a1b4c..ca559a1f7e86 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -269,9 +269,11 @@ private function searchCommon($method, $args) { /** * @param int $id + * @param boolean $first only return the first node that is found * @return \OC\Files\Node\Node[] + * @throws NotFoundException */ - public function getById($id) { + public function getById($id, $first = false) { $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 @@ -290,6 +292,9 @@ public function getById($id) { $fullPath = $mount->getMountPoint() . $internalPath; if (($path = $this->getRelativePath($fullPath)) !== null) { $nodes[] = $this->get($path); + if ($first) { + break; + } } } } diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index 9e43f7e09b47..c9bf01f71525 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -417,7 +417,7 @@ public function searchByTag($tag, $userId) { /** * @inheritDoc */ - public function getById($id) { + public function getById($id, $first = false) { return $this->__call(__FUNCTION__, \func_get_args()); } diff --git a/lib/private/Files/Node/NonExistingFolder.php b/lib/private/Files/Node/NonExistingFolder.php index c3634403fea6..032e5764312c 100644 --- a/lib/private/Files/Node/NonExistingFolder.php +++ b/lib/private/Files/Node/NonExistingFolder.php @@ -154,7 +154,7 @@ public function searchByTag($tag, $userId) { throw new NotFoundException(); } - public function getById($id) { + public function getById($id, $first = false) { throw new NotFoundException(); } diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index f533cd8330f1..24d12774d8a0 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -26,6 +26,7 @@ use OC\Files\Cache\Wrapper\CacheJail; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IVersionedStorage; use OCP\Lock\ILockingProvider; /** @@ -33,7 +34,8 @@ * * This restricts access to a subfolder of the wrapped storage with the subfolder becoming the root folder new storage */ -class Jail extends Wrapper { +class Jail extends Wrapper /* implements IVersionedStorage */ +{ /** * @var string */ @@ -497,4 +499,62 @@ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceIntern } return $this->getWrapperStorage()->moveFromStorage($sourceStorage, $sourceInternalPath, $this->getSourcePath($targetInternalPath)); } + + /** + * Get the content of a given version of a given file as stream resource + * + * @param string $internalPath + * @param string $versionId + * @return resource + * @since 10.0.9 + */ + public function getContentOfVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->getContentOfVersion($this->getSourcePath($internalPath), $versionId); + } + + /** + * Restore the given version of a given file + * + * @param string $internalPath + * @param string $versionId + * @return boolean + * @since 10.0.9 + */ + public function restoreVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->restoreVersion($this->getSourcePath($internalPath), $versionId); + } + + /** + * Tells the storage to explicitly create a version of a given file + * + * @param string $internalPath + * @return bool + * @since 10.0.9 + */ + public function saveVersion($internalPath) { + return $this->getWrapperStorage()->saveVersion($this->getSourcePath($internalPath)); + } + + /** + * List all versions for the given file + * + * @param string $internalPath + * @return array + * @since 10.0.9 + */ + public function getVersions($internalPath) { + return $this->getWrapperStorage()->getVersions($this->getSourcePath($internalPath)); + } + + /** + * Get one explicit version for the given file + * + * @param string $internalPath + * @param string $versionId + * @return array + * @since 10.0.9 + */ + public function getVersion($internalPath, $versionId) { + return $this->getWrapperStorage()->getVersion($this->getSourcePath($internalPath), $versionId); + } } diff --git a/lib/private/Lock/Persistent/LockManager.php b/lib/private/Lock/Persistent/LockManager.php index 2ce08606a1f8..02e9682e123a 100644 --- a/lib/private/Lock/Persistent/LockManager.php +++ b/lib/private/Lock/Persistent/LockManager.php @@ -60,7 +60,7 @@ public function lock(int $storageId, string $internalPath, int $fileId, array $l if ($user !== null) { $owner = $user->getDisplayName(); if ($user->getEMailAddress() !== null) { - $owner .= " <{$user->getEMailAddress()}>"; + $owner .= " ({$user->getEMailAddress()})"; } } } diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index 6f774f08f653..4fa7e4ccfe93 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -164,7 +164,7 @@ public function getNode() { $userFolder = $this->rootFolder->getUserFolder($this->sharedBy); } - $nodes = $userFolder->getById($this->fileId); + $nodes = $userFolder->getById($this->fileId, true); if (empty($nodes)) { throw new NotFoundException(); } diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php index 2dfa83a409ed..55f5211bc22d 100644 --- a/lib/public/Files/Folder.php +++ b/lib/public/Files/Folder.php @@ -145,10 +145,11 @@ public function searchByTag($tag, $userId); * get a file or folder inside the folder by it's internal id * * @param int $id + * @param boolean $first only return the first node that is found * @return \OCP\Files\Node[] * @since 6.0.0 */ - public function getById($id); + public function getById($id, $first = false); /** * Get the amount of free space inside the folder diff --git a/settings/l10n/ja.js b/settings/l10n/ja.js index 6242ea3ad692..217c3dd9aae9 100644 --- a/settings/l10n/ja.js +++ b/settings/l10n/ja.js @@ -239,7 +239,8 @@ OC.L10N.register( "No display name set" : "表示名が未設定", "Email" : "メール", "Your email address" : "あなたのメールアドレス", - "Change email" : "メールを変更", + "Change email" : "メールアドレスを変更", + "Set email" : "メールアドレスを設定", "For password recovery and notifications" : "パスワード回復と通知用です", "No email address set" : "メールアドレスが設定されていません", "You are member of the following groups:" : "以下のグループのメンバーです:", @@ -250,7 +251,8 @@ OC.L10N.register( "New password" : "新しいパスワード", "Change password" : "パスワードを変更", "Help translate" : "翻訳に協力する", - "You are using %s" : "%sを使用しています", + "You are using %s" : "%s使用中", + "You are using %s of %s (%s %%)" : "%s/%s使用中(%s%%)", "Developed by the {communityopen}ownCloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : " {communityopen}ownCloud community{linkclose} によって開発されています。{githubopen}ソースコード{linkclose} は {licenseopen}AGPL{linkclose} によってライセンスされます。", "Sessions" : "セッション", "Browser" : "ブラウザー", diff --git a/settings/l10n/ja.json b/settings/l10n/ja.json index 93ca5eb27aa2..0f045a012465 100644 --- a/settings/l10n/ja.json +++ b/settings/l10n/ja.json @@ -237,7 +237,8 @@ "No display name set" : "表示名が未設定", "Email" : "メール", "Your email address" : "あなたのメールアドレス", - "Change email" : "メールを変更", + "Change email" : "メールアドレスを変更", + "Set email" : "メールアドレスを設定", "For password recovery and notifications" : "パスワード回復と通知用です", "No email address set" : "メールアドレスが設定されていません", "You are member of the following groups:" : "以下のグループのメンバーです:", @@ -248,7 +249,8 @@ "New password" : "新しいパスワード", "Change password" : "パスワードを変更", "Help translate" : "翻訳に協力する", - "You are using %s" : "%sを使用しています", + "You are using %s" : "%s使用中", + "You are using %s of %s (%s %%)" : "%s/%s使用中(%s%%)", "Developed by the {communityopen}ownCloud community{linkclose}, the {githubopen}source code{linkclose} is licensed under the {licenseopen}AGPL{linkclose}." : " {communityopen}ownCloud community{linkclose} によって開発されています。{githubopen}ソースコード{linkclose} は {licenseopen}AGPL{linkclose} によってライセンスされます。", "Sessions" : "セッション", "Browser" : "ブラウザー", diff --git a/tests/acceptance/config/behat.yml b/tests/acceptance/config/behat.yml index 32236c15bbbf..3e501733276d 100644 --- a/tests/acceptance/config/behat.yml +++ b/tests/acceptance/config/behat.yml @@ -42,6 +42,7 @@ default: - %paths.base%/../features/apiFavorites contexts: - FeatureContext: *common_feature_context_params + - FavoritesContext: apiProvisioning-v1: paths: diff --git a/tests/acceptance/features/apiFavorites/favorites.feature b/tests/acceptance/features/apiFavorites/favorites.feature index 33c2686eba3e..a461fd89044c 100644 --- a/tests/acceptance/features/apiFavorites/favorites.feature +++ b/tests/acceptance/features/apiFavorites/favorites.feature @@ -74,6 +74,8 @@ Feature: favorite Then the user in folder "/subfolder" should have favorited the following elements | /subfolder/textfile0.txt | | /subfolder/textfile2.txt | + And the user in folder "/subfolder" should not have favorited the following elements + | /subfolder/textfile1.txt | Examples: | dav_version | | old | @@ -94,7 +96,28 @@ Feature: favorite | old | | new | - Scenario Outline: Get favorited elements paginated + @skip @issue-33840 + Scenario Outline: Get favorited elements and limit count of entries + Given using DAV path + And the user has favorited element "/textfile0.txt" + And the user has favorited element "/textfile1.txt" + And the user has favorited element "/textfile2.txt" + And the user has favorited element "/textfile3.txt" + And the user has favorited element "/textfile4.txt" + When the user lists the favorites of folder "/" and limits the result to 3 elements using the WebDAV API + Then the search result of "user0" shoud contain any "3" of these entries: + | /textfile0.txt | + | /textfile1.txt | + | /textfile2.txt | + | /textfile3.txt | + | /textfile4.txt | + Examples: + | dav_version | + | old | + | new | + + @skip @issue-33840 + Scenario Outline: Get favorited elements paginated in subfolder Given using DAV path And the user has created folder "/subfolder" And the user has copied file "/textfile0.txt" to "/subfolder/textfile0.txt" @@ -103,15 +126,19 @@ Feature: favorite And the user has copied file "/textfile0.txt" to "/subfolder/textfile3.txt" And the user has copied file "/textfile0.txt" to "/subfolder/textfile4.txt" And the user has copied file "/textfile0.txt" to "/subfolder/textfile5.txt" - When the user favorites element "/subfolder/textfile0.txt" using the WebDAV API - And the user favorites element "/subfolder/textfile1.txt" using the WebDAV API - And the user favorites element "/subfolder/textfile2.txt" using the WebDAV API - And the user favorites element "/subfolder/textfile3.txt" using the WebDAV API - And the user favorites element "/subfolder/textfile4.txt" using the WebDAV API - And the user favorites element "/subfolder/textfile5.txt" using the WebDAV API - Then the user in folder "/subfolder" should have favorited the following elements from offset 3 and limit 2 + And the user has favorited element "/subfolder/textfile0.txt" + And the user has favorited element "/subfolder/textfile1.txt" + And the user has favorited element "/subfolder/textfile2.txt" + And the user has favorited element "/subfolder/textfile3.txt" + And the user has favorited element "/subfolder/textfile4.txt" + And the user has favorited element "/subfolder/textfile5.txt" + When the user lists the favorites of folder "/" and limits the result to 3 elements using the WebDAV API + Then the search result of "user0" shoud contain any "3" of these entries: + | /subfolder/textfile0.txt | + | /subfolder/textfile1.txt | | /subfolder/textfile2.txt | | /subfolder/textfile3.txt | + | /subfolder/textfile4.txt | Examples: | dav_version | | old | diff --git a/tests/acceptance/features/apiWebdavOperations/uploadFileUsingNewChunking.feature b/tests/acceptance/features/apiWebdavOperations/uploadFileUsingNewChunking.feature index 96ca6cf95b1b..adc0b6193328 100644 --- a/tests/acceptance/features/apiWebdavOperations/uploadFileUsingNewChunking.feature +++ b/tests/acceptance/features/apiWebdavOperations/uploadFileUsingNewChunking.feature @@ -121,4 +121,15 @@ Feature: upload file using new chunking | file-name | | &#? | | TIÄFÜ | - | 0 | + + # The bug for this scenario was fixed by https://github.com/owncloud/core/pull/33276 + # The fix is released in 10.1 - all 10.0.* versions will fail this scenario + @skipOnOcV10.0 + Scenario: Upload a file called "0" using new chunking + When user "user0" creates a new chunking upload with id "chunking-42" using the WebDAV API + And user "user0" uploads new chunk file "1" with "AAAAA" to id "chunking-42" using the WebDAV API + And user "user0" uploads new chunk file "2" with "BBBBB" to id "chunking-42" using the WebDAV API + And user "user0" uploads new chunk file "3" with "CCCCC" to id "chunking-42" using the WebDAV API + And user "user0" moves new chunk file with id "chunking-42" to "/0" using the WebDAV API + And as "user0" file "/0" should exist + And the content of file "/0" for user "user0" should be "AAAAABBBBBCCCCC" diff --git a/tests/acceptance/features/bootstrap/FavoritesContext.php b/tests/acceptance/features/bootstrap/FavoritesContext.php new file mode 100644 index 000000000000..f1535a6f78b8 --- /dev/null +++ b/tests/acceptance/features/bootstrap/FavoritesContext.php @@ -0,0 +1,284 @@ + + * @copyright Copyright (c) 2018 Artur Neumann artur@jankaritech.com + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, + * as published by the Free Software Foundation; + * either version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see + * + */ + +use Behat\Behat\Context\Context; +use Behat\Behat\Hook\Scope\BeforeScenarioScope; +use Behat\Gherkin\Node\TableNode; +use TestHelpers\WebDavHelper; + +require_once 'bootstrap.php'; + +/** + * context containing favorites related API steps + */ +class FavoritesContext implements Context { + + /** + * + * @var FeatureContext + */ + private $featureContext; + + /** + * @When user :user favorites element :path using the WebDAV API + * @Given user :user has favorited element :path + * + * @param string $user + * @param string $path + * + * @return void + * @throws \Sabre\HTTP\ClientException + * @throws \Sabre\HTTP\ClientHttpException + */ + public function userFavoritesElement($user, $path) { + $response = $this->changeFavStateOfAnElement($user, $path, 1); + $this->featureContext->setResponse($response); + } + + /** + * @When the user favorites element :path using the WebDAV API + * @Given the user has favorited element :path + * + * @param string $path + * + * @return void + * @throws \Sabre\HTTP\ClientException + * @throws \Sabre\HTTP\ClientHttpException + */ + public function theUserFavoritesElement($path) { + $response = $this->changeFavStateOfAnElement( + $this->featureContext->getCurrentUser(), $path, 1 + ); + $this->featureContext->setResponse($response); + } + + /** + * @When user :user unfavorites element :path using the WebDAV API + * @Given user :user has unfavorited element :path + * + * @param string $user + * @param string $path + * + * @return void + * @throws \Sabre\HTTP\ClientException + * @throws \Sabre\HTTP\ClientHttpException + */ + public function userUnfavoritesElement($user, $path) { + $response = $this->changeFavStateOfAnElement( + $user, $path, 0 + ); + $this->featureContext->setResponse($response); + } + + /** + * @Then /^user "([^"]*)" in folder "([^"]*)" should (not|)\s?have favorited the following elements$/ + * + * @param string $user + * @param string $folder + * @param string $shouldOrNot (not|) + * @param TableNode $expectedElements + * + * @return void + */ + public function checkFavoritedElements( + $user, $folder, $shouldOrNot, $expectedElements + ) { + $this->userListsFavoriteOfFolder($user, $folder, null); + $this->featureContext->propfindResultShouldContainEntries( + $user, $shouldOrNot, $expectedElements + ); + } + + /** + * @Then /^the user in folder "([^"]*)" should (not|)\s?have favorited the following elements$/ + * + * @param string $folder + * @param string $shouldOrNot (not|) + * @param TableNode $expectedElements + * + * @return void + */ + public function checkFavoritedElementsForCurrentUser( + $folder, $shouldOrNot, $expectedElements + ) { + $this->checkFavoritedElements( + $this->featureContext->getCurrentUser(), + $folder, $shouldOrNot, $expectedElements + ); + } + + /** + * @When /^user "([^"]*)" lists the favorites of folder "([^"]*)" and limits the result to ([\d*]) elements using the WebDAV API$/ + * + * @param string $user + * @param string $folder + * @param int $limit + * + * @return void + */ + public function userListsFavoriteOfFolder($user, $folder, $limit = null) { + $baseUrl = $this->featureContext->getBaseUrl(); + $password = $this->featureContext->getPasswordForUser($user); + $body + = "\n" . + " \n" . + " \n" . + " 1\n"; + + if ($limit !== null) { + $body .= " \n" . + " $limit\n" . + " \n"; + } + + $body .= " "; + $response = WebDavHelper::makeDavRequest( + $baseUrl, $user, $password, "REPORT", "/", null, $body, null, + $this->featureContext->getDavPathVersion() + ); + $this->featureContext->setResponse($response); + } + + /** + * @When /^the user lists the favorites of folder "([^"]*)" and limits the result to ([\d*]) elements using the WebDAV API$/ + * + * @param string $folder + * @param int $limit + * + * @return void + */ + public function listFavoriteOfFolder($folder, $limit = null) { + $this->userListsFavoriteOfFolder( + $this->featureContext->getCurrentUser(), $folder, $limit + ); + } + + /** + * @When the user unfavorites element :path using the WebDAV API + * @Given the user has unfavorited element :path + * + * @param string $path + * + * @return void + * @throws \Sabre\HTTP\ClientException + * @throws \Sabre\HTTP\ClientHttpException + */ + public function theUserUnfavoritesElement($path) { + $response = $this->changeFavStateOfAnElement( + $this->featureContext->getCurrentUser(), $path, 0 + ); + $this->featureContext->setResponse($response); + } + + /** + * @Then /^as user "([^"]*)" the (?:file|folder|entry) "([^"]*)" should be favorited$/ + * + * @param string $user + * @param string $path + * @param integer $expectedValue 0|1 + * + * @return void + */ + public function asUserTheFileOrFolderShouldBeFavorited($user, $path, $expectedValue = 1) { + $property = "{http://owncloud.org/ns}favorite"; + $this->featureContext->asUserFolderShouldContainAPropertyWithValue( + $user, $path, $property, $expectedValue + ); + } + + /** + * @Then /^as user "([^"]*)" (?:file|folder|entry) "([^"]*)" should not be favorited$/ + * + * @param string $user + * @param string $path + * + * @return void + */ + public function asUserFileShouldNotBeFavorited($user, $path) { + $this->asUserTheFileOrFolderShouldBeFavorited($user, $path, 0); + } + + /** + * @Then /^as the user (?:file|folder|entry) "([^"]*)" should be favorited$/ + * + * @param string $path + * @param integer $expectedValue 0|1 + * + * @return void + */ + public function asTheUserFileOrFolderShouldBeFavorited($path, $expectedValue = 1) { + $this->asUserTheFileOrFolderShouldBeFavorited( + $this->featureContext->getCurrentUser(), $path, $expectedValue + ); + } + + /** + * @Then /^as the user (?:file|folder|entry) "([^"]*)" should not be favorited$/ + * + * @param string $path + * + * @return void + */ + public function asTheUserFileOrFolderShouldNotBeFavorited($path) { + $this->asTheUserFileOrFolderShouldBeFavorited($path, 0); + } + + /** + * Set the elements of a proppatch + * + * @param string $user + * @param string $path + * @param int $favOrUnfav 1 = favorite, 0 = unfavorite + * + * @return bool + */ + public function changeFavStateOfAnElement( + $user, $path, $favOrUnfav + ) { + $client = $this->featureContext->getSabreClient($user); + $properties = [ + '{http://owncloud.org/ns}favorite' => $favOrUnfav + ]; + + $response = $client->proppatch( + $this->featureContext->getDavFilesPath($user) . $path, $properties + ); + return $response; + } + + /** + * This will run before EVERY scenario. + * It will set the properties for this object. + * + * @BeforeScenario + * + * @param BeforeScenarioScope $scope + * + * @return void + */ + public function before(BeforeScenarioScope $scope) { + // Get the environment + $environment = $scope->getEnvironment(); + // Get all the contexts you need in this context + $this->featureContext = $environment->getContext('FeatureContext'); + } +} diff --git a/tests/acceptance/features/bootstrap/WebDav.php b/tests/acceptance/features/bootstrap/WebDav.php index 0bfcc6f057b9..b5af3f112181 100644 --- a/tests/acceptance/features/bootstrap/WebDav.php +++ b/tests/acceptance/features/bootstrap/WebDav.php @@ -1189,7 +1189,7 @@ public function asFileOrFolderShouldNotExist($user, $entry, $path) { $response = $client->request( 'GET', $this->makeSabrePath($user, $path) ); - if ($response['statusCode'] < 401 && $response['statusCode'] > 404) { + if ($response['statusCode'] < 401 || $response['statusCode'] > 404) { throw new \Exception( "$entry '$path' expected to not exist " . "(status code {$response['statusCode']}, expected 401 - 404)" @@ -1340,57 +1340,6 @@ public function asUserFolderShouldContainAPropertyWithValue( ); } - /** - * @Then /^as user "([^"]*)" the (?:file|folder|entry) "([^"]*)" should be favorited$/ - * - * @param string $user - * @param string $path - * @param integer $expectedValue 0|1 - * - * @return void - */ - public function asUserTheFileOrFolderShouldBeFavorited($user, $path, $expectedValue = 1) { - $property = "{http://owncloud.org/ns}favorite"; - $this->asUserFolderShouldContainAPropertyWithValue( - $user, $path, $property, $expectedValue - ); - } - - /** - * @Then /^as user "([^"]*)" (?:file|folder|entry) "([^"]*)" should not be favorited$/ - * - * @param string $user - * @param string $path - * - * @return void - */ - public function asUserFileShouldNotBeFavorited($user, $path) { - $this->asUserTheFileOrFolderShouldBeFavorited($user, $path, 0); - } - - /** - * @Then /^as the user (?:file|folder|entry) "([^"]*)" should be favorited$/ - * - * @param string $path - * @param integer $expectedValue 0|1 - * - * @return void - */ - public function asTheUserFileOrFolderShouldBeFavorited($path, $expectedValue = 1) { - $this->asUserTheFileOrFolderShouldBeFavorited($this->getCurrentUser(), $path, $expectedValue); - } - - /** - * @Then /^as the user (?:file|folder|entry) "([^"]*)" should not be favorited$/ - * - * @param string $path - * - * @return void - */ - public function asTheUserFileOrFolderShouldNotBeFavorited($path) { - $this->asTheUserFileOrFolderShouldBeFavorited($path, 0); - } - /** * @Then the single response should contain a property :key with value like :regex * @@ -1486,57 +1435,6 @@ public function listFolder($user, $path, $folderDepth, $properties = null) { return $response; } - /** - * Returns the elements of a report command - * - * @param string $user - * @param string $path - * @param string $properties properties which needs to be included in the report - * @param string $filterRules filter-rules to choose what needs to appear in the report - * @param int|null $offset - * @param int|null $limit - * - * @return array - * - * TODO: move into Helper - */ - public function reportFolder( - $user, $path, $properties, $filterRules, $offset = null, $limit = null - ) { - $client = $this->getSabreClient($user); - - $body = ' - - - ' . $properties . ' - - - ' . $filterRules . ' - '; - if (\is_int($offset) || \is_int($limit)) { - $body .= ' - '; - if (\is_int($offset)) { - $body .= " - ${offset}"; - } - if (\is_int($limit)) { - $body .= " - ${limit}"; - } - $body .= ' - '; - } - $body .= ' - '; - - $response = $client->request( - 'REPORT', $this->makeSabrePath($user, $path), $body - ); - $parsedResponse = $client->parseMultistatus($response['body']); - return $parsedResponse; - } - /** * @param string $user * @param string $path @@ -1731,11 +1629,12 @@ public function uploadFileWithHeaders( * @param int $noOfChunks * @param string $chunkingVersion old|v1|new|v2 null for autodetect * @param bool $async use asynchronous move at the end or not + * @param array $headers * * @return void */ public function userUploadsAFileToWithChunks( - $user, $source, $destination, $noOfChunks = 2, $chunkingVersion = null, $async = false + $user, $source, $destination, $noOfChunks = 2, $chunkingVersion = null, $async = false, $headers = [] ) { PHPUnit_Framework_Assert::assertGreaterThan( 0, $noOfChunks, "What does it mean to have $noOfChunks chunks?" @@ -1757,9 +1656,8 @@ public function userUploadsAFileToWithChunks( "invalid chunking/webdav version combination" ); - $headers = []; if ($async === true) { - $headers = ['OC-LazyOps' => 'true']; + $headers['OC-LazyOps'] = 'true'; } $this->uploadFileWithHeaders( $user, @@ -2530,95 +2428,6 @@ public function encodePath($path) { return \str_replace('%2F', '/', \rawurlencode($path)); } - /** - * @When user :user favorites element :path using the WebDAV API - * @Given user :user has favorited element :path - * - * @param string $user - * @param string $path - * - * @return void - * @throws \Sabre\HTTP\ClientException - * @throws \Sabre\HTTP\ClientHttpException - */ - public function userFavoritesElement($user, $path) { - $this->response = $this->changeFavStateOfAnElement( - $user, $path, 1 - ); - } - - /** - * @When the user favorites element :path using the WebDAV API - * @Given the user has favorited element :path - * - * @param string $path - * - * @return void - * @throws \Sabre\HTTP\ClientException - * @throws \Sabre\HTTP\ClientHttpException - */ - public function theUserFavoritesElement($path) { - $this->response = $this->changeFavStateOfAnElement( - $this->getCurrentUser(), $path, 1 - ); - } - - /** - * @When user :user unfavorites element :path using the WebDAV API - * @Given user :user has unfavorited element :path - * - * @param string $user - * @param string $path - * - * @return void - * @throws \Sabre\HTTP\ClientException - * @throws \Sabre\HTTP\ClientHttpException - */ - public function userUnfavoritesElement($user, $path) { - $this->response = $this->changeFavStateOfAnElement( - $user, $path, 0 - ); - } - - /** - * @When the user unfavorites element :path using the WebDAV API - * @Given the user has unfavorited element :path - * - * @param string $path - * - * @return void - * @throws \Sabre\HTTP\ClientException - * @throws \Sabre\HTTP\ClientHttpException - */ - public function theUserUnfavoritesElement($path) { - $this->response = $this->changeFavStateOfAnElement( - $this->getCurrentUser(), $path, 0 - ); - } - - /** - * Set the elements of a proppatch - * - * @param string $user - * @param string $path - * @param int $favOrUnfav 1 = favorite, 0 = unfavorite - * - * @return bool - */ - public function changeFavStateOfAnElement( - $user, $path, $favOrUnfav - ) { - $client = $this->getSabreClient($user); - $properties = [ - '{http://owncloud.org/ns}favorite' => $favOrUnfav - ]; - - $response = $client->proppatch( - $this->getDavFilesPath($user) . $path, $properties - ); - return $response; - } - /** * @When user :user stores etag of element :path using the WebDAV API * @Given user :user has stored etag of element :path @@ -2766,87 +2575,6 @@ public function headersShouldMatchRegularExpressions(TableNode $table) { } } - /** - * @Then /^user "([^"]*)" in folder "([^"]*)" should have favorited the following elements$/ - * - * @param string $user - * @param string $folder - * @param TableNode $expectedElements - * - * @return void - */ - public function checkFavoritedElements($user, $folder, $expectedElements) { - $this->checkFavoritedElementsPaginated( - $user, $folder, null, null, $expectedElements - ); - } - - /** - * @Then /^the user in folder "([^"]*)" should have favorited the following elements$/ - * - * @param string $folder - * @param TableNode $expectedElements - * - * @return void - */ - public function checkFavoritedElementsForCurrentUser($folder, $expectedElements) { - $this->checkFavoritedElementsPaginated( - $this->getCurrentUser(), $folder, null, null, $expectedElements - ); - } - - /** - * @Then /^user "([^"]*)" in folder "([^"]*)" should have favorited the following elements from offset ([\d*]) and limit ([\d*])$/ - * - * @param string $user - * @param string $folder - * @param int $offset unused - * @param int $limit unused - * @param TableNode $expectedElements - * - * @return void - */ - public function checkFavoritedElementsPaginated( - $user, $folder, $offset, $limit, $expectedElements - ) { - $elementList = $this->reportFolder( - $user, - $folder, - '', - '1' - ); - if ($expectedElements instanceof TableNode) { - $elementRows = $expectedElements->getRows(); - $elementsSimplified = $this->simplifyArray($elementRows); - foreach ($elementsSimplified as $expectedElement) { - $webdavPath = "/" . $this->getFullDavFilesPath($user) . $expectedElement; - if (!\array_key_exists($webdavPath, $elementList)) { - PHPUnit_Framework_Assert::fail( - "$webdavPath is not in report answer" - ); - } - } - } - } - - /** - * @Then /^the user in folder "([^"]*)" should have favorited the following elements from offset ([\d*]) and limit ([\d*])$/ - * - * @param string $folder - * @param int $offset unused - * @param int $limit unused - * @param TableNode $expectedElements - * - * @return void - */ - public function checkFavoritedElementsPaginatedForCurrentUser( - $folder, $offset, $limit, $expectedElements - ) { - $this->checkFavoritedElementsPaginated( - $this->getCurrentUser(), $folder, $offset, $limit, $expectedElements - ); - } - /** * @When /^user "([^"]*)" deletes everything from folder "([^"]*)" using the WebDAV API$/ * @Given /^user "([^"]*)" has deleted everything from folder "([^"]*)"$/ diff --git a/tests/acceptance/features/cliMain/files.feature b/tests/acceptance/features/cliMain/files.feature index 28219179710c..56b872138c26 100644 --- a/tests/acceptance/features/cliMain/files.feature +++ b/tests/acceptance/features/cliMain/files.feature @@ -110,10 +110,10 @@ Feature: Files Operations command | username | | user0 | | user1 | - When the administrator creates the local storage mount "local_storage1" using the occ command - And the administrator adds user "user0" as the applicable user for the last local storage mount using the occ command - And the administrator creates the local storage mount "local_storage2" using the occ command - And the administrator adds user "user1" as the applicable user for the last local storage mount using the occ command + And the administrator has created the local storage mount "local_storage1" + And the administrator has added user "user0" as the applicable user for the last local storage mount + And the administrator has created the local storage mount "local_storage2" + And the administrator has added user "user1" as the applicable user for the last local storage mount And the administrator has set the external storage "local_storage1" to be never scanned automatically And the administrator has set the external storage "local_storage2" to be never scanned automatically And the administrator has scanned the filesystem for all users @@ -133,6 +133,47 @@ Feature: Files Operations command Then the propfind result of "user1" should not contain these entries: | /local_storage2/hello1.txt | + Scenario: Adding a file on local storage and running file scan for a specific group should add file for only the users of that group + Given using new DAV path + And these users have been created with default attributes: + | username | + | user0 | + | user1 | + | user2 | + And group "newgroup" has been created + And group "newgroup2" has been created + And user "user0" has been added to group "newgroup" + And user "user1" has been added to group "newgroup" + And user "user2" has been added to group "newgroup2" + And the administrator has created the local storage mount "local_storage1" + And the administrator has added group "newgroup" as the applicable group for the last local storage mount + And the administrator has created the local storage mount "local_storage2" + And the administrator has added group "newgroup2" as the applicable group for the last local storage mount + And the administrator has set the external storage "local_storage1" to be never scanned automatically + And the administrator has set the external storage "local_storage2" to be never scanned automatically + And the administrator has scanned the filesystem for all users + When the administrator creates file "hello1.txt" with content "assertNull($lock->getPath()); $this->assertEquals(0, $lock->getDepth()); $this->assertEquals(ILock::LOCK_SCOPE_EXCLUSIVE, $lock->getScope()); - $this->assertEquals('Alice ', $lock->getOwner()); + $this->assertEquals('Alice (alice@example.net)', $lock->getOwner()); $this->assertEquals(999, $lock->getOwnerAccountId()); $this->assertEquals(123456, $lock->getCreatedAt()); });