summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Appelman <icewind@owncloud.com>2016-07-14 16:00:57 +0200
committerRobin Appelman <icewind@owncloud.com>2016-07-14 16:00:57 +0200
commite34ee9099dba69a7d198022ca87ccf3ed4c590c0 (patch)
tree85a0e78cd358c35df5978a22d7002ad2830ae060
parente1fa314b18b8606ddfe219ee66b43e586b7d8651 (diff)
downloadnextcloud-server-e34ee9099dba69a7d198022ca87ccf3ed4c590c0.tar.gz
nextcloud-server-e34ee9099dba69a7d198022ca87ccf3ed4c590c0.zip
add files_external:notify command
-rw-r--r--apps/files_external/appinfo/register_command.php3
-rw-r--r--apps/files_external/lib/Command/Notify.php173
2 files changed, 176 insertions, 0 deletions
diff --git a/apps/files_external/appinfo/register_command.php b/apps/files_external/appinfo/register_command.php
index a5163f96d54..629970722de 100644
--- a/apps/files_external/appinfo/register_command.php
+++ b/apps/files_external/appinfo/register_command.php
@@ -31,6 +31,7 @@ use OCA\Files_External\Command\Delete;
use OCA\Files_External\Command\Create;
use OCA\Files_External\Command\Backends;
use OCA\Files_External\Command\Verify;
+use OCA\Files_External\Command\Notify;
$userManager = OC::$server->getUserManager();
$userSession = OC::$server->getUserSession();
@@ -42,6 +43,7 @@ $globalStorageService = $app->getContainer()->query('\OCA\Files_External\Service
$userStorageService = $app->getContainer()->query('\OCA\Files_External\Service\UserStoragesService');
$importLegacyStorageService = $app->getContainer()->query('\OCA\Files_External\Service\ImportLegacyStoragesService');
$backendService = $app->getContainer()->query('OCA\Files_External\Service\BackendService');
+$connection = $app->getContainer()->getServer()->getDatabaseConnection();
/** @var Symfony\Component\Console\Application $application */
$application->add(new ListCommand($globalStorageService, $userStorageService, $userSession, $userManager));
@@ -54,3 +56,4 @@ $application->add(new Delete($globalStorageService, $userStorageService, $userSe
$application->add(new Create($globalStorageService, $userStorageService, $userManager, $userSession, $backendService));
$application->add(new Backends($backendService));
$application->add(new Verify($globalStorageService));
+$application->add(new Notify($globalStorageService, $connection));
diff --git a/apps/files_external/lib/Command/Notify.php b/apps/files_external/lib/Command/Notify.php
new file mode 100644
index 00000000000..63d027b6778
--- /dev/null
+++ b/apps/files_external/lib/Command/Notify.php
@@ -0,0 +1,173 @@
+<?php
+/**
+ * @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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 (at your option) 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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Files_External\Command;
+
+use OC\Core\Command\Base;
+use OCA\Files_External\Lib\InsufficientDataForMeaningfulAnswerException;
+use OCA\Files_External\Lib\StorageConfig;
+use OCA\Files_External\Service\GlobalStoragesService;
+use OCP\Files\Storage\INotifyStorage;
+use OCP\Files\StorageNotAvailableException;
+use OCP\IDBConnection;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Notify extends Base {
+ /** @var GlobalStoragesService */
+ private $globalService;
+ /** @var IDBConnection */
+ private $connection;
+ /** @var \OCP\DB\QueryBuilder\IQueryBuilder */
+ private $updateQuery;
+
+ function __construct(GlobalStoragesService $globalService, IDBConnection $connection) {
+ parent::__construct();
+ $this->globalService = $globalService;
+ $this->connection = $connection;
+ // the query builder doesn't really like subqueries with parameters
+ $this->updateQuery = $this->connection->prepare(
+ 'UPDATE *PREFIX*filecache SET size = -1
+ WHERE `path` = ?
+ AND `storage` IN (SELECT storage_id FROM *PREFIX*mounts WHERE mount_id = ?)'
+ );
+ }
+
+ protected function configure() {
+ $this
+ ->setName('files_external:notify')
+ ->setDescription('Listen for active update notifications for a configured external mount')
+ ->addArgument(
+ 'mount_id',
+ InputArgument::REQUIRED,
+ 'the mount id of the mount to listen to'
+ )->addOption(
+ 'user',
+ 'u',
+ InputOption::VALUE_REQUIRED,
+ 'The username for the remote mount (required only for some mount configuration that don\'t store credentials)'
+ )->addOption(
+ 'password',
+ 'p',
+ InputOption::VALUE_REQUIRED,
+ 'The password for the remote mount (required only for some mount configuration that don\'t store credentials)'
+ )->addOption(
+ 'path',
+ null,
+ InputOption::VALUE_REQUIRED,
+ 'The directory in the storage to listen for updates in',
+ '/'
+ );
+ parent::configure();
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $mount = $this->globalService->getStorage($input->getArgument('mount_id'));
+ if (is_null($mount)) {
+ $output->writeln('<error>Mount not found</error>');
+ return 1;
+ }
+ $noAuth = false;
+ try {
+ $authBackend = $mount->getAuthMechanism();
+ $authBackend->manipulateStorageConfig($mount);
+ } catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $noAuth = true;
+ } catch (StorageNotAvailableException $e) {
+ $noAuth = true;
+ }
+
+ if ($input->getOption('user')) {
+ $mount->setBackendOption('user', $input->getOption('user'));
+ }
+ if ($input->getOption('password')) {
+ $mount->setBackendOption('password', $input->getOption('password'));
+ }
+
+ try {
+ $storage = $this->createStorage($mount);
+ } catch (\Exception $e) {
+ $output->writeln('<error>Error while trying to create storage</error>');
+ if ($noAuth) {
+ $output->writeln('<error>Username and/or password required</error>');
+ }
+ return 1;
+ }
+ if (!$storage instanceof INotifyStorage) {
+ $output->writeln('<error>Mount of type "' . $mount->getBackend()->getText() . '" does not support active update notifications</error>');
+ return 1;
+ }
+
+ $verbose = $input->getOption('verbose');
+
+ $path = trim($input->getOption('path'), '/');
+ $storage->listen($path, function ($type, $path, $renameTarget) use ($mount, $verbose, $output) {
+ if ($verbose) {
+ $this->logUpdate($type, $path, $renameTarget, $output);
+ }
+ if ($type == INotifyStorage::NOTIFY_RENAMED) {
+ $this->markParentAsOutdated($mount->getId(), $renameTarget);
+ }
+ $this->markParentAsOutdated($mount->getId(), $path);
+ });
+ }
+
+ private function createStorage(StorageConfig $mount) {
+ $class = $mount->getBackend()->getStorageClass();
+ return new $class($mount->getBackendOptions());
+ }
+
+ private function markParentAsOutdated($mountId, $path) {
+ $parent = dirname($path);
+ if ($parent === '.') {
+ $parent = '';
+ }
+ $this->updateQuery->execute([$parent, $mountId]);
+ }
+
+ private function logUpdate($type, $path, $renameTarget, OutputInterface $output) {
+ switch ($type) {
+ case INotifyStorage::NOTIFY_ADDED:
+ $text = 'added';
+ break;
+ case INotifyStorage::NOTIFY_MODIFIED:
+ $text = 'modified';
+ break;
+ case INotifyStorage::NOTIFY_REMOVED:
+ $text = 'removed';
+ break;
+ case INotifyStorage::NOTIFY_RENAMED:
+ $text = 'renamed';
+ break;
+ default:
+ return;
+ }
+
+ $text .= ' ' . $path;
+ if ($type === INotifyStorage::NOTIFY_RENAMED) {
+ $text .= ' to ' . $renameTarget;
+ }
+
+ $output->writeln($text);
+ }
+}