From 3039681803225918d156c13547250922fe8055ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Wed, 10 Oct 2018 14:50:58 +0200 Subject: [PATCH 1/7] Reorganize models and adjust code accordingly --- lib/Exporter/MetadataExtractor.php | 8 ++-- .../MetadataExtractor/FilesExtractor.php | 2 +- .../PreferencesExtractor.php | 2 +- .../MetadataExtractor/SharesExtractor.php | 2 +- .../MetadataExtractor/UserExtractor.php | 2 +- lib/Importer.php | 6 +-- lib/Importer/FilesImporter.php | 2 +- lib/Importer/MetadataImporter.php | 6 +-- .../MetadataImporter/PreferencesImporter.php | 2 +- .../MetadataImporter/ShareImporter.php | 2 +- .../MetadataImporter/UserImporter.php | 2 +- lib/Model/AbstractModel.php | 46 +++++++++++++++++++ lib/Model/{Metadata.php => UserMetadata.php} | 18 +++++--- lib/Model/{ => UserMetadata}/User.php | 20 ++++++-- lib/Model/{ => UserMetadata}/User/File.php | 6 ++- .../{ => UserMetadata}/User/Preference.php | 6 ++- lib/Model/{ => UserMetadata}/User/Share.php | 6 ++- lib/Serializer.php | 5 +- .../MetadataExtractor/FilesExtractorTest.php | 2 +- .../PreferencesExtractorTest.php | 2 +- .../MetadataExtractor/SharesExtractorTest.php | 2 +- .../MetadataImporter/ShareImporterTest.php | 2 +- 22 files changed, 110 insertions(+), 41 deletions(-) create mode 100644 lib/Model/AbstractModel.php rename lib/Model/{Metadata.php => UserMetadata.php} (81%) rename lib/Model/{ => UserMetadata}/User.php (88%) rename lib/Model/{ => UserMetadata}/User/File.php (94%) rename lib/Model/{ => UserMetadata}/User/Preference.php (92%) rename lib/Model/{ => UserMetadata}/User/Share.php (97%) diff --git a/lib/Exporter/MetadataExtractor.php b/lib/Exporter/MetadataExtractor.php index 44d103c..506faa1 100644 --- a/lib/Exporter/MetadataExtractor.php +++ b/lib/Exporter/MetadataExtractor.php @@ -27,7 +27,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor\PreferencesExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\UserExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\SharesExtractor; -use OCA\DataExporter\Model\Metadata; +use OCA\DataExporter\Model\UserMetadata; use OCP\IURLGenerator; /** @@ -76,11 +76,11 @@ public function __construct( * Extract all metadata required for export in to the database * * @param string $uid - * @return Metadata + * @return UserMetadata * @throws \Exception * @throws \RuntimeException if user can not be read */ - public function extract(string $uid) : Metadata { + public function extract(string $uid) : UserMetadata { $user = $this->userExtractor->extract($uid); $user->setPreferences( $this->preferencesExtractor->extract($uid) @@ -90,7 +90,7 @@ public function extract(string $uid) : Metadata { $this->sharesExtractor->extract($uid) ); - $metadata = new Metadata(); + $metadata = new UserMetadata(); $metadata->setDate(new \DateTimeImmutable()) ->setUser($user) ->setOriginServer($this->urlGenerator->getAbsoluteURL('/')); diff --git a/lib/Exporter/MetadataExtractor/FilesExtractor.php b/lib/Exporter/MetadataExtractor/FilesExtractor.php index 3f8b894..9d3a4d0 100644 --- a/lib/Exporter/MetadataExtractor/FilesExtractor.php +++ b/lib/Exporter/MetadataExtractor/FilesExtractor.php @@ -24,7 +24,7 @@ namespace OCA\DataExporter\Exporter\MetadataExtractor; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use OCA\DataExporter\Model\User\File; +use OCA\DataExporter\Model\UserMetadata\User\File; use OCP\Files\Node; class FilesExtractor { diff --git a/lib/Exporter/MetadataExtractor/PreferencesExtractor.php b/lib/Exporter/MetadataExtractor/PreferencesExtractor.php index f354dc7..7501672 100644 --- a/lib/Exporter/MetadataExtractor/PreferencesExtractor.php +++ b/lib/Exporter/MetadataExtractor/PreferencesExtractor.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Exporter\MetadataExtractor; -use OCA\DataExporter\Model\User\Preference; +use OCA\DataExporter\Model\UserMetadata\User\Preference; use OCP\IAppConfig; use OCP\IConfig; diff --git a/lib/Exporter/MetadataExtractor/SharesExtractor.php b/lib/Exporter/MetadataExtractor/SharesExtractor.php index 1b442ea..de53104 100644 --- a/lib/Exporter/MetadataExtractor/SharesExtractor.php +++ b/lib/Exporter/MetadataExtractor/SharesExtractor.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Exporter\MetadataExtractor; -use OCA\DataExporter\Model\User\Share; +use OCA\DataExporter\Model\UserMetadata\User\Share; use OCP\Share\IManager; use OCP\Share as ShareConstants; use OCP\Files\IRootFolder; diff --git a/lib/Exporter/MetadataExtractor/UserExtractor.php b/lib/Exporter/MetadataExtractor/UserExtractor.php index bda6d97..739871c 100644 --- a/lib/Exporter/MetadataExtractor/UserExtractor.php +++ b/lib/Exporter/MetadataExtractor/UserExtractor.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Exporter\MetadataExtractor; -use OCA\DataExporter\Model\User; +use OCA\DataExporter\Model\UserMetadata\User; use OCP\IGroupManager; use OCP\IUserManager; diff --git a/lib/Importer.php b/lib/Importer.php index 37a9acf..09697a6 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -25,7 +25,7 @@ use OCA\DataExporter\Importer\ImportException; use OCA\DataExporter\Importer\MetadataImporter; -use OCA\DataExporter\Model\Metadata; +use OCA\DataExporter\Model\UserMetadata; use Symfony\Component\Filesystem\Filesystem; use OCA\DataExporter\Importer\FilesImporter; use OCA\DataExporter\Importer\MetadataImporter\ShareImporter; @@ -73,10 +73,10 @@ public function import(string $pathToExportDir, $alias = null) { throw new ImportException("metadata.json not found in '$metaDataPath'"); } - /** @var Metadata $metadata */ + /** @var UserMetadata $metadata */ $metadata = $this->serializer->deserialize( \file_get_contents($metaDataPath), - Metadata::class + UserMetadata::class ); if ($alias) { diff --git a/lib/Importer/FilesImporter.php b/lib/Importer/FilesImporter.php index c707799..c438c16 100644 --- a/lib/Importer/FilesImporter.php +++ b/lib/Importer/FilesImporter.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Importer; -use OCA\DataExporter\Model\User\File; +use OCA\DataExporter\Model\UserMetadata\User\File; use OCP\Files\IRootFolder; use Symfony\Component\Filesystem\Filesystem; diff --git a/lib/Importer/MetadataImporter.php b/lib/Importer/MetadataImporter.php index d67cdd5..05320c0 100644 --- a/lib/Importer/MetadataImporter.php +++ b/lib/Importer/MetadataImporter.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Importer\MetadataImporter\PreferencesImporter; use OCA\DataExporter\Importer\MetadataImporter\UserImporter; -use OCA\DataExporter\Model\Metadata; +use OCA\DataExporter\Model\UserMetadata; class MetadataImporter { @@ -39,10 +39,10 @@ public function __construct(UserImporter $userImporter, PreferencesImporter $pre } /** - * @param Metadata $metadata + * @param UserMetadata $metadata * @throws \OCP\PreConditionNotMetException */ - public function import(Metadata $metadata) { + public function import(UserMetadata $metadata) { $user = $metadata->getUser(); $this->userImporter->import($user); $this->preferencesImporter->import( diff --git a/lib/Importer/MetadataImporter/PreferencesImporter.php b/lib/Importer/MetadataImporter/PreferencesImporter.php index 0aedc11..0cefdba 100644 --- a/lib/Importer/MetadataImporter/PreferencesImporter.php +++ b/lib/Importer/MetadataImporter/PreferencesImporter.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Importer\MetadataImporter; -use OCA\DataExporter\Model\User\Preference; +use OCA\DataExporter\Model\UserMetadata\User\Preference; use OCP\IConfig; class PreferencesImporter { diff --git a/lib/Importer/MetadataImporter/ShareImporter.php b/lib/Importer/MetadataImporter/ShareImporter.php index 2331924..d28ff4e 100644 --- a/lib/Importer/MetadataImporter/ShareImporter.php +++ b/lib/Importer/MetadataImporter/ShareImporter.php @@ -29,7 +29,7 @@ use OCP\Share as ShareConstants; use OCP\IURLGenerator; use OCP\ILogger; -use OCA\DataExporter\Model\User\Share; +use OCA\DataExporter\Model\UserMetadata\User\Share; use OCA\DataExporter\Utilities\ShareConverter; class ShareImporter { diff --git a/lib/Importer/MetadataImporter/UserImporter.php b/lib/Importer/MetadataImporter/UserImporter.php index 51e52ae..3ae32c4 100644 --- a/lib/Importer/MetadataImporter/UserImporter.php +++ b/lib/Importer/MetadataImporter/UserImporter.php @@ -22,7 +22,7 @@ */ namespace OCA\DataExporter\Importer\MetadataImporter; -use OCA\DataExporter\Model\User; +use OCA\DataExporter\Model\UserMetadata\User; use OCA\DataExporter\Importer\ImportException; use OCP\IGroupManager; use OCP\IUserManager; diff --git a/lib/Model/AbstractModel.php b/lib/Model/AbstractModel.php new file mode 100644 index 0000000..8efd45f --- /dev/null +++ b/lib/Model/AbstractModel.php @@ -0,0 +1,46 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Model; + +abstract class AbstractModel { + /** @var AbstractModel|null */ + private $parent = null; + + /** + * Set the parent model of this one. Note that each model can only have + * one parent, and setting a different parent will overwrite the previous one + * @param AbstractModel $model the model to be set as parent + */ + final public function setParent(AbstractModel $model) { + $this->parent = $model; + } + + /** + * Get the parent set or null if no parent is set + * @return AbstractModel|null no model set with setParent or null if no parent + * has been set + */ + final public function getParent() { + return $this->parent; + } +} diff --git a/lib/Model/Metadata.php b/lib/Model/UserMetadata.php similarity index 81% rename from lib/Model/Metadata.php rename to lib/Model/UserMetadata.php index dc50a10..8875f5c 100644 --- a/lib/Model/Metadata.php +++ b/lib/Model/UserMetadata.php @@ -23,7 +23,10 @@ */ namespace OCA\DataExporter\Model; -class Metadata { +use OCA\DataExporter\Model\AbstractModel; +use OCA\DataExporter\Model\UserMetadata\User; + +class UserMetadata extends AbstractModel { /** @var \DateTimeImmutable */ private $date; @@ -41,9 +44,9 @@ public function getDate(): \DateTimeImmutable { /** * @param \DateTimeImmutable $date - * @return Metadata + * @return UserMetadata */ - public function setDate(\DateTimeImmutable $date): Metadata { + public function setDate(\DateTimeImmutable $date): UserMetadata { $this->date = $date; return $this; } @@ -58,9 +61,9 @@ public function getOriginServer(): string { /** * @param string $originServer the address of the exporting server, * including port, like "10.10.10.10:8080" or "my.server:443" - * @return Metadata + * @return UserMetadata */ - public function setOriginServer(string $originServer): Metadata { + public function setOriginServer(string $originServer): UserMetadata { $this->originServer = $originServer; return $this; } @@ -74,9 +77,10 @@ public function getUser(): User { /** * @param User $user - * @return Metadata + * @return UserMetadata */ - public function setUser(User $user): Metadata { + public function setUser(User $user): UserMetadata { + $user->setParent($this); $this->user = $user; return $this; } diff --git a/lib/Model/User.php b/lib/Model/UserMetadata/User.php similarity index 88% rename from lib/Model/User.php rename to lib/Model/UserMetadata/User.php index 1071e71..665f29c 100644 --- a/lib/Model/User.php +++ b/lib/Model/UserMetadata/User.php @@ -21,13 +21,14 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Model; +namespace OCA\DataExporter\Model\UserMetadata; -use OCA\DataExporter\Model\User\File; -use OCA\DataExporter\Model\User\Preference; -use OCA\DataExporter\Model\User\Share; +use OCA\DataExporter\Model\AbstractModel; +use OCA\DataExporter\Model\UserMetadata\User\File; +use OCA\DataExporter\Model\UserMetadata\User\Preference; +use OCA\DataExporter\Model\UserMetadata\User\Share; -class User { +class User extends AbstractModel { /** @var string */ private $userId; @@ -174,6 +175,9 @@ public function getPreferences(): array { * @return User */ public function setPreferences(array $preferences): User { + foreach ($preferences as $preference) { + $preference->setParent($this); + } $this->preferences = $preferences; return $this; } @@ -190,6 +194,9 @@ public function getFiles(): array { * @return User */ public function setFiles(array $files): User { + foreach ($files as $file) { + $file->setParent($this); + } $this->files = $files; return $this; } @@ -206,6 +213,9 @@ public function getShares(): array { * @return User */ public function setShares(array $shares): User { + foreach ($shares as $share) { + $share->setParent($this); + } $this->shares = $shares; return $this; } diff --git a/lib/Model/User/File.php b/lib/Model/UserMetadata/User/File.php similarity index 94% rename from lib/Model/User/File.php rename to lib/Model/UserMetadata/User/File.php index 53d0d6d..ff4f5ed 100644 --- a/lib/Model/User/File.php +++ b/lib/Model/UserMetadata/User/File.php @@ -21,9 +21,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Model\User; +namespace OCA\DataExporter\Model\UserMetadata\User; -class File { +use OCA\DataExporter\Model\AbstractModel; + +class File extends AbstractModel { const TYPE_FOLDER = 'folder'; const TYPE_FILE = 'file'; diff --git a/lib/Model/User/Preference.php b/lib/Model/UserMetadata/User/Preference.php similarity index 92% rename from lib/Model/User/Preference.php rename to lib/Model/UserMetadata/User/Preference.php index cd86a44..fdb0a52 100644 --- a/lib/Model/User/Preference.php +++ b/lib/Model/UserMetadata/User/Preference.php @@ -20,9 +20,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Model\User; +namespace OCA\DataExporter\Model\UserMetadata\User; -class Preference { +use OCA\DataExporter\Model\AbstractModel; + +class Preference extends AbstractModel { /** @var string */ private $appId; /** @var string */ diff --git a/lib/Model/User/Share.php b/lib/Model/UserMetadata/User/Share.php similarity index 97% rename from lib/Model/User/Share.php rename to lib/Model/UserMetadata/User/Share.php index 478a422..8ae2f28 100644 --- a/lib/Model/User/Share.php +++ b/lib/Model/UserMetadata/User/Share.php @@ -20,9 +20,11 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Model\User; +namespace OCA\DataExporter\Model\UserMetadata\User; -class Share { +use OCA\DataExporter\Model\AbstractModel; + +class Share extends AbstractModel { const SHARETYPE_USER = 'user'; const SHARETYPE_GROUP = 'group'; const SHARETYPE_LINK = 'link'; diff --git a/lib/Serializer.php b/lib/Serializer.php index 8c5ad2a..affdab5 100644 --- a/lib/Serializer.php +++ b/lib/Serializer.php @@ -35,10 +35,13 @@ class Serializer { public function __construct() { $encoders = [new JsonEncoder()]; + $objectNormalizer = new ObjectNormalizer(null, null, null, new PhpDocExtractor()); + $objectNormalizer->setIgnoredAttributes(['parent']); + $normalizers = [ new DateTimeNormalizer(), new ArrayDenormalizer(), - new ObjectNormalizer(null, null, null, new PhpDocExtractor()) + $objectNormalizer ]; $this->serializer = new \Symfony\Component\Serializer\Serializer($normalizers, $encoders); diff --git a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php index 1ac2bb7..0f8766d 100644 --- a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php +++ b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor\FilesExtractor; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use OCA\DataExporter\Model\User\File; +use OCA\DataExporter\Model\UserMetadata\User\File; use OCP\Files\Node; use OCP\Files\Folder; use Test\TestCase; diff --git a/tests/unit/Exporter/MetadataExtractor/PreferencesExtractorTest.php b/tests/unit/Exporter/MetadataExtractor/PreferencesExtractorTest.php index 56e61aa..6d5e1bd 100644 --- a/tests/unit/Exporter/MetadataExtractor/PreferencesExtractorTest.php +++ b/tests/unit/Exporter/MetadataExtractor/PreferencesExtractorTest.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Tests\Unit\Exporter\MetadataExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\PreferencesExtractor; -use OCA\DataExporter\Model\User\Preference; +use OCA\DataExporter\Model\UserMetadata\User\Preference; use OCP\IAppConfig; use OCP\IConfig; use Test\TestCase; diff --git a/tests/unit/Exporter/MetadataExtractor/SharesExtractorTest.php b/tests/unit/Exporter/MetadataExtractor/SharesExtractorTest.php index ff322d0..28a1f58 100644 --- a/tests/unit/Exporter/MetadataExtractor/SharesExtractorTest.php +++ b/tests/unit/Exporter/MetadataExtractor/SharesExtractorTest.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Tests\Unit\Exporter\MetadataExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\SharesExtractor; -use OCA\DataExporter\Model\User\Share; +use OCA\DataExporter\Model\UserMetadata\User\Share; use OCP\Share\IShare; use OCP\Share as ShareConstants; use OCP\Share\IManager; diff --git a/tests/unit/Importer/MetadataImporter/ShareImporterTest.php b/tests/unit/Importer/MetadataImporter/ShareImporterTest.php index 9e8874a..2d27747 100644 --- a/tests/unit/Importer/MetadataImporter/ShareImporterTest.php +++ b/tests/unit/Importer/MetadataImporter/ShareImporterTest.php @@ -24,7 +24,7 @@ use OC\Share20\Share as PrivateShare; use OCA\DataExporter\Importer\MetadataImporter\ShareImporter; -use OCA\DataExporter\Model\User\Share; +use OCA\DataExporter\Model\UserMetadata\User\Share; use OCP\Share\IManager; use OCP\Share\IShare; use OCP\Files\IRootFolder; From 263ed6092b999a6512a049a23aab9db76a522412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Mon, 15 Oct 2018 14:26:02 +0200 Subject: [PATCH 2/7] Add FSAccess and factory to deal with FS access --- lib/Utilities/FSAccess/FSAccess.php | 201 ++++++++++++++++ lib/Utilities/FSAccess/FSAccessFactory.php | 36 +++ .../FSAccess/FSAccessFactoryTest.php | 43 ++++ .../unit/Utilities/FSAccess/FSAccessTest.php | 219 ++++++++++++++++++ 4 files changed, 499 insertions(+) create mode 100644 lib/Utilities/FSAccess/FSAccess.php create mode 100644 lib/Utilities/FSAccess/FSAccessFactory.php create mode 100644 tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php create mode 100644 tests/unit/Utilities/FSAccess/FSAccessTest.php diff --git a/lib/Utilities/FSAccess/FSAccess.php b/lib/Utilities/FSAccess/FSAccess.php new file mode 100644 index 0000000..08d77de --- /dev/null +++ b/lib/Utilities/FSAccess/FSAccess.php @@ -0,0 +1,201 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Utilities\FSAccess; + +/** + * Jail FS operations inside the $root folder + * This root folder can be '/tmp', '/opt', '/tmp/oC/Folder', or even '/', as long as the folder exists + * Note that PHP wrapper protocols should work for the most part ('vfs://virtual/folder', or + * 'ssh://remote/folder'), but this class will use the basic FS actions (mkdir, fopen, file_exists, etc) + * + * Also note that concurrency isn't considered, so using this class could cause + * bugs in a concurrent environment. + */ +class FSAccess { + /** @var string */ + private $root; + + + public function __construct(string $root) { + $this->root = \rtrim($root, '/'); + } + + /** + * Get the root of this FSAccess instance + */ + public function getRoot(): string { + return $this->root; + } + + private function checkPath(string $path): string { + if ($path === '') { + return '/'; + } + if ($path[0] !== '/') { + return "/$path"; + } + return $path; + } + + private function recursiveDirCreation(string $checkedPath) { + $splittedPath = \explode('/', \ltrim($checkedPath, '/')); + $realPath = $this->root; + foreach ($splittedPath as $item) { + $realPath .= "/$item"; + if (!\file_exists($realPath)) { + $result = \mkdir($realPath); + if ($result === false) { + return false; + } + } + } + return true; + } + + /** + * Make a directory. The directory will be created recirsively if needed + * inside this FSAccess instance's root folder (if possible) + * Note that in case of recursive creation, the operation might fail and some + * directories might have been created + * @param string $path the path representing the folder to be created + * @return bool true of the directory has been created (as well as the + * required upper folders), false otherwise + */ + public function mkdir(string $path): bool { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + if (\mkdir($realPath) === false) { + if ($this->recursiveDirCreation(\dirname($checkedPath))) { + return \mkdir($realPath); + } else { + return false; + } + } + return true; + } + + /** + * Check if the file exists inside this FSAccess instance + * @param string $path the path to be checked + * @return bool true if the file exists, false otherwise + */ + public function fileExists(string $path): bool { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + return \file_exists($realPath); + } + + public function getStream(string $path) { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + return \fopen($realPath, 'rb'); + } + + /** + * Write the string in a new file in $path. Note that previous content will be overwritten. + * This function won't append the string + * @param string $content the content to be written + * @param string $path the path inside the FSAccess instance where the content will be written + * @return int|bool the number of bytes written or false in case of error + */ + public function copyContentToPath(string $content, string $path) { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + if (!\file_exists(\dirname($realPath))) { + if (!$this->recursiveDirCreation(\dirname($checkedPath))) { + return false; + } + } + + return \file_put_contents($realPath, $content); + } + + /** + * Get the contents of the file in $path. The whole content will be fetched as string + * @param string $path the path to get the contents from + * @return string|bool the contents of the file or false in case of error + */ + public function getContentFromPath(string $path) { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + if (!\file_exists($realPath)) { + return false; + } + return \file_get_contents($realPath); + } + + /** + * Copy the contents of the stream into a new file inside this FSAccess instance + * The stream needs to be opened, and the cursor should be placed properly (usually + * at the beginning of the stream) before using this function. Note that this function + * WON'T close the stream, so you'll need to close it on your own. + * The file corresponding to $path will be handled completely by this function. + * @param resource $stream the stream (typically fetched with "fopen") that will be copied + * @param string $path the path inside this FSAccess instance where the contents will be written + * @return int|bool the number of bytes copied from the stream, or false if something went wrong + */ + public function copyStreamToPath($stream, string $path) { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + if (!\file_exists(\dirname($realPath))) { + if (!$this->recursiveDirCreation(\dirname($checkedPath))) { + return false; + } + } + + $dst = @\fopen($realPath, 'wb'); + if ($dst === false) { + return false; + } + + $result = \stream_copy_to_stream($stream, $dst); + \fclose($dst); + return $result; + } + + /** + * Copy the contents of the file in the $path into the stream. + * The file must exists. Opening, reading and closing the file will be handled by this function + * The stream needs to be opened for writing and the cursor positioned properly, usually at the + * beginning of the file (you can write some content before if needed, or position the cursor at the + * end of the file) + * The stream WON'T be closed, so you'll need to close the stram on your own. + * @param string $path the path inside this FSAccess instance to read the contents from + * @param resource $stream the opened stream (typically fetched with "fopen") where the contents will + * be written + * @return int|bool the number of bytes copied to the stream, or false if something went wrong + */ + public function copyPathToStream(string $path, $stream) { + $checkedPath = $this->checkPath($path); + $realPath = $this->root . $checkedPath; + + $src = @\fopen($realPath, 'rb'); + if ($src === false) { + return false; + } + + $result = \stream_copy_to_stream($src, $stream); + \fclose($src); + return $result; + } +} \ No newline at end of file diff --git a/lib/Utilities/FSAccess/FSAccessFactory.php b/lib/Utilities/FSAccess/FSAccessFactory.php new file mode 100644 index 0000000..d351880 --- /dev/null +++ b/lib/Utilities/FSAccess/FSAccessFactory.php @@ -0,0 +1,36 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Utilities\FSAccess; + +class FSAccessFactory { + /** + * Get a new FSAccess instance with a fixed root folder. This usually is something + * like '/tmp', but could also be 'file:///tmp', 'vfs://virtual/folder' or others + * according to the specific support of the FSAccess class + * @param string $fsPrefix the base root folder for the FSAccess class + * @return FSAccess the created FSAccess with the specified root folder + */ + public function getFSAccess(string $fsPrefix): FSAccess { + return new FSAccess($fsPrefix); + } +} diff --git a/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php b/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php new file mode 100644 index 0000000..9d49afd --- /dev/null +++ b/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php @@ -0,0 +1,43 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Tests\Unit\Utilities\FSAccess; + +use org\bovigo\vfs\vfsStream; +use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use Test\TestCase; + +class FSAccessFactoryTest extends TestCase { + private $fsAccessFactory; + + public function setUp() { + $this->fsAccessFactory = new FSAccessFactory(); + } + + public function testGetFSAccess() { + vfsStream::setup('root'); + $fsAccess = $this->fsAccessFactory->getFSAccess('vfs://root'); + $this->assertInstanceOf(FSAccess::class, $fsAccess); + $this->assertEquals('vfs://root', $fsAccess->getRoot()); + } +} diff --git a/tests/unit/Utilities/FSAccess/FSAccessTest.php b/tests/unit/Utilities/FSAccess/FSAccessTest.php new file mode 100644 index 0000000..d7b92a1 --- /dev/null +++ b/tests/unit/Utilities/FSAccess/FSAccessTest.php @@ -0,0 +1,219 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Tests\Unit\Utilities\FSAccess; + +use org\bovigo\vfs\vfsStream; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use Test\TestCase; + +class FSAccessTest extends TestCase { + private $openedFiles = []; + + private $fsaccess; + + private $baseVfsDir; + + public function setUp() { + $this->openedFiles = []; + $vfsRoot = vfsStream::setup('root'); + $prefix = \uniqid(); + $this->baseVfsDir = vfsStream::newDirectory($prefix)->at($vfsRoot); + $this->fsaccess = new FSAccess($this->baseVfsDir->url()); + } + + public function tearDown() { + foreach ($this->openedFiles as $openedFile) { + \fclose($openedFile); + } + } + + public function testGetRoot() { + $this->assertEquals($this->baseVfsDir->url(), $this->fsaccess->getRoot()); + } + + public function mkdirSimpleDataProvider() { + return [ + ['/test1'], + ['test2'], + ]; + } + + /** + * @dataProvider mkdirSimpleDataProvider + */ + public function testMkdirSimple($dir) { + $base = $this->baseVfsDir->url(); + $this->assertTrue($this->fsaccess->mkdir($dir)); + + $this->assertTrue(\file_exists("$base/$dir")); + $this->assertTrue(\is_dir("$base/$dir")); + } + + public function testMkdirRecursive() { + $base = $this->baseVfsDir->url(); + $this->assertTrue($this->fsaccess->mkdir('/test2/test3/test4')); + + $this->assertTrue(\file_exists("$base/test2")); + $this->assertTrue(\file_exists("$base/test2/test3")); + $this->assertTrue(\file_exists("$base/test2/test3/test4")); + $this->assertTrue(\is_dir("$base/test2")); + $this->assertTrue(\is_dir("$base/test2/test3")); + $this->assertTrue(\is_dir("$base/test2/test3/test4")); + } + + public function testMkdirCannotCreateWrongPermissions() { + $this->baseVfsDir->chmod(0444); + $this->assertFalse($this->fsaccess->mkdir('/test2/test3/test4')); + } + + public function testFileExistsRoot() { + $this->assertTrue($this->fsaccess->fileExists('/')); + $this->assertTrue($this->fsaccess->fileExists('')); + } + + public function testFileExistsNewFiles() { + $base = $this->baseVfsDir->url(); + $file = vfsStream::newFile('new_file.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $folder = vfsStream::newDirectory('foo')->at($this->baseVfsDir); + + $this->assertTrue($this->fsaccess->fileExists('/new_file.txt')); + $this->assertTrue($this->fsaccess->fileExists('foo')); + } + + public function testCopyContentToPath() { + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + + $this->assertEquals(\strlen($content), $this->fsaccess->copyContentToPath($content, '/copiedContent.txt')); + $this->assertEquals($content, \file_get_contents("$base/copiedContent.txt")); + } + + public function copyStreamToPathDataProvider() { + return [ + ['/another_file.txt'], + ['another_file.txt'] + ]; + } + + /** + * @dataProvider copyStreamToPathDataProvider + */ + public function testCopyStreamToPathSimple($filename) { + $base = $this->baseVfsDir->url(); + $file = vfsStream::newFile('new_file.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $fileStream = \fopen($file->url(), 'rb'); + $this->openedFiles[] = $fileStream; + + $this->assertEquals(\strlen($content), $this->fsaccess->copyStreamToPath($fileStream, $filename)); + $this->assertTrue(\file_exists("$base/$filename")); + $this->assertEquals($content, \file_get_contents("$base/$filename")); + } + + public function testCopyStreamToPathWithRecursiveCreation() { + $base = $this->baseVfsDir->url(); + $file = vfsStream::newFile('new_file2.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $fileStream = \fopen($file->url(), 'rb'); + $this->openedFiles[] = $fileStream; + + $this->assertEquals(\strlen($content), $this->fsaccess->copyStreamToPath($fileStream, '/foo1/foo2/foo3/another_file.txt')); + $this->assertTrue(\file_exists("$base/foo1/foo2/foo3/another_file.txt")); + $this->assertEquals($content, \file_get_contents("$base/foo1/foo2/foo3/another_file.txt")); + } + + public function testCopyStreamToPathWrong() { + $file = vfsStream::newFile('new_file2.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $fileStream = \fopen($file->url(), 'rb'); + $this->openedFiles[] = $fileStream; + + $this->baseVfsDir->chmod(0444); + + $this->assertFalse($this->fsaccess->copyStreamToPath($fileStream, '/another_file.txt')); + } + + public function testCopyStreamToPathWrongRecursiveCreation() { + $file = vfsStream::newFile('new_file2.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $fileStream = \fopen($file->url(), 'rb'); + $this->openedFiles[] = $fileStream; + + $this->baseVfsDir->chmod(0444); + + $this->assertFalse($this->fsaccess->copyStreamToPath($fileStream, '/foo1/foo2/foo3/another_file.txt')); + } + + public function copyPathToStreamDataProvider() { + return [ + ['/new_file.txt'], + ['new_file.txt'] + ]; + } + + /** + * @dataProvider copyPathToStreamDataProvider + */ + public function testCopyPathToStreamSimple($filename) { + $base = $this->baseVfsDir->url(); + $file = vfsStream::newFile(\ltrim($filename, '/'))->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $copiedFile = vfsStream::newFile('copied_file.txt')->at($this->baseVfsDir); + + $fileStream = \fopen($copiedFile->url(), 'wb'); + $this->openedFiles[] = $fileStream; + + $this->assertEquals(\strlen($content), $this->fsaccess->copyPathToStream($filename, $fileStream)); + + $this->assertTrue(\file_exists("$base/copied_file.txt")); + $this->assertEquals($content, \file_get_contents("$base/copied_file.txt")); + } + + public function testCopyPathToStreamWrong() { + $file = vfsStream::newFile('new_file.txt')->at($this->baseVfsDir); + $content = 'The not-so-random content for the file'; + $file->setContent($content); + + $file->chmod(0300); + + $copiedFile = vfsStream::newFile('copied_file.txt')->at($this->baseVfsDir); + + $fileStream = \fopen($copiedFile->url(), 'wb'); + $this->openedFiles[] = $fileStream; + + $this->assertFalse($this->fsaccess->copyPathToStream('/new_file.txt', $fileStream)); + } +} From 22cb92c41d519d5fc9bb601d7c982e1d96153143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Tue, 16 Oct 2018 18:58:37 +0200 Subject: [PATCH 3/7] version import and export --- lib/Command/ExportUser.php | 19 +++-- lib/Command/ImportUser.php | 14 ++-- lib/Exporter.php | 21 ++---- lib/Exporter/FilesExporter.php | 18 ++--- lib/Exporter/MetadataExtractor.php | 5 +- .../MetadataExtractor/FilesExtractor.php | 11 ++- .../MetadataExtractor/VersionsExtractor.php | 73 +++++++++++++++++++ lib/Importer.php | 23 +++--- lib/Importer/FilesImporter.php | 41 ++++++++--- .../MetadataImporter/VersionImporter.php | 63 ++++++++++++++++ lib/Model/UserMetadata/User/File.php | 15 ++++ lib/Model/UserMetadata/User/File/Version.php | 46 ++++++++++++ .../Nodes/RecursiveNodeIteratorFactory.php | 5 +- .../integration/MetadataImportExportTest.php | 13 +++- tests/unit/Exporter/FilesExporterTest.php | 58 +++++++++------ .../MetadataExtractor/FilesExtractorTest.php | 26 +++++-- .../RecursiveNodeIteratorFactoryTest.php | 2 - 17 files changed, 356 insertions(+), 97 deletions(-) create mode 100644 lib/Exporter/MetadataExtractor/VersionsExtractor.php create mode 100644 lib/Importer/MetadataImporter/VersionImporter.php create mode 100644 lib/Model/UserMetadata/User/File/Version.php diff --git a/lib/Command/ExportUser.php b/lib/Command/ExportUser.php index a2e0ae9..b5a5937 100644 --- a/lib/Command/ExportUser.php +++ b/lib/Command/ExportUser.php @@ -23,6 +23,7 @@ namespace OCA\DataExporter\Command; use OCA\DataExporter\Exporter; +use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -33,9 +34,13 @@ class ExportUser extends Command { /** @var Exporter */ private $exporter; - public function __construct(Exporter $importer) { + /** @var FSAccessFactory */ + private $fsAccessFactory; + + public function __construct(Exporter $importer, FSAccessFactory $fsAccessFactory) { parent::__construct(); $this->exporter = $importer; + $this->fsAccessFactory = $fsAccessFactory; } protected function configure() { @@ -46,11 +51,15 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output) { + $userId = $input->getArgument('userId'); + $targetDir = $input->getArgument('exportDirectory'); + + $fsAccessExportingDir = $this->fsAccessFactory->getFSAccess($targetDir); + $fsAccessExportingDir->mkdir($userId); + + $fsAccess = $this->fsAccessFactory->getFSAccess("$targetDir/$userId"); try { - $this->exporter->export( - $input->getArgument('userId'), - $input->getArgument('exportDirectory') - ); + $this->exporter->export($userId, $fsAccess); } catch (\Exception $e) { $output->writeln("{$e->getMessage()}"); } diff --git a/lib/Command/ImportUser.php b/lib/Command/ImportUser.php index 4644236..c15c8cb 100644 --- a/lib/Command/ImportUser.php +++ b/lib/Command/ImportUser.php @@ -23,6 +23,7 @@ namespace OCA\DataExporter\Command; use OCA\DataExporter\Importer; +use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -34,9 +35,13 @@ class ImportUser extends Command { /** @var Importer */ private $importer; - public function __construct(Importer $importer) { + /** @var FSAccessFactory */ + private $fsAccessFactory; + + public function __construct(Importer $importer, FSAccessFactory $fsAccessFactory) { parent::__construct(); $this->importer = $importer; + $this->fsAccessFactory = $fsAccessFactory; } protected function configure() { @@ -47,11 +52,10 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output) { + $targetDir = $input->getArgument('exportDirectory'); + $fsAccess = $this->fsAccessFactory->getFSAccess($targetDir); try { - $this->importer->import( - $input->getArgument('exportDirectory'), - $input->getOption('as') - ); + $this->importer->import($fsAccess, $input->getOption('as')); } catch (\Exception $e) { $output->writeln("{$e->getMessage()}"); } diff --git a/lib/Exporter.php b/lib/Exporter.php index 42a0f3f..025e094 100644 --- a/lib/Exporter.php +++ b/lib/Exporter.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Exporter\FilesExporter; use OCA\DataExporter\Exporter\MetadataExtractor; -use Symfony\Component\Filesystem\Filesystem; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; class Exporter { @@ -34,25 +34,18 @@ class Exporter { private $metadataExtractor; /** @var FilesExporter */ private $filesExporter; - /** @var Filesystem */ - private $filesystem; - public function __construct(Serializer $serializer, MetadataExtractor $metadataExtractor, FilesExporter $filesExporter, Filesystem $filesystem) { + public function __construct(Serializer $serializer, MetadataExtractor $metadataExtractor, FilesExporter $filesExporter) { $this->serializer = $serializer; $this->metadataExtractor = $metadataExtractor; $this->filesExporter = $filesExporter; - $this->filesystem = $filesystem; } - public function export(string $uid, string $exportDirectoryPath) { - $exportPath = "$exportDirectoryPath/$uid"; - $metaData = $this->metadataExtractor->extract($uid); - $this->filesystem->dumpFile( - "$exportPath/metadata.json", - $this->serializer->serialize($metaData) - ); + public function export(string $uid, FSAccess $fsAccessBase) { + $metaData = $this->metadataExtractor->extract($uid, $fsAccessBase); - $filesPath = \ltrim("$exportPath/files"); - $this->filesExporter->export($uid, $filesPath); + $fsAccessBase->copyContentToPath($this->serializer->serialize($metaData), '/metadata.json'); + + $this->filesExporter->export($uid, $fsAccessBase); } } diff --git a/lib/Exporter/FilesExporter.php b/lib/Exporter/FilesExporter.php index 7ead994..ea5b433 100644 --- a/lib/Exporter/FilesExporter.php +++ b/lib/Exporter/FilesExporter.php @@ -24,20 +24,16 @@ namespace OCA\DataExporter\Exporter; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCP\Files\File; use OCP\Files\Folder; -use Symfony\Component\Filesystem\Filesystem; class FilesExporter { /** @var RecursiveNodeIteratorFactory */ private $iteratorFactory; - /** @var Filesystem */ - private $filesystem; - - public function __construct(RecursiveNodeIteratorFactory $iteratorFactory, Filesystem $filesystem) { + public function __construct(RecursiveNodeIteratorFactory $iteratorFactory) { $this->iteratorFactory = $iteratorFactory; - $this->filesystem = $filesystem; } /** @@ -46,22 +42,24 @@ public function __construct(RecursiveNodeIteratorFactory $iteratorFactory, Files * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException */ - public function export(string $userId, string $exportPath) { + public function export(string $userId, FSAccess $fsAccess) { list($iterator, $baseFolder) = $this->iteratorFactory->getUserFolderParentRecursiveIterator($userId); /** @var \OCP\Files\Node $node */ foreach ($iterator as $node) { $nodePath = $node->getPath(); $relativeFileCachePath = $baseFolder->getRelativePath($nodePath); // $relativeFileCachePath is expected to have a leading slash always - $path = "${exportPath}${relativeFileCachePath}"; + $path = "/files${relativeFileCachePath}"; if ($node instanceof File) { - $this->filesystem->dumpFile($path, $node->getContent()); + $stream = $node->fopen('rb'); + $fsAccess->copyStreamToPath($stream, $path); + \fclose($stream); continue; } if ($node instanceof Folder) { - $this->filesystem->mkdir($path); + $fsAccess->mkdir($path); } } } diff --git a/lib/Exporter/MetadataExtractor.php b/lib/Exporter/MetadataExtractor.php index 506faa1..e973cfe 100644 --- a/lib/Exporter/MetadataExtractor.php +++ b/lib/Exporter/MetadataExtractor.php @@ -28,6 +28,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor\UserExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\SharesExtractor; use OCA\DataExporter\Model\UserMetadata; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCP\IURLGenerator; /** @@ -80,12 +81,12 @@ public function __construct( * @throws \Exception * @throws \RuntimeException if user can not be read */ - public function extract(string $uid) : UserMetadata { + public function extract(string $uid, FSAccess $fsAccess) : UserMetadata { $user = $this->userExtractor->extract($uid); $user->setPreferences( $this->preferencesExtractor->extract($uid) )->setFiles( - $this->filesExtractor->extract($uid) + $this->filesExtractor->extract($uid, $fsAccess) )->setShares( $this->sharesExtractor->extract($uid) ); diff --git a/lib/Exporter/MetadataExtractor/FilesExtractor.php b/lib/Exporter/MetadataExtractor/FilesExtractor.php index 9d3a4d0..52a3e6a 100644 --- a/lib/Exporter/MetadataExtractor/FilesExtractor.php +++ b/lib/Exporter/MetadataExtractor/FilesExtractor.php @@ -25,14 +25,20 @@ use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; use OCA\DataExporter\Model\UserMetadata\User\File; +use OCA\DataExporter\Exporter\MetadataExtractor\VersionsExtractor; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCP\Files\Node; class FilesExtractor { /** @var RecursiveNodeIteratorFactory */ private $iteratorFactory; - public function __construct(RecursiveNodeIteratorFactory $iteratorFactory) { + /** @var VersionsExtractor */ + private $versionsExtractor; + + public function __construct(RecursiveNodeIteratorFactory $iteratorFactory, VersionsExtractor $versionsExtractor) { $this->iteratorFactory = $iteratorFactory; + $this->versionsExtractor = $versionsExtractor; } /** @@ -41,7 +47,7 @@ public function __construct(RecursiveNodeIteratorFactory $iteratorFactory) { * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException */ - public function extract(string $userId) : array { + public function extract(string $userId, FSAccess $fsAccess) : array { list($iterator, $baseFolder) = $this->iteratorFactory->getUserFolderParentRecursiveIterator($userId); $files = []; @@ -60,6 +66,7 @@ public function extract(string $userId) : array { } else { $file->setType(File::TYPE_FOLDER); } + $file->setVersions($this->versionsExtractor->extract($userId, $node->getPath(), $fsAccess)); $files[] = $file; } diff --git a/lib/Exporter/MetadataExtractor/VersionsExtractor.php b/lib/Exporter/MetadataExtractor/VersionsExtractor.php new file mode 100644 index 0000000..0de7ed2 --- /dev/null +++ b/lib/Exporter/MetadataExtractor/VersionsExtractor.php @@ -0,0 +1,73 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Exporter\MetadataExtractor; + +use OCA\DataExporter\Model\UserMetadata\User\File\Version; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCP\Files\Node; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\Storage\IVersionedStorage; + +class VersionsExtractor { + /** @var IRootFolder */ + private $rootFolder; + + public function __construct(IRootFolder $rootFolder) { + $this->rootFolder = $rootFolder; + } + + public function extract(string $userId, string $path, FSAccess $fsAccess) { + $fileNode = $this->rootFolder->get($path); + if (!$fileNode instanceof File) { + // only files will have versions + return []; + } + + $storage = $fileNode->getStorage(); + $internalPath = $fileNode->getInternalPath(); + $versions = $storage->getVersions($internalPath); + $versionModels = []; + if ($storage->instanceOfStorage(IVersionedStorage::class)) { + // traverse the version list backwards so older versions are first + for (\end($versions); \key($versions) !== null; \prev($versions)) { + $fileVersion = \current($versions); + if ($fileVersion['path'][0] !== '/') { + $versionPath = "/versions/{$fileVersion['path']}.{$fileVersion['version']}"; + } else { + $versionPath = "/versions{$fileVersion['path']}.{$fileVersion['version']}"; + } + $versionModel = new Version(); + $versionModel->setPath($versionPath); + + // copy the version over + $versionContentStream = $storage->getContentOfVersion($internalPath, $fileVersion['version']); + $fsAccess->copyStreamToPath($versionContentStream, "/files${versionPath}"); + \fclose($versionContentStream); + + $versionModels[] = $versionModel; + } + } + return $versionModels; + } +} diff --git a/lib/Importer.php b/lib/Importer.php index 09697a6..f58e1d8 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -26,7 +26,7 @@ use OCA\DataExporter\Importer\ImportException; use OCA\DataExporter\Importer\MetadataImporter; use OCA\DataExporter\Model\UserMetadata; -use Symfony\Component\Filesystem\Filesystem; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCA\DataExporter\Importer\FilesImporter; use OCA\DataExporter\Importer\MetadataImporter\ShareImporter; @@ -36,8 +36,6 @@ class Importer { private $metadataImporter; /** @var Serializer */ private $serializer; - /** @var Filesystem */ - private $filesystem; /** @var FilesImporter */ private $filesImporter; /** @var ShareImporter */ @@ -47,11 +45,9 @@ public function __construct( Serializer $serializer, MetadataImporter $metadataImporter, FilesImporter $filesImporter, - ShareImporter $shareImporter, - Filesystem $filesystem + ShareImporter $shareImporter ) { $this->metadataImporter = $metadataImporter; - $this->filesystem = $filesystem; $this->serializer = $serializer; $this->filesImporter = $filesImporter; $this->shareImporter = $shareImporter; @@ -66,16 +62,14 @@ public function __construct( * @throws \OCP\Files\StorageNotAvailableException * @throws \OCP\PreConditionNotMetException */ - public function import(string $pathToExportDir, $alias = null) { - $metaDataPath = "$pathToExportDir/metadata.json"; - - if (!$this->filesystem->exists($metaDataPath)) { - throw new ImportException("metadata.json not found in '$metaDataPath'"); + public function import(FSAccess $fsAccess, $alias = null) { + if (!$fsAccess->fileExists('/metadata.json')) { + throw new ImportException("metadata.json not found in '{$fsAccess->getRoot()}'"); } /** @var UserMetadata $metadata */ $metadata = $this->serializer->deserialize( - \file_get_contents($metaDataPath), + $fsAccess->getContentFromPath('/metadata.json'), UserMetadata::class ); @@ -84,10 +78,13 @@ public function import(string $pathToExportDir, $alias = null) { } $this->metadataImporter->import($metadata); + + \OC_Util::setupFS($metadata->getUser()->getUserId()); + $this->filesImporter->import( $metadata->getUser()->getUserId(), $metadata->getUser()->getFiles(), - "$pathToExportDir/files" + $fsAccess ); $this->shareImporter->import( $metadata->getUser()->getUserId(), diff --git a/lib/Importer/FilesImporter.php b/lib/Importer/FilesImporter.php index c438c16..9c96d27 100644 --- a/lib/Importer/FilesImporter.php +++ b/lib/Importer/FilesImporter.php @@ -23,19 +23,21 @@ namespace OCA\DataExporter\Importer; use OCA\DataExporter\Model\UserMetadata\User\File; +use OCA\DataExporter\Importer\MetadataImporter\VersionImporter; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCP\Files\IRootFolder; -use Symfony\Component\Filesystem\Filesystem; +use OCP\Files\NotFoundException; class FilesImporter { - /** @var Filesystem */ - private $filesystem; /** @var IRootFolder */ private $rootFolder; + /** @var VersionImporter */ + private $versionImporter; - public function __construct(Filesystem $filesystem, IRootFolder $rootFolder) { - $this->filesystem = $filesystem; + public function __construct(IRootFolder $rootFolder, VersionImporter $versionImporter) { $this->rootFolder = $rootFolder; + $this->versionImporter = $versionImporter; } /** @@ -47,7 +49,7 @@ public function __construct(Filesystem $filesystem, IRootFolder $rootFolder) { * @throws \OCP\Files\NotPermittedException * @throws \OCP\Files\StorageNotAvailableException */ - public function import(string $userId, array $filesMetadata, string $exportRootFilesPath) { + public function import(string $userId, array $filesMetadata, FSAccess $fsAccess) { // Trigger creation of user-folder $this->rootFolder->getUserFolder($userId); /** @var \OCP\Files\Folder $userFolder */ @@ -56,15 +58,32 @@ public function import(string $userId, array $filesMetadata, string $exportRootF /** @var File $fileMetadata */ foreach ($filesMetadata as $fileMetadata) { $fileCachePath = $fileMetadata->getPath(); - $pathToFileInExport = "$exportRootFilesPath/$fileCachePath"; + $fileLocation = "/files{$fileCachePath}"; - if (!$this->filesystem->exists($pathToFileInExport)) { - throw new ImportException("File '$pathToFileInExport' not found in export but exists in metadata.json"); + if (!$fsAccess->fileExists($fileLocation)) { + $fullFilePath = $fsAccess->getRoot() . "/files{$fileCachePath}"; + throw new ImportException("File '$fullFilePath' not found in export but exists in metadata.json"); } if ($fileMetadata->getType() === File::TYPE_FILE) { - $file = $userFolder->newFile($fileCachePath); - $file->putContent(\file_get_contents($pathToFileInExport)); + $fileVersions = $fileMetadata->getVersions(); + // versions must have been sorted older to newer + foreach ($fileVersions as $fileVersion) { + $this->versionImporter->import($fileVersion, $fsAccess); + } + + try { + $file = $userFolder->get($fileCachePath); + } catch (NotFoundException $e) { + $file = $userFolder->newFile($fileCachePath); + } + + $stream = $fsAccess->getStream($fileLocation); + $file->putContent($stream); + if (\is_resource($stream)) { + \fclose($stream); + } + $file->getStorage()->getCache()->update($file->getId(), [ 'etag' => $fileMetadata->getETag(), 'permissions' => $fileMetadata->getPermissions() diff --git a/lib/Importer/MetadataImporter/VersionImporter.php b/lib/Importer/MetadataImporter/VersionImporter.php new file mode 100644 index 0000000..8e53ede --- /dev/null +++ b/lib/Importer/MetadataImporter/VersionImporter.php @@ -0,0 +1,63 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Importer\MetadataImporter; + +use OCA\DataExporter\Model\UserMetadata\User\File; +use OCA\DataExporter\Model\UserMetadata\User\File\Version; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\Importer\ImportException; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; + +class VersionImporter { + private $rootFolder; + + public function __construct(IRootFolder $rootFolder) { + $this->rootFolder = $rootFolder; + } + + public function import(Version $versionModel, FSAccess $fsAccess) { + $fileLocation = "/files{$versionModel->getPath()}"; + + /** @var File $parentFileModel */ + $parentFileModel = $versionModel->getParent(); + $filePath = $parentFileModel->getPath(); + + $parentUserModel = $parentFileModel->getParent(); + $userId = $parentUserModel->getUserId(); + + $ocTargetNodePath = "{$userId}{$filePath}"; + + try { + $node = $this->rootFolder->get($ocTargetNodePath); + } catch (NotFoundException $e) { + $node = $this->rootFolder->newFile($ocTargetNodePath); + } + + $stream = $fsAccess->getStream($fileLocation); + $node->putContent($stream); + if (\is_resource($stream)) { + \fclose($stream); + } + } +} diff --git a/lib/Model/UserMetadata/User/File.php b/lib/Model/UserMetadata/User/File.php index ff4f5ed..54db60a 100644 --- a/lib/Model/UserMetadata/User/File.php +++ b/lib/Model/UserMetadata/User/File.php @@ -24,6 +24,7 @@ namespace OCA\DataExporter\Model\UserMetadata\User; use OCA\DataExporter\Model\AbstractModel; +use OCA\DataExporter\Model\UserMetadata\User\File\Version; class File extends AbstractModel { const TYPE_FOLDER = 'folder'; @@ -36,6 +37,8 @@ class File extends AbstractModel { private $eTag; /** @var int */ private $permissions; + /** @var Version[] */ + private $versions; /** * @return string @@ -100,4 +103,16 @@ public function setPermissions(int $permissions): File { $this->permissions = $permissions; return $this; } + + public function getVersions(): array { + return $this->versions; + } + + public function setVersions(array $versions): File { + foreach ($versions as $version) { + $version->setParent($this); + } + $this->versions = $versions; + return $this; + } } diff --git a/lib/Model/UserMetadata/User/File/Version.php b/lib/Model/UserMetadata/User/File/Version.php new file mode 100644 index 0000000..8eeb717 --- /dev/null +++ b/lib/Model/UserMetadata/User/File/Version.php @@ -0,0 +1,46 @@ + + * + * @copyright Copyright (c) 2018, ownCloud GmbH + * @license GPL-2.0 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 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 General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ +namespace OCA\DataExporter\Model\UserMetadata\User\File; + +use OCA\DataExporter\Model\AbstractModel; + +class Version extends AbstractModel { + /** @var string */ + private $path; + + /** + * @return string + */ + public function getPath(): string { + return $this->path; + } + + /** + * @param string $path + * @return File + */ + public function setPath(string $path): Version { + $this->path = $path; + return $this; + } +} diff --git a/lib/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactory.php b/lib/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactory.php index 18d3e2d..8330f0b 100644 --- a/lib/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactory.php +++ b/lib/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactory.php @@ -100,7 +100,10 @@ public function getUserFolderParentRecursiveIterator(string $userId, $mode = \Re $parentFolder = $userFolder->getParent(); $nodeIterator = new RecursiveNodeIterator($parentFolder); $conditionDifferentStorage = new SkipNodeConditionDifferentStorage($parentFolder->getStorage()->getId()); - $conditionIgnorePaths = new SkipNodeConditionIgnorePath($parentFolder, ['/cache', '/thumbnails', '/uploads']); + $conditionIgnorePaths = new SkipNodeConditionIgnorePath( + $parentFolder, + ['/files_versions', '/files_zsync', '/cache', '/thumbnails', '/uploads'] + ); $nodeIterator->addSkipCondition($conditionDifferentStorage); $nodeIterator->addSkipCondition($conditionIgnorePaths); return [new \RecursiveIteratorIterator($nodeIterator, $mode), $parentFolder]; diff --git a/tests/integration/MetadataImportExportTest.php b/tests/integration/MetadataImportExportTest.php index 8af2664..2dc30a7 100644 --- a/tests/integration/MetadataImportExportTest.php +++ b/tests/integration/MetadataImportExportTest.php @@ -21,9 +21,12 @@ * */ namespace OCA\DataExporter\Tests\Integration; + +use org\bovigo\vfs\vfsStream; use OCA\DataExporter\Exporter\MetadataExtractor; use OCA\DataExporter\Importer\MetadataImporter; use OCA\DataExporter\Importer\MetadataImporter\UserImporter; +use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; use OCA\DataExporter\Serializer; use OCP\IUser; use OCP\IUserManager; @@ -62,7 +65,13 @@ public function testExtractThenImportThenExtractGeneratesSameMetadata() { /** @var MetadataImporter $importer */ $importer = \OC::$server->query(MetadataImporter::class); - $export = $extractor->extract($this->testUser->getUID()); + $fsAccessFactory = \OC::$server->query(FSAccessFactory::class); + $vfsRoot = vfsStream::setup('root'); + $prefix = \uniqid(); + $baseVfsDir = vfsStream::newDirectory($prefix)->at($vfsRoot); + $fsAccess = $fsAccessFactory->getFSAccess($baseVfsDir->url()); + + $export = $extractor->extract($this->testUser->getUID(), $fsAccess); // Don't test files for now $export->getUser()->setFiles([]); @@ -70,7 +79,7 @@ public function testExtractThenImportThenExtractGeneratesSameMetadata() { $importer->import($export); - $reExport = $extractor->extract($this->testUser->getUID()); + $reExport = $extractor->extract($this->testUser->getUID(), $fsAccess); // Don't test files for now $reExport->getUser()->setFiles([]); diff --git a/tests/unit/Exporter/FilesExporterTest.php b/tests/unit/Exporter/FilesExporterTest.php index ec5f24d..7556fa7 100644 --- a/tests/unit/Exporter/FilesExporterTest.php +++ b/tests/unit/Exporter/FilesExporterTest.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Exporter\FilesExporter; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use Symfony\Component\Filesystem\Filesystem; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCP\Files\File; use OCP\Files\Folder; use Test\TestCase; @@ -33,23 +33,27 @@ class FilesExporterTest extends TestCase { /** @var RecursiveNodeIteratorFactory */ private $iteratorFactory; - /** @var Filesystem */ - private $filesystem; - /** @var FilesExporter */ private $filesExporter; + /** @var FSAccess */ + private $fsAccess; + protected function setUp() { $this->iteratorFactory = $this->createMock(RecursiveNodeIteratorFactory::class); - $this->filesystem = $this->createMock(Filesystem::class); - $this->filesExporter = new FilesExporter($this->iteratorFactory, $this->filesystem); + $this->filesExporter = new FilesExporter($this->iteratorFactory); + // FSAccess instance for the export calls + $this->fsAccess = $this->createMock(FSAccess::class); } public function testExportFile() { $mockFile = $this->createMock(File::class); $mockFile->method('getPath')->willReturn('/usertest/files/foo/bar.txt'); - $mockFile->method('getContent')->willReturn('weeee eee eeeeeeeee!'); + $mockFile->method('fopen') + ->will($this->returnCallback(function ($mode) { + return \fopen('php://memory', $mode); + })); $userFolderParent = $this->createMock(Folder::class); $userFolderParent->method('getRelativePath') @@ -64,11 +68,11 @@ public function testExportFile() { // iterator can return an array because will just need to traverse it $this->iteratorFactory->method('getUserFolderParentRecursiveIterator')->willReturn([[$mockFile], $userFolderParent]); - $this->filesystem->expects($this->once()) - ->method('dumpFile') - ->with($this->equalTo('/tmp/randomF/files/foo/bar.txt'), $this->equalTo('weeee eee eeeeeeeee!')); + $this->fsAccess->expects($this->once()) + ->method('copyStreamToPath') + ->with($this->anything(), $this->equalTo('/files/files/foo/bar.txt')); - $this->filesExporter->export('usertest', '/tmp/randomF'); + $this->filesExporter->export('usertest', $this->fsAccess); } public function testExportFolder() { @@ -88,11 +92,11 @@ public function testExportFolder() { // iterator can return an array because will just need to traverse it $this->iteratorFactory->method('getUserFolderParentRecursiveIterator')->willReturn([[$mockFolder], $userFolderParent]); - $this->filesystem->expects($this->once()) + $this->fsAccess->expects($this->once()) ->method('mkdir') - ->with($this->equalTo('/tmp/randomF/files/foo/courses')); + ->with($this->equalTo('/files/files/foo/courses')); - $this->filesExporter->export('usertest', '/tmp/randomF'); + $this->filesExporter->export('usertest', $this->fsAccess); } public function testExportFileAndFolder() { @@ -104,11 +108,17 @@ public function testExportFileAndFolder() { $mockFile1 = $this->createMock(File::class); $mockFile1->method('getPath')->willReturn('/usertest/files/foo/courses/awesome qwerty'); - $mockFile1->method('getContent')->willReturn('qwerty!!'); + $mockFile1->method('fopen') + ->will($this->returnCallback(function ($mode) { + return \fopen('php://memory', $mode); + })); $mockFile2 = $this->createMock(File::class); $mockFile2->method('getPath')->willReturn('/usertest/files/foo/bar.txt'); - $mockFile2->method('getContent')->willReturn('weeee eee eeeeeeeee!'); + $mockFile2->method('fopen') + ->will($this->returnCallback(function ($mode) { + return \fopen('php://memory', $mode); + })); $userFolderParent = $this->createMock(Folder::class); $userFolderParent->method('getRelativePath') @@ -124,20 +134,20 @@ public function testExportFileAndFolder() { $this->iteratorFactory->method('getUserFolderParentRecursiveIterator') ->willReturn([[$mockFolder1, $mockFolder2, $mockFile1, $mockFile2], $userFolderParent]); - $this->filesystem->expects($this->exactly(2)) + $this->fsAccess->expects($this->exactly(2)) ->method('mkdir') ->withConsecutive( - [$this->equalTo('/tmp/randomF/files/foo')], - [$this->equalTo('/tmp/randomF/files/foo/courses')] + [$this->equalTo('/files/files/foo')], + [$this->equalTo('/files/files/foo/courses')] ); - $this->filesystem->expects($this->exactly(2)) - ->method('dumpFile') + $this->fsAccess->expects($this->exactly(2)) + ->method('copyStreamToPath') ->withConsecutive( - [$this->equalTo('/tmp/randomF/files/foo/courses/awesome qwerty'), $this->equalTo('qwerty!!')], - [$this->equalTo('/tmp/randomF/files/foo/bar.txt'), $this->equalTo('weeee eee eeeeeeeee!')] + [$this->anything(), $this->equalTo('/files/files/foo/courses/awesome qwerty')], + [$this->anything(), $this->equalTo('/files/files/foo/bar.txt')] ); - $this->filesExporter->export('usertest', '/tmp/randomF'); + $this->filesExporter->export('usertest', $this->fsAccess); } } diff --git a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php index 0f8766d..fad4971 100644 --- a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php +++ b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php @@ -23,7 +23,9 @@ namespace OCA\DataExporter\Tests\Unit\Exporter\MetadataExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\FilesExtractor; +use OCA\DataExporter\Exporter\MetadataExtractor\VersionsExtractor; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; +use OCA\DataExporter\Utilities\FSAccess\FSAccess; use OCA\DataExporter\Model\UserMetadata\User\File; use OCP\Files\Node; use OCP\Files\Folder; @@ -33,13 +35,17 @@ class FilesExtractorTest extends TestCase { /** @var RecursiveNodeIteratorFactory */ private $iteratorFactory; + /** @var VersionsExtractor */ + private $versionsExtractor; + /** @var FilesExtractor */ private $filesExtractor; protected function setUp() { $this->iteratorFactory = $this->createMock(RecursiveNodeIteratorFactory::class); + $this->versionsExtractor = $this->createMock(VersionsExtractor::class); - $this->filesExtractor = new FilesExtractor($this->iteratorFactory); + $this->filesExtractor = new FilesExtractor($this->iteratorFactory, $this->versionsExtractor); } public function testExtract() { @@ -85,28 +91,36 @@ public function testExtract() { $expectedFolder1->setPath('/files/foo') ->setEtag('123qweasdzxc') ->setPermissions(31) - ->setType(File::TYPE_FOLDER); + ->setType(File::TYPE_FOLDER) + ->setVersions([]); $expectedFolder2 = new File(); $expectedFolder2->setPath('/files/foo/courses') ->setEtag('zaqxswcde') ->setPermissions(31) - ->setType(File::TYPE_FOLDER); + ->setType(File::TYPE_FOLDER) + ->setVersions([]); $expectedFile1 = new File(); $expectedFile1->setPath('/files/foo/courses/awesome qwerty') ->setEtag('poiulkjhmnbv') ->setPermissions(1) - ->setType(File::TYPE_FILE); + ->setType(File::TYPE_FILE) + ->setVersions([]); $expectedFile2 = new File(); $expectedFile2->setPath('/files/foo/bar.txt') ->setEtag('123456789') ->setPermissions(9) - ->setType(File::TYPE_FILE); + ->setType(File::TYPE_FILE) + ->setVersions([]); + + $this->versionsExtractor->method('extract')->willReturn([]); + + $fsAccessMock = $this->createMock(FSAccess::class); $expectedFileModels = [$expectedFolder1, $expectedFolder2, $expectedFile1, $expectedFile2]; - $currentFileModels = $this->filesExtractor->extract('usertest'); + $currentFileModels = $this->filesExtractor->extract('usertest', $fsAccessMock); $this->assertEquals($expectedFileModels, $currentFileModels); } } diff --git a/tests/unit/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactoryTest.php b/tests/unit/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactoryTest.php index 0d19145..804028c 100644 --- a/tests/unit/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactoryTest.php +++ b/tests/unit/Utilities/Iterators/Nodes/RecursiveNodeIteratorFactoryTest.php @@ -176,8 +176,6 @@ public function testGetUserFolderParentRecursiveIterator() { $fooFolder = "/usertest/files/foo"; $expectedList = [ - "/usertest/files_versions" => "/usertest/files_versions", - "/usertest/files_versions/bar1.txt.v001" => "/usertest/files_versions/bar1.txt.v001", "/usertest/files" => "/usertest/files", "$fooFolder/bar1.txt" => "$fooFolder/bar1.txt", "$fooFolder/bar2.txt" => "$fooFolder/bar2.txt", From 35f920d94b4093dae1803721bb82849a57f20a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Wed, 17 Oct 2018 14:50:32 +0200 Subject: [PATCH 4/7] Code style changes --- lib/Command/ExportUser.php | 6 +++++- lib/Exporter/FilesExporter.php | 2 +- .../MetadataExtractor/VersionsExtractor.php | 6 ++++-- lib/Importer.php | 2 +- lib/Importer/FilesImporter.php | 7 ++++++- .../MetadataImporter/VersionImporter.php | 3 +++ lib/Model/UserMetadata/User/File.php | 7 +++++++ lib/Model/UserMetadata/User/File/Version.php | 2 +- lib/Utilities/FSAccess/FSAccess.php | 20 +++++++++++++++++-- 9 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/Command/ExportUser.php b/lib/Command/ExportUser.php index b5a5937..182b4b9 100644 --- a/lib/Command/ExportUser.php +++ b/lib/Command/ExportUser.php @@ -51,11 +51,15 @@ protected function configure() { } protected function execute(InputInterface $input, OutputInterface $output) { + /** @var string $userId */ $userId = $input->getArgument('userId'); + /** @var string $targetDir */ $targetDir = $input->getArgument('exportDirectory'); $fsAccessExportingDir = $this->fsAccessFactory->getFSAccess($targetDir); - $fsAccessExportingDir->mkdir($userId); + if (!$fsAccessExportingDir->fileExists($userId)) { + $fsAccessExportingDir->mkdir($userId); + } $fsAccess = $this->fsAccessFactory->getFSAccess("$targetDir/$userId"); try { diff --git a/lib/Exporter/FilesExporter.php b/lib/Exporter/FilesExporter.php index ea5b433..ca2b9bb 100644 --- a/lib/Exporter/FilesExporter.php +++ b/lib/Exporter/FilesExporter.php @@ -38,7 +38,7 @@ public function __construct(RecursiveNodeIteratorFactory $iteratorFactory) { /** * @param string $userId - * @param string $exportPath + * @param FSAccess $fsAccess * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException */ diff --git a/lib/Exporter/MetadataExtractor/VersionsExtractor.php b/lib/Exporter/MetadataExtractor/VersionsExtractor.php index 0de7ed2..6806b53 100644 --- a/lib/Exporter/MetadataExtractor/VersionsExtractor.php +++ b/lib/Exporter/MetadataExtractor/VersionsExtractor.php @@ -44,11 +44,13 @@ public function extract(string $userId, string $path, FSAccess $fsAccess) { return []; } + // assume the $storage is IVersionedStorage; we don't do anything otherwise $storage = $fileNode->getStorage(); $internalPath = $fileNode->getInternalPath(); - $versions = $storage->getVersions($internalPath); $versionModels = []; + if ($storage->instanceOfStorage(IVersionedStorage::class)) { + $versions = $storage->getVersions($internalPath); // traverse the version list backwards so older versions are first for (\end($versions); \key($versions) !== null; \prev($versions)) { $fileVersion = \current($versions); @@ -64,7 +66,7 @@ public function extract(string $userId, string $path, FSAccess $fsAccess) { $versionContentStream = $storage->getContentOfVersion($internalPath, $fileVersion['version']); $fsAccess->copyStreamToPath($versionContentStream, "/files${versionPath}"); \fclose($versionContentStream); - + $versionModels[] = $versionModel; } } diff --git a/lib/Importer.php b/lib/Importer.php index f58e1d8..e08b233 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -54,7 +54,7 @@ public function __construct( } /** - * @param string $pathToExportDir + * @param FSAccess $fsAccess * @param string|null $alias * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException diff --git a/lib/Importer/FilesImporter.php b/lib/Importer/FilesImporter.php index 9c96d27..db4a9ef 100644 --- a/lib/Importer/FilesImporter.php +++ b/lib/Importer/FilesImporter.php @@ -43,7 +43,7 @@ public function __construct(IRootFolder $rootFolder, VersionImporter $versionImp /** * @param string $userId * @param array $filesMetadata - * @param string $exportRootFilesPath + * @param FSAccess $fsAccess * @throws \OCP\Files\InvalidPathException * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException @@ -69,16 +69,21 @@ public function import(string $userId, array $filesMetadata, FSAccess $fsAccess) $fileVersions = $fileMetadata->getVersions(); // versions must have been sorted older to newer foreach ($fileVersions as $fileVersion) { + // import the versions first $this->versionImporter->import($fileVersion, $fsAccess); } try { + /** @var \OCP\Files\File $file */ $file = $userFolder->get($fileCachePath); } catch (NotFoundException $e) { + /** @var \OCP\Files\File $file */ $file = $userFolder->newFile($fileCachePath); } + // import the file over the versions $stream = $fsAccess->getStream($fileLocation); + // assume $file will be always a file node $file->putContent($stream); if (\is_resource($stream)) { \fclose($stream); diff --git a/lib/Importer/MetadataImporter/VersionImporter.php b/lib/Importer/MetadataImporter/VersionImporter.php index 8e53ede..2456ecf 100644 --- a/lib/Importer/MetadataImporter/VersionImporter.php +++ b/lib/Importer/MetadataImporter/VersionImporter.php @@ -22,6 +22,7 @@ */ namespace OCA\DataExporter\Importer\MetadataImporter; +use OCA\DataExporter\Model\UserMetadata\User; use OCA\DataExporter\Model\UserMetadata\User\File; use OCA\DataExporter\Model\UserMetadata\User\File\Version; use OCA\DataExporter\Utilities\FSAccess\FSAccess; @@ -43,6 +44,7 @@ public function import(Version $versionModel, FSAccess $fsAccess) { $parentFileModel = $versionModel->getParent(); $filePath = $parentFileModel->getPath(); + /** @var User $parentUserModel */ $parentUserModel = $parentFileModel->getParent(); $userId = $parentUserModel->getUserId(); @@ -54,6 +56,7 @@ public function import(Version $versionModel, FSAccess $fsAccess) { $node = $this->rootFolder->newFile($ocTargetNodePath); } + // write the version content in the node $stream = $fsAccess->getStream($fileLocation); $node->putContent($stream); if (\is_resource($stream)) { diff --git a/lib/Model/UserMetadata/User/File.php b/lib/Model/UserMetadata/User/File.php index 54db60a..6add9a6 100644 --- a/lib/Model/UserMetadata/User/File.php +++ b/lib/Model/UserMetadata/User/File.php @@ -104,10 +104,17 @@ public function setPermissions(int $permissions): File { return $this; } + /** + * @return Version[] + */ public function getVersions(): array { return $this->versions; } + /** + * @param Version[] $versions + * @return File + */ public function setVersions(array $versions): File { foreach ($versions as $version) { $version->setParent($this); diff --git a/lib/Model/UserMetadata/User/File/Version.php b/lib/Model/UserMetadata/User/File/Version.php index 8eeb717..83a262b 100644 --- a/lib/Model/UserMetadata/User/File/Version.php +++ b/lib/Model/UserMetadata/User/File/Version.php @@ -37,7 +37,7 @@ public function getPath(): string { /** * @param string $path - * @return File + * @return Version */ public function setPath(string $path): Version { $this->path = $path; diff --git a/lib/Utilities/FSAccess/FSAccess.php b/lib/Utilities/FSAccess/FSAccess.php index 08d77de..8689084 100644 --- a/lib/Utilities/FSAccess/FSAccess.php +++ b/lib/Utilities/FSAccess/FSAccess.php @@ -35,7 +35,6 @@ class FSAccess { /** @var string */ private $root; - public function __construct(string $root) { $this->root = \rtrim($root, '/'); } @@ -47,6 +46,9 @@ public function getRoot(): string { return $this->root; } + /** + * Return a path making sure that the beginning '/' is set + */ private function checkPath(string $path): string { if ($path === '') { return '/'; @@ -57,6 +59,15 @@ private function checkPath(string $path): string { return $path; } + /** + * Create the directory and all the parents. The function assumes that the $checkedPath + * is a path returned by the`"checkPath" function. + * Note that the directories will be created inside this FSAccess instance's root folder + * @param string $checkedPath the path returned by the "checkPath" function + * @return bool true if the directory is created properly, false otherwise. Note that this + * function won't return where the failure is located in case one of the parent folder + * can't be created + */ private function recursiveDirCreation(string $checkedPath) { $splittedPath = \explode('/', \ltrim($checkedPath, '/')); $realPath = $this->root; @@ -105,6 +116,11 @@ public function fileExists(string $path): bool { return \file_exists($realPath); } + /** + * Get a **read** stream to read from the file in that path + * @param string $path the path inside this FSAccess instance to read from + * @return resource|false a "fopen" resource or false (according to the "fopen" function) + */ public function getStream(string $path) { $checkedPath = $this->checkPath($path); $realPath = $this->root . $checkedPath; @@ -198,4 +214,4 @@ public function copyPathToStream(string $path, $stream) { \fclose($src); return $result; } -} \ No newline at end of file +} From 804d1278ab23f47e4fd44ede23308743a2d96e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Wed, 17 Oct 2018 18:32:30 +0200 Subject: [PATCH 5/7] Adjust code style --- .../MetadataExtractor/VersionsExtractor.php | 21 +++++++++++++++---- lib/Importer/FilesImporter.php | 19 ++++++++++------- .../MetadataImporter/VersionImporter.php | 17 ++++++++++----- lib/Utilities/FSAccess/FSAccess.php | 9 ++++---- phpstan.neon | 2 ++ 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/lib/Exporter/MetadataExtractor/VersionsExtractor.php b/lib/Exporter/MetadataExtractor/VersionsExtractor.php index 6806b53..94cc36e 100644 --- a/lib/Exporter/MetadataExtractor/VersionsExtractor.php +++ b/lib/Exporter/MetadataExtractor/VersionsExtractor.php @@ -24,10 +24,10 @@ use OCA\DataExporter\Model\UserMetadata\User\File\Version; use OCA\DataExporter\Utilities\FSAccess\FSAccess; -use OCP\Files\Node; use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\Files\Storage\IVersionedStorage; +use OCP\Files\Storage\IStorage; class VersionsExtractor { /** @var IRootFolder */ @@ -44,12 +44,11 @@ public function extract(string $userId, string $path, FSAccess $fsAccess) { return []; } - // assume the $storage is IVersionedStorage; we don't do anything otherwise - $storage = $fileNode->getStorage(); + $storage = $this->getStorage($fileNode); $internalPath = $fileNode->getInternalPath(); $versionModels = []; - if ($storage->instanceOfStorage(IVersionedStorage::class)) { + if ($storage !== null) { $versions = $storage->getVersions($internalPath); // traverse the version list backwards so older versions are first for (\end($versions); \key($versions) !== null; \prev($versions)) { @@ -72,4 +71,18 @@ public function extract(string $userId, string $path, FSAccess $fsAccess) { } return $versionModels; } + + /** + * @param File $fileNode the target file node to fetch the storage from + * @return IStorage|IVersionedStorage|null the versioned storage or null if the storage is of + * a different type + */ + private function getStorage(File $fileNode) { + /** @var IStorage|IVersionedStorage $storage */ + $storage = $fileNode->getStorage(); + if (!$storage->instanceOfStorage(IVersionedStorage::class)) { + return null; + } + return $storage; + } } diff --git a/lib/Importer/FilesImporter.php b/lib/Importer/FilesImporter.php index db4a9ef..0cd5c93 100644 --- a/lib/Importer/FilesImporter.php +++ b/lib/Importer/FilesImporter.php @@ -84,15 +84,18 @@ public function import(string $userId, array $filesMetadata, FSAccess $fsAccess) // import the file over the versions $stream = $fsAccess->getStream($fileLocation); // assume $file will be always a file node - $file->putContent($stream); - if (\is_resource($stream)) { - \fclose($stream); - } + if ($stream !== false) { + // @phan-suppress-next-line PhanTypeMismatchArgument + $file->putContent($stream); + if (\is_resource($stream)) { + \fclose($stream); + } - $file->getStorage()->getCache()->update($file->getId(), [ - 'etag' => $fileMetadata->getETag(), - 'permissions' => $fileMetadata->getPermissions() - ]); + $file->getStorage()->getCache()->update($file->getId(), [ + 'etag' => $fileMetadata->getETag(), + 'permissions' => $fileMetadata->getPermissions() + ]); + } continue; } diff --git a/lib/Importer/MetadataImporter/VersionImporter.php b/lib/Importer/MetadataImporter/VersionImporter.php index 2456ecf..174edc1 100644 --- a/lib/Importer/MetadataImporter/VersionImporter.php +++ b/lib/Importer/MetadataImporter/VersionImporter.php @@ -40,12 +40,16 @@ public function __construct(IRootFolder $rootFolder) { public function import(Version $versionModel, FSAccess $fsAccess) { $fileLocation = "/files{$versionModel->getPath()}"; - /** @var File $parentFileModel */ $parentFileModel = $versionModel->getParent(); + if (!$parentFileModel instanceof File) { + throw new ImportException('current version model isn\'t associated with a file model as parent'); + } $filePath = $parentFileModel->getPath(); - /** @var User $parentUserModel */ $parentUserModel = $parentFileModel->getParent(); + if (!$parentUserModel instanceof User) { + throw new ImportException('file model associated to the model doesn\'t have a user model as parent'); + } $userId = $parentUserModel->getUserId(); $ocTargetNodePath = "{$userId}{$filePath}"; @@ -58,9 +62,12 @@ public function import(Version $versionModel, FSAccess $fsAccess) { // write the version content in the node $stream = $fsAccess->getStream($fileLocation); - $node->putContent($stream); - if (\is_resource($stream)) { - \fclose($stream); + if ($stream !== false) { + // @phan-suppress-next-line PhanTypeMismatchArgument + $node->putContent($stream); + if (\is_resource($stream)) { + \fclose($stream); + } } } } diff --git a/lib/Utilities/FSAccess/FSAccess.php b/lib/Utilities/FSAccess/FSAccess.php index 8689084..5d2e9f2 100644 --- a/lib/Utilities/FSAccess/FSAccess.php +++ b/lib/Utilities/FSAccess/FSAccess.php @@ -41,6 +41,7 @@ public function __construct(string $root) { /** * Get the root of this FSAccess instance + * @return string */ public function getRoot(): string { return $this->root; @@ -132,7 +133,7 @@ public function getStream(string $path) { * This function won't append the string * @param string $content the content to be written * @param string $path the path inside the FSAccess instance where the content will be written - * @return int|bool the number of bytes written or false in case of error + * @return int|false the number of bytes written or false in case of error */ public function copyContentToPath(string $content, string $path) { $checkedPath = $this->checkPath($path); @@ -149,7 +150,7 @@ public function copyContentToPath(string $content, string $path) { /** * Get the contents of the file in $path. The whole content will be fetched as string * @param string $path the path to get the contents from - * @return string|bool the contents of the file or false in case of error + * @return string|false the contents of the file or false in case of error */ public function getContentFromPath(string $path) { $checkedPath = $this->checkPath($path); @@ -168,7 +169,7 @@ public function getContentFromPath(string $path) { * The file corresponding to $path will be handled completely by this function. * @param resource $stream the stream (typically fetched with "fopen") that will be copied * @param string $path the path inside this FSAccess instance where the contents will be written - * @return int|bool the number of bytes copied from the stream, or false if something went wrong + * @return int|false the number of bytes copied from the stream, or false if something went wrong */ public function copyStreamToPath($stream, string $path) { $checkedPath = $this->checkPath($path); @@ -199,7 +200,7 @@ public function copyStreamToPath($stream, string $path) { * @param string $path the path inside this FSAccess instance to read the contents from * @param resource $stream the opened stream (typically fetched with "fopen") where the contents will * be written - * @return int|bool the number of bytes copied to the stream, or false if something went wrong + * @return int|false the number of bytes copied to the stream, or false if something went wrong */ public function copyPathToStream(string $path, $stream) { $checkedPath = $this->checkPath($path); diff --git a/phpstan.neon b/phpstan.neon index cb42d86..835ab6d 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,3 +5,5 @@ parameters: # lib/Exporter/MetadataExtractor/SharesExtractor.php:159 (require changes in core) - '#Strict comparison using === between string and null will always evaluate to false#' # lib/Utilities/Iterators/Nodes/SkipNodeConditionIgnorePath.php:47 (require changes in core, mismatch between interface and implementation) + -'#Parameter \#1 \$data of method OCP\\Files\\File::putContent\(\) expects string, resource given.#' + # lib/Importer/FilesImporter.php:87 (known, implementation uses view->file_put_contents which accepts a stream resource. Trying to avoid loading the whole file) From c3f870980fe974c5496e87b9af6622e2d5bd35d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Wed, 17 Oct 2018 19:21:54 +0200 Subject: [PATCH 6/7] Add unittests --- lib/Utilities/FSAccess/FSAccess.php | 6 +- .../unit/Utilities/FSAccess/FSAccessTest.php | 58 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/lib/Utilities/FSAccess/FSAccess.php b/lib/Utilities/FSAccess/FSAccess.php index 5d2e9f2..bfe4a17 100644 --- a/lib/Utilities/FSAccess/FSAccess.php +++ b/lib/Utilities/FSAccess/FSAccess.php @@ -125,7 +125,7 @@ public function fileExists(string $path): bool { public function getStream(string $path) { $checkedPath = $this->checkPath($path); $realPath = $this->root . $checkedPath; - return \fopen($realPath, 'rb'); + return @\fopen($realPath, 'rb'); } /** @@ -144,7 +144,7 @@ public function copyContentToPath(string $content, string $path) { } } - return \file_put_contents($realPath, $content); + return @\file_put_contents($realPath, $content); } /** @@ -158,7 +158,7 @@ public function getContentFromPath(string $path) { if (!\file_exists($realPath)) { return false; } - return \file_get_contents($realPath); + return @\file_get_contents($realPath); } /** diff --git a/tests/unit/Utilities/FSAccess/FSAccessTest.php b/tests/unit/Utilities/FSAccess/FSAccessTest.php index d7b92a1..3b07d86 100644 --- a/tests/unit/Utilities/FSAccess/FSAccessTest.php +++ b/tests/unit/Utilities/FSAccess/FSAccessTest.php @@ -103,6 +103,19 @@ public function testFileExistsNewFiles() { $this->assertTrue($this->fsaccess->fileExists('foo')); } + public function testGetStream() { + $base = $this->baseVfsDir->url(); + \file_put_contents("$base/content1.txt", "my tailor is rich"); + + $stream = $this->fsaccess->getStream('/content1.txt'); + $fileContents = \stream_get_contents($stream); + $this->assertEquals('my tailor is rich', $fileContents); + } + + public function testGetStreamMissingFile() { + $this->assertFalse($this->fsaccess->getStream('/missingFile000.egg')); + } + public function testCopyContentToPath() { $base = $this->baseVfsDir->url(); $content = 'This might not be random enough'; @@ -111,6 +124,51 @@ public function testCopyContentToPath() { $this->assertEquals($content, \file_get_contents("$base/copiedContent.txt")); } + public function testCopyContentToPathRecursive() { + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + + $this->assertEquals(\strlen($content), $this->fsaccess->copyContentToPath($content, '/foo/bar/copiedContent.txt')); + $this->assertEquals($content, \file_get_contents("$base/foo/bar/copiedContent.txt")); + } + + public function testCopyContentToPathRecursiveWrongPermissions() { + $this->baseVfsDir->chmod(0444); + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + + $this->assertFalse($this->fsaccess->copyContentToPath($content, '/foo/bar/copiedContent.txt')); + } + + public function testCopyContentToPathRecursiveWrongPermissionsNotRecursive() { + $this->baseVfsDir->chmod(0444); + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + + $this->assertFalse($this->fsaccess->copyContentToPath($content, '/copiedContent.txt')); + } + + public function testGetContentFromPath() { + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + \file_put_contents("$base/tmpfile.txt", $content); + + $this->assertEquals($content, $this->fsaccess->GetContentFromPath('/tmpfile.txt')); + } + + public function testGetContentFromPathMissingFile() { + $this->assertFalse($this->fsaccess->GetContentFromPath('/missing_tmpfile.txt')); + } + + public function testGetContentFromPathWrongPermissions() { + $base = $this->baseVfsDir->url(); + $content = 'This might not be random enough'; + \file_put_contents("$base/tmpfile.txt", $content); + \chmod("$base/tmpfile.txt", 0333); + + $this->assertFalse($this->fsaccess->GetContentFromPath('/tmpfile.txt')); + } + public function copyStreamToPathDataProvider() { return [ ['/another_file.txt'], From da0098b68bcca66d178a232b0e4a08b98512d3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Pablo=20Villaf=C3=A1=C3=B1ez?= Date: Fri, 19 Oct 2018 11:00:59 +0200 Subject: [PATCH 7/7] Move FSAccess out of the Utilities folder --- lib/Command/ExportUser.php | 2 +- lib/Command/ImportUser.php | 2 +- lib/Exporter.php | 2 +- lib/Exporter/FilesExporter.php | 2 +- lib/Exporter/MetadataExtractor.php | 2 +- lib/Exporter/MetadataExtractor/FilesExtractor.php | 2 +- lib/Exporter/MetadataExtractor/VersionsExtractor.php | 2 +- lib/{Utilities => }/FSAccess/FSAccess.php | 2 +- lib/{Utilities => }/FSAccess/FSAccessFactory.php | 2 +- lib/Importer.php | 2 +- lib/Importer/FilesImporter.php | 2 +- lib/Importer/MetadataImporter/VersionImporter.php | 2 +- tests/integration/MetadataImportExportTest.php | 2 +- tests/unit/Exporter/FilesExporterTest.php | 2 +- tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php | 2 +- tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php | 4 ++-- tests/unit/Utilities/FSAccess/FSAccessTest.php | 2 +- 17 files changed, 18 insertions(+), 18 deletions(-) rename lib/{Utilities => }/FSAccess/FSAccess.php (99%) rename lib/{Utilities => }/FSAccess/FSAccessFactory.php (96%) diff --git a/lib/Command/ExportUser.php b/lib/Command/ExportUser.php index 182b4b9..9424a8f 100644 --- a/lib/Command/ExportUser.php +++ b/lib/Command/ExportUser.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Command; use OCA\DataExporter\Exporter; -use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; +use OCA\DataExporter\FSAccess\FSAccessFactory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/lib/Command/ImportUser.php b/lib/Command/ImportUser.php index c15c8cb..af7c645 100644 --- a/lib/Command/ImportUser.php +++ b/lib/Command/ImportUser.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Command; use OCA\DataExporter\Importer; -use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; +use OCA\DataExporter\FSAccess\FSAccessFactory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; diff --git a/lib/Exporter.php b/lib/Exporter.php index 025e094..8d55c08 100644 --- a/lib/Exporter.php +++ b/lib/Exporter.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Exporter\FilesExporter; use OCA\DataExporter\Exporter\MetadataExtractor; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; class Exporter { diff --git a/lib/Exporter/FilesExporter.php b/lib/Exporter/FilesExporter.php index ca2b9bb..d9d0529 100644 --- a/lib/Exporter/FilesExporter.php +++ b/lib/Exporter/FilesExporter.php @@ -24,7 +24,7 @@ namespace OCA\DataExporter\Exporter; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\Files\File; use OCP\Files\Folder; diff --git a/lib/Exporter/MetadataExtractor.php b/lib/Exporter/MetadataExtractor.php index e973cfe..6349260 100644 --- a/lib/Exporter/MetadataExtractor.php +++ b/lib/Exporter/MetadataExtractor.php @@ -28,7 +28,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor\UserExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\SharesExtractor; use OCA\DataExporter\Model\UserMetadata; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\IURLGenerator; /** diff --git a/lib/Exporter/MetadataExtractor/FilesExtractor.php b/lib/Exporter/MetadataExtractor/FilesExtractor.php index 52a3e6a..acc0173 100644 --- a/lib/Exporter/MetadataExtractor/FilesExtractor.php +++ b/lib/Exporter/MetadataExtractor/FilesExtractor.php @@ -26,7 +26,7 @@ use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; use OCA\DataExporter\Model\UserMetadata\User\File; use OCA\DataExporter\Exporter\MetadataExtractor\VersionsExtractor; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\Files\Node; class FilesExtractor { diff --git a/lib/Exporter/MetadataExtractor/VersionsExtractor.php b/lib/Exporter/MetadataExtractor/VersionsExtractor.php index 94cc36e..20f5ea5 100644 --- a/lib/Exporter/MetadataExtractor/VersionsExtractor.php +++ b/lib/Exporter/MetadataExtractor/VersionsExtractor.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Exporter\MetadataExtractor; use OCA\DataExporter\Model\UserMetadata\User\File\Version; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\Files\Storage\IVersionedStorage; diff --git a/lib/Utilities/FSAccess/FSAccess.php b/lib/FSAccess/FSAccess.php similarity index 99% rename from lib/Utilities/FSAccess/FSAccess.php rename to lib/FSAccess/FSAccess.php index bfe4a17..efb1c07 100644 --- a/lib/Utilities/FSAccess/FSAccess.php +++ b/lib/FSAccess/FSAccess.php @@ -20,7 +20,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Utilities\FSAccess; +namespace OCA\DataExporter\FSAccess; /** * Jail FS operations inside the $root folder diff --git a/lib/Utilities/FSAccess/FSAccessFactory.php b/lib/FSAccess/FSAccessFactory.php similarity index 96% rename from lib/Utilities/FSAccess/FSAccessFactory.php rename to lib/FSAccess/FSAccessFactory.php index d351880..d6181f6 100644 --- a/lib/Utilities/FSAccess/FSAccessFactory.php +++ b/lib/FSAccess/FSAccessFactory.php @@ -20,7 +20,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ -namespace OCA\DataExporter\Utilities\FSAccess; +namespace OCA\DataExporter\FSAccess; class FSAccessFactory { /** diff --git a/lib/Importer.php b/lib/Importer.php index e08b233..238f872 100644 --- a/lib/Importer.php +++ b/lib/Importer.php @@ -26,7 +26,7 @@ use OCA\DataExporter\Importer\ImportException; use OCA\DataExporter\Importer\MetadataImporter; use OCA\DataExporter\Model\UserMetadata; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCA\DataExporter\Importer\FilesImporter; use OCA\DataExporter\Importer\MetadataImporter\ShareImporter; diff --git a/lib/Importer/FilesImporter.php b/lib/Importer/FilesImporter.php index 0cd5c93..ed6a26d 100644 --- a/lib/Importer/FilesImporter.php +++ b/lib/Importer/FilesImporter.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Model\UserMetadata\User\File; use OCA\DataExporter\Importer\MetadataImporter\VersionImporter; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; diff --git a/lib/Importer/MetadataImporter/VersionImporter.php b/lib/Importer/MetadataImporter/VersionImporter.php index 174edc1..3b175b0 100644 --- a/lib/Importer/MetadataImporter/VersionImporter.php +++ b/lib/Importer/MetadataImporter/VersionImporter.php @@ -25,7 +25,7 @@ use OCA\DataExporter\Model\UserMetadata\User; use OCA\DataExporter\Model\UserMetadata\User\File; use OCA\DataExporter\Model\UserMetadata\User\File\Version; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCA\DataExporter\Importer\ImportException; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; diff --git a/tests/integration/MetadataImportExportTest.php b/tests/integration/MetadataImportExportTest.php index 2dc30a7..76cb283 100644 --- a/tests/integration/MetadataImportExportTest.php +++ b/tests/integration/MetadataImportExportTest.php @@ -26,7 +26,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor; use OCA\DataExporter\Importer\MetadataImporter; use OCA\DataExporter\Importer\MetadataImporter\UserImporter; -use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; +use OCA\DataExporter\FSAccess\FSAccessFactory; use OCA\DataExporter\Serializer; use OCP\IUser; use OCP\IUserManager; diff --git a/tests/unit/Exporter/FilesExporterTest.php b/tests/unit/Exporter/FilesExporterTest.php index 7556fa7..0783fc7 100644 --- a/tests/unit/Exporter/FilesExporterTest.php +++ b/tests/unit/Exporter/FilesExporterTest.php @@ -24,7 +24,7 @@ use OCA\DataExporter\Exporter\FilesExporter; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCP\Files\File; use OCP\Files\Folder; use Test\TestCase; diff --git a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php index fad4971..ace1c28 100644 --- a/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php +++ b/tests/unit/Exporter/MetadataExtractor/FilesExtractorTest.php @@ -25,7 +25,7 @@ use OCA\DataExporter\Exporter\MetadataExtractor\FilesExtractor; use OCA\DataExporter\Exporter\MetadataExtractor\VersionsExtractor; use OCA\DataExporter\Utilities\Iterators\Nodes\RecursiveNodeIteratorFactory; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use OCA\DataExporter\Model\UserMetadata\User\File; use OCP\Files\Node; use OCP\Files\Folder; diff --git a/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php b/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php index 9d49afd..46fde5d 100644 --- a/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php +++ b/tests/unit/Utilities/FSAccess/FSAccessFactoryTest.php @@ -23,8 +23,8 @@ namespace OCA\DataExporter\Tests\Unit\Utilities\FSAccess; use org\bovigo\vfs\vfsStream; -use OCA\DataExporter\Utilities\FSAccess\FSAccessFactory; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccessFactory; +use OCA\DataExporter\FSAccess\FSAccess; use Test\TestCase; class FSAccessFactoryTest extends TestCase { diff --git a/tests/unit/Utilities/FSAccess/FSAccessTest.php b/tests/unit/Utilities/FSAccess/FSAccessTest.php index 3b07d86..749b8cf 100644 --- a/tests/unit/Utilities/FSAccess/FSAccessTest.php +++ b/tests/unit/Utilities/FSAccess/FSAccessTest.php @@ -23,7 +23,7 @@ namespace OCA\DataExporter\Tests\Unit\Utilities\FSAccess; use org\bovigo\vfs\vfsStream; -use OCA\DataExporter\Utilities\FSAccess\FSAccess; +use OCA\DataExporter\FSAccess\FSAccess; use Test\TestCase; class FSAccessTest extends TestCase {