diff --git a/lib/private/files/node/hookconnector.php b/lib/private/files/node/hookconnector.php new file mode 100644 index 0000000000..c42a329d31 --- /dev/null +++ b/lib/private/files/node/hookconnector.php @@ -0,0 +1,151 @@ + + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see + * + */ + +namespace OC\Files\Node; + +use OC\Files\Filesystem; +use OC\Files\View; +use OCP\Files\FileInfo; +use OCP\Util; + +class HookConnector { + /** + * @var Root + */ + private $root; + + /** + * @var View + */ + private $view; + + /** + * HookConnector constructor. + * + * @param Root $root + * @param View $view + */ + public function __construct(Root $root, View $view) { + $this->root = $root; + $this->view = $view; + } + + public function viewToNode() { + Util::connectHook('OC_Filesystem', 'write', $this, 'write'); + Util::connectHook('OC_Filesystem', 'post_write', $this, 'postWrite'); + + Util::connectHook('OC_Filesystem', 'create', $this, 'create'); + Util::connectHook('OC_Filesystem', 'post_create', $this, 'postCreate'); + + Util::connectHook('OC_Filesystem', 'delete', $this, 'delete'); + Util::connectHook('OC_Filesystem', 'post_delete', $this, 'postDelete'); + + Util::connectHook('OC_Filesystem', 'rename', $this, 'rename'); + Util::connectHook('OC_Filesystem', 'post_rename', $this, 'postRename'); + + Util::connectHook('OC_Filesystem', 'copy', $this, 'copy'); + Util::connectHook('OC_Filesystem', 'post_copy', $this, 'postCopy'); + + Util::connectHook('OC_Filesystem', 'touch', $this, 'touch'); + Util::connectHook('OC_Filesystem', 'post_touch', $this, 'postTouch'); + } + + public function write($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'preWrite', [$node]); + } + + public function postWrite($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'postWrite', [$node]); + } + + public function create($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'preCreate', [$node]); + } + + public function postCreate($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'postCreate', [$node]); + } + + public function delete($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'preDelete', [$node]); + } + + public function postDelete($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'postDelete', [$node]); + } + + public function touch($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'preTouch', [$node]); + } + + public function postTouch($arguments) { + $node = $this->getNodeForPath($arguments['path']); + $this->root->emit('\OC\Files', 'postTouch', [$node]); + } + + public function rename($arguments) { + $source = $this->getNodeForPath($arguments['oldpath']); + $target = $this->getNodeForPath($arguments['newpath']); + $this->root->emit('\OC\Files', 'preRename', [$source, $target]); + } + + public function postRename($arguments) { + $source = $this->getNodeForPath($arguments['oldpath']); + $target = $this->getNodeForPath($arguments['newpath']); + $this->root->emit('\OC\Files', 'postRename', [$source, $target]); + } + + public function copy($arguments) { + $source = $this->getNodeForPath($arguments['oldpath']); + $target = $this->getNodeForPath($arguments['newpath']); + $this->root->emit('\OC\Files', 'preCopy', [$source, $target]); + } + + public function postCopy($arguments) { + $source = $this->getNodeForPath($arguments['oldpath']); + $target = $this->getNodeForPath($arguments['newpath']); + $this->root->emit('\OC\Files', 'postCopy', [$source, $target]); + } + + private function getNodeForPath($path) { + $info = Filesystem::getView()->getFileInfo($path); + if (!$info) { + $fullPath = Filesystem::getView()->getAbsolutePath($path); + if (Filesystem::is_dir($path)) { + return new NonExistingFolder($this->root, $this->view, $fullPath); + } else { + return new NonExistingFile($this->root, $this->view, $fullPath); + } + } + if ($info->getType() === FileInfo::TYPE_FILE) { + return new File($this->root, $this->view, $info->getPath(), $info); + } else { + return new Folder($this->root, $this->view, $info->getPath(), $info); + } + } +} diff --git a/lib/private/server.php b/lib/private/server.php index 26eb99927f..9055f09280 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -46,6 +46,7 @@ use OC\Diagnostics\EventLogger; use OC\Diagnostics\NullEventLogger; use OC\Diagnostics\NullQueryLogger; use OC\Diagnostics\QueryLogger; +use OC\Files\Node\HookConnector; use OC\Files\Node\Root; use OC\Files\View; use OC\Http\Client\ClientService; @@ -144,7 +145,10 @@ class Server extends SimpleContainer implements IServerContainer { $user = $userManager->get($user); $manager = \OC\Files\Filesystem::getMountManager(); $view = new View(); - return new Root($manager, $view, $user); + $root = new Root($manager, $view, $user); + $connector = new HookConnector($root, $view); + $connector->viewToNode(); + return $root; }); $this->registerService('UserManager', function (Server $c) { $config = $c->getConfig(); diff --git a/tests/lib/files/node/hookconnector.php b/tests/lib/files/node/hookconnector.php new file mode 100644 index 0000000000..10566cf5fb --- /dev/null +++ b/tests/lib/files/node/hookconnector.php @@ -0,0 +1,176 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Node; + +use OC\Files\Filesystem; +use OC\Files\Node\Root; +use OC\Files\Storage\Temporary; +use OC\Files\View; +use OCP\Files\Node; +use Test\TestCase; +use Test\Traits\MountProviderTrait; +use Test\Traits\UserTrait; + +class HookConnector extends TestCase { + use UserTrait; + use MountProviderTrait; + + /** + * @var View + */ + private $view; + + /** + * @var Root + */ + private $root; + + /** + * @var string + */ + private $userId; + + public function setUp() { + parent::setUp(); + $this->userId = $this->getUniqueID(); + $this->createUser($this->userId, 'pass'); + $this->registerMount($this->userId, new Temporary(), '/' . $this->userId . '/files/'); + \OC_Util::setupFS($this->userId); + $this->view = new View(); + $this->root = new Root(Filesystem::getMountManager(), $this->view, \OC::$server->getUserManager()->get($this->userId)); + } + + public function tearDown() { + parent::tearDown(); + \OC_Hook::clear('OC_Filesystem'); + \OC_Util::tearDownFS(); + } + + public function viewToNodeProvider() { + return [ + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'preWrite'], + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'postWrite'], + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'preCreate'], + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + }, 'postCreate'], + [function () { + Filesystem::mkdir('test.txt'); + }, 'preCreate'], + [function () { + Filesystem::mkdir('test.txt'); + }, 'postCreate'], + [function () { + Filesystem::touch('test.txt'); + }, 'preTouch'], + [function () { + Filesystem::touch('test.txt'); + }, 'postTouch'], + [function () { + Filesystem::touch('test.txt'); + }, 'preCreate'], + [function () { + Filesystem::touch('test.txt'); + }, 'postCreate'], + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + Filesystem::unlink('test.txt'); + }, 'preDelete'], + [function () { + Filesystem::file_put_contents('test.txt', 'asd'); + Filesystem::unlink('test.txt'); + }, 'postDelete'], + [function () { + Filesystem::mkdir('test.txt'); + Filesystem::rmdir('test.txt'); + }, 'preDelete'], + [function () { + Filesystem::mkdir('test.txt'); + Filesystem::rmdir('test.txt'); + }, 'postDelete'], + ]; + } + + /** + * @param callable $operation + * @param string $expectedHook + * @dataProvider viewToNodeProvider + */ + public function testViewToNode(callable $operation, $expectedHook) { + $connector = new \OC\Files\Node\HookConnector($this->root, $this->view); + $connector->viewToNode(); + $hookCalled = false; + /** @var Node $hookNode */ + $hookNode = null; + + $this->root->listen('\OC\Files', $expectedHook, function ($node) use (&$hookNode, &$hookCalled) { + $hookCalled = true; + $hookNode = $node; + }); + + $operation(); + + $this->assertTrue($hookCalled); + $this->assertEquals('/' . $this->userId . '/files/test.txt', $hookNode->getPath()); + } + + public function viewToNodeProviderCopyRename() { + return [ + [function () { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::rename('source', 'target'); + }, 'preRename'], + [function () { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::rename('source', 'target'); + }, 'postRename'], + [function () { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::copy('source', 'target'); + }, 'preCopy'], + [function () { + Filesystem::file_put_contents('source', 'asd'); + Filesystem::copy('source', 'target'); + }, 'postCopy'], + ]; + } + + /** + * @param callable $operation + * @param string $expectedHook + * @dataProvider viewToNodeProviderCopyRename + */ + public function testViewToNodeCopyRename(callable $operation, $expectedHook) { + $connector = new \OC\Files\Node\HookConnector($this->root, $this->view); + $connector->viewToNode(); + $hookCalled = false; + /** @var Node $hookSourceNode */ + $hookSourceNode = null; + /** @var Node $hookTargetNode */ + $hookTargetNode = null; + + $this->root->listen('\OC\Files', $expectedHook, function ($sourceNode, $targetNode) use (&$hookCalled, &$hookSourceNode, &$hookTargetNode) { + $hookCalled = true; + $hookSourceNode = $sourceNode; + $hookTargetNode = $targetNode; + }); + + $operation(); + + $this->assertTrue($hookCalled); + $this->assertEquals('/' . $this->userId . '/files/source', $hookSourceNode->getPath()); + $this->assertEquals('/' . $this->userId . '/files/target', $hookTargetNode->getPath()); + } +}