From a5a89ed5717ef3ce47ce7ac50ea32af89270f24c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 12 Oct 2017 12:00:57 +0200 Subject: [PATCH 1/5] Introducing Abstract base implementations for node api interface --- lib/private/Files/Node/AbstractFile.php | 72 ++++++ lib/private/Files/Node/AbstractFolder.php | 142 +++++++++++ lib/private/Files/Node/AbstractNode.php | 257 ++++++++++++++++++++ tests/lib/Files/Node/AbstractFileTest.php | 91 +++++++ tests/lib/Files/Node/AbstractFolderTest.php | 99 ++++++++ tests/lib/Files/Node/AbstractNodeTest.php | 85 +++++++ 6 files changed, 746 insertions(+) create mode 100644 lib/private/Files/Node/AbstractFile.php create mode 100644 lib/private/Files/Node/AbstractFolder.php create mode 100644 lib/private/Files/Node/AbstractNode.php create mode 100644 tests/lib/Files/Node/AbstractFileTest.php create mode 100644 tests/lib/Files/Node/AbstractFolderTest.php create mode 100644 tests/lib/Files/Node/AbstractNodeTest.php diff --git a/lib/private/Files/Node/AbstractFile.php b/lib/private/Files/Node/AbstractFile.php new file mode 100644 index 000000000000..5b6697fbc5c4 --- /dev/null +++ b/lib/private/Files/Node/AbstractFile.php @@ -0,0 +1,72 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Node; + + +use OCP\Files\FileInfo; +use OCP\Files\NotPermittedException; + +class AbstractFile extends AbstractNode implements \OCP\Files\File { + + /** + * @inheritdoc + */ + public function getContent() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function putContent($data) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function fopen($mode) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function hash($type, $raw = false) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getMimetype() { + return 'application/octet-stream'; + } + + /** + * @inheritdoc + */ + public function getType() { + return FileInfo::TYPE_FILE; + } +} diff --git a/lib/private/Files/Node/AbstractFolder.php b/lib/private/Files/Node/AbstractFolder.php new file mode 100644 index 000000000000..299f1c8eeedd --- /dev/null +++ b/lib/private/Files/Node/AbstractFolder.php @@ -0,0 +1,142 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Node; + + +use OC\Files\FileInfo; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; + +class AbstractFolder extends AbstractNode implements \OCP\Files\Folder { + + /** + * @inheritdoc + */ + public function getMimetype() { + return 'httpd/unix-directory'; + } + + /** + * @inheritdoc + */ + public function getType() { + return FileInfo::TYPE_FOLDER; + } + + /** + * @inheritdoc + */ + public function isSubNode($node) { + return strpos($node->getPath(), $this->getPath()) === 0; + } + + /** + * @inheritdoc + */ + public function getDirectoryListing() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function get($path) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function nodeExists($path) { + try { + $this->get($path); + return true; + } catch (NotFoundException $ex) { + return false; + } + } + + /** + * @inheritdoc + */ + public function newFolder($path) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function newFile($path) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function search($query) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function searchByMime($mimetype) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function searchByTag($tag, $userId) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getById($id) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getFreeSpace() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getNonExistingName($name) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getRelativePath($path) { + throw new NotPermittedException(); + } + +} diff --git a/lib/private/Files/Node/AbstractNode.php b/lib/private/Files/Node/AbstractNode.php new file mode 100644 index 000000000000..411762621fa7 --- /dev/null +++ b/lib/private/Files/Node/AbstractNode.php @@ -0,0 +1,257 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Node; + +use OCP\Files\NotPermittedException; + +abstract class AbstractNode implements \OCP\Files\Node { + + /** + * @inheritdoc + */ + public function getMimePart() { + $mime = $this->getMimetype(); + $parts = explode('/', $mime, 2); + return $parts[0]; + } + + /** + * @inheritdoc + */ + public function getFullPath($path) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getRelativePath($path) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isEncrypted() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isCreatable() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isShared() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isMounted() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getMountPoint() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getOwner() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getChecksum() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function move($targetPath) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function delete() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function copy($targetPath) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function touch($mtime = null) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getStorage() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getPath() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getInternalPath() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getId() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function stat() { + return [ + 'mtime' => $this->getMTime(), + 'size' => $this->getSize(), + ]; + } + + /** + * @inheritdoc + */ + public function getMTime() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getSize() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getEtag() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getPermissions() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isReadable() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isUpdateable() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isDeletable() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function isShareable() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getParent() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function getName() { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function lock($type) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function changeLock($targetType) { + throw new NotPermittedException(); + } + + /** + * @inheritdoc + */ + public function unlock($type) { + throw new NotPermittedException(); + } +} diff --git a/tests/lib/Files/Node/AbstractFileTest.php b/tests/lib/Files/Node/AbstractFileTest.php new file mode 100644 index 000000000000..6f1d6361512b --- /dev/null +++ b/tests/lib/Files/Node/AbstractFileTest.php @@ -0,0 +1,91 @@ + + * + * @copyright Copyright (c) 2017, 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 Test\Files\Node; + + +use OC\Files\Node\AbstractFile; +use OCP\Files\FileInfo; +use Test\TestCase; + +class AbstractFileTest extends TestCase { + + public function testMime() { + /** @var AbstractFile | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = new AbstractFile(); + $this->assertEquals('application/octet-stream', $node->getMimetype()); + $this->assertEquals('application', $node->getMimePart()); + $this->assertEquals(FileInfo::TYPE_FILE, $node->getType()); + } + + /** + * @expectedException \OCP\Files\NotPermittedException + * @dataProvider providesOperations + */ + public function testOperations($operation) { + /** @var AbstractFile | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockForAbstractClass(AbstractFile::class); + $node->$operation(''); + } + + public function providesOperations() { + return [ + ['getId'], + ['getFullPath'], + ['getRelativePath'], + ['isEncrypted'], + ['isCreatable'], + ['isShared'], + ['isMounted'], + ['getMountPoint'], + ['getOwner'], + ['getChecksum'], + ['move'], + ['delete'], + ['copy'], + ['touch'], + ['getStorage'], + ['getPath'], + ['getInternalPath'], + ['getId'], + ['stat'], + ['getMTime'], + ['getSize'], + ['getEtag'], + ['getPermissions'], + ['isReadable'], + ['isUpdateable'], + ['isDeletable'], + ['isShareable'], + ['getParent'], + ['getName'], + ['lock'], + ['changeLock'], + ['unlock'], + // file methods + ['getContent'], + ['putContent'], + ['fopen'], + ['hash'], + ]; + } +} diff --git a/tests/lib/Files/Node/AbstractFolderTest.php b/tests/lib/Files/Node/AbstractFolderTest.php new file mode 100644 index 000000000000..2235913f1aa2 --- /dev/null +++ b/tests/lib/Files/Node/AbstractFolderTest.php @@ -0,0 +1,99 @@ + + * + * @copyright Copyright (c) 2017, 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 Test\Files\Node; + + +use OC\Files\Node\AbstractFolder; +use OCP\Files\FileInfo; +use Test\TestCase; + +class AbstractFolderTest extends TestCase { + + public function testMimeAndGetType() { + /** @var AbstractFolder | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockForAbstractClass(AbstractFolder::class); + $this->assertEquals('httpd/unix-directory', $node->getMimetype()); + $this->assertEquals('httpd', $node->getMimePart()); + $this->assertEquals(FileInfo::TYPE_FOLDER, $node->getType()); + } + + /** + * @expectedException \OCP\Files\NotPermittedException + * @dataProvider providesOperations + */ + public function testOperations($operation) { + /** @var AbstractFolder | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockForAbstractClass(AbstractFolder::class); + $node->$operation('', ''); + } + + public function providesOperations() { + return [ + ['getId'], + ['getFullPath'], + ['getRelativePath'], + ['isEncrypted'], + ['isCreatable'], + ['isShared'], + ['isMounted'], + ['getMountPoint'], + ['getOwner'], + ['getChecksum'], + ['move'], + ['delete'], + ['copy'], + ['touch'], + ['getStorage'], + ['getPath'], + ['getInternalPath'], + ['getId'], + ['stat'], + ['getMTime'], + ['getSize'], + ['getEtag'], + ['getPermissions'], + ['isReadable'], + ['isUpdateable'], + ['isDeletable'], + ['isShareable'], + ['getParent'], + ['getName'], + ['lock'], + ['changeLock'], + ['unlock'], + // folder methods + ['getDirectoryListing'], + ['get'], + ['nodeExists'], + ['newFolder'], + ['newFile'], + ['search'], + ['searchByMime'], + ['searchByTag'], + ['getById'], + ['getFreeSpace'], + ['getNonExistingName'], + ['getRelativePath'], + ]; + } +} diff --git a/tests/lib/Files/Node/AbstractNodeTest.php b/tests/lib/Files/Node/AbstractNodeTest.php new file mode 100644 index 000000000000..d2de9336841a --- /dev/null +++ b/tests/lib/Files/Node/AbstractNodeTest.php @@ -0,0 +1,85 @@ + + * + * @copyright Copyright (c) 2017, 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 Test\Files\Node; + + +use OC\Files\Node\AbstractNode; +use Test\TestCase; + +class AbstractNodeTest extends TestCase { + + public function testMime() { + /** @var AbstractNode | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockForAbstractClass(AbstractNode::class); + $node->expects($this->any())->method('getMimetype')->willReturn('foo/bar'); + $this->assertEquals('foo/bar', $node->getMimetype()); + $this->assertEquals('foo', $node->getMimePart()); + } + + /** + * @expectedException \OCP\Files\NotPermittedException + * @dataProvider providesOperations + */ + public function testOperations($operation) { + /** @var AbstractNode | \PHPUnit_Framework_MockObject_MockObject $node */ + $node = $this->getMockForAbstractClass(AbstractNode::class); + $node->$operation(''); + } + + public function providesOperations() { + return [ + ['getId'], + ['getFullPath'], + ['getRelativePath'], + ['isEncrypted'], + ['isCreatable'], + ['isShared'], + ['isMounted'], + ['getMountPoint'], + ['getOwner'], + ['getChecksum'], + ['move'], + ['delete'], + ['copy'], + ['touch'], + ['getStorage'], + ['getPath'], + ['getInternalPath'], + ['getId'], + ['stat'], + ['getMTime'], + ['getSize'], + ['getEtag'], + ['getPermissions'], + ['isReadable'], + ['isUpdateable'], + ['isDeletable'], + ['isShareable'], + ['getParent'], + ['getName'], + ['lock'], + ['changeLock'], + ['unlock'], + ]; + } +} From 3169cb33b4b70c923b066d9ea01ff47e0d88828f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 12 Oct 2017 12:01:26 +0200 Subject: [PATCH 2/5] Introducing IVersionedStorage --- .../Files/Storage/IVersionedStorage.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 lib/public/Files/Storage/IVersionedStorage.php diff --git a/lib/public/Files/Storage/IVersionedStorage.php b/lib/public/Files/Storage/IVersionedStorage.php new file mode 100644 index 000000000000..9b608c5db695 --- /dev/null +++ b/lib/public/Files/Storage/IVersionedStorage.php @@ -0,0 +1,72 @@ + + * + * @copyright Copyright (c) 2017, 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 OCP\Files\Storage; + +/** + * Interface IVersionedStorage - storage layer to access version of a file + * + * @package OCP\Files\Storage + * @since 10.1.0 + */ +interface IVersionedStorage { + + /** + * List all versions for the given file + * + * @param string $internalPath + * @return array + * @since 10.1.0 + */ + public function getVersions($internalPath); + + /** + * Get one explicit version for the given file + * + * @param string $internalPath + * @param string $versionId + * @return array + * @since 10.1.0 + */ + public function getVersion($internalPath, $versionId); + + /** + * Get the content of a given version of a given file as string + * + * @param string $internalPath + * @param string $versionId + * @return string + * @since 10.1.0 + */ + public function getContentOfVersion($internalPath, $versionId); + + /** + * Restore the given version of a given file + * + * @param string $internalPath + * @param string $versionId + * @return void + * @since 10.1.0 + */ + public function restoreVersion($internalPath, $versionId); + +} From df65671bc65f0da1302725bf82cd0f444f70d391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 11 Oct 2017 13:45:39 +0200 Subject: [PATCH 3/5] Adding meta data file tree to node api --- .../BackgroundJob/GetSharedSecretTest.php | 2 +- lib/private/AvatarManager.php | 2 - lib/private/Files/Meta/MetaFileIdNode.php | 170 ++++++++++++++++++ .../Files/Meta/MetaFileVersionNode.php | 84 +++++++++ lib/private/Files/Meta/MetaRootNode.php | 159 ++++++++++++++++ .../Files/Meta/MetaVersionCollection.php | 118 ++++++++++++ lib/private/Files/Node/Root.php | 19 ++ tests/lib/AvatarManagerTest.php | 4 - tests/lib/Files/MetaFilesTest.php | 102 +++++++++++ 9 files changed, 653 insertions(+), 7 deletions(-) create mode 100644 lib/private/Files/Meta/MetaFileIdNode.php create mode 100644 lib/private/Files/Meta/MetaFileVersionNode.php create mode 100644 lib/private/Files/Meta/MetaRootNode.php create mode 100644 lib/private/Files/Meta/MetaVersionCollection.php create mode 100644 tests/lib/Files/MetaFilesTest.php diff --git a/apps/federation/tests/BackgroundJob/GetSharedSecretTest.php b/apps/federation/tests/BackgroundJob/GetSharedSecretTest.php index 6b5805943ab9..beb48138b67d 100644 --- a/apps/federation/tests/BackgroundJob/GetSharedSecretTest.php +++ b/apps/federation/tests/BackgroundJob/GetSharedSecretTest.php @@ -26,7 +26,6 @@ use OCA\Federation\BackgroundJob\GetSharedSecret; -use OCA\Files_Sharing\Tests\TestCase; use OCA\Federation\DbHandler; use OCA\Federation\TrustedServers; use OCP\AppFramework\Http; @@ -35,6 +34,7 @@ use OCP\Http\Client\IResponse; use OCP\ILogger; use OCP\IURLGenerator; +use Test\TestCase; /** * Class GetSharedSecretTest diff --git a/lib/private/AvatarManager.php b/lib/private/AvatarManager.php index 7d4ab5eb605e..ebdf9156e7c4 100644 --- a/lib/private/AvatarManager.php +++ b/lib/private/AvatarManager.php @@ -86,8 +86,6 @@ public function getAvatar($userId) { throw new \Exception('user does not exist'); } - $userId = $user->getUID(); - $avatarsFolder = $this->getAvatarFolder($user); return new Avatar($avatarsFolder, $this->l, $user, $this->logger); } diff --git a/lib/private/Files/Meta/MetaFileIdNode.php b/lib/private/Files/Meta/MetaFileIdNode.php new file mode 100644 index 000000000000..56950726906e --- /dev/null +++ b/lib/private/Files/Meta/MetaFileIdNode.php @@ -0,0 +1,170 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Meta; + +use OC\Files\Node\AbstractFolder; +use OCP\Constants; +use OCP\Files\FileInfo; +use OCP\Files\NotFoundException; + +/** + * Class MetaFileIdNode - this class represents the file id part of the meta endpoint + * + * @package OC\Files\Meta + */ +class MetaFileIdNode extends AbstractFolder { + + /** @var int */ + private $fileId; + /** @var MetaRootNode */ + private $parentNode; + + /** + * MetaFileIdNode constructor. + * + * @param MetaRootNode $parentNode + * @param int $fileId + */ + public function __construct(MetaRootNode $parentNode, $fileId) { + $this->parentNode = $parentNode; + $this->fileId = $fileId; + } + + /** + * @inheritdoc + */ + public function isEncrypted() { + return false; + } + + /** + * @inheritdoc + */ + public function isShared() { + return false; + } + + /** + * @inheritdoc + */ + public function isMounted() { + return false; + } + + /** + * @inheritdoc + */ + public function getDirectoryListing() { + return [ + new MetaVersionCollection($this->fileId) + ]; + } + + /** + * @inheritdoc + */ + public function get($path) { + $pieces = explode('/', $path); + if($pieces[0] === 'v') { + array_shift($pieces); + $node = new MetaVersionCollection($this->fileId); + if (empty($pieces)) { + return $node; + } + return $node->get(implode('/', $pieces)); + } + throw new NotFoundException(); + + } + + /** + * @inheritdoc + */ + public function getFreeSpace() { + return FileInfo::SPACE_UNKNOWN; + } + + /** + * @inheritdoc + */ + public function getPath() { + return $this->getInternalPath(); + } + + /** + * @inheritdoc + */ + public function getInternalPath() { + return "/meta/{$this->fileId}"; + } + + /** + * @inheritdoc + */ + public function getPermissions() { + return Constants::PERMISSION_READ; + } + + /** + * @inheritdoc + */ + public function isReadable() { + return true; + } + + /** + * @inheritdoc + */ + public function isUpdateable() { + return false; + } + + /** + * @inheritdoc + */ + public function isDeletable() { + return false; + } + + /** + * @inheritdoc + */ + public function isShareable() { + return false; + } + + /** + * @inheritdoc + */ + public function getParent() { + return $this->parentNode; + } + + /** + * @inheritdoc + */ + public function getName() { + return "{$this->fileId}"; + } + +} diff --git a/lib/private/Files/Meta/MetaFileVersionNode.php b/lib/private/Files/Meta/MetaFileVersionNode.php new file mode 100644 index 000000000000..419f5e557563 --- /dev/null +++ b/lib/private/Files/Meta/MetaFileVersionNode.php @@ -0,0 +1,84 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Meta; + + +use OC\Files\Node\AbstractFile; +use OC\Files\Node\File; +use OCP\Files\Storage\IVersionedStorage; +use OCP\Files\NotPermittedException; +use OCP\Files\Storage; + +/** + * Class MetaFileVersionNode - this class represents a version of a file in the + * meta endpoint + * + * @package OC\Files\Meta + */ +class MetaFileVersionNode extends AbstractFile { + + /** @var string */ + private $versionId; + /** @var MetaVersionCollection */ + private $parent; + /** @var IVersionedStorage */ + private $storage; + /** @var string */ + private $internalPath; + + /** + * MetaFileVersionNode constructor. + * + * @param MetaVersionCollection $parent + * @param string $versionId + * @param Storage $storage + * @param string $internalPath + */ + public function __construct(MetaVersionCollection $parent, + $versionId, Storage $storage, $internalPath) { + $this->parent = $parent; + $this->versionId = $versionId; + $this->storage = $storage; + $this->internalPath = $internalPath; + } + + public function getName() { + return $this->versionId; + } + + public function getContent() { + return $this->storage->getContentOfVersion($this->internalPath, $this->versionId); + } + + public function copy($targetPath) { + //TODO: inject + $target = \OC::$server->getRootFolder()->get($targetPath); + if ($target instanceof File && $target->getId() === $this->parent->getId()) { + $this->storage->restoreVersion($this->internalPath, $this->versionId); + return; + } + + // for now we only allow restoring of a version + throw new NotPermittedException(); + } +} diff --git a/lib/private/Files/Meta/MetaRootNode.php b/lib/private/Files/Meta/MetaRootNode.php new file mode 100644 index 000000000000..cf44c27afdf2 --- /dev/null +++ b/lib/private/Files/Meta/MetaRootNode.php @@ -0,0 +1,159 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Meta; + + +use OC\Files\Node\AbstractFolder; +use OCP\Constants; +use OCP\Files\FileInfo; +use OCP\Files\NotFoundException; + +/** + * Class MetaRootNode - this class represents the root node of the meta endpoint + * + * @package OC\Files\Meta + */ +class MetaRootNode extends AbstractFolder { + + /** + * @inheritdoc + */ + public function isEncrypted() { + return false; + } + + /** + * @inheritdoc + */ + public function isShared() { + return false; + } + + /** + * @inheritdoc + */ + public function isMounted() { + return false; + } + + /** + * @inheritdoc + */ + public function getDirectoryListing() { + // TODO: in debug mode we might want to return the list of all fileids + return []; + } + + /** + * @inheritdoc + */ + public function get($path) { + $pieces = explode('/', $path); + $fileId = (int)$pieces[0]; + + // check if file exists + if (empty(\OC::$server->getRootFolder()->getById($fileId))) { + throw new NotFoundException(); + } + + array_shift($pieces); + $node = new MetaFileIdNode($this, $fileId); + if (empty($pieces)) { + return $node; + } + return $node->get(implode('/', $pieces)); + } + + /** + * @inheritdoc + */ + public function getById($id) { + return [ + $this->get("$id") + ]; + } + + /** + * @inheritdoc + */ + public function getFreeSpace() { + return FileInfo::SPACE_UNKNOWN; + } + + /** + * @inheritdoc + */ + public function isCreatable() { + return false; + } + + /** + * @inheritdoc + */ + public function getPath() { + return $this->getName(); + } + + /** + * @inheritdoc + */ + public function getPermissions() { + return Constants::PERMISSION_READ; + } + + /** + * @inheritdoc + */ + public function isReadable() { + // TODO: false if not debug + return true; + } + + /** + * @inheritdoc + */ + public function isUpdateable() { + return false; + } + + /** + * @inheritdoc + */ + public function isDeletable() { + return false; + } + + /** + * @inheritdoc + */ + public function isShareable() { + return false; + } + + /** + * @inheritdoc + */ + public function getName() { + return 'meta'; + } +} diff --git a/lib/private/Files/Meta/MetaVersionCollection.php b/lib/private/Files/Meta/MetaVersionCollection.php new file mode 100644 index 000000000000..a11da2c43d68 --- /dev/null +++ b/lib/private/Files/Meta/MetaVersionCollection.php @@ -0,0 +1,118 @@ + + * + * @copyright Copyright (c) 2017, 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 OC\Files\Meta; + + +use OC\Files\Node\AbstractFolder; +use OCP\Files\Storage\IVersionedStorage; +use OC\Files\View; +use OCP\Files\NotFoundException; +use OCP\Files\Storage; + +/** + * Class MetaVersionCollection - this class represents the versions sub folder + * of a file + * + * @package OC\Files\Meta + */ +class MetaVersionCollection extends AbstractFolder { + + /** @var int */ + private $fileId; + + /** + * MetaVersionCollection constructor. + * + * @param int $fileId + */ + public function __construct($fileId) { + $this->fileId = $fileId; + } + + /** + * @inheritdoc + */ + public function isEncrypted() { + return false; + } + + /** + * @inheritdoc + */ + public function isShared() { + return false; + } + + /** + * @inheritdoc + */ + public function getDirectoryListing() { + $view = new View(); + $path = $view->getPath($this->fileId); + /** @var Storage $storage */ + list($storage, $internalPath) = $view->resolvePath($path); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { + return []; + } + /** @var IVersionedStorage | Storage $storage */ + $versions = $storage->getVersions($internalPath); + return array_map(function($version) use ($storage, $internalPath) { + return new MetaFileVersionNode($this, $version['version'],$storage, $internalPath); + }, $versions); + } + + /** + * @inheritdoc + */ + public function get($path) { + $pieces = explode('/', $path); + if (count($pieces) !== 1) { + throw new NotFoundException(); + } + $versionId = $pieces[0]; + $view = new View(); + $path = $view->getPath($this->fileId); + /** @var Storage $storage */ + list($storage, $internalPath) = $view->resolvePath($path); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { + throw new NotFoundException(); + } + /** @var IVersionedStorage | Storage $storage */ + $version = $storage->getVersion($internalPath, $versionId); + if ($version === null) { + throw new NotFoundException(); + } + return new MetaFileVersionNode($this, $version['version'], $storage, $internalPath); + } + + /** + * @inheritdoc + */ + public function getId() { + return $this->fileId; + } + + public function getName() { + return "v"; + } +} diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index b9116580386a..9a0072fcf95c 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -29,6 +29,7 @@ namespace OC\Files\Node; +use OC\Files\Meta\MetaRootNode; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; use OCP\Files\NotFoundException; @@ -179,6 +180,10 @@ public function get($path) { $path = $this->normalizePath($path); if ($this->isValidPath($path)) { $fullPath = $this->getFullPath($path); + $virtualNode = $this->resolveVirtualNode($fullPath); + if ($virtualNode !== null) { + return $virtualNode; + } $fileInfo = $this->view->getFileInfo($fullPath); if ($fileInfo) { return $this->createNode($fullPath, $fileInfo); @@ -365,4 +370,18 @@ public function getUserFolder($userId) { return $folder; } + + private function resolveVirtualNode($fullPath) { + $pieces = explode('/', $fullPath); + if ($pieces[1] !== 'meta') { + return null; + } + array_shift($pieces); + array_shift($pieces); + $node = new MetaRootNode(); + if (empty($pieces)) { + return $node; + } + return $node->get(implode('/', $pieces)); + } } diff --git a/tests/lib/AvatarManagerTest.php b/tests/lib/AvatarManagerTest.php index e9b2e69a6bb6..ccff5fd24c99 100644 --- a/tests/lib/AvatarManagerTest.php +++ b/tests/lib/AvatarManagerTest.php @@ -113,10 +113,6 @@ public function testGetAvatarValidUserDifferentCasing() { ->with('vaLid-USER') ->willReturn($user); - $user->expects($this->once()) - ->method('getUID') - ->willReturn('valid-user'); - $folder = $this->createMock(Folder::class); $this->avatarManager->expects($this->once()) ->method('getAvatarFolder') diff --git a/tests/lib/Files/MetaFilesTest.php b/tests/lib/Files/MetaFilesTest.php new file mode 100644 index 000000000000..2f02c78642d8 --- /dev/null +++ b/tests/lib/Files/MetaFilesTest.php @@ -0,0 +1,102 @@ + + * + * @copyright Copyright (c) 2017, 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 Test\Files; + + +use OC\Files\Meta\MetaFileIdNode; +use OC\Files\Meta\MetaFileVersionNode; +use OC\Files\Meta\MetaRootNode; +use OC\Files\Meta\MetaVersionCollection; +use OC\Files\View; +use OCA\Files_Versions\Hooks; +use OCP\Files\Folder; +use Test\TestCase; +use Test\Traits\UserTrait; + +/** + * Class MetaFilesTest + * + * @package Test\Files + * @group DB + */ +class MetaFilesTest extends TestCase { + use UserTrait; + + protected function tearDown() { + self::logout(); + parent::tearDown(); + } + + public function testMetaInNodeAPI() { + // workaround: re-setup versions hooks + Hooks::connectHooks(); + + // create user + $userId = 'meta-data-user'; + $this->createUser($userId); + $this->loginAsUser($userId); + + // create file + $fileName = "$userId/files/" . $this->getUniqueID('file') . '.txt'; + $view = new View(); + $view->file_put_contents($fileName, '1234'); + $info = $view->getFileInfo($fileName); + + // work on node api + /** @var Folder $metaNodeOfFile */ + $metaNodeOfFile = \OC::$server->getRootFolder()->get("meta"); + $this->assertInstanceOf(MetaRootNode::class, $metaNodeOfFile); + $this->assertEquals([], $metaNodeOfFile->getDirectoryListing()); + + $metaNodeOfFile = \OC::$server->getRootFolder()->get("meta/{$info->getId()}"); + $this->assertInstanceOf(MetaFileIdNode::class, $metaNodeOfFile); + $children = $metaNodeOfFile->getDirectoryListing(); + $this->assertEquals(1, count($children)); + $this->assertInstanceOf(MetaVersionCollection::class, $children[0]); + + $metaNodeOfFile = \OC::$server->getRootFolder()->get("meta/{$info->getId()}/v"); + $this->assertInstanceOf(MetaVersionCollection::class, $metaNodeOfFile); + $children = $metaNodeOfFile->getDirectoryListing(); + $this->assertEquals(0, count($children)); + + // write again to get another version + $view->file_put_contents($fileName, '1234567890'); + $children = $metaNodeOfFile->getDirectoryListing(); + $this->assertEquals(1, count($children)); + $this->assertInstanceOf(MetaFileVersionNode::class, $children[0]); + + $versionId = $children[0]->getName(); + $metaNodeOfFile = \OC::$server->getRootFolder()->get("meta/{$info->getId()}/v/$versionId"); + $this->assertInstanceOf(MetaFileVersionNode::class, $metaNodeOfFile); + $this->assertEquals($versionId, $metaNodeOfFile->getName()); + /** @var MetaFileVersionNode $metaNodeOfFile */ + $this->assertEquals('1234', $metaNodeOfFile->getContent()); + + // restore a version using move + $target = \OC::$server->getRootFolder()->get($fileName); + $this->assertEquals('1234567890', $target->getContent()); + $metaNodeOfFile->copy($fileName); + $this->assertEquals('1234', $target->getContent()); + + } +} From 9da7bf9eefff470784b835e8b563254c6a88c928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Thu, 12 Oct 2017 13:59:09 +0200 Subject: [PATCH 4/5] Minimalistic implementation of versions integration - needs change in the future --- apps/files_versions/lib/Storage.php | 1 + lib/private/Files/Storage/Common.php | 69 ++++++++++++++++++++++++---- lib/private/Files/Storage/Local.php | 6 +-- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/apps/files_versions/lib/Storage.php b/apps/files_versions/lib/Storage.php index 50b3c5522856..d8215b66da8c 100644 --- a/apps/files_versions/lib/Storage.php +++ b/apps/files_versions/lib/Storage.php @@ -461,6 +461,7 @@ public static function getVersions($uid, $filename, $userFullPath = '') { $versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename); $versions[$key]['name'] = $versionedFile; $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName); + $versions[$key]['storage_location'] = "$dir/$entryName"; } } } diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 1c56606f9610..072b324152a7 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -44,11 +44,14 @@ use OC\Files\Cache\Updater; use OC\Files\Filesystem; use OC\Files\Cache\Watcher; +use OCP\Constants; +use OCP\Files\FileInfo; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; use OCP\Files\InvalidPathException; use OCP\Files\ReservedWordException; use OCP\Files\Storage\ILockingStorage; +use OCP\Files\Storage\IVersionedStorage; use OCP\Lock\ILockingProvider; /** @@ -62,7 +65,7 @@ * Some \OC\Files\Storage\Common methods call functions which are first defined * in classes which extend it, e.g. $this->stat() . */ -abstract class Common implements Storage, ILockingStorage { +abstract class Common implements Storage, ILockingStorage, IVersionedStorage { use LocalTempFileTrait; @@ -151,19 +154,19 @@ public function isSharable($path) { public function getPermissions($path) { $permissions = 0; if ($this->isCreatable($path)) { - $permissions |= \OCP\Constants::PERMISSION_CREATE; + $permissions |= Constants::PERMISSION_CREATE; } if ($this->isReadable($path)) { - $permissions |= \OCP\Constants::PERMISSION_READ; + $permissions |= Constants::PERMISSION_READ; } if ($this->isUpdatable($path)) { - $permissions |= \OCP\Constants::PERMISSION_UPDATE; + $permissions |= Constants::PERMISSION_UPDATE; } if ($this->isDeletable($path)) { - $permissions |= \OCP\Constants::PERMISSION_DELETE; + $permissions |= Constants::PERMISSION_DELETE; } if ($this->isSharable($path)) { - $permissions |= \OCP\Constants::PERMISSION_SHARE; + $permissions |= Constants::PERMISSION_SHARE; } return $permissions; } @@ -259,7 +262,7 @@ private function addLocalFolder($path, $target) { $dh = $this->opendir($path); if (is_resource($dh)) { while (($file = readdir($dh)) !== false) { - if (!\OC\Files\Filesystem::isIgnoredDir($file)) { + if (!Filesystem::isIgnoredDir($file)) { if ($this->is_dir($path . '/' . $file)) { mkdir($target . '/' . $file); $this->addLocalFolder($path . '/' . $file, $target . '/' . $file); @@ -282,7 +285,7 @@ protected function searchInDir($query, $dir = '') { $dh = $this->opendir($dir); if (is_resource($dh)) { while (($item = readdir($dh)) !== false) { - if (\OC\Files\Filesystem::isIgnoredDir($item)) continue; + if (Filesystem::isIgnoredDir($item)) continue; if (strstr(strtolower($item), strtolower($query)) !== false) { $files[] = $dir . '/' . $item; } @@ -446,7 +449,7 @@ public function test() { * @return int|false */ public function free_space($path) { - return \OCP\Files\FileInfo::SPACE_UNKNOWN; + return FileInfo::SPACE_UNKNOWN; } /** @@ -620,7 +623,7 @@ public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceIntern */ public function getMetaData($path) { $permissions = $this->getPermissions($path); - if (!$permissions & \OCP\Constants::PERMISSION_READ) { + if (!$permissions & Constants::PERMISSION_READ) { //can't read, nothing we can do return null; } @@ -684,4 +687,50 @@ public function getAvailability() { public function setAvailability($isAvailable) { $this->getStorageCache()->setAvailability($isAvailable); } + public function getVersions($internalPath) { + // KISS implementation + if (!\OC_App::isEnabled('files_versions')) { + return []; + } + $p = $this->convertInternalPathToGlobalPath($internalPath); + + return array_values( + \OCA\Files_Versions\Storage::getVersions($this->getOwner($internalPath), $p)); + } + + public function getVersion($internalPath, $versionId) { + $versions = $this->getVersions($internalPath); + $versions = array_filter($versions, function ($version) use($versionId){ + return $version['version'] === $versionId; + }); + return array_shift($versions); + } + + public function getContentOfVersion($internalPath, $versionId) { + $v = $this->getVersion($internalPath, $versionId); + return $this->file_get_contents($v['storage_location']); + } + + public function restoreVersion($internalPath, $versionId) { + // KISS implementation + if (!\OC_App::isEnabled('files_versions')) { + return; + } + $p = $this->convertInternalPathToGlobalPath($internalPath); + \OCA\Files_Versions\Storage::rollback($p, $versionId); + } + + /** + * @param $internalPath + * @return array|string + */ + private function convertInternalPathToGlobalPath($internalPath) { + $mount = \OC::$server->getMountManager()->findByStorageId($this->getId()); + $p = $mount[0]->getMountPoint() . $internalPath; + $p = explode('/', ltrim($p, '/')); + array_shift($p); + array_shift($p); + $p = implode('/', $p); + return $p; + } } diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index d1cc475225b5..180d354e9f08 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -41,7 +41,7 @@ /** * for local filestore, we only have to map the paths */ -class Local extends \OC\Files\Storage\Common { +class Local extends Common { protected $datadir; protected $dataDirLength; @@ -412,7 +412,7 @@ public function getETag($path) { * @param string $targetInternalPath * @return bool */ - public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { + public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) { if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) { /** * @var \OC\Files\Storage\Local $sourceStorage @@ -420,7 +420,7 @@ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceIntern $rootStorage = new Local(['datadir' => '/']); return $rootStorage->copy($sourceStorage->getSourcePath($sourceInternalPath), $this->getSourcePath($targetInternalPath)); } else { - return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); + return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime); } } From a9c228d3488791dc941af0c6b05b693b485a2833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 18 Oct 2017 12:28:05 +0200 Subject: [PATCH 5/5] Adding dav layer on top of the MetaNodes --- apps/dav/lib/Meta/MetaFile.php | 55 +++++++++++++++++++++ apps/dav/lib/Meta/MetaFolder.php | 72 ++++++++++++++++++++++++++++ apps/dav/lib/Meta/RootCollection.php | 68 ++++++++++++++++++++++++++ apps/dav/lib/RootCollection.php | 3 +- 4 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 apps/dav/lib/Meta/MetaFile.php create mode 100644 apps/dav/lib/Meta/MetaFolder.php create mode 100644 apps/dav/lib/Meta/RootCollection.php diff --git a/apps/dav/lib/Meta/MetaFile.php b/apps/dav/lib/Meta/MetaFile.php new file mode 100644 index 000000000000..8172dc316b53 --- /dev/null +++ b/apps/dav/lib/Meta/MetaFile.php @@ -0,0 +1,55 @@ + + * + * @copyright Copyright (c) 2017, 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\Meta; + + +use OCP\Files\Folder; +use Sabre\DAV\Collection; +use Sabre\DAV\File; + +class MetaFile extends File { + + /** @var \OCP\Files\File */ + private $file; + + /** + * MetaFolder constructor. + * + * @param \OCP\Files\File $file + */ + public function __construct(\OCP\Files\File $file) { + $this->file = $file; + } + + /** + * @inheritdoc + */ + function getName() { + return $this->file->getName(); + } + + public function get() { + // FIXME: use fopen and return the stream + return $this->file->getContent(); + } +} diff --git a/apps/dav/lib/Meta/MetaFolder.php b/apps/dav/lib/Meta/MetaFolder.php new file mode 100644 index 000000000000..46082d69a777 --- /dev/null +++ b/apps/dav/lib/Meta/MetaFolder.php @@ -0,0 +1,72 @@ + + * + * @copyright Copyright (c) 2017, 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\Meta; + + +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\Node; +use Sabre\DAV\Collection; + +class MetaFolder extends Collection { + + /** @var Folder */ + private $folder; + + /** + * MetaFolder constructor. + * + * @param Folder $folder + */ + public function __construct(Folder $folder) { + $this->folder = $folder; + } + + /** + * @inheritdoc + */ + function getChildren() { + $nodes = $this->folder->getDirectoryListing(); + return array_map(function($node) { + return static::nodeFactory($node); + }, $nodes); + } + + /** + * @inheritdoc + */ + function getName() { + return $this->folder->getName(); + } + + public static function nodeFactory(Node $node) { + if ($node instanceof Folder) { + return new MetaFolder($node); + } + if ($node instanceof File) { + return new MetaFile($node); + } + throw new \InvalidArgumentException(); + } + +} diff --git a/apps/dav/lib/Meta/RootCollection.php b/apps/dav/lib/Meta/RootCollection.php new file mode 100644 index 000000000000..9e287bfe7ca3 --- /dev/null +++ b/apps/dav/lib/Meta/RootCollection.php @@ -0,0 +1,68 @@ + + * + * @copyright Copyright (c) 2017, 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\Meta; + + +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use Sabre\DAV\Collection; +use Sabre\DAV\Exception\MethodNotAllowed; + +class RootCollection extends Collection { + + /** @var IRootFolder */ + private $rootFolder; + + /** + * RootCollection constructor. + * + * @param IRootFolder $rootFolder + */ + public function __construct(IRootFolder $rootFolder) { + $this->rootFolder = $rootFolder; + } + + /** + * @inheritdoc + */ + public function getChild($name) { + $child = $this->rootFolder->get("meta/$name"); + return MetaFolder::nodeFactory($child); + } + + /** + * @inheritdoc + */ + function getChildren() { + throw new MethodNotAllowed('Listing members of this collection is disabled'); + } + + /** + * @inheritdoc + */ + function getName() { + return 'meta'; + } +} diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 8965c0c92550..15679220cc32 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -106,7 +106,8 @@ public function __construct() { $systemTagCollection, $systemTagRelationsCollection, $uploadCollection, - $avatarCollection + $avatarCollection, + new \OCA\DAV\Meta\RootCollection(\OC::$server->getRootFolder()) ]; parent::__construct('root', $children);