From 53b43c9fd05d8bf881cdcf8f7186abdef71688c9 Mon Sep 17 00:00:00 2001 From: VicDeo Date: Tue, 26 Mar 2019 17:40:13 +0100 Subject: [PATCH 1/3] Add a command to check federated share updates --- apps/federatedfilesharing/appinfo/info.xml | 3 + .../lib/Command/PollIncomingShares.php | 132 ++++++++++++++++++ .../tests/Command/PollIncomingSharesTest.php | 79 +++++++++++ 3 files changed, 214 insertions(+) create mode 100644 apps/federatedfilesharing/lib/Command/PollIncomingShares.php create mode 100644 apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php diff --git a/apps/federatedfilesharing/appinfo/info.xml b/apps/federatedfilesharing/appinfo/info.xml index e4f7777cf249..67385005bee7 100644 --- a/apps/federatedfilesharing/appinfo/info.xml +++ b/apps/federatedfilesharing/appinfo/info.xml @@ -20,4 +20,7 @@ OCA\FederatedFileSharing\Panels\GeneralPersonalPanel OCA\FederatedFileSharing\Panels\SharingPersonalPanel + + OCA\FederatedFileSharing\Command\PollIncomingShares + diff --git a/apps/federatedfilesharing/lib/Command/PollIncomingShares.php b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php new file mode 100644 index 000000000000..e09099c818f8 --- /dev/null +++ b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php @@ -0,0 +1,132 @@ + + * + * @copyright Copyright (c) 2019, 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\FederatedFileSharing\Command; + +use OC\ServerNotAvailableException; +use OCA\Files_Sharing\AppInfo\Application; +use OCP\Files\Storage\IStorage; +use OCP\Files\StorageInvalidException; +use OCP\Files\StorageNotAvailableException; +use OCP\IDBConnection; +use OCP\IUserManager; +use OCP\Lock\LockedException; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class PollIncomingShares extends Command { + /** @var IDBConnection */ + private $dbConnection; + + /** @var IUserManager */ + private $userManager; + + /** + * PollIncomingShares constructor. + * + * @param IDBConnection $dbConnection + * @param IUserManager $userManager + */ + public function __construct(IDBConnection $dbConnection, IUserManager $userManager) { + parent::__construct(); + $this->dbConnection = $dbConnection; + $this->userManager = $userManager; + } + + protected function configure() { + $this->setName('incoming-shares:poll') + ->setDescription('Poll incoming federated shares manually to detect updates'); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * + * @return int|null|void + */ + public function execute(InputInterface $input, OutputInterface $output) { + $mountProvider = $this->getExternalMountProvider(); + $loader = $this->getLoader(); + $cursor = $this->getCursor(); + while ($data = $cursor->fetch()) { + $user = $this->userManager->get($data['user']); + $userMounts = $mountProvider->getMountsForUser($user, $loader); + /** @var \OCA\Files_Sharing\External\Mount $mount */ + foreach ($userMounts as $mount) { + /** @var Storage $storage */ + $storage = $mount->getStorage(); + $this->refreshStorageRoot($storage); + } + } + $cursor->closeCursor(); + } + + /** + * @param IStorage $storage + */ + protected function refreshStorageRoot(IStorage $storage) { + try { + $localMtime = $storage->filemtime(''); + /** @var \OCA\Files_Sharing\External\Storage $storage */ + if ($storage->hasUpdated('', $localMtime)) { + try { + $storage->getScanner('')->scan('', false, 0); + } catch (LockedException $e) { + // it can be locked, let's skip it then + } catch (ServerNotAvailableException $e) { + // remote server hasn't responded + } + } + } catch (StorageNotAvailableException $e) { + // pass + } catch (StorageInvalidException $e) { + // pass + } + } + + /** + * @return \Doctrine\DBAL\Driver\Statement + */ + protected function getCursor() { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->selectDistinct('user') + ->from('share_external') + ->where($qb->expr()->eq('accepted', $qb->expr()->literal('1'))); + + return $qb->execute(); + } + + /** + * @return \OCP\Files\Storage\IStorageFactory + */ + protected function getLoader() { + return \OC\Files\Filesystem::getLoader(); + } + + /** + * @return \OCA\Files_Sharing\External\MountProvider + */ + protected function getExternalMountProvider() { + $app = new Application(); + return $app->getContainer()->query('ExternalMountProvider'); + } +} diff --git a/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php new file mode 100644 index 000000000000..4df13976d56f --- /dev/null +++ b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php @@ -0,0 +1,79 @@ + + * + * @copyright Copyright (c) 2019, 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\FederatedFileSharing\Tests\Command; + +use Doctrine\DBAL\Driver\Statement; +use OCA\FederatedFileSharing\Tests\TestCase; +use OCA\FederatedFileSharing\Command\PollIncomingShares; +use OCP\DB\QueryBuilder\IExpressionBuilder; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Tester\CommandTester; + +/** + * Class PollIncomingSharesTest + * + * @group DB + * @package OCA\FederatedFileSharing\Tests\Command + */ +class PollIncomingSharesTest extends TestCase { + /** @var CommandTester */ + private $commandTester; + + /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject */ + private $dbConnection; + + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + private $userManager; + + protected function setUp() { + parent::setUp(); + $this->dbConnection = $this->createMock(IDBConnection::class); + $this->userManager = $this->createMock(IUserManager::class); + $command = new PollIncomingShares($this->dbConnection, $this->userManager); + $this->commandTester = new CommandTester($command); + } + + public function testNoSharesPoll() { + $uid = 'foo'; + $exprBuilder = $this->createMock(IExpressionBuilder::class); + $statementMock = $this->createMock(Statement::class); + $statementMock->method('fetch')->willReturnOnConsecutiveCalls(['user' => $uid], false); + $qbMock = $this->createMock(IQueryBuilder::class); + $qbMock->method('selectDistinct')->willReturnSelf(); + $qbMock->method('from')->willReturnSelf(); + $qbMock->method('where')->willReturnSelf(); + $qbMock->method('expr')->willReturn($exprBuilder); + $qbMock->method('execute')->willReturn($statementMock); + + $userMock = $this->createMock(IUser::class); + $this->userManager->expects($this->once())->method('get') + ->with($uid)->willReturn($userMock); + + $this->dbConnection->method('getQueryBuilder')->willReturn($qbMock); + $this->commandTester->execute([]); + $output = $this->commandTester->getDisplay(); + $this->assertEmpty($output); + } +} From 8c1e701162c27ebefb3d4b80f2d0b1ed61222344 Mon Sep 17 00:00:00 2001 From: VicDeo Date: Thu, 28 Mar 2019 18:11:12 +0300 Subject: [PATCH 2/3] Init command in appinfo --- .../lib/AppInfo/Application.php | 14 ++++ .../lib/Command/PollIncomingShares.php | 72 +++++++++---------- .../tests/Command/PollIncomingSharesTest.php | 15 +++- 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index 0ce061170b14..8bd76399140b 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -22,6 +22,7 @@ namespace OCA\FederatedFileSharing\AppInfo; use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\Command\PollIncomingShares; use OCA\FederatedFileSharing\Controller\OcmController; use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; @@ -157,6 +158,19 @@ function ($c) { return new Permissions(); } ); + + $container->registerService( + PollIncomingShares::class, + function ($c) use ($server) { + $sharingApp = new \OCA\Files_Sharing\AppInfo\Application(); + return new PollIncomingShares( + $server->getDatabaseConnection(), + $server->getUserManager(), + $sharingApp->getContainer()->query('ExternalMountProvider'), + \OC\Files\Filesystem::getLoader() + ); + } + ); } /** diff --git a/apps/federatedfilesharing/lib/Command/PollIncomingShares.php b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php index e09099c818f8..16538b2a651a 100644 --- a/apps/federatedfilesharing/lib/Command/PollIncomingShares.php +++ b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php @@ -23,7 +23,9 @@ use OC\ServerNotAvailableException; use OCA\Files_Sharing\AppInfo\Application; +use OCA\Files_Sharing\External\MountProvider; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IStorageFactory; use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; use OCP\IDBConnection; @@ -40,16 +42,30 @@ class PollIncomingShares extends Command { /** @var IUserManager */ private $userManager; + /** + * @var MountProvider + */ + private $externalMountProvider; + + /** + * @var IStorageFactory + */ + private $loader; + /** * PollIncomingShares constructor. * * @param IDBConnection $dbConnection * @param IUserManager $userManager + * @param MountProvider $externalMountProvider + * @param IStorageFactory $loader */ - public function __construct(IDBConnection $dbConnection, IUserManager $userManager) { + public function __construct(IDBConnection $dbConnection, IUserManager $userManager, MountProvider $externalMountProvider, IStorageFactory $loader) { parent::__construct(); $this->dbConnection = $dbConnection; $this->userManager = $userManager; + $this->externalMountProvider = $externalMountProvider; + $this->loader = $loader; } protected function configure() { @@ -64,17 +80,19 @@ protected function configure() { * @return int|null|void */ public function execute(InputInterface $input, OutputInterface $output) { - $mountProvider = $this->getExternalMountProvider(); - $loader = $this->getLoader(); $cursor = $this->getCursor(); while ($data = $cursor->fetch()) { $user = $this->userManager->get($data['user']); - $userMounts = $mountProvider->getMountsForUser($user, $loader); + $userMounts = $this->externalMountProvider->getMountsForUser($user, $this->loader); /** @var \OCA\Files_Sharing\External\Mount $mount */ foreach ($userMounts as $mount) { - /** @var Storage $storage */ - $storage = $mount->getStorage(); - $this->refreshStorageRoot($storage); + try { + /** @var Storage $storage */ + $storage = $mount->getStorage(); + $this->refreshStorageRoot($storage); + } catch (\Exception $e) { + $output->writeln($e->getMessage()); + } } } $cursor->closeCursor(); @@ -82,24 +100,17 @@ public function execute(InputInterface $input, OutputInterface $output) { /** * @param IStorage $storage + * + * @throws LockedException + * @throws ServerNotAvailableException + * @throws StorageInvalidException + * @throws StorageNotAvailableException */ protected function refreshStorageRoot(IStorage $storage) { - try { - $localMtime = $storage->filemtime(''); - /** @var \OCA\Files_Sharing\External\Storage $storage */ - if ($storage->hasUpdated('', $localMtime)) { - try { - $storage->getScanner('')->scan('', false, 0); - } catch (LockedException $e) { - // it can be locked, let's skip it then - } catch (ServerNotAvailableException $e) { - // remote server hasn't responded - } - } - } catch (StorageNotAvailableException $e) { - // pass - } catch (StorageInvalidException $e) { - // pass + $localMtime = $storage->filemtime(''); + /** @var \OCA\Files_Sharing\External\Storage $storage */ + if ($storage->hasUpdated('', $localMtime)) { + $storage->getScanner('')->scan('', false, 0); } } @@ -114,19 +125,4 @@ protected function getCursor() { return $qb->execute(); } - - /** - * @return \OCP\Files\Storage\IStorageFactory - */ - protected function getLoader() { - return \OC\Files\Filesystem::getLoader(); - } - - /** - * @return \OCA\Files_Sharing\External\MountProvider - */ - protected function getExternalMountProvider() { - $app = new Application(); - return $app->getContainer()->query('ExternalMountProvider'); - } } diff --git a/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php index 4df13976d56f..ad85f9644ff2 100644 --- a/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php +++ b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php @@ -24,8 +24,10 @@ use Doctrine\DBAL\Driver\Statement; use OCA\FederatedFileSharing\Tests\TestCase; use OCA\FederatedFileSharing\Command\PollIncomingShares; +use OCA\Files_Sharing\External\MountProvider; use OCP\DB\QueryBuilder\IExpressionBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\Storage\IStorageFactory; use OCP\IDBConnection; use OCP\IUser; use OCP\IUserManager; @@ -47,11 +49,19 @@ class PollIncomingSharesTest extends TestCase { /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ private $userManager; + /** @var MountProvider | \PHPUnit_Framework_MockObject_MockObject */ + private $externalMountProvider; + + /** @var IStorageFactory | \PHPUnit_Framework_MockObject_MockObject */ + private $loader; + protected function setUp() { parent::setUp(); $this->dbConnection = $this->createMock(IDBConnection::class); $this->userManager = $this->createMock(IUserManager::class); - $command = new PollIncomingShares($this->dbConnection, $this->userManager); + $this->externalMountProvider = $this->createMock(MountProvider::class); + $this->loader = $this->createMock(IStorageFactory::class); + $command = new PollIncomingShares($this->dbConnection, $this->userManager, $this->externalMountProvider, $this->loader); $this->commandTester = new CommandTester($command); } @@ -71,6 +81,9 @@ public function testNoSharesPoll() { $this->userManager->expects($this->once())->method('get') ->with($uid)->willReturn($userMock); + $this->externalMountProvider->expects($this->once())->method('getMountsForUser') + ->willReturn([]); + $this->dbConnection->method('getQueryBuilder')->willReturn($qbMock); $this->commandTester->execute([]); $output = $this->commandTester->getDisplay(); From 06b148e8f5c33edcebbcf3e93a4fd83ff6c76b44 Mon Sep 17 00:00:00 2001 From: VicDeo Date: Fri, 29 Mar 2019 14:22:39 +0300 Subject: [PATCH 3/3] Check files_sharing app presence --- .../lib/AppInfo/Application.php | 12 ++++++++--- .../lib/Command/PollIncomingShares.php | 21 +++++++++---------- .../tests/Command/PollIncomingSharesTest.php | 10 ++++++++- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index 8bd76399140b..d50dfc7c9ae5 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -162,12 +162,18 @@ function ($c) { $container->registerService( PollIncomingShares::class, function ($c) use ($server) { - $sharingApp = new \OCA\Files_Sharing\AppInfo\Application(); + if ($server->getAppManager()->isEnabledForUser('files_sharing')) { + $sharingApp = new \OCA\Files_Sharing\AppInfo\Application(); + $externalMountProvider = $sharingApp->getContainer()->query('ExternalMountProvider'); + } else { + $externalMountProvider = null; + } + return new PollIncomingShares( $server->getDatabaseConnection(), $server->getUserManager(), - $sharingApp->getContainer()->query('ExternalMountProvider'), - \OC\Files\Filesystem::getLoader() + \OC\Files\Filesystem::getLoader(), + $externalMountProvider ); } ); diff --git a/apps/federatedfilesharing/lib/Command/PollIncomingShares.php b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php index 16538b2a651a..7bf0af08cbe7 100644 --- a/apps/federatedfilesharing/lib/Command/PollIncomingShares.php +++ b/apps/federatedfilesharing/lib/Command/PollIncomingShares.php @@ -22,7 +22,6 @@ namespace OCA\FederatedFileSharing\Command; use OC\ServerNotAvailableException; -use OCA\Files_Sharing\AppInfo\Application; use OCA\Files_Sharing\External\MountProvider; use OCP\Files\Storage\IStorage; use OCP\Files\Storage\IStorageFactory; @@ -42,16 +41,12 @@ class PollIncomingShares extends Command { /** @var IUserManager */ private $userManager; - /** - * @var MountProvider - */ - private $externalMountProvider; - - /** - * @var IStorageFactory - */ + /** @var IStorageFactory */ private $loader; + /** @var MountProvider | null */ + private $externalMountProvider; + /** * PollIncomingShares constructor. * @@ -60,12 +55,12 @@ class PollIncomingShares extends Command { * @param MountProvider $externalMountProvider * @param IStorageFactory $loader */ - public function __construct(IDBConnection $dbConnection, IUserManager $userManager, MountProvider $externalMountProvider, IStorageFactory $loader) { + public function __construct(IDBConnection $dbConnection, IUserManager $userManager, IStorageFactory $loader, MountProvider $externalMountProvider = null) { parent::__construct(); $this->dbConnection = $dbConnection; $this->userManager = $userManager; - $this->externalMountProvider = $externalMountProvider; $this->loader = $loader; + $this->externalMountProvider = $externalMountProvider; } protected function configure() { @@ -80,6 +75,10 @@ protected function configure() { * @return int|null|void */ public function execute(InputInterface $input, OutputInterface $output) { + if ($this->externalMountProvider === null) { + $output->writeln("Polling is not possible when files_sharing app is disabled. Please enable it with 'occ app:enable files_sharing'"); + return 1; + } $cursor = $this->getCursor(); while ($data = $cursor->fetch()) { $user = $this->userManager->get($data['user']); diff --git a/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php index ad85f9644ff2..c281c0e5b1ed 100644 --- a/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php +++ b/apps/federatedfilesharing/tests/Command/PollIncomingSharesTest.php @@ -61,7 +61,7 @@ protected function setUp() { $this->userManager = $this->createMock(IUserManager::class); $this->externalMountProvider = $this->createMock(MountProvider::class); $this->loader = $this->createMock(IStorageFactory::class); - $command = new PollIncomingShares($this->dbConnection, $this->userManager, $this->externalMountProvider, $this->loader); + $command = new PollIncomingShares($this->dbConnection, $this->userManager, $this->loader, $this->externalMountProvider); $this->commandTester = new CommandTester($command); } @@ -89,4 +89,12 @@ public function testNoSharesPoll() { $output = $this->commandTester->getDisplay(); $this->assertEmpty($output); } + + public function testWithFilesSharingDisabled() { + $command = new PollIncomingShares($this->dbConnection, $this->userManager, $this->loader, null); + $this->commandTester = new CommandTester($command); + $this->commandTester->execute([]); + $output = $this->commandTester->getDisplay(); + $this->assertContains("Polling is not possible when files_sharing app is disabled. Please enable it with 'occ app:enable files_sharing'", $output); + } }