diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f168cb30f..c15c8e40b 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -47,7 +47,6 @@ use OCA\GroupFolders\Trash\TrashBackend; use OCA\GroupFolders\Trash\TrashManager; use OCA\GroupFolders\Versions\GroupVersionsExpireManager; -use OCA\GroupFolders\Versions\GroupVersionsMapper; use OCA\GroupFolders\Versions\VersionsBackend; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IBootContext; @@ -56,8 +55,6 @@ use OCP\AppFramework\IAppContainer; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\Config\IMountProviderCollection; -use OCP\Files\IMimeTypeLoader; -use OCP\Files\IRootFolder; use OCP\ICacheFactory; use OCP\IConfig; use OCP\IDBConnection; @@ -134,13 +131,10 @@ public function register(IRegistrationContext $context): void { $context->registerService(VersionsBackend::class, function (IAppContainer $c): VersionsBackend { return new VersionsBackend( - $c->get(IRootFolder::class), $c->get('GroupAppFolder'), $c->get(MountProvider::class), $c->get(ITimeFactory::class), - $c->get(LoggerInterface::class), - $c->get(GroupVersionsMapper::class), - $c->get(IMimeTypeLoader::class), + $c->get(LoggerInterface::class) ); }); diff --git a/lib/Helper/LazyFolder.php b/lib/Helper/LazyFolder.php index d98d43e27..ad4d0292b 100644 --- a/lib/Helper/LazyFolder.php +++ b/lib/Helper/LazyFolder.php @@ -266,8 +266,4 @@ public function changeLock($targetType) { public function unlock($type) { return $this->__call(__FUNCTION__, func_get_args()); } - - public function getParentId(): int { - return $this->__call(__FUNCTION__, func_get_args()); - } } diff --git a/lib/Migration/Version16000Date20230821085801.php b/lib/Migration/Version16000Date20230821085801.php deleted file mode 100644 index e34346bff..000000000 --- a/lib/Migration/Version16000Date20230821085801.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * @author Louis Chmn - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -namespace OCA\GroupFolders\Migration; - -use Closure; -use OCP\DB\ISchemaWrapper; -use OCP\DB\Types; -use OCP\Migration\IOutput; -use OCP\Migration\SimpleMigrationStep; - -/** - * Auto-generated migration step: Please modify to your needs! - */ -class Version16000Date20230821085801 extends SimpleMigrationStep { - - /** - * @param IOutput $output - * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` - * @param array $options - * @return null|ISchemaWrapper - */ - public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { - /** @var ISchemaWrapper $schema */ - $schema = $schemaClosure(); - - if ($schema->hasTable("group_folders_versions")) { - return null; - } - - $table = $schema->createTable("group_folders_versions"); - $table->addColumn('id', Types::BIGINT, [ - 'autoincrement' => true, - 'notnull' => true, - 'length' => 20, - ]); - $table->addColumn('file_id', Types::BIGINT, [ - 'notnull' => true, - 'length' => 20, - ]); - $table->addColumn('timestamp', Types::BIGINT, [ - 'notnull' => true, - 'length' => 20, - ]); - $table->addColumn('size', Types::BIGINT, [ - 'notnull' => true, - 'length' => 20, - ]); - $table->addColumn('mimetype', Types::BIGINT, [ - 'notnull' => true, - 'length' => 20, - ]); - $table->addColumn('metadata', Types::TEXT, [ - 'notnull' => true, - 'default' => '{}', - ]); - - $table->setPrimaryKey(['id']); - $table->addUniqueIndex(['file_id', 'timestamp'], 'gf_versions_uniq_index'); - - return $schema; - } -} diff --git a/lib/Versions/GroupVersion.php b/lib/Versions/GroupVersion.php index 20fda8d72..6e40fd780 100644 --- a/lib/Versions/GroupVersion.php +++ b/lib/Versions/GroupVersion.php @@ -30,6 +30,11 @@ use OCP\IUser; class GroupVersion extends Version { + /** @var File */ + private $versionFile; + + /** @var int */ + private $folderId; public function __construct( int $timestamp, @@ -41,11 +46,12 @@ public function __construct( FileInfo $sourceFileInfo, IVersionBackend $backend, IUser $user, - string $label, - private File $versionFile, - private int $folderId, + File $versionFile, + int $folderId ) { - parent::__construct($timestamp, $revisionId, $name, $size, $mimetype, $path, $sourceFileInfo, $backend, $user, $label); + parent::__construct($timestamp, $revisionId, $name, $size, $mimetype, $path, $sourceFileInfo, $backend, $user); + $this->versionFile = $versionFile; + $this->folderId = $folderId; } public function getVersionFile(): File { diff --git a/lib/Versions/GroupVersionEntity.php b/lib/Versions/GroupVersionEntity.php deleted file mode 100644 index 6284e18ba..000000000 --- a/lib/Versions/GroupVersionEntity.php +++ /dev/null @@ -1,92 +0,0 @@ - - * - * @author Louis Chmn - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -namespace OCA\GroupFolders\Versions; - -use JsonSerializable; - -use OCP\AppFramework\Db\Entity; -use OCP\DB\Types; - -/** - * @method int getFileId() - * @method void setFileId(int $fileId) - * @method int getTimestamp() - * @method void setTimestamp(int $timestamp) - * @method int|float getSize() - * @method void setSize(int|float $size) - * @method int getMimetype() - * @method void setMimetype(int $mimetype) - * @method string getMetadata() - * @method void setMetadata(string $metadata) - */ -class GroupVersionEntity extends Entity implements JsonSerializable { - protected ?int $fileId = null; - protected ?int $timestamp = null; - protected ?int $size = null; - protected ?int $mimetype = null; - protected ?string $metadata = null; - - public function __construct() { - $this->addType('id', Types::INTEGER); - $this->addType('file_id', Types::INTEGER); - $this->addType('timestamp', Types::INTEGER); - $this->addType('size', Types::INTEGER); - $this->addType('mimetype', Types::INTEGER); - $this->addType('metadata', Types::STRING); - } - - public function jsonSerialize(): array { - return [ - 'id' => $this->id, - 'file_id' => $this->fileId, - 'timestamp' => $this->timestamp, - 'size' => $this->size, - 'mimetype' => $this->mimetype, - 'metadata' => $this->metadata, - ]; - } - - public function getLabel(): string { - return $this->getDecodedMetadata()['label'] ?? ''; - } - - public function setLabel(string $label): void { - $metadata = $this->getDecodedMetadata(); - $metadata['label'] = $label; - $this->setDecodedMetadata($metadata); - $this->markFieldUpdated('metadata'); - } - - public function getDecodedMetadata(): array { - return json_decode($this->metadata ?? '', true, 512, JSON_THROW_ON_ERROR) ?? []; - } - - public function setDecodedMetadata(array $value): void { - $this->metadata = json_encode($value, JSON_THROW_ON_ERROR); - $this->markFieldUpdated('metadata'); - } -} diff --git a/lib/Versions/GroupVersionsMapper.php b/lib/Versions/GroupVersionsMapper.php deleted file mode 100644 index e7d932051..000000000 --- a/lib/Versions/GroupVersionsMapper.php +++ /dev/null @@ -1,86 +0,0 @@ - - * - * @author Louis Chmn - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -namespace OCA\GroupFolders\Versions; - -use OCP\AppFramework\Db\QBMapper; -use OCP\IDBConnection; - -/** - * @extends QBMapper - */ -class GroupVersionsMapper extends QBMapper { - public function __construct(IDBConnection $db) { - parent::__construct($db, 'group_folders_versions', GroupVersionEntity::class); - } - - /** - * @return GroupVersionEntity[] - */ - public function findAllVersionsForFileId(int $fileId): array { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId))); - - return $this->findEntities($qb); - } - - /** - * @return GroupVersionEntity - */ - public function findCurrentVersionForFileId(int $fileId): GroupVersionEntity { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId))) - ->orderBy('timestamp', 'DESC') - ->setMaxResults(1); - - return $this->findEntity($qb); - } - - public function findVersionForFileId(int $fileId, int $timestamp): GroupVersionEntity { - $qb = $this->db->getQueryBuilder(); - - $qb->select('*') - ->from($this->getTableName()) - ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId))) - ->andWhere($qb->expr()->eq('timestamp', $qb->createNamedParameter($timestamp))); - - return $this->findEntity($qb); - } - - public function deleteAllVersionsForFileId(int $fileId): int { - $qb = $this->db->getQueryBuilder(); - - return $qb->delete($this->getTableName()) - ->where($qb->expr()->eq('file_id', $qb->createNamedParameter($fileId))) - ->executeStatement(); - } -} diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php index c79fe1631..c27ec5ddc 100644 --- a/lib/Versions/VersionsBackend.php +++ b/lib/Versions/VersionsBackend.php @@ -23,9 +23,6 @@ namespace OCA\GroupFolders\Versions; -use OCA\Files_Versions\Versions\IDeletableVersionBackend; -use OCA\Files_Versions\Versions\INameableVersionBackend; -use OCA\Files_Versions\Versions\INeedSyncVersionBackend; use OCA\Files_Versions\Versions\IVersion; use OCA\Files_Versions\Versions\IVersionBackend; use OCA\GroupFolders\Mount\GroupMountPoint; @@ -35,121 +32,66 @@ use OCP\Files\File; use OCP\Files\FileInfo; use OCP\Files\Folder; -use OCP\Files\IMimeTypeLoader; -use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\IUser; use Psr\Log\LoggerInterface; -class VersionsBackend implements IVersionBackend, INameableVersionBackend, IDeletableVersionBackend, INeedSyncVersionBackend { - public function __construct( - private IRootFolder $rootFolder, - private Folder $appFolder, - private MountProvider $mountProvider, - private ITimeFactory $timeFactory, - private LoggerInterface $logger, - private GroupVersionsMapper $groupVersionsMapper, - private IMimeTypeLoader $mimeTypeLoader, - ) { +class VersionsBackend implements IVersionBackend { + private Folder $appFolder; + private MountProvider $mountProvider; + private ITimeFactory $timeFactory; + private LoggerInterface $logger; + + public function __construct(Folder $appFolder, MountProvider $mountProvider, ITimeFactory $timeFactory, LoggerInterface $logger) { + $this->appFolder = $appFolder; + $this->mountProvider = $mountProvider; + $this->timeFactory = $timeFactory; + $this->logger = $logger; } public function useBackendForStorage(IStorage $storage): bool { return true; } - public function getVersionsForFile(IUser $user, FileInfo $fileInfo): array { - $mount = $fileInfo->getMountPoint(); - if (!($mount instanceof GroupMountPoint)) { - return []; - } - - try { - $folderId = $mount->getFolderId(); - /** @var Folder $versionsFolder */ - $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$fileInfo->getId()); - - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - $nodes = $userFolder->getById($fileInfo->getId()); - $file = array_pop($nodes); - - $versions = $this->getVersionsForFileFromDB($fileInfo, $user, $folderId); - - // Early exit if we find any version in the database. - // Else we continue to populate the DB from what's on disk. - if (count($versions) > 0) { + public function getVersionsForFile(IUser $user, FileInfo $file): array { + $mount = $file->getMountPoint(); + if ($mount instanceof GroupMountPoint) { + try { + $folderId = $mount->getFolderId(); + /** @var Folder $versionsFolder */ + $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$file->getId()); + $versions = array_map(function (Node $versionFile) use ($file, $user, $folderId): GroupVersion { + if ($versionFile instanceof Folder) { + $this->logger->error('Found an unexpected subfolder inside the groupfolder version folder.'); + } + return new GroupVersion( + (int)$versionFile->getName(), + (int)$versionFile->getName(), + $file->getName(), + $versionFile->getSize(), + $versionFile->getMimetype(), + $versionFile->getPath(), + $file, + $this, + $user, + $versionFile, + $folderId + ); + }, $versionsFolder->getDirectoryListing()); + usort($versions, function (GroupVersion $v1, GroupVersion $v2): int { + return $v2->getTimestamp() <=> $v1->getTimestamp(); + }); return $versions; + } catch (NotFoundException $e) { + return []; } - - // Insert the entry in the DB for the current version. - $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($file->getId()); - $versionEntity->setTimestamp($file->getMTime()); - $versionEntity->setSize($file->getSize()); - $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); - $versionEntity->setDecodedMetadata([]); - $this->groupVersionsMapper->insert($versionEntity); - - // Insert entries in the DB for existing versions. - $versionsOnFS = $versionsFolder->getDirectoryListing(); - foreach ($versionsOnFS as $version) { - if ($version instanceof Folder) { - $this->logger->error('Found an unexpected subfolder inside the groupfolder version folder.'); - } - - $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($file->getId()); - // HACK: before this commit, versions were created with the current timestamp instead of the version's mtime. - // This means that the name of some versions is the exact mtime of the next version. This behavior is now fixed. - // To prevent occasional conflicts between the last version and the current one, we decrement the last version mtime. - $mtime = (int)$version->getName(); - if ($mtime === $file->getMTime()) { - $versionEntity->setTimestamp($mtime - 1); - $version->move($version->getParent()->getPath() . '/' . ($mtime - 1)); - } else { - $versionEntity->setTimestamp($mtime); - } - $versionEntity->setSize($version->getSize()); - // Use the main file mimetype for this initialization as the original mimetype is unknown. - $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); - $versionEntity->setDecodedMetadata([]); - $this->groupVersionsMapper->insert($versionEntity); - } - - return $this->getVersionsForFileFromDB($file, $user, $folderId); - } catch (NotFoundException $e) { + } else { return []; } } - /** - * @return IVersion[] - */ - private function getVersionsForFileFromDB(FileInfo $file, IUser $user, int $folderId): array { - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - /** @var Folder $versionsFolder */ - $versionsFolder = $this->getVersionsFolder($folderId)->get((string)$file->getId()); - - return array_map( - fn (GroupVersionEntity $versionEntity) => new GroupVersion( - $versionEntity->getTimestamp(), - $versionEntity->getTimestamp(), - $file->getName(), - $versionEntity->getSize(), - $this->mimeTypeLoader->getMimetypeById($versionEntity->getMimetype()), - $userFolder->getRelativePath($file->getPath()), - $file, - $this, - $user, - $versionEntity->getLabel(), - $file->getMtime() === $versionEntity->getTimestamp() ? $file : $versionsFolder->get((string)$versionEntity->getTimestamp()), - $folderId, - ), - $this->groupVersionsMapper->findAllVersionsForFileId($file->getId()) - ); - } - /** * @return void */ @@ -169,7 +111,7 @@ public function createVersion(IUser $user, FileInfo $file) { $versionMount = $versionFolder->getMountPoint(); $sourceMount = $file->getMountPoint(); $sourceCache = $sourceMount->getStorage()->getCache(); - $revision = $file->getMtime(); + $revision = $this->timeFactory->getTime(); $versionInternalPath = $versionFolder->getInternalPath() . '/' . $revision; $sourceInternalPath = $file->getInternalPath(); @@ -256,7 +198,6 @@ public function deleteAllVersionsForFile(int $folderId, int $fileId): void { $versionsFolder = $this->getVersionsFolder($folderId); try { $versionsFolder->get((string)$fileId)->delete(); - $this->groupVersionsMapper->deleteAllVersionsForFileId($fileId); } catch (NotFoundException $e) { } } @@ -270,67 +211,4 @@ private function getVersionsFolder(int $folderId): Folder { return $trashRoot->newFolder((string)$folderId); } } - - public function setVersionLabel(IVersion $version, string $label): void { - $versionEntity = $this->groupVersionsMapper->findVersionForFileId( - $version->getSourceFile()->getId(), - $version->getTimestamp(), - ); - if (trim($label) === '') { - $label = null; - } - $versionEntity->setLabel($label ?? ''); - $this->groupVersionsMapper->update($versionEntity); - } - - public function deleteVersion(IVersion $version): void { - $sourceFile = $version->getSourceFile(); - $mount = $sourceFile->getMountPoint(); - - if (!($mount instanceof GroupMountPoint)) { - return; - } - - $versionsFolder = $this->getVersionsFolder($mount->getFolderId())->get((string)$sourceFile->getId()); - /** @var Folder $versionsFolder */ - $versionsFolder->get((string)$version->getRevisionId())->delete(); - - $versionEntity = $this->groupVersionsMapper->findVersionForFileId( - $version->getSourceFile()->getId(), - $version->getTimestamp(), - ); - $this->groupVersionsMapper->delete($versionEntity); - } - - public function createVersionEntity(File $file): void { - $versionEntity = new GroupVersionEntity(); - $versionEntity->setFileId($file->getId()); - $versionEntity->setTimestamp($file->getMTime()); - $versionEntity->setSize($file->getSize()); - $versionEntity->setMimetype($this->mimeTypeLoader->getId($file->getMimetype())); - $versionEntity->setDecodedMetadata([]); - $this->groupVersionsMapper->insert($versionEntity); - } - - public function updateVersionEntity(File $sourceFile, int $revision, array $properties): void { - $versionEntity = $this->groupVersionsMapper->findVersionForFileId($sourceFile->getId(), $revision); - - if (isset($properties['timestamp'])) { - $versionEntity->setTimestamp($properties['timestamp']); - } - - if (isset($properties['size'])) { - $versionEntity->setSize($properties['size']); - } - - if (isset($properties['mimetype'])) { - $versionEntity->setMimetype($properties['mimetype']); - } - - $this->groupVersionsMapper->update($versionEntity); - } - - public function deleteVersionsEntity(File $file): void { - $this->groupVersionsMapper->deleteAllVersionsForFileId($file->getId()); - } } diff --git a/tests/stub.phpstub b/tests/stub.phpstub index d56c9259e..8eab93f5f 100644 --- a/tests/stub.phpstub +++ b/tests/stub.phpstub @@ -260,20 +260,6 @@ namespace OCA\Files_Versions\Versions { public function getVersionFile(IUser $user, FileInfo $sourceFile, $revision): ?File; } - interface INameableVersionBackend { - public function setVersionLabel(IVersion $version, string $label): void; - } - - interface IDeletableVersionBackend { - public function deleteVersion(IVersion $version): void; - } - - interface INeedSyncVersionBackend { - public function createVersionEntity(File $file): void; - public function updateVersionEntity(File $sourceFile, int $revision, array $properties): void; - public function deleteVersionsEntity(File $file): void; - } - interface IVersion { public function getBackend(): IVersionBackend; @@ -307,8 +293,7 @@ namespace OCA\Files_Versions\Versions { string $path, FileInfo $sourceFileInfo, IVersionBackend $backend, - IUser $user, - string $label = '' + IUser $user ) { }