diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index e9258903b..0d53cdde8 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -41,4 +41,4 @@ jobs: run: composer i - name: Run coding standards check - run: composer run psalm + run: composer run psalm:ci diff --git a/composer.json b/composer.json index bbd7de96b..7ac364f5b 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,7 @@ "cs:check": "php-cs-fixer fix --dry-run --diff", "cs:fix": "php-cs-fixer fix", "psalm": "psalm --threads=$(nproc) --no-cache", + "psalm:ci": "psalm --threads=1 --no-cache", "psalm:update-baseline": "psalm --threads=$(nproc) --no-cache --update-baseline", "psalm:fix": "psalm --no-cache --alter --issues=InvalidReturnType,InvalidNullableReturnType,MissingParamType,InvalidFalsableReturnType", "test:unit": "phpunit -c tests/phpunit.xml", diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index f21e66595..70ad302d2 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -47,7 +47,9 @@ use OCP\Files\Folder; use OCP\Files\IMimeTypeLoader; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountManager; use OCP\Files\NotFoundException; +use OCP\Files\Storage\IStorageFactory; use OCP\Group\Events\GroupDeletedEvent; use OCP\IAppConfig; use OCP\ICacheFactory; @@ -131,6 +133,8 @@ public function register(IRegistrationContext $context): void { $c->get(LoggerInterface::class), $c->get(IUserManager::class), $c->get(IUserSession::class), + $c->get(IMountManager::class), + $c->get(IStorageFactory::class), ); $hasVersionApp = interface_exists(\OCA\Files_Versions\Versions\IVersionBackend::class); if ($hasVersionApp) { diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index c8ca119fb..5ab28bb6f 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -51,6 +51,10 @@ public function getOwner(string $path): string|false { return false; } + public function getUser(): ?IUser { + return $this->mountOwner; + } + /** * @inheritDoc */ diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index f5bc74acb..daa3ee1c4 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -83,7 +83,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array { $aclManager = $this->aclManagerFactory->getACLManager($user, $this->getRootStorageId()); $rootRules = $aclManager->getRelevantRulesForPath($aclRootPaths); - return array_merge(...array_filter(array_map(function (array $folder) use ($user, $loader, $conflicts, $aclManager, $rootRules): ?array { + return array_filter(array_map(function (array $folder) use ($user, $loader, $conflicts, $aclManager, $rootRules): ?IMountPoint { // check for existing files in the user home and rename them if needed $originalFolderName = $folder['mount_point']; if (in_array($originalFolderName, $conflicts)) { @@ -102,7 +102,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array { $userStorage->getPropagator()->propagateChange("files/$folderName", time()); } - $mount = $this->getMount( + return $this->getMount( $folder['folder_id'], '/' . $user->getUID() . '/files/' . $folder['mount_point'], $folder['permissions'], @@ -114,23 +114,7 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array { $aclManager, $rootRules ); - if (!$mount) { - return null; - } - $trashMount = $this->getTrashMount( - $folder['folder_id'], - '/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folder['folder_id'], - $folder['quota'], - $loader, - $user - ); - - return [ - $mount, - $trashMount, - ]; - - }, $folders))); + }, $folders)); } private function getCurrentUID(): ?string { @@ -225,6 +209,7 @@ public function getTrashMount( int $quota, IStorageFactory $loader, IUser $user, + ?ICacheEntry $cacheEntry = null, ): IMountPoint { $storage = $this->getRootFolder()->getStorage(); @@ -233,7 +218,7 @@ public function getTrashMount( $trashPath = $this->getRootFolder()->getInternalPath() . '/trash/' . $id; - $trashStorage = $this->getGroupFolderStorage($id, $storage, $user, $trashPath, $quota, null); + $trashStorage = $this->getGroupFolderStorage($id, $storage, $user, $trashPath, $quota, $cacheEntry); return new GroupMountPoint( $id, diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 1d5090367..c0e912a79 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -19,13 +19,16 @@ use OCA\GroupFolders\Mount\MountProvider; use OCA\GroupFolders\Versions\VersionsBackend; use OCP\Constants; +use OCP\Files\FileInfo; use OCP\Files\Folder; use OCP\Files\IRootFolder; +use OCP\Files\Mount\IMountManager; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\Storage\ISharedStorage; use OCP\Files\Storage\IStorage; +use OCP\Files\Storage\IStorageFactory; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; @@ -47,6 +50,8 @@ public function __construct( private LoggerInterface $logger, private IUserManager $userManager, private IUserSession $userSession, + private IMountManager $mountManager, + private IStorageFactory $storageFactory, ) { } @@ -235,16 +240,17 @@ public function removeItem(ITrashItem $item): void { public function moveToTrash(IStorage $storage, string $internalPath): bool { if ($storage->instanceOfStorage(GroupFolderStorage::class) && $storage->isDeletable($internalPath)) { + /** @var GroupFolderStorage $storage */ $name = basename($internalPath); $fileEntry = $storage->getCache()->get($internalPath); $folderId = $storage->getFolderId(); $user = $this->userSession->getUser(); - // ensure the folder exists - $this->getTrashFolder($folderId); + $owner = $storage->getUser(); - $owner = $storage->getOwner($internalPath); - $trashFolder = $this->rootFolder->get('/' . $owner . '/files_trashbin/groupfolders/' . $folderId); + $this->setupTrashFolder($folderId, $owner); + + $trashFolder = $this->rootFolder->get('/' . $owner->getUID() . '/files_trashbin/groupfolders/' . $folderId); $trashStorage = $trashFolder->getStorage(); $time = time(); $trashName = $name . '.d' . $time; @@ -371,7 +377,22 @@ private function getTrashRoot(): Folder { } } - private function getTrashFolder(int $folderId): Folder { + private function setupTrashFolder(int $folderId, ?IUser $user = null): Folder { + if ($user) { + $mountPoint = '/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId; + $mount = $this->mountManager->find($mountPoint); + if ($mount->getMountPoint() !== $mountPoint) { + $trashMount = $this->mountProvider->getTrashMount( + $folderId, + $mountPoint, + FileInfo::SPACE_UNLIMITED, + $this->storageFactory, + $user, + ); + $this->mountManager->addMount($trashMount); + } + } + try { /** @var Folder $folder */ $folder = $this->appFolder->get('trash/' . $folderId); @@ -418,7 +439,7 @@ private function getTrashForFolders(IUser $user, array $folders): array { $mountPoint = $folder['mount_point']; // ensure the trash folder exists - $this->getTrashFolder($folderId); + $this->setupTrashFolder($folderId, $user); $trashFolder = $this->rootFolder->get('/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId); $content = $trashFolder->getDirectoryListing(); @@ -513,7 +534,7 @@ public function getTrashNodeById(IUser $user, int $fileId): ?Node { } public function cleanTrashFolder(int $folderid): void { - $trashFolder = $this->getTrashFolder($folderid); + $trashFolder = $this->setupTrashFolder($folderid); foreach ($trashFolder->getDirectoryListing() as $node) { $node->delete(); @@ -532,7 +553,7 @@ public function expire(Expiration $expiration): array { // calculate size of trash items $sizeInTrash = 0; - $trashFolder = $this->getTrashFolder($folderId); + $trashFolder = $this->setupTrashFolder($folderId); $nodes = []; // cache foreach ($trashItems as $groupTrashItem) { $nodeName = $groupTrashItem['name'] . '.d' . $groupTrashItem['deleted_time']; @@ -593,7 +614,7 @@ private function cleanupDeletedFoldersTrash(array $existingFolders): void { $folderId = (int)$folderId; if (!isset($existingFolders[$folderId])) { $this->cleanTrashFolder($folderId); - $this->getTrashFolder($folderId)->delete(); + $this->setupTrashFolder($folderId)->delete(); } } }