diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml
index e4a04ed31..f7b1dd975 100644
--- a/.github/workflows/cypress.yml
+++ b/.github/workflows/cypress.yml
@@ -147,7 +147,7 @@ jobs:
- name: Extract NC logs
if: failure() && matrix.containers != 'component'
- run: docker logs nextcloud-cypress-tests-${{ env.APP_NAME }} > nextcloud.log
+ run: docker logs nextcloud-cypress-tests_${{ env.APP_NAME }} > nextcloud.log
- name: Upload NC logs
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
diff --git a/appinfo/info.xml b/appinfo/info.xml
index d5f267353..b0de39072 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -14,7 +14,7 @@ Folders can be configured from *Team folders* in the admin settings.
After a folder is created, the admin can give access to the folder to one or more teams, control their write/sharing permissions and assign a quota for the folder.
As of Hub 10/Nextcloud 31, the admin needs to be a part of the team to be able to assign it a Teamfolder.
]]>
- 20.0.0-dev.0
+ 20.0.0-dev.1
agpl
Robin Appelman
GroupFolders
diff --git a/lib/ACL/ACLCacheWrapper.php b/lib/ACL/ACLCacheWrapper.php
index 2c94c5495..17df40bea 100644
--- a/lib/ACL/ACLCacheWrapper.php
+++ b/lib/ACL/ACLCacheWrapper.php
@@ -27,7 +27,7 @@ private function getACLPermissionsForPath(string $path, array $rules = []): int
if ($rules) {
$permissions = $this->aclManager->getPermissionsForPathFromRules($path, $rules);
} else {
- $permissions = $this->aclManager->getACLPermissionsForPath($path);
+ $permissions = $this->aclManager->getACLPermissionsForPath($this->getNumericStorageId(), $path);
}
// if there is no read permissions, than deny everything
@@ -89,6 +89,6 @@ public function searchQuery(ISearchQuery $query): array {
private function preloadEntries(array $entries): array {
$paths = array_map(fn (ICacheEntry $entry): string => $entry->getPath(), $entries);
- return $this->aclManager->getRelevantRulesForPath($paths, false);
+ return $this->aclManager->getRelevantRulesForPath($this->getNumericStorageId(), $paths, false);
}
}
diff --git a/lib/ACL/ACLManager.php b/lib/ACL/ACLManager.php
index 3e79bab4a..d636f4892 100644
--- a/lib/ACL/ACLManager.php
+++ b/lib/ACL/ACLManager.php
@@ -12,7 +12,6 @@
use OCA\GroupFolders\Trash\TrashManager;
use OCP\Cache\CappedMemoryCache;
use OCP\Constants;
-use OCP\Files\IRootFolder;
use OCP\IUser;
use Psr\Log\LoggerInterface;
use RuntimeException;
@@ -26,32 +25,20 @@ public function __construct(
private readonly IUserMappingManager $userMappingManager,
private readonly LoggerInterface $logger,
private readonly IUser $user,
- private readonly \Closure $rootFolderProvider,
- private ?int $rootStorageId = null,
private readonly bool $inheritMergePerUser = false,
) {
$this->ruleCache = new CappedMemoryCache();
}
- private function getRootStorageId(): int {
- if ($this->rootStorageId === null) {
- $provider = $this->rootFolderProvider;
- /** @var IRootFolder $rootFolder */
- $rootFolder = $provider();
- $this->rootStorageId = $rootFolder->getMountPoint()->getNumericStorageId() ?? -1;
- }
-
- return $this->rootStorageId;
- }
-
/**
* Get the list of rules applicable for a set of paths
*
+ * @param int $storageId
* @param string[] $paths
* @param bool $cache whether to cache the retrieved rules
* @return array sorted parent first
*/
- private function getRules(array $paths, bool $cache = true): array {
+ private function getRules(int $storageId, array $paths, bool $cache = true): array {
// beware: adding new rules to the cache besides the cap
// might discard former cached entries, so we can't assume they'll stay
// cached, so we read everything out initially to be able to return it
@@ -60,7 +47,7 @@ private function getRules(array $paths, bool $cache = true): array {
$nonCachedPaths = array_filter($paths, fn (string $path): bool => !isset($rules[$path]));
if (!empty($nonCachedPaths)) {
- $newRules = $this->ruleManager->getRulesForFilesByPath($this->user, $this->getRootStorageId(), $nonCachedPaths);
+ $newRules = $this->ruleManager->getRulesForFilesByPath($this->user, $storageId, $nonCachedPaths);
foreach ($newRules as $path => $rulesForPath) {
if ($cache) {
$this->ruleCache->set($path, $rulesForPath);
@@ -75,6 +62,30 @@ private function getRules(array $paths, bool $cache = true): array {
return $rules;
}
+ /**
+ * Get the list of rules applicable for a set of paths
+ *
+ * @param int[] $fileIds
+ * @param bool $cache whether to cache the retrieved rules
+ * @return array sorted parent first
+ */
+ public function getRulesByFileIds(array $fileIds, bool $cache = true): array {
+ $rules = [];
+
+ $newRules = $this->ruleManager->getRulesForFilesByIds($this->user, $fileIds);
+ foreach ($newRules as $path => $rulesForPath) {
+ if ($cache) {
+ $this->ruleCache->set($path, $rulesForPath);
+ }
+
+ $rules[$path] = $rulesForPath;
+ }
+
+ ksort($rules);
+
+ return $rules;
+ }
+
/**
* Get a list of all path that might contain relevant rules when calculating the permissions for a path
*
@@ -130,22 +141,23 @@ private function getRelevantPaths(string $path): array {
/**
* Get the list of rules applicable for a set of paths, including rules for any parent
*
+ * @param int $storageId
* @param string[] $paths
* @param bool $cache whether to cache the retrieved rules
* @return array sorted parent first
*/
- public function getRelevantRulesForPath(array $paths, bool $cache = true): array {
+ public function getRelevantRulesForPath(int $storageId, array $paths, bool $cache = true): array {
$allPaths = [];
foreach ($paths as $path) {
$allPaths = array_unique(array_merge($allPaths, $this->getRelevantPaths($path)));
}
- return $this->getRules($allPaths, $cache);
+ return $this->getRules($storageId, $allPaths, $cache);
}
- public function getACLPermissionsForPath(string $path): int {
+ public function getACLPermissionsForPath(int $storageId, string $path): int {
$path = ltrim($path, '/');
- $rules = $this->getRules($this->getRelevantPaths($path));
+ $rules = $this->getRules($storageId, $this->getRelevantPaths($path));
return $this->calculatePermissionsForPath($rules);
}
@@ -155,9 +167,9 @@ public function getACLPermissionsForPath(string $path): int {
*
* @param list $newRules
*/
- public function testACLPermissionsForPath(string $path, array $newRules): int {
+ public function testACLPermissionsForPath(int $storageId, string $path, array $newRules): int {
$path = ltrim($path, '/');
- $rules = $this->getRules($this->getRelevantPaths($path));
+ $rules = $this->getRules($storageId, $this->getRelevantPaths($path));
$rules[$path] = $this->filterApplicableRulesToUser($newRules);
@@ -228,15 +240,15 @@ private function calculatePermissionsForPath(array $rules): int {
/**
* Get the combined "lowest" permissions for an entire directory tree
*/
- public function getPermissionsForTree(string $path): int {
+ public function getPermissionsForTree(int $storageId, string $path): int {
$path = ltrim($path, '/');
- $rules = $this->ruleManager->getRulesForPrefix($this->user, $this->getRootStorageId(), $path);
+ $rules = $this->ruleManager->getRulesForPrefix($this->user, $storageId, $path);
if ($this->inheritMergePerUser) {
$pathsWithRules = array_keys($rules);
$permissions = Constants::PERMISSION_ALL;
foreach ($pathsWithRules as $path) {
- $permissions &= $this->getACLPermissionsForPath($path);
+ $permissions &= $this->getACLPermissionsForPath($storageId, $path);
}
return $permissions;
} else {
@@ -247,8 +259,8 @@ public function getPermissionsForTree(string $path): int {
}
}
- public function preloadRulesForFolder(string $path): void {
- $this->ruleManager->getRulesForFilesByParent($this->user, $this->getRootStorageId(), $path);
+ public function preloadRulesForFolder(int $storageId, string $path): void {
+ $this->ruleManager->getRulesForFilesByParent($this->user, $storageId, $path);
}
/**
diff --git a/lib/ACL/ACLManagerFactory.php b/lib/ACL/ACLManagerFactory.php
index 80cdce2bc..391e55bf2 100644
--- a/lib/ACL/ACLManagerFactory.php
+++ b/lib/ACL/ACLManagerFactory.php
@@ -21,19 +21,16 @@ public function __construct(
private readonly IAppConfig $config,
private readonly LoggerInterface $logger,
private readonly IUserMappingManager $userMappingManager,
- private readonly \Closure $rootFolderProvider,
) {
}
- public function getACLManager(IUser $user, ?int $rootStorageId = null): ACLManager {
+ public function getACLManager(IUser $user): ACLManager {
return new ACLManager(
$this->ruleManager,
$this->trashManager,
$this->userMappingManager,
$this->logger,
$user,
- $this->rootFolderProvider,
- $rootStorageId,
$this->config->getValueString('groupfolders', 'acl-inherit-per-user', 'false') === 'true',
);
}
diff --git a/lib/ACL/ACLStorageWrapper.php b/lib/ACL/ACLStorageWrapper.php
index d8379dd05..a3856d4bf 100644
--- a/lib/ACL/ACLStorageWrapper.php
+++ b/lib/ACL/ACLStorageWrapper.php
@@ -19,15 +19,17 @@
class ACLStorageWrapper extends Wrapper implements IConstructableStorage {
private readonly ACLManager $aclManager;
private readonly bool $inShare;
+ private int $storageId;
public function __construct($arguments) {
parent::__construct($arguments);
$this->aclManager = $arguments['acl_manager'];
$this->inShare = $arguments['in_share'];
+ $this->storageId = $arguments['storage_id'];
}
private function getACLPermissionsForPath(string $path): int {
- $permissions = $this->aclManager->getACLPermissionsForPath($path);
+ $permissions = $this->aclManager->getACLPermissionsForPath($this->storageId, $path);
// if there is no read permissions, than deny everything
if ($this->inShare) {
@@ -151,7 +153,7 @@ public function unlink(string $path): bool {
* This check is fairly expensive so we only do it for the actual delete and not metadata operations
*/
private function canDeleteTree(string $path): int {
- return $this->aclManager->getPermissionsForTree($path) & Constants::PERMISSION_DELETE;
+ return $this->aclManager->getPermissionsForTree($this->storageId, $path) & Constants::PERMISSION_DELETE;
}
public function file_put_contents(string $path, mixed $data): int|float|false {
diff --git a/lib/ACL/RuleManager.php b/lib/ACL/RuleManager.php
index 7997c544f..59b8513d2 100644
--- a/lib/ACL/RuleManager.php
+++ b/lib/ACL/RuleManager.php
@@ -26,6 +26,9 @@ public function __construct(
}
private function createRule(array $data): ?Rule {
+ if (!isset($data['mapping_type'])) {
+ return null;
+ }
$mapping = $this->userMappingManager->mappingFromId($data['mapping_type'], $data['mapping_id']);
if ($mapping) {
return new Rule(
@@ -105,6 +108,31 @@ public function getRulesForFilesByPath(IUser $user, int $storageId, array $fileP
return $this->rulesByPath($rows, $result);
}
+ /**
+ * @param int[] $fileIds
+ * @return array
+ */
+ public function getRulesForFilesByIds(IUser $user, array $fileIds): array {
+ $userMappings = $this->userMappingManager->getMappingsForUser($user);
+
+ $rows = [];
+ foreach (array_chunk($fileIds, 1000) as $chunk) {
+ $query = $this->connection->getQueryBuilder();
+ $query->select(['f.fileid', 'a.mapping_type', 'a.mapping_id', 'a.mask', 'a.permissions', 'f.path'])
+ ->from('filecache', 'f')
+ ->leftJoin('f', 'group_folders_acl', 'a', $query->expr()->eq('f.fileid', 'a.fileid'))
+ ->where($query->expr()->in('f.fileid', $query->createNamedParameter($chunk, IQueryBuilder::PARAM_INT_ARRAY)))
+ ->andWhere($query->expr()->orX(...array_map(fn (IUserMapping $userMapping): ICompositeExpression => $query->expr()->andX(
+ $query->expr()->eq('a.mapping_type', $query->createNamedParameter($userMapping->getType())),
+ $query->expr()->eq('a.mapping_id', $query->createNamedParameter($userMapping->getId()))
+ ), $userMappings)));
+
+ $rows = array_merge($rows, $query->executeQuery()->fetchAll());
+ }
+
+ return $this->rulesByFileId($rows);
+ }
+
/**
* @return array
*/
@@ -199,6 +227,24 @@ private function rulesByPath(array $rows, array $result = []): array {
return $result;
}
+ private function rulesByFileId(array $rows): array {
+ $result = [];
+ foreach ($rows as $row) {
+ if (!isset($result[$row['path']])) {
+ $result[$row['path']] = [];
+ }
+
+ $rule = $this->createRule($row);
+ if ($rule) {
+ $result[$row['path']][] = $rule;
+ }
+ }
+
+ ksort($result);
+
+ return $result;
+ }
+
/**
* @return array
*/
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 6576ac0f0..8db671213 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -15,7 +15,6 @@
use OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent;
use OCA\Files_Trashbin\Expiration;
use OCA\GroupFolders\ACL\ACLManagerFactory;
-use OCA\GroupFolders\ACL\RuleManager;
use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager;
use OCA\GroupFolders\ACL\UserMapping\UserMappingManager;
use OCA\GroupFolders\AuthorizedAdminSettingMiddleware;
@@ -31,6 +30,7 @@
use OCA\GroupFolders\Listeners\CircleDestroyedEventListener;
use OCA\GroupFolders\Listeners\LoadAdditionalScriptsListener;
use OCA\GroupFolders\Listeners\NodeRenamedListener;
+use OCA\GroupFolders\Mount\FolderStorageManager;
use OCA\GroupFolders\Mount\MountProvider;
use OCA\GroupFolders\Trash\TrashBackend;
use OCA\GroupFolders\Trash\TrashManager;
@@ -54,7 +54,6 @@
use OCP\Files\Storage\IStorageFactory;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\IAppConfig;
-use OCP\ICacheFactory;
use OCP\IDBConnection;
use OCP\IRequest;
use OCP\IUserManager;
@@ -118,7 +117,7 @@ public function register(IRegistrationContext $context): void {
$c->get(IRequest::class),
$c->get(IMountProviderCollection::class),
$c->get(IDBConnection::class),
- $c->get(ICacheFactory::class)->createLocal('groupfolders'),
+ $c->get(FolderStorageManager::class),
$allowRootShare,
$enableEncryption
);
@@ -216,19 +215,6 @@ public function register(IRegistrationContext $context): void {
return new ExpireGroupPlaceholder($c->get(ITimeFactory::class));
});
- $context->registerService(ACLManagerFactory::class, function (ContainerInterface $c): ACLManagerFactory {
- $rootFolderProvider = fn (): \OCP\Files\IRootFolder => $c->get(IRootFolder::class);
-
- return new ACLManagerFactory(
- $c->get(RuleManager::class),
- $c->get(TrashManager::class),
- $c->get(IAppConfig::class),
- $c->get(LoggerInterface::class),
- $c->get(IUserMappingManager::class),
- $rootFolderProvider
- );
- });
-
$context->registerServiceAlias(IUserMappingManager::class, UserMappingManager::class);
$context->registerMiddleware(AuthorizedAdminSettingMiddleware::class);
diff --git a/lib/BackgroundJob/ExpireGroupVersions.php b/lib/BackgroundJob/ExpireGroupVersions.php
index 1ae23153a..304e58497 100644
--- a/lib/BackgroundJob/ExpireGroupVersions.php
+++ b/lib/BackgroundJob/ExpireGroupVersions.php
@@ -9,6 +9,7 @@
namespace OCA\GroupFolders\BackgroundJob;
use OCA\GroupFolders\AppInfo\Application;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCA\GroupFolders\Folder\FolderManager;
use OCA\GroupFolders\Versions\GroupVersionsExpireManager;
use OCP\AppFramework\Utility\ITimeFactory;
@@ -39,7 +40,7 @@ public function __construct(
*/
protected function run(mixed $argument): void {
$lastFolder = $this->appConfig->getValueInt(Application::APP_ID, 'cron_last_folder_index', 0);
- $folders = $this->folderManager->getAllFolders();
+ $folders = $this->folderManager->getAllFoldersWithSize();
$folderCount = count($folders);
$currentRunHour = (int)date('G', $this->time->getTime());
@@ -63,7 +64,7 @@ protected function run(mixed $argument): void {
// Determine the set of folders to process
$folderSet = array_slice($folders, $lastFolder, $toDo);
- $folderIDs = array_map(fn (array $folder): int => $folder['id'], $folderSet);
+ $folderIDs = array_map(fn (FolderDefinition $folder): int => $folder->id, $folderSet);
// Log and start the expiration process
$this->logger->debug('Expiring versions for ' . count($folderSet) . ' folders', ['app' => 'cron', 'folders' => $folderIDs]);
diff --git a/lib/Command/ACL.php b/lib/Command/ACL.php
index 06fad5dad..ad1f59019 100644
--- a/lib/Command/ACL.php
+++ b/lib/Command/ACL.php
@@ -12,6 +12,8 @@
use OCA\GroupFolders\ACL\Rule;
use OCA\GroupFolders\ACL\RuleManager;
use OCA\GroupFolders\ACL\UserMapping\UserMapping;
+use OCA\GroupFolders\Folder\FolderDefinition;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
use OCA\GroupFolders\Mount\MountProvider;
use OCP\Constants;
@@ -61,9 +63,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
if ($input->getOption('enable')) {
- $this->folderManager->setFolderACL($folder['id'], true);
+ $this->folderManager->setFolderACL($folder->id, true);
} elseif ($input->getOption('disable')) {
- $this->folderManager->setFolderACL($folder['id'], false);
+ $this->folderManager->setFolderACL($folder->id, false);
} elseif ($input->getOption('test')) {
if ($input->getOption('user') && ($input->getArgument('path'))) {
$mappingId = $input->getOption('user');
@@ -73,13 +75,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return -1;
}
- $jailPath = $this->mountProvider->getJailPath((int)$folder['id']);
+ $jailPath = $this->mountProvider->getJailPath($folder->id);
$path = $input->getArgument('path');
$aclManager = $this->aclManagerFactory->getACLManager($user);
- if ($this->folderManager->getFolderPermissionsForUser($user, $folder['id']) === 0) {
+ if ($this->folderManager->getFolderPermissionsForUser($user, $folder->id) === 0) {
$permissions = 0;
} else {
- $permissions = $aclManager->getACLPermissionsForPath($jailPath . rtrim('/' . $path, '/'));
+ $permissions = $aclManager->getACLPermissionsForPath($folder->storageId, $jailPath . rtrim('/' . $path, '/'));
}
$permissionString = Rule::formatRulePermissions(Constants::PERMISSION_ALL, $permissions);
$output->writeln($permissionString);
@@ -89,8 +91,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$output->writeln('--user and options needs to be set for permissions testing');
return -3;
}
- } elseif (!$folder['acl']) {
- $output->writeln('Advanced permissions not enabled for folder: ' . $folder['id'] . '');
+ } elseif (!$folder->acl) {
+ $output->writeln('Advanced permissions not enabled for folder: ' . $folder->id . '');
return -2;
} elseif (
!$input->getArgument('path')
@@ -102,10 +104,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->printPermissions($input, $output, $folder);
} elseif ($input->getOption('manage-add') && ($input->getOption('user') || $input->getOption('group') || $input->getOption('team'))) {
[$mappingType, $mappingId] = $this->convertMappingOptions($input);
- $this->folderManager->setManageACL($folder['id'], $mappingType, $mappingId, true);
+ $this->folderManager->setManageACL($folder->id, $mappingType, $mappingId, true);
} elseif ($input->getOption('manage-remove') && ($input->getOption('user') || $input->getOption('group') || $input->getOption('team'))) {
[$mappingType, $mappingId] = $this->convertMappingOptions($input);
- $this->folderManager->setManageACL($folder['id'], $mappingType, $mappingId, false);
+ $this->folderManager->setManageACL($folder->id, $mappingType, $mappingId, false);
} elseif (!$input->getArgument('path')) {
$output->writeln(' argument has to be set when not using --enable or --disable');
return -3;
@@ -133,13 +135,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
$mount = $this->mountProvider->getMount(
- $folder['id'],
- '/dummy/files/' . $folder['mount_point'],
- Constants::PERMISSION_ALL,
- $folder['quota'],
- null,
- null,
- $folder['acl']
+ FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL),
+ '/dummy/files/' . $folder->mountPoint,
);
$id = $mount->getStorage()->getCache()->getId($path);
if ($id === -1) {
@@ -189,8 +186,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 0;
}
- private function printPermissions(InputInterface $input, OutputInterface $output, array $folder): void {
- $jailPath = $this->mountProvider->getJailPath((int)$folder['id']);
+ private function printPermissions(InputInterface $input, OutputInterface $output, FolderDefinition $folder): void {
+ $jailPath = $this->mountProvider->getJailPath($folder->id);
$rules = $this->ruleManager->getAllRulesForPrefix(
$this->rootFolder->getMountPoint()->getNumericStorageId(),
$jailPath
diff --git a/lib/Command/Create.php b/lib/Command/Create.php
index 38bc08df5..eb4b84522 100644
--- a/lib/Command/Create.php
+++ b/lib/Command/Create.php
@@ -41,7 +41,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// Check if mount point already exists
$folders = $this->folderManager->getAllFolders();
foreach ($folders as $folder) {
- if ($folder['mount_point'] === $name) {
+ if ($folder->mountPoint === $name) {
$output->writeln('A Folder with the name ' . $name . ' already exists');
return 1;
}
diff --git a/lib/Command/Delete.php b/lib/Command/Delete.php
index 3033ab0b9..bd5df756d 100644
--- a/lib/Command/Delete.php
+++ b/lib/Command/Delete.php
@@ -31,10 +31,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
$helper = $this->getHelper('question');
- $question = new ConfirmationQuestion('Are you sure you want to delete the Team folder ' . $folder['mount_point'] . ' and all files within, this cannot be undone (y/N).', false);
+ $question = new ConfirmationQuestion('Are you sure you want to delete the Team folder ' . $folder->mountPoint . ' and all files within, this cannot be undone (y/N).', false);
if ($input->getOption('force') || $helper->ask($input, $output, $question)) {
- $folderMount = $this->mountProvider->getFolder($folder['id']);
- $this->folderManager->removeFolder($folder['id']);
+ $folderMount = $this->mountProvider->getFolder($folder->id);
+ $this->folderManager->removeFolder($folder->id);
$folderMount->delete();
}
diff --git a/lib/Command/ExpireGroup/ExpireGroupVersions.php b/lib/Command/ExpireGroup/ExpireGroupVersions.php
index 63d53fcf8..4363ddc48 100644
--- a/lib/Command/ExpireGroup/ExpireGroupVersions.php
+++ b/lib/Command/ExpireGroup/ExpireGroupVersions.php
@@ -36,7 +36,7 @@ protected function configure(): void {
protected function execute(InputInterface $input, OutputInterface $output): int {
$this->eventDispatcher->addListener(GroupVersionsExpireEnterFolderEvent::class, function (GroupVersionsExpireEnterFolderEvent $event) use ($output): void {
- $output->writeln("Expiring version in '{$event->folder['mount_point']}'");
+ $output->writeln("Expiring version in '{$event->folder->mountPoint}'");
});
$this->eventDispatcher->addListener(GroupVersionsExpireDeleteVersionEvent::class, function (GroupVersionsExpireDeleteVersionEvent $event) use ($output): void {
$id = $event->version->getRevisionId();
diff --git a/lib/Command/FolderCommand.php b/lib/Command/FolderCommand.php
index 9a9800196..f1acf23c8 100644
--- a/lib/Command/FolderCommand.php
+++ b/lib/Command/FolderCommand.php
@@ -10,6 +10,7 @@
use OC\Core\Command\Base;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCA\GroupFolders\Mount\MountProvider;
use OCP\Files\IRootFolder;
use Symfony\Component\Console\Input\InputInterface;
@@ -17,8 +18,6 @@
/**
* Base command for commands asking the user for a folder id.
- *
- * @psalm-import-type InternalFolderOut from FolderManager
*/
abstract class FolderCommand extends Base {
@@ -30,10 +29,7 @@ public function __construct(
parent::__construct();
}
- /**
- * @return ?InternalFolderOut
- */
- protected function getFolder(InputInterface $input, OutputInterface $output): ?array {
+ protected function getFolder(InputInterface $input, OutputInterface $output): ?FolderWithMappingsAndCache {
$folderId = (int)$input->getArgument('folder_id');
if ((string)$folderId !== $input->getArgument('folder_id')) {
// Protect against removing folderId === 0 when typing a string (e.g. folder name instead of folder id)
@@ -42,14 +38,7 @@ protected function getFolder(InputInterface $input, OutputInterface $output): ?a
return null;
}
- $rootStorageId = $this->rootFolder->getMountPoint()->getNumericStorageId();
- if ($rootStorageId === null) {
- $output->writeln('Root storage id not found');
- return null;
- }
-
-
- $folder = $this->folderManager->getFolder($folderId, $rootStorageId);
+ $folder = $this->folderManager->getFolder($folderId);
if ($folder === null) {
$output->writeln('Folder not found: ' . $folderId . '');
return null;
diff --git a/lib/Command/Group.php b/lib/Command/Group.php
index 172b9e32d..17015a353 100644
--- a/lib/Command/Group.php
+++ b/lib/Command/Group.php
@@ -56,17 +56,17 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$groupString = $input->getArgument('group');
$group = $this->groupManager->get($groupString);
if ($input->getOption('delete')) {
- $this->folderManager->removeApplicableGroup($folder['id'], $groupString);
+ $this->folderManager->removeApplicableGroup($folder->id, $groupString);
return 0;
} elseif ($group || $this->folderManager->isACircle($groupString)) {
$permissionsString = $input->getArgument('permissions');
$permissions = $this->getNewPermissions($permissionsString);
if ($permissions) {
- if (!isset($folder['groups'][$groupString])) {
- $this->folderManager->addApplicableGroup($folder['id'], $groupString);
+ if (!isset($folder->groups[$groupString])) {
+ $this->folderManager->addApplicableGroup($folder->id, $groupString);
}
- $this->folderManager->setGroupPermissions($folder['id'], $groupString, $permissions);
+ $this->folderManager->setGroupPermissions($folder->id, $groupString, $permissions);
return 0;
}
diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php
index 925c5e919..a5eb32535 100644
--- a/lib/Command/ListCommand.php
+++ b/lib/Command/ListCommand.php
@@ -9,9 +9,10 @@
namespace OCA\GroupFolders\Command;
use OC\Core\Command\Base;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCP\Constants;
-use OCP\Files\IRootFolder;
use OCP\IGroupManager;
use OCP\IUserManager;
use Symfony\Component\Console\Helper\Table;
@@ -31,7 +32,6 @@ class ListCommand extends Base {
public function __construct(
private readonly FolderManager $folderManager,
- private readonly IRootFolder $rootFolder,
private readonly IGroupManager $groupManager,
private readonly IUserManager $userManager,
) {
@@ -54,12 +54,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$groupNames[$group->getGID()] = $group->getDisplayName();
}
- $rootStorageId = $this->rootFolder->getMountPoint()->getNumericStorageId();
- if ($rootStorageId === null) {
- $output->writeln('Root storage id not found');
- return 1;
- }
-
if ($userId) {
$user = $this->userManager->get($userId);
if (!$user) {
@@ -67,12 +61,12 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return 1;
}
- $folders = $this->folderManager->getAllFoldersForUserWithSize($rootStorageId, $user);
+ $folders = $this->folderManager->getAllFoldersForUserWithSize($user);
} else {
- $folders = $this->folderManager->getAllFoldersWithSize($rootStorageId);
+ $folders = $this->folderManager->getAllFoldersWithSize();
}
- usort($folders, fn (array $a, array $b): int => $a['id'] - $b['id']);
+ usort($folders, fn (FolderDefinition $a, FolderDefinition $b): int => $a->id - $b->id);
$outputType = $input->getOption('output');
if (count($folders) === 0) {
@@ -86,7 +80,10 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) {
- foreach ($folders as &$folder) {
+ $formatted = array_map(fn (FolderWithMappingsAndCache $folder): array => $folder->toArray(), $folders);
+ foreach ($formatted as &$folder) {
+ $folder['size'] = $folder['root_cache_entry']->getSize();
+ unset($folder['root_cache_entry']);
$folder['group_details'] = $folder['groups'];
$folder['groups'] = array_map(fn (array $group): int => $group['permissions'], $folder['groups']);
}
@@ -95,21 +92,22 @@ protected function execute(InputInterface $input, OutputInterface $output): int
} else {
$table = new Table($output);
$table->setHeaders(['Folder Id', 'Name', 'Groups', 'Quota', 'Size', 'Advanced Permissions', 'Manage advanced permissions']);
- $table->setRows(array_map(function (array $folder) use ($groupNames): array {
- $folder['size'] = \OCP\Util::humanFileSize($folder['size']);
- $folder['quota'] = ($folder['quota'] > 0) ? \OCP\Util::humanFileSize($folder['quota']) : 'Unlimited';
+ $table->setRows(array_map(function (FolderWithMappingsAndCache $folder) use ($groupNames): array {
+ $formatted = ['id' => $folder->id, 'name' => $folder->mountPoint];
+ $formatted['quota'] = ($folder->quota > 0) ? \OCP\Util::humanFileSize($folder->quota) : 'Unlimited';
$groupStrings = array_map(function (string $groupId, array $entry) use ($groupNames): string {
[$permissions, $displayName] = [$entry['permissions'], $entry['displayName']];
$groupName = array_key_exists($groupId, $groupNames) && ($groupNames[$groupId] !== $groupId) ? $groupNames[$groupId] . ' (' . $groupId . ')' : $displayName;
return $groupName . ': ' . $this->permissionsToString($permissions);
- }, array_keys($folder['groups']), array_values($folder['groups']));
- $folder['groups'] = implode("\n", $groupStrings);
- $folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled';
- $manageStrings = array_map(fn (array $manage): string => $manage['displayname'] . ' (' . $manage['type'] . ')', $folder['manage']);
- $folder['manage'] = implode("\n", $manageStrings);
-
- return $folder;
+ }, array_keys($folder->groups), array_values($folder->groups));
+ $formatted['groups'] = implode("\n", $groupStrings);
+ $formatted['size'] = $folder->rootCacheEntry->getSize();
+ $formatted['acl'] = $folder->acl ? 'Enabled' : 'Disabled';
+ $manageStrings = array_map(fn (array $manage): string => $manage['displayname'] . ' (' . $manage['type'] . ')', $folder->manage);
+ $formatted['manage'] = implode("\n", $manageStrings);
+
+ return $formatted;
}, $folders));
$table->render();
}
diff --git a/lib/Command/Quota.php b/lib/Command/Quota.php
index 4e777f949..af817a3de 100644
--- a/lib/Command/Quota.php
+++ b/lib/Command/Quota.php
@@ -38,7 +38,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$quotaString = strtolower($quotaString);
$quota = ($quotaString === 'unlimited') ? FileInfo::SPACE_UNLIMITED : \OCP\Util::computerFileSize($quotaString);
if ($quota) {
- $this->folderManager->setFolderQuota($folder['id'], (int)$quota);
+ $this->folderManager->setFolderQuota($folder->id, (int)$quota);
return 0;
}
diff --git a/lib/Command/Rename.php b/lib/Command/Rename.php
index e0a12dd8c..859929ea1 100644
--- a/lib/Command/Rename.php
+++ b/lib/Command/Rename.php
@@ -36,7 +36,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
// Check if the name actually changed
- if ($folder['mount_point'] === $name) {
+ if ($folder->mountPoint === $name) {
$output->writeln('The name is already set to ' . $name);
return 0;
}
@@ -44,13 +44,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
// Check if mount point already exists
$folders = $this->folderManager->getAllFolders();
foreach ($folders as $existingFolder) {
- if ($existingFolder['mount_point'] === $name) {
+ if ($existingFolder->mountPoint === $name) {
$output->writeln('A Folder with the name ' . $name . ' already exists');
return 1;
}
}
- $this->folderManager->renameFolder($folder['id'], $input->getArgument('name'));
+ $this->folderManager->renameFolder($folder->id, $input->getArgument('name'));
return 0;
}
diff --git a/lib/Command/Scan.php b/lib/Command/Scan.php
index 5b1519e4e..c5c68c1b5 100644
--- a/lib/Command/Scan.php
+++ b/lib/Command/Scan.php
@@ -9,6 +9,7 @@
namespace OCA\GroupFolders\Command;
use OC\Files\ObjectStore\ObjectStoreScanner;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCP\Constants;
use OCP\Files\Cache\IScanner;
use Symfony\Component\Console\Helper\Table;
@@ -60,14 +61,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
if ($all) {
- $folders = $this->folderManager->getAllFolders();
+ $folders = $this->folderManager->getAllFoldersWithSize();
} else {
$folder = $this->getFolder($input, $output);
if ($folder === null) {
return -1;
}
- $folders = [$folder['id'] => $folder];
+ $folders = [$folder->id => $folder];
}
$inputPath = $input->getOption('path');
@@ -86,13 +87,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$stats = [];
foreach ($folders as $folder) {
- $folderId = $folder['id'];
+ $folderId = $folder->id;
$statsRow = [$folderId, 0, 0, 0];
- $mount = $this->mountProvider->getMount($folder['id'], '/' . $folder['mount_point'], Constants::PERMISSION_ALL, $folder['quota']);
+ $folderWithPermissions = FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL);
+ $mount = $this->mountProvider->getMount($folderWithPermissions, '/' . $folder->mountPoint);
/** @var IScanner&\OC\Hooks\BasicEmitter $scanner */
$scanner = $mount->getStorage()->getScanner();
- $output->writeln("Scanning Team folder with id\t{$folder['id']}", OutputInterface::VERBOSITY_VERBOSE);
+ $output->writeln("Scanning Team folder with id\t{$folder->id}", OutputInterface::VERBOSITY_VERBOSE);
if ($scanner instanceof ObjectStoreScanner) {
$output->writeln('Scanning Team folders using an object store as primary storage is not supported.');
return -1;
diff --git a/lib/Command/Trashbin/Cleanup.php b/lib/Command/Trashbin/Cleanup.php
index 12391d3fe..8abc2ff0d 100644
--- a/lib/Command/Trashbin/Cleanup.php
+++ b/lib/Command/Trashbin/Cleanup.php
@@ -9,9 +9,12 @@
namespace OCA\GroupFolders\Command\Trashbin;
use OC\Core\Command\Base;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCA\GroupFolders\Trash\TrashBackend;
use OCP\App\IAppManager;
+use OCP\Constants;
use OCP\Server;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputArgument;
@@ -50,18 +53,21 @@ protected function execute(InputInterface $input, OutputInterface $output): int
/** @var QuestionHelper $helper */
$helper = $this->getHelper('question');
- $folders = $this->folderManager->getAllFolders();
+ $folders = $this->folderManager->getAllFoldersWithSize();
+ $folders = array_map(function (FolderWithMappingsAndCache $folder): FolderDefinitionWithPermissions {
+ return FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL);
+ }, $folders);
if ($input->getArgument('folder_id') !== null) {
$folderId = (int)$input->getArgument('folder_id');
foreach ($folders as $folder) {
- if ($folder['id'] === $folderId) {
+ if ($folder->id === $folderId) {
$question = new ConfirmationQuestion('Are you sure you want to empty the trashbin of your Team folder with id ' . $folderId . ', this can not be undone (y/N).', false);
if (!$input->getOption('force') && !$helper->ask($input, $output, $question)) {
return -1;
}
- $this->trashBackend->cleanTrashFolder($folder['id']);
+ $this->trashBackend->cleanTrashFolder($folder);
return 0;
}
@@ -77,7 +83,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
foreach ($folders as $folder) {
- $this->trashBackend->cleanTrashFolder($folder['id']);
+ $this->trashBackend->cleanTrashFolder($folder);
}
}
diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php
index 495acde37..1a3081b5e 100644
--- a/lib/Controller/FolderController.php
+++ b/lib/Controller/FolderController.php
@@ -11,6 +11,7 @@
use OC\AppFramework\OCS\V1Response;
use OCA\GroupFolders\Attribute\RequireGroupFolderAdmin;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCA\GroupFolders\Mount\MountProvider;
use OCA\GroupFolders\ResponseDefinitions;
use OCA\GroupFolders\Service\DelegationService;
@@ -36,7 +37,6 @@
* @psalm-import-type GroupFoldersCircle from ResponseDefinitions
* @psalm-import-type GroupFoldersUser from ResponseDefinitions
* @psalm-import-type GroupFoldersFolder from ResponseDefinitions
- * @psalm-import-type InternalFolderOut from FolderManager
*/
class FolderController extends OCSController {
private readonly ?IUser $user;
@@ -86,15 +86,19 @@ private function filterNonAdminFolder(array $folder): ?array {
}
/**
- * @param InternalFolderOut $folder
* @return GroupFoldersFolder
*/
- private function formatFolder(array $folder): array {
- // keep compatibility with the old 'groups' field
- $folder['group_details'] = $folder['groups'];
- $folder['groups'] = array_map(fn (array $group): int => $group['permissions'], $folder['groups']);
-
- return $folder;
+ private function formatFolder(FolderWithMappingsAndCache $folder): array {
+ return [
+ 'id' => $folder->id,
+ 'mount_point' => $folder->mountPoint,
+ 'quota' => $folder->quota,
+ 'acl' => $folder->acl,
+ 'size' => $folder->rootCacheEntry->getSize(),
+ 'groups' => array_map(fn (array $group): int => $group['permissions'], $folder->groups),
+ 'group_details' => $folder->groups,
+ 'manage' => $folder->manage,
+ ];
}
/**
@@ -123,7 +127,7 @@ public function getFolders(bool $applicable = false, int $offset = 0, ?int $limi
}
$folders = [];
- foreach ($this->manager->getAllFoldersWithSize($storageId) as $id => $folder) {
+ foreach ($this->manager->getAllFoldersWithSize() as $id => $folder) {
// Make them string-indexed for OpenAPI JSON output
$folders[(string)$id] = $this->formatFolder($folder);
}
@@ -200,10 +204,7 @@ public function getFolder(int $id): DataResponse {
throw new OCSNotFoundException();
}
- $this->checkFolderExists($id);
-
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id, $storageId);
+ $folder = $this->checkedGetFolder($id);
$folder = $this->formatFolder($folder);
if (!$this->delegationService->hasApiAccess()) {
@@ -216,21 +217,13 @@ public function getFolder(int $id): DataResponse {
return new DataResponse($folder);
}
- /**
- * @return DataResponse, array{}>|null
- */
- private function checkFolderExists(int $id): ?DataResponse {
- $storageId = $this->getRootFolderStorageId();
- if ($storageId === null) {
- throw new OCSNotFoundException('Groupfolder not found');
- }
-
- $folder = $this->manager->getFolder($id, $storageId);
+ private function checkedGetFolder(int $id): FolderWithMappingsAndCache {
+ $folder = $this->manager->getFolder($id);
if ($folder === null) {
throw new OCSNotFoundException('Groupfolder not found');
}
- return null;
+ return $folder;
}
private function checkMountPointExists(string $mountpoint): ?DataResponse {
@@ -241,7 +234,7 @@ private function checkMountPointExists(string $mountpoint): ?DataResponse {
$folders = $this->manager->getAllFolders();
foreach ($folders as $folder) {
- if ($folder['mount_point'] === $mountpoint) {
+ if ($folder->mountPoint === $mountpoint) {
throw new OCSBadRequestException('Mount point already exists');
}
}
@@ -276,10 +269,7 @@ public function addFolder(string $mountpoint): DataResponse {
$this->checkMountPointExists(trim($mountpoint));
$id = $this->manager->createFolder(trim($mountpoint));
- $this->checkFolderExists($id);
-
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id, $storageId);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse($this->formatFolder($folder));
}
@@ -299,7 +289,7 @@ public function addFolder(string $mountpoint): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'DELETE', url: '/folders/{id}')]
public function removeFolder(int $id): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
/** @var Folder */
$folder = $this->mountProvider->getFolder($id);
@@ -327,12 +317,9 @@ public function removeFolder(int $id): DataResponse {
public function setMountPoint(int $id, string $mountPoint): DataResponse {
$this->manager->renameFolder($id, trim($mountPoint));
- $this->checkFolderExists($id);
+ $folder = $this->checkedGetFolder($id);
$this->checkMountPointExists(trim($mountPoint));
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
-
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -351,12 +338,11 @@ public function setMountPoint(int $id, string $mountPoint): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/groups')]
public function addGroup(int $id, string $group): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->addApplicableGroup($id, $group);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -376,12 +362,11 @@ public function addGroup(int $id, string $group): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'DELETE', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
public function removeGroup(int $id, string $group): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->removeApplicableGroup($id, $group);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -402,12 +387,11 @@ public function removeGroup(int $id, string $group): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/groups/{group}', requirements: ['group' => '.+'])]
public function setPermissions(int $id, string $group, int $permissions): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->setGroupPermissions($id, $group, $permissions);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -429,12 +413,11 @@ public function setPermissions(int $id, string $group, int $permissions): DataRe
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/manageACL')]
public function setManageACL(int $id, string $mappingType, string $mappingId, bool $manageAcl): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->setManageACL($id, $mappingType, $mappingId, $manageAcl);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -454,12 +437,11 @@ public function setManageACL(int $id, string $mappingType, string $mappingId, bo
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/quota')]
public function setQuota(int $id, int $quota): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->setFolderQuota($id, $quota);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -479,12 +461,11 @@ public function setQuota(int $id, int $quota): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/acl')]
public function setACL(int $id, bool $acl): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
$this->manager->setFolderACL($id, $acl);
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
@@ -506,27 +487,23 @@ public function setACL(int $id, bool $acl): DataResponse {
#[NoAdminRequired]
#[FrontpageRoute(verb: 'POST', url: '/folders/{id}/mountpoint')]
public function renameFolder(int $id, string $mountpoint): DataResponse {
- $this->checkFolderExists($id);
+ $this->checkedGetFolder($id);
// Check if the new mountpoint is valid
if (empty($mountpoint)) {
throw new OCSBadRequestException('Mount point cannot be empty');
}
- // Check if we actually need to do anything
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
- if ($folder['mount_point'] === trim($mountpoint)) {
+ if ($folder->mountPoint === trim($mountpoint)) {
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
$this->checkMountPointExists(trim($mountpoint));
$this->manager->renameFolder($id, trim($mountpoint));
- // Get the new folder data
- /** @var InternalFolderOut */
- $folder = $this->manager->getFolder($id);
+ $folder = $this->checkedGetFolder($id);
return new DataResponse(['success' => true, 'folder' => $this->formatFolder($folder)]);
}
diff --git a/lib/DAV/ACLPlugin.php b/lib/DAV/ACLPlugin.php
index 5b72c777a..fc9312561 100644
--- a/lib/DAV/ACLPlugin.php
+++ b/lib/DAV/ACLPlugin.php
@@ -227,7 +227,7 @@ public function propPatch(string $path, PropPatch $propPatch): void {
}
$aclManager = $this->aclManagerFactory->getACLManager($this->user);
- $newPermissions = $aclManager->testACLPermissionsForPath($path, $rules);
+ $newPermissions = $aclManager->testACLPermissionsForPath($mount->getNumericStorageId(), $path, $rules);
if (!($newPermissions & Constants::PERMISSION_READ)) {
throw new BadRequest($this->l10n->t('You cannot remove your own read permission.'));
}
diff --git a/lib/DAV/GroupFoldersHome.php b/lib/DAV/GroupFoldersHome.php
index 663679b8d..a909e5fa0 100644
--- a/lib/DAV/GroupFoldersHome.php
+++ b/lib/DAV/GroupFoldersHome.php
@@ -9,6 +9,7 @@
namespace OCA\GroupFolders\DAV;
use OC\Files\Filesystem;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCA\GroupFolders\Folder\FolderManager;
use OCP\Files\IRootFolder;
use OCP\IUser;
@@ -17,9 +18,6 @@
use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
-/**
- * @psalm-import-type InternalFolder from FolderManager
- */
class GroupFoldersHome implements ICollection {
public function __construct(
private array $principalInfo,
@@ -51,17 +49,17 @@ public function createDirectory($name): never {
}
/**
- * @return ?InternalFolder
+ * @return ?FolderDefinition
*/
- private function getFolder(string $name): ?array {
+ private function getFolder(string $name): ?FolderDefinition {
$storageId = $this->rootFolder->getMountPoint()->getNumericStorageId();
if ($storageId === null) {
return null;
}
- $folders = $this->folderManager->getFoldersForUser($this->user, $storageId);
+ $folders = $this->folderManager->getFoldersForUser($this->user);
foreach ($folders as $folder) {
- if (basename($folder['mount_point']) === $name) {
+ if (basename($folder->mountPoint) === $name) {
return $folder;
}
}
@@ -69,19 +67,16 @@ private function getFolder(string $name): ?array {
return null;
}
- /**
- * @param InternalFolder $folder
- */
- private function getDirectoryForFolder(array $folder): GroupFolderNode {
+ private function getDirectoryForFolder(FolderDefinition $folder): GroupFolderNode {
$userHome = '/' . $this->user->getUID() . '/files';
- $node = $this->rootFolder->get($userHome . '/' . $folder['mount_point']);
+ $node = $this->rootFolder->get($userHome . '/' . $folder->mountPoint);
$view = Filesystem::getView();
if ($view === null) {
throw new RuntimeException('Unable to create view.');
}
- return new GroupFolderNode($view, $node, $folder['folder_id']);
+ return new GroupFolderNode($view, $node, $folder->id);
}
public function getChild($name): GroupFolderNode {
@@ -102,11 +97,11 @@ public function getChildren(): array {
return [];
}
- $folders = $this->folderManager->getFoldersForUser($this->user, $storageId);
+ $folders = $this->folderManager->getFoldersForUser($this->user);
// Filter out non top-level folders
- $folders = array_filter($folders, function (array $folder) {
- return !str_contains($folder['mount_point'], '/');
+ $folders = array_filter($folders, function (FolderDefinition $folder) {
+ return !str_contains($folder->mountPoint, '/');
});
return array_map($this->getDirectoryForFolder(...), $folders);
diff --git a/lib/Event/GroupVersionsExpireEnterFolderEvent.php b/lib/Event/GroupVersionsExpireEnterFolderEvent.php
index 77929cc8c..febf1f7b6 100644
--- a/lib/Event/GroupVersionsExpireEnterFolderEvent.php
+++ b/lib/Event/GroupVersionsExpireEnterFolderEvent.php
@@ -8,6 +8,7 @@
namespace OCA\GroupFolders\Event;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCP\EventDispatcher\Event;
/**
@@ -15,7 +16,7 @@
*/
class GroupVersionsExpireEnterFolderEvent extends Event {
public function __construct(
- public array $folder,
+ public readonly FolderDefinition $folder,
) {
parent::__construct();
}
diff --git a/lib/Folder/FolderDefinition.php b/lib/Folder/FolderDefinition.php
new file mode 100644
index 000000000..40cebd420
--- /dev/null
+++ b/lib/Folder/FolderDefinition.php
@@ -0,0 +1,21 @@
+ $groups
+ * @psalm-param list $manage
+ */
+ public function __construct(
+ int $id,
+ string $mountPoint,
+ int $quota,
+ bool $acl,
+ int $storageId,
+ int $rootId,
+ public readonly array $groups,
+ public readonly array $manage,
+ ) {
+ parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId);
+ }
+
+ /**
+ * @psalm-param array $groups
+ * @psalm-param list $manage
+ */
+ public static function fromFolder(FolderDefinition $folder, array $groups, array $manage): FolderDefinitionWithMappings {
+ return new FolderDefinitionWithMappings(
+ $folder->id,
+ $folder->mountPoint,
+ $folder->quota,
+ $folder->acl,
+ $folder->storageId,
+ $folder->rootId,
+ $groups,
+ $manage,
+ );
+ }
+
+ public function toArray(): array {
+ return [
+ 'id' => $this->id,
+ 'mount_point' => $this->mountPoint,
+ 'quota' => $this->quota,
+ 'acl' => $this->acl,
+ 'storage_id' => $this->storageId,
+ 'root_id' => $this->rootId,
+ 'groups' => $this->groups,
+ 'manage' => $this->manage,
+ ];
+ }
+}
diff --git a/lib/Folder/FolderDefinitionWithPermissions.php b/lib/Folder/FolderDefinitionWithPermissions.php
new file mode 100644
index 000000000..4b46a2aa5
--- /dev/null
+++ b/lib/Folder/FolderDefinitionWithPermissions.php
@@ -0,0 +1,78 @@
+ $groups
+ * @psalm-param list $manage
+ */
+ public function __construct(
+ int $id,
+ string $mountPoint,
+ int $quota,
+ bool $acl,
+ int $storageId,
+ int $rootId,
+ public readonly ICacheEntry $rootCacheEntry,
+ public readonly int $permissions,
+ ) {
+ parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId);
+ }
+
+ /**
+ * @psalm-param array $groups
+ * @psalm-param list $manage
+ */
+ public static function fromFolder(FolderDefinition $folder, ICacheEntry $rootCacheEntry, int $permissions): FolderDefinitionWithPermissions {
+ return new FolderDefinitionWithPermissions(
+ $folder->id,
+ $folder->mountPoint,
+ $folder->quota,
+ $folder->acl,
+ $folder->storageId,
+ $folder->rootId,
+ $rootCacheEntry,
+ $permissions,
+ );
+ }
+
+ public function toArray(): array {
+ return [
+ 'id' => $this->id,
+ 'mount_point' => $this->mountPoint,
+ 'permissions' => $this->permissions,
+ 'quota' => $this->quota,
+ 'acl' => $this->acl,
+ 'storage_id' => $this->storageId,
+ 'root_id' => $this->rootId,
+ 'root_cache_entry' => $this->rootCacheEntry,
+ ];
+ }
+
+ public function withAddedPermissions(int $permissions): self {
+ return new FolderDefinitionWithPermissions(
+ $this->id,
+ $this->mountPoint,
+ $this->quota,
+ $this->acl,
+ $this->storageId,
+ $this->rootId,
+ $this->rootCacheEntry,
+ $this->permissions | $permissions,
+ );
+ }
+}
diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php
index 84b836651..baa07af03 100644
--- a/lib/Folder/FolderManager.php
+++ b/lib/Folder/FolderManager.php
@@ -9,7 +9,6 @@
namespace OCA\GroupFolders\Folder;
use OC\Files\Cache\Cache;
-use OC\Files\Cache\CacheEntry;
use OC\Files\Node\Node;
use OCA\Circles\CirclesManager;
use OCA\Circles\Exceptions\CircleNotFoundException;
@@ -19,6 +18,7 @@
use OCA\GroupFolders\ACL\UserMapping\IUserMapping;
use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager;
use OCA\GroupFolders\ACL\UserMapping\UserMapping;
+use OCA\GroupFolders\Mount\FolderStorageManager;
use OCA\GroupFolders\Mount\GroupMountPoint;
use OCA\GroupFolders\ResponseDefinitions;
use OCP\AutoloadNotAllowedException;
@@ -46,23 +46,6 @@
* @psalm-import-type GroupFoldersUser from ResponseDefinitions
* @psalm-import-type GroupFoldersAclManage from ResponseDefinitions
* @psalm-import-type GroupFoldersApplicable from ResponseDefinitions
- * @psalm-type InternalFolder = array{
- * folder_id: int,
- * mount_point: string,
- * permissions: int,
- * quota: int,
- * acl: bool,
- * rootCacheEntry: ?CacheEntry,
- * }
- * @psalm-type InternalFolderOut = array{
- * id: int,
- * mount_point: string,
- * groups: array,
- * quota: int,
- * size: int,
- * acl: bool,
- * manage: list,
- * }
* @psalm-type InternalFolderMapping = array{
* folder_id: int,
* mapping_type: 'user'|'group',
@@ -80,73 +63,78 @@ public function __construct(
private readonly IEventDispatcher $eventDispatcher,
private readonly IConfig $config,
private readonly IUserMappingManager $userMappingManager,
+ private readonly FolderStorageManager $folderStorageManager,
) {
}
/**
- * @return array
+ * @return array
* @throws Exception
*/
public function getAllFolders(): array {
$applicableMap = $this->getAllApplicable();
+ $folderMappings = $this->getAllFolderMappings();
$query = $this->connection->getQueryBuilder();
- $query->select('folder_id', 'mount_point', 'quota', 'acl')
+ $query->select('folder_id', 'mount_point', 'quota', 'acl', 'storage_id', 'root_id')
->from('group_folders', 'f');
$rows = $query->executeQuery()->fetchAll();
$folderMap = [];
foreach ($rows as $row) {
- $id = (int)$row['folder_id'];
- $folderMap[$id] = [
- 'id' => $id,
- 'mount_point' => $row['mount_point'],
- 'groups' => $applicableMap[$id] ?? [],
- 'quota' => $this->getRealQuota((int)$row['quota']),
- 'size' => 0,
- 'acl' => (bool)$row['acl']
- ];
+ $folder = $this->rowToFolder($row);
+ $id = $folder->id;
+ $folderMap[$id] = FolderDefinitionWithMappings::fromFolder(
+ $folder,
+ $applicableMap[$id] ?? [],
+ $this->getManageAcl($folderMappings[$id] ?? []),
+ );
}
return $folderMap;
}
- /**
- * @throws Exception
- */
- private function getGroupFolderRootId(int $rootStorageId): int {
- $query = $this->connection->getQueryBuilder();
-
- $query->select('fileid')
- ->from('filecache')
- ->where($query->expr()->eq('storage', $query->createNamedParameter($rootStorageId)))
- ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5('__groupfolders'))));
-
- return (int)$query->executeQuery()->fetchOne();
- }
+ private function selectWithFileCache(?IQueryBuilder $query = null): IQueryBuilder {
+ if (!$query) {
+ $query = $this->connection->getQueryBuilder();
+ }
- private function joinQueryWithFileCache(IQueryBuilder $query, int $rootStorageId): void {
- $query->leftJoin('f', 'filecache', 'c', $query->expr()->andX(
- $query->expr()->eq('c.name', $query->expr()->castColumn('f.folder_id', IQueryBuilder::PARAM_STR)),
- $query->expr()->eq('c.parent', $query->createNamedParameter($this->getGroupFolderRootId($rootStorageId))),
- ));
- $query->hintShardKey('c.storage', $rootStorageId);
+ $query->select(
+ 'f.folder_id',
+ 'mount_point',
+ 'quota',
+ 'acl',
+ 'storage_id',
+ 'root_id',
+ 'c.fileid',
+ 'c.storage',
+ 'c.path',
+ 'c.name',
+ 'c.mimetype',
+ 'c.mimepart',
+ 'c.size',
+ 'c.mtime',
+ 'c.storage_mtime',
+ 'c.etag',
+ 'c.encrypted',
+ 'c.parent',
+ )
+ ->selectAlias('c.permissions', 'permissions')
+ ->from('group_folders', 'f')
+ ->leftJoin('f', 'filecache', 'c', $query->expr()->eq('c.fileid', 'f.root_id'));
+ return $query;
}
/**
- * @return array
+ * @return array
* @throws Exception
*/
- public function getAllFoldersWithSize(int $rootStorageId): array {
+ public function getAllFoldersWithSize(): array {
$applicableMap = $this->getAllApplicable();
- $query = $this->connection->getQueryBuilder();
-
- $query->select('folder_id', 'mount_point', 'quota', 'c.size', 'acl')
- ->from('group_folders', 'f');
- $this->joinQueryWithFileCache($query, $rootStorageId);
+ $query = $this->selectWithFileCache();
$rows = $query->executeQuery()->fetchAll();
@@ -154,42 +142,38 @@ public function getAllFoldersWithSize(int $rootStorageId): array {
$folderMap = [];
foreach ($rows as $row) {
- $id = (int)$row['folder_id'];
- $mappings = $folderMappings[$id] ?? [];
- $folderMap[$id] = [
- 'id' => $id,
- 'mount_point' => $row['mount_point'],
- 'groups' => $applicableMap[$id] ?? [],
- 'quota' => $this->getRealQuota((int)$row['quota']),
- 'size' => $row['size'] ? (int)$row['size'] : 0,
- 'acl' => (bool)$row['acl'],
- 'manage' => $this->getManageAcl($mappings)
- ];
+ $folder = $this->rowToFolder($row);
+ $id = $folder->id;
+ $folderMap[$id] = FolderWithMappingsAndCache::fromFolderWithMapping(
+ FolderDefinitionWithMappings::fromFolder(
+ $folder,
+ $applicableMap[$id] ?? [],
+ $this->getManageAcl($folderMappings[$id] ?? []),
+ ),
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ );
}
return $folderMap;
}
/**
- * @return array
+ * @return array
* @throws Exception
*/
- public function getAllFoldersForUserWithSize(int $rootStorageId, IUser $user): array {
+ public function getAllFoldersForUserWithSize(IUser $user): array {
$groups = $this->groupManager->getUserGroupIds($user);
$applicableMap = $this->getAllApplicable();
- $query = $this->connection->getQueryBuilder();
-
- $query->select('f.folder_id', 'mount_point', 'quota', 'c.size', 'acl')
- ->from('group_folders', 'f')
- ->innerJoin(
- 'f',
- 'group_folders_groups',
- 'a',
- $query->expr()->eq('f.folder_id', 'a.folder_id')
- )
+ $query = $this->selectWithFileCache();
+ $query->innerJoin(
+ 'f',
+ 'group_folders_groups',
+ 'a',
+ $query->expr()->eq('f.folder_id', 'a.folder_id'),
+ )
+ ->selectAlias('a.permissions', 'group_permissions')
->where($query->expr()->in('a.group_id', $query->createNamedParameter($groups, IQueryBuilder::PARAM_STR_ARRAY)));
- $this->joinQueryWithFileCache($query, $rootStorageId);
$rows = $query->executeQuery()->fetchAll();
@@ -197,17 +181,16 @@ public function getAllFoldersForUserWithSize(int $rootStorageId, IUser $user): a
$folderMap = [];
foreach ($rows as $row) {
- $id = (int)$row['folder_id'];
- $mappings = $folderMappings[$id] ?? [];
- $folderMap[$id] = [
- 'id' => $id,
- 'mount_point' => $row['mount_point'],
- 'groups' => $applicableMap[$id] ?? [],
- 'quota' => $this->getRealQuota((int)$row['quota']),
- 'size' => $row['size'] ? (int)$row['size'] : 0,
- 'acl' => (bool)$row['acl'],
- 'manage' => $this->getManageAcl($mappings)
- ];
+ $folder = $this->rowToFolder($row);
+ $id = $folder->id;
+ $folderMap[$id] = FolderWithMappingsAndCache::fromFolderWithMapping(
+ FolderDefinitionWithMappings::fromFolder(
+ $folder,
+ $applicableMap[$id] ?? [],
+ $this->getManageAcl($folderMappings[$id] ?? []),
+ ),
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ );
}
return $folderMap;
@@ -267,7 +250,7 @@ private function getManageAcl(array $mappings): array {
return [
'type' => 'user',
'id' => (string)$user->getUID(),
- 'displayname' => (string)$user->getDisplayName()
+ 'displayname' => (string)$user->getDisplayName(),
];
}
@@ -280,7 +263,7 @@ private function getManageAcl(array $mappings): array {
return [
'type' => 'group',
'id' => $group->getGID(),
- 'displayname' => $group->getDisplayName()
+ 'displayname' => $group->getDisplayName(),
];
}
@@ -293,7 +276,7 @@ private function getManageAcl(array $mappings): array {
return [
'type' => 'circle',
'id' => $circle->getSingleId(),
- 'displayname' => $circle->getDisplayName()
+ 'displayname' => $circle->getDisplayName(),
];
}
@@ -301,19 +284,12 @@ private function getManageAcl(array $mappings): array {
}, $mappings)));
}
- /**
- * @return ?InternalFolderOut
- * @throws Exception
- */
- public function getFolder(int $id, int $rootStorageId = 0): ?array {
+ public function getFolder(int $id): ?FolderWithMappingsAndCache {
$applicableMap = $this->getAllApplicable();
- $query = $this->connection->getQueryBuilder();
+ $query = $this->selectWithFileCache();
- $query->select('folder_id', 'mount_point', 'quota', 'c.size', 'acl')
- ->from('group_folders', 'f')
- ->where($query->expr()->eq('folder_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
- $this->joinQueryWithFileCache($query, $rootStorageId);
+ $query->where($query->expr()->eq('f.folder_id', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
$result = $query->executeQuery();
$row = $result->fetch();
@@ -324,15 +300,16 @@ public function getFolder(int $id, int $rootStorageId = 0): ?array {
$folderMappings = $this->getFolderMappings($id);
- return [
- 'id' => $id,
- 'mount_point' => (string)$row['mount_point'],
- 'groups' => $applicableMap[$id] ?? [],
- 'quota' => $this->getRealQuota((int)$row['quota']),
- 'size' => $row['size'] ?: 0,
- 'acl' => (bool)$row['acl'],
- 'manage' => $this->getManageAcl($folderMappings)
- ];
+ $folder = $this->rowToFolder($row);
+ $id = $folder->id;
+ return FolderWithMappingsAndCache::fromFolderWithMapping(
+ FolderDefinitionWithMappings::fromFolder(
+ $folder,
+ $applicableMap[$id] ?? [],
+ $this->getManageAcl($folderMappings),
+ ),
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ );
}
/**
@@ -388,7 +365,7 @@ private function getAllApplicable(): array {
$entry = [
'displayName' => $row['group_id'],
'permissions' => (int)$row['permissions'],
- 'type' => 'group'
+ 'type' => 'group',
];
} else {
$entityId = (string)$row['circle_id'];
@@ -401,7 +378,7 @@ private function getAllApplicable(): array {
$entry = [
'displayName' => $circle?->getDisplayName() ?? $row['circle_id'],
'permissions' => (int)$row['permissions'],
- 'type' => 'circle'
+ 'type' => 'circle',
];
}
@@ -412,8 +389,8 @@ private function getAllApplicable(): array {
}
/**
- * @throws Exception
* @return list
+ * @throws Exception
*/
private function getGroups(int $id): array {
$groups = $this->getAllApplicable()[$id] ?? [];
@@ -421,13 +398,13 @@ private function getGroups(int $id): array {
return array_map(fn (IGroup $group): array => [
'gid' => $group->getGID(),
- 'displayname' => $group->getDisplayName()
+ 'displayname' => $group->getDisplayName(),
], array_values(array_filter($groups)));
}
/**
- * @throws Exception
* @return list
+ * @throws Exception
*/
private function getCircles(int $id): array {
$circles = $this->getAllApplicable()[$id] ?? [];
@@ -451,7 +428,7 @@ private function getCircles(int $id): array {
return array_map(fn (Circle $circle): array => [
'sid' => $circle->getSingleId(),
- 'displayname' => $circle->getDisplayName()
+ 'displayname' => $circle->getDisplayName(),
], array_values(array_filter(array_merge($circles, $nested))));
}
@@ -459,6 +436,7 @@ private function getCircles(int $id): array {
* Check if the user is able to configure the advanced folder permissions. This
* is the case if the user is an admin, has admin permissions for the group folder
* app or is member of a group that can manage permissions for the specific folder.
+ *
* @throws Exception
*/
public function canManageACL(int $folderId, IUser $user): bool {
@@ -499,8 +477,8 @@ private function getManagerMappings(int $folderId): array {
}
/**
- * @throws Exception
* @return list
+ * @throws Exception
*/
public function searchGroups(int $id, string $search = ''): array {
$groups = $this->getGroups($id);
@@ -512,8 +490,8 @@ public function searchGroups(int $id, string $search = ''): array {
}
/**
- * @throws Exception
* @return list
+ * @throws Exception
*/
public function searchCircles(int $id, string $search = ''): array {
$circles = $this->getCircles($id);
@@ -525,8 +503,8 @@ public function searchCircles(int $id, string $search = ''): array {
}
/**
- * @throws Exception
* @return list
+ * @throws Exception
*/
public function searchUsers(int $id, string $search = '', int $limit = 10, int $offset = 0): array {
$groups = $this->getGroups($id);
@@ -539,7 +517,7 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $
if (!isset($users[$uid])) {
$users[$uid] = [
'uid' => $uid,
- 'displayname' => $displayName
+ 'displayname' => $displayName,
];
}
}
@@ -561,7 +539,7 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $
if (!isset($users[$uid])) {
$users[$uid] = [
'uid' => $uid,
- 'displayname' => $member->getDisplayName()
+ 'displayname' => $member->getDisplayName(),
];
}
}
@@ -570,92 +548,62 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $
return array_values($users);
}
+ private function rowToFolder(array $row): FolderDefinition {
+ return new FolderDefinition(
+ (int)$row['folder_id'],
+ (string)$row['mount_point'],
+ $this->getRealQuota((int)$row['quota']),
+ (bool)$row['acl'],
+ (int)$row['storage_id'],
+ (int)$row['root_id'],
+ );
+ }
+
/**
- * @return list
+ * @return list
* @throws Exception
*/
- public function getFoldersForGroup(string $groupId, int $rootStorageId = 0): array {
- $query = $this->connection->getQueryBuilder();
+ public function getFoldersForGroup(string $groupId): array {
+ $query = $this->selectWithFileCache();
- $query->select(
- 'f.folder_id',
- 'mount_point',
- 'quota',
- 'acl',
- 'c.fileid',
- 'c.storage',
- 'c.path',
- 'c.name',
- 'c.mimetype',
- 'c.mimepart',
- 'c.size',
- 'c.mtime',
- 'c.storage_mtime',
- 'c.etag',
- 'c.encrypted',
- 'c.parent'
+ $query->innerJoin(
+ 'f',
+ 'group_folders_groups',
+ 'a',
+ $query->expr()->eq('f.folder_id', 'a.folder_id'),
)
->selectAlias('a.permissions', 'group_permissions')
- ->selectAlias('c.permissions', 'permissions')
- ->from('group_folders', 'f')
- ->innerJoin(
- 'f',
- 'group_folders_groups',
- 'a',
- $query->expr()->eq('f.folder_id', 'a.folder_id')
- )
->where($query->expr()->eq('a.group_id', $query->createNamedParameter($groupId)));
- $this->joinQueryWithFileCache($query, $rootStorageId);
-
- $result = $query->executeQuery()->fetchAll();
-
- return array_values(array_map(fn (array $folder): array => [
- 'folder_id' => (int)$folder['folder_id'],
- 'mount_point' => (string)$folder['mount_point'],
- 'permissions' => (int)$folder['group_permissions'],
- 'quota' => $this->getRealQuota((int)$folder['quota']),
- 'acl' => (bool)$folder['acl'],
- 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null
- ], $result));
+
+ return array_values(array_map(function (array $row): FolderDefinitionWithPermissions {
+ $folder = $this->rowToFolder($row);
+ return FolderDefinitionWithPermissions::fromFolder(
+ $folder,
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ (int)$row['group_permissions']
+ );
+ }, $query->executeQuery()->fetchAll()));
}
/**
* @param string[] $groupIds
- * @return list
+ * @return list
* @throws Exception
*/
- public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): array {
- $query = $this->connection->getQueryBuilder();
+ public function getFoldersForGroups(array $groupIds): array {
+ if (count($groupIds) === 0) {
+ return [];
+ }
+ $query = $this->selectWithFileCache();
- $query->select(
- 'f.folder_id',
- 'mount_point',
- 'quota',
- 'acl',
- 'c.fileid',
- 'c.storage',
- 'c.path',
- 'c.name',
- 'c.mimetype',
- 'c.mimepart',
- 'c.size',
- 'c.mtime',
- 'c.storage_mtime',
- 'c.etag',
- 'c.encrypted',
- 'c.parent'
+ $query->innerJoin(
+ 'f',
+ 'group_folders_groups',
+ 'a',
+ $query->expr()->eq('f.folder_id', 'a.folder_id'),
)
->selectAlias('a.permissions', 'group_permissions')
- ->selectAlias('c.permissions', 'permissions')
- ->from('group_folders', 'f')
- ->innerJoin(
- 'f',
- 'group_folders_groups',
- 'a',
- $query->expr()->eq('f.folder_id', 'a.folder_id')
- )
->where($query->expr()->in('a.group_id', $query->createParameter('groupIds')));
- $this->joinQueryWithFileCache($query, $rootStorageId);
// add chunking because Oracle can't deal with more than 1000 values in an expression list for in queries.
$result = [];
@@ -664,21 +612,21 @@ public function getFoldersForGroups(array $groupIds, int $rootStorageId = 0): ar
$result = array_merge($result, $query->executeQuery()->fetchAll());
}
- return array_map(fn (array $folder): array => [
- 'folder_id' => (int)$folder['folder_id'],
- 'mount_point' => (string)$folder['mount_point'],
- 'permissions' => (int)$folder['group_permissions'],
- 'quota' => $this->getRealQuota((int)$folder['quota']),
- 'acl' => (bool)$folder['acl'],
- 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null
- ], array_values($result));
+ return array_values(array_map(function (array $row): FolderDefinitionWithPermissions {
+ $folder = $this->rowToFolder($row);
+ return FolderDefinitionWithPermissions::fromFolder(
+ $folder,
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ (int)$row['group_permissions']
+ );
+ }, $result));
}
/**
- * @return list
+ * @return list
* @throws Exception
*/
- public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId = 0): array {
+ public function getFoldersFromCircleMemberships(IUser $user): array {
$circlesManager = $this->getCirclesManager();
if ($circlesManager === null) {
return [];
@@ -691,35 +639,15 @@ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId
}
$queryHelper = $circlesManager->getQueryHelper();
- $query = $queryHelper->getQueryBuilder();
+ $query = $this->selectWithFileCache($queryHelper->getQueryBuilder());
- $query->select(
- 'f.folder_id',
- 'f.mount_point',
- 'f.quota',
- 'f.acl',
- 'c.fileid',
- 'c.storage',
- 'c.path',
- 'c.name',
- 'c.mimetype',
- 'c.mimepart',
- 'c.size',
- 'c.mtime',
- 'c.storage_mtime',
- 'c.etag',
- 'c.encrypted',
- 'c.parent'
+ $query->innerJoin(
+ 'f',
+ 'group_folders_groups',
+ 'a',
+ $query->expr()->eq('f.folder_id', 'a.folder_id'),
)
->selectAlias('a.permissions', 'group_permissions')
- ->selectAlias('c.permissions', 'permissions')
- ->from('group_folders', 'f')
- ->innerJoin(
- 'f',
- 'group_folders_groups',
- 'a',
- $query->expr()->eq('f.folder_id', 'a.folder_id')
- )
->where($query->expr()->neq('a.circle_id', $query->createNamedParameter('')));
/** @psalm-suppress RedundantCondition */
@@ -728,16 +656,15 @@ public function getFoldersFromCircleMemberships(IUser $user, int $rootStorageId
} else {
$queryHelper->limitToInheritedMembers('a', 'circle_id', $federatedUser);
}
- $this->joinQueryWithFileCache($query, $rootStorageId);
-
- return array_map(fn (array $folder): array => [
- 'folder_id' => (int)$folder['folder_id'],
- 'mount_point' => (string)$folder['mount_point'],
- 'permissions' => (int)$folder['group_permissions'],
- 'quota' => $this->getRealQuota((int)$folder['quota']),
- 'acl' => (bool)$folder['acl'],
- 'rootCacheEntry' => (isset($folder['fileid'])) ? Cache::cacheEntryFromData($folder, $this->mimeTypeLoader) : null
- ], array_values($query->executeQuery()->fetchAll()));
+
+ return array_values(array_map(function (array $row): FolderDefinitionWithPermissions {
+ $folder = $this->rowToFolder($row);
+ return FolderDefinitionWithPermissions::fromFolder(
+ $folder,
+ Cache::cacheEntryFromData($row, $this->mimeTypeLoader),
+ $row['group_permissions']
+ );
+ }, $query->executeQuery()->fetchAll()));
}
@@ -755,6 +682,13 @@ public function createFolder(string $mountPoint): int {
$query->executeStatement();
$id = $query->getLastInsertId();
+ ['storage_id' => $storageId, 'root_id' => $rootId] = $this->folderStorageManager->getRootAndStorageIdForFolder($id);
+ $query->update('group_folders')
+ ->set('root_id', $query->createNamedParameter($rootId))
+ ->set('storage_id', $query->createNamedParameter($storageId))
+ ->where($query->expr()->eq('folder_id', $query->createNamedParameter($id)));
+ $query->executeStatement();
+
$this->eventDispatcher->dispatchTyped(new CriticalActionPerformedEvent('A new groupfolder "%s" was created with id %d', [$mountPoint, $id]));
return $id;
@@ -776,7 +710,7 @@ public function addApplicableGroup(int $folderId, string $groupId): void {
'folder_id' => $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT),
'group_id' => $query->createNamedParameter($groupId),
'circle_id' => $query->createNamedParameter($circleId ?? ''),
- 'permissions' => $query->createNamedParameter(Constants::PERMISSION_ALL)
+ 'permissions' => $query->createNamedParameter(Constants::PERMISSION_ALL),
]);
$query->executeStatement();
@@ -792,14 +726,14 @@ public function removeApplicableGroup(int $folderId, string $groupId): void {
$query->delete('group_folders_groups')
->where(
$query->expr()->eq(
- 'folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)
- )
+ 'folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT),
+ ),
)
->andWhere(
$query->expr()->orX(
$query->expr()->eq('group_id', $query->createNamedParameter($groupId)),
- $query->expr()->eq('circle_id', $query->createNamedParameter($groupId))
- )
+ $query->expr()->eq('circle_id', $query->createNamedParameter($groupId)),
+ ),
);
$query->executeStatement();
@@ -817,14 +751,14 @@ public function setGroupPermissions(int $folderId, string $groupId, int $permiss
->set('permissions', $query->createNamedParameter($permissions, IQueryBuilder::PARAM_INT))
->where(
$query->expr()->eq(
- 'folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT)
- )
+ 'folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT),
+ ),
)
->andWhere(
$query->expr()->orX(
$query->expr()->eq('group_id', $query->createNamedParameter($groupId)),
- $query->expr()->eq('circle_id', $query->createNamedParameter($groupId))
- )
+ $query->expr()->eq('circle_id', $query->createNamedParameter($groupId)),
+ ),
);
$query->executeStatement();
@@ -842,7 +776,7 @@ public function setManageACL(int $folderId, string $type, string $id, bool $mana
->values([
'folder_id' => $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT),
'mapping_type' => $query->createNamedParameter($type),
- 'mapping_id' => $query->createNamedParameter($id)
+ 'mapping_id' => $query->createNamedParameter($id),
]);
} else {
$query->delete('group_folders_manage')
@@ -962,21 +896,23 @@ public function setFolderACL(int $folderId, bool $acl): void {
}
/**
- * @return list
+ * @return list
* @throws Exception
*/
- public function getFoldersForUser(IUser $user, int $rootStorageId = 0): array {
+ public function getFoldersForUser(IUser $user): array {
$groups = $this->groupManager->getUserGroupIds($user);
+ /** @var list $folders */
$folders = array_merge(
- $this->getFoldersForGroups($groups, $rootStorageId),
- $this->getFoldersFromCircleMemberships($user, $rootStorageId)
+ $this->getFoldersForGroups($groups),
+ $this->getFoldersFromCircleMemberships($user),
);
+ /** @var array $mergedFolders */
$mergedFolders = [];
foreach ($folders as $folder) {
- $id = $folder['folder_id'];
+ $id = $folder->id;
if (isset($mergedFolders[$id])) {
- $mergedFolders[$id]['permissions'] |= $folder['permissions'];
+ $mergedFolders[$id] = $mergedFolders[$id]->withAddedPermissions($folder->permissions);
} else {
$mergedFolders[$id] = $folder;
}
@@ -990,15 +926,16 @@ public function getFoldersForUser(IUser $user, int $rootStorageId = 0): array {
*/
public function getFolderPermissionsForUser(IUser $user, int $folderId): int {
$groups = $this->groupManager->getUserGroupIds($user);
+ /** @var list $folders */
$folders = array_merge(
$this->getFoldersForGroups($groups),
- $this->getFoldersFromCircleMemberships($user)
+ $this->getFoldersFromCircleMemberships($user),
);
$permissions = 0;
foreach ($folders as $folder) {
- if ($folderId === $folder['folder_id']) {
- $permissions |= $folder['permissions'];
+ if ($folderId === $folder->id) {
+ $permissions |= $folder->permissions;
}
}
diff --git a/lib/Folder/FolderWithMappingsAndCache.php b/lib/Folder/FolderWithMappingsAndCache.php
new file mode 100644
index 000000000..4ddf3d4fe
--- /dev/null
+++ b/lib/Folder/FolderWithMappingsAndCache.php
@@ -0,0 +1,68 @@
+ $groups
+ * @psalm-param list $manage
+ */
+ public function __construct(
+ int $id,
+ string $mountPoint,
+ int $quota,
+ bool $acl,
+ int $storageId,
+ int $rootId,
+ array $groups,
+ array $manage,
+ public readonly ICacheEntry $rootCacheEntry,
+ ) {
+ parent::__construct($id, $mountPoint, $quota, $acl, $storageId, $rootId, $groups, $manage);
+ }
+
+ /**
+ * @psalm-param array $groups
+ * @psalm-param list $manage
+ */
+ public static function fromFolderWithMapping(FolderDefinitionWithMappings $folder, ICacheEntry $rootCacheEntry): FolderWithMappingsAndCache {
+ return new FolderWithMappingsAndCache(
+ $folder->id,
+ $folder->mountPoint,
+ $folder->quota,
+ $folder->acl,
+ $folder->storageId,
+ $folder->rootId,
+ $folder->groups,
+ $folder->manage,
+ $rootCacheEntry,
+ );
+ }
+
+ public function toArray(): array {
+ return [
+ 'id' => $this->id,
+ 'mount_point' => $this->mountPoint,
+ 'quota' => $this->quota,
+ 'acl' => $this->acl,
+ 'storage_id' => $this->storageId,
+ 'root_id' => $this->rootId,
+ 'root_cache_entry' => $this->rootCacheEntry,
+ 'groups' => $this->groups,
+ 'manage' => $this->manage,
+ ];
+ }
+}
diff --git a/lib/Migration/Version102020Date20180806161449.php b/lib/Migration/Version102020Date20180806161449.php
index 9881f71c4..0b9933505 100644
--- a/lib/Migration/Version102020Date20180806161449.php
+++ b/lib/Migration/Version102020Date20180806161449.php
@@ -9,6 +9,7 @@
namespace OCA\GroupFolders\Migration;
use OCP\DB\ISchemaWrapper;
+use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
@@ -34,6 +35,12 @@ public function changeSchema(IOutput $output, \Closure $schemaClosure, array $op
// Removed in migration Version19000Date20240903062631
//'default' => -3,
]);
+
+ // from Version20000Date20250612140256.php
+ $table->addColumn('root_id', Types::BIGINT, ['notnull' => false]);
+ $table->addColumn('storage_id', Types::BIGINT, ['notnull' => false]);
+ $table->addColumn('options', Types::TEXT, ['notnull' => false]);
+
$table->setPrimaryKey(['folder_id']);
}
diff --git a/lib/Migration/Version20000Date20250612140256.php b/lib/Migration/Version20000Date20250612140256.php
new file mode 100644
index 000000000..550101ab7
--- /dev/null
+++ b/lib/Migration/Version20000Date20250612140256.php
@@ -0,0 +1,131 @@
+hasTable('group_folders')) {
+ $table = $schema->getTable('group_folders');
+ if (!$table->hasColumn('root_id')) {
+ $table->addColumn('root_id', Types::BIGINT, ['notnull' => false]);
+ }
+ if (!$table->hasColumn('storage_id')) {
+ $table->addColumn('storage_id', Types::BIGINT, ['notnull' => false]);
+ }
+ if (!$table->hasColumn('options')) {
+ $table->addColumn('options', Types::TEXT, ['notnull' => false]);
+ }
+ return $schema;
+ }
+ return null;
+ }
+
+ #[Override]
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
+ $rootIds = $this->getJailedRootIds();
+ $storageId = $this->getJailedGroupFolderStorageId();
+ if (count($rootIds) === 0 || $storageId === null) {
+ return;
+ }
+
+ try {
+ $this->connection->beginTransaction();
+
+ $query = $this->connection->getQueryBuilder();
+ $query->update('group_folders')
+ ->set('root_id', $query->createParameter('root_id'))
+ ->set('storage_id', $query->createNamedParameter($storageId))
+ ->where($query->expr()->eq('folder_id', $query->createParameter('folder_id')));
+
+ foreach ($rootIds as $folderId => $rootId) {
+ $query->setParameter('root_id', $rootId);
+ $query->setParameter('folder_id', $folderId);
+ $query->executeStatement();
+ }
+
+ $this->connection->commit();
+ } catch (\Exception $e) {
+ $this->connection->rollBack();
+ throw $e;
+ }
+ }
+
+ /**
+ * @return array
+ */
+ private function getJailedRootIds(): array {
+ $parentFolderId = $this->getJailedGroupFolderRootId();
+ if ($parentFolderId === null) {
+ return [];
+ }
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select('name', 'fileid')
+ ->from('filecache')
+ ->where($query->expr()->eq('parent', $query->createNamedParameter($parentFolderId)));
+ $result = $query->executeQuery();
+
+ $rootIds = [];
+ while ($row = $result->fetch()) {
+ if (is_numeric($row['name'])) {
+ $rootIds[(int)$row['name']] = (int)$row['fileid'];
+ }
+ }
+ return $rootIds;
+ }
+
+ private function getJailedGroupFolderRootId(): ?int {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('fileid')
+ ->from('filecache')
+ ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5('__groupfolders'))));
+
+ $id = $query->executeQuery()->fetchOne();
+ if ($id === null) {
+ return null;
+ } else {
+ return (int)$id;
+ }
+ }
+
+ private function getJailedGroupFolderStorageId(): ?int {
+ $query = $this->connection->getQueryBuilder();
+ $query->select('storage')
+ ->from('filecache')
+ ->andWhere($query->expr()->eq('path_hash', $query->createNamedParameter(md5('__groupfolders'))));
+
+ $id = $query->executeQuery()->fetchOne();
+ if ($id === null) {
+ return null;
+ } else {
+ return (int)$id;
+ }
+ }
+}
diff --git a/lib/Migration/WrongDefaultQuotaRepairStep.php b/lib/Migration/WrongDefaultQuotaRepairStep.php
index 60eb7487a..1048eef63 100644
--- a/lib/Migration/WrongDefaultQuotaRepairStep.php
+++ b/lib/Migration/WrongDefaultQuotaRepairStep.php
@@ -30,7 +30,7 @@ public function getName(): string {
*/
public function run(IOutput $output): void {
foreach ($this->manager->getAllFolders() as $id => $folder) {
- $quota = $folder['quota'];
+ $quota = $folder->quota;
$changed = false;
if ($quota === 1073741274) {
diff --git a/lib/Mount/FolderStorageManager.php b/lib/Mount/FolderStorageManager.php
new file mode 100644
index 000000000..fd1768a28
--- /dev/null
+++ b/lib/Mount/FolderStorageManager.php
@@ -0,0 +1,104 @@
+
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\GroupFolders\Mount;
+
+use OC\Files\Storage\Wrapper\Jail;
+use OCA\GroupFolders\ACL\ACLManagerFactory;
+use OCA\GroupFolders\ACL\ACLStorageWrapper;
+use OCP\Files\Folder;
+use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
+use OCP\Files\Storage\IStorage;
+use OCP\IAppConfig;
+use OCP\IUser;
+
+class FolderStorageManager {
+ private readonly bool $enableEncryption;
+
+ public function __construct(
+ private readonly IRootFolder $rootFolder,
+ private readonly IAppConfig $appConfig,
+ private readonly ACLManagerFactory $aclManagerFactory,
+ ) {
+ $this->enableEncryption = $this->appConfig->getValueString('groupfolders', 'enable_encryption', 'false') === 'true';
+ }
+
+ /**
+ * @return array{storage_id: int, root_id: int}
+ */
+ public function getRootAndStorageIdForFolder(int $folderId): array {
+ $storage = $this->getBaseStorageForFolder($folderId);
+ $cache = $storage->getCache();
+ $id = $cache->getId('');
+ if ($id === -1) {
+ $storage->getScanner()->scan('');
+ $id = $cache->getId('');
+ if ($id === -1) {
+ throw new \Exception('Group folder root is not in cache even after scanning for folder ' . $folderId);
+ }
+ }
+ return [
+ 'storage_id' => $cache->getNumericStorageId(),
+ 'root_id' => $id,
+ ];
+ }
+
+ /**
+ * @param 'files'|'trash'|'versions' $type
+ */
+ public function getBaseStorageForFolder(int $folderId, ?\OCA\GroupFolders\Folder\FolderDefinition $folder = null, ?IUser $user = null, bool $inShare = false, string $type = 'files'): IStorage {
+ try {
+ /** @var Folder $parentFolder */
+ $parentFolder = $this->rootFolder->get('__groupfolders');
+ } catch (NotFoundException) {
+ $parentFolder = $this->rootFolder->newFolder('__groupfolders');
+ }
+
+ if ($type !== 'files') {
+ try {
+ /** @var Folder $parentFolder */
+ $parentFolder = $parentFolder->get($type);
+ } catch (NotFoundException) {
+ $parentFolder = $parentFolder->newFolder($type);
+ }
+ }
+
+ try {
+ /** @var Folder $storageFolder */
+ $storageFolder = $parentFolder->get((string)$folderId);
+ } catch (NotFoundException) {
+ $storageFolder = $parentFolder->newFolder((string)$folderId);
+ }
+ $rootStorage = $storageFolder->getStorage();
+ $rootPath = $storageFolder->getInternalPath();
+
+ // apply acl before jail
+ if ($folder && $folder->acl && $user) {
+ $aclManager = $this->aclManagerFactory->getACLManager($user);
+ $rootStorage = new ACLStorageWrapper([
+ 'storage' => $rootStorage,
+ 'acl_manager' => $aclManager,
+ 'in_share' => $inShare,
+ 'storage_id' => $rootStorage->getCache()->getNumericStorageId(),
+ ]);
+ }
+
+ if ($this->enableEncryption) {
+ return new GroupFolderEncryptionJail([
+ 'storage' => $rootStorage,
+ 'root' => $rootPath,
+ ]);
+ } else {
+ return new Jail([
+ 'storage' => $rootStorage,
+ 'root' => $rootPath,
+ ]);
+ }
+ }
+}
diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php
index cbbc2800c..694e25201 100644
--- a/lib/Mount/GroupFolderStorage.php
+++ b/lib/Mount/GroupFolderStorage.php
@@ -12,6 +12,7 @@
use OC\Files\ObjectStore\ObjectStoreScanner;
use OC\Files\ObjectStore\ObjectStoreStorage;
use OC\Files\Storage\Wrapper\Quota;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCP\Files\Cache\ICache;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\Cache\IScanner;
@@ -21,7 +22,7 @@
use OCP\IUserSession;
class GroupFolderStorage extends Quota implements IConstructableStorage {
- private readonly int $folderId;
+ private readonly FolderDefinition $folder;
private readonly ?ICacheEntry $rootEntry;
private readonly IUserSession $userSession;
private readonly ?IUser $mountOwner;
@@ -30,14 +31,18 @@ class GroupFolderStorage extends Quota implements IConstructableStorage {
public function __construct($parameters) {
parent::__construct($parameters);
- $this->folderId = $parameters['folder_id'];
+ $this->folder = $parameters['folder'];
$this->rootEntry = $parameters['rootCacheEntry'];
$this->userSession = $parameters['userSession'];
$this->mountOwner = $parameters['mountOwner'];
}
public function getFolderId(): int {
- return $this->folderId;
+ return $this->folder->id;
+ }
+
+ public function getFolder(): FolderDefinition {
+ return $this->folder;
}
public function getOwner(string $path): string|false {
diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php
index 2008669a9..ee3e55d55 100644
--- a/lib/Mount/MountProvider.php
+++ b/lib/Mount/MountProvider.php
@@ -8,11 +8,10 @@
namespace OCA\GroupFolders\Mount;
-use OC\Files\Storage\Wrapper\Jail;
use OC\Files\Storage\Wrapper\PermissionsMask;
use OCA\GroupFolders\ACL\ACLManager;
use OCA\GroupFolders\ACL\ACLManagerFactory;
-use OCA\GroupFolders\ACL\ACLStorageWrapper;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
use OCP\Constants;
use OCP\DB\QueryBuilder\IQueryBuilder;
@@ -25,18 +24,13 @@
use OCP\Files\NotFoundException;
use OCP\Files\Storage\IStorage;
use OCP\Files\Storage\IStorageFactory;
-use OCP\ICache;
use OCP\IDBConnection;
use OCP\IRequest;
use OCP\IUser;
use OCP\IUserSession;
-/**
- * @psalm-import-type InternalFolder from FolderManager
- */
class MountProvider implements IMountProvider {
private ?Folder $root = null;
- private ?int $rootStorageId = null;
public function __construct(
private readonly FolderManager $folderManager,
@@ -46,54 +40,40 @@ public function __construct(
private readonly IRequest $request,
private readonly IMountProviderCollection $mountProviderCollection,
private readonly IDBConnection $connection,
- private readonly ICache $cache,
+ private readonly FolderStorageManager $folderStorageManager,
private readonly bool $allowRootShare,
private readonly bool $enableEncryption,
) {
}
- private function getRootStorageId(): int {
- if ($this->rootStorageId === null) {
- $cached = $this->cache->get('root_storage_id');
- if ($cached !== null) {
- $this->rootStorageId = $cached;
- } else {
- $id = $this->getRootFolder()->getStorage()->getCache()->getNumericStorageId();
- $this->cache->set('root_storage_id', $id);
- $this->rootStorageId = $id;
- }
- }
-
- return $this->rootStorageId;
- }
-
/**
- * @return list
+ * @return list
*/
public function getFoldersForUser(IUser $user): array {
- return $this->folderManager->getFoldersForUser($user, $this->getRootStorageId());
+ return $this->folderManager->getFoldersForUser($user);
}
public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
$folders = $this->getFoldersForUser($user);
- $mountPoints = array_map(fn (array $folder): string => 'files/' . $folder['mount_point'], $folders);
+ $mountPoints = array_map(fn (FolderDefinitionWithPermissions $folder): string => 'files/' . $folder->mountPoint, $folders);
$conflicts = $this->findConflictsForUser($user, $mountPoints);
- $foldersWithAcl = array_filter($folders, fn (array $folder): bool => $folder['acl']);
- $aclRootPaths = array_map(fn (array $folder): string => $this->getJailPath($folder['folder_id']), $foldersWithAcl);
- $aclManager = $this->aclManagerFactory->getACLManager($user, $this->getRootStorageId());
- $rootRules = $aclManager->getRelevantRulesForPath($aclRootPaths);
+ /** @var array $foldersWithAcl */
+ $foldersWithAcl = array_filter($folders, fn (FolderDefinitionWithPermissions $folder): bool => $folder->acl);
+ $rootFileIds = array_map(fn (FolderDefinitionWithPermissions $folder): int => $folder->rootId, $foldersWithAcl);
+ $aclManager = $this->aclManagerFactory->getACLManager($user);
+ $rootRules = $aclManager->getRulesByFileIds($rootFileIds);
- return array_filter(array_map(function (array $folder) use ($user, $loader, $conflicts, $aclManager, $rootRules): ?IMountPoint {
+ return array_filter(array_map(function (FolderDefinitionWithPermissions $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'];
+ $originalFolderName = $folder->mountPoint;
if (in_array($originalFolderName, $conflicts)) {
/** @var IStorage $userStorage */
$userStorage = $this->mountProviderCollection->getHomeMountForUser($user)->getStorage();
$userCache = $userStorage->getCache();
$i = 1;
- $folderName = $folder['mount_point'] . ' (' . $i++ . ')';
+ $folderName = $folder->mountPoint . ' (' . $i++ . ')';
while ($userCache->inCache("files/$folderName")) {
$folderName = $originalFolderName . ' (' . $i++ . ')';
@@ -105,13 +85,9 @@ public function getMountsForUser(IUser $user, IStorageFactory $loader): array {
}
return $this->getMount(
- $folder['folder_id'],
- '/' . $user->getUID() . '/files/' . $folder['mount_point'],
- $folder['permissions'],
- $folder['quota'],
- $folder['rootCacheEntry'],
+ $folder,
+ '/' . $user->getUID() . '/files/' . $folder->mountPoint,
$loader,
- $folder['acl'],
$user,
$aclManager,
$rootRules
@@ -139,54 +115,31 @@ private function getCurrentUID(): ?string {
}
public function getMount(
- int $id,
+ FolderDefinitionWithPermissions $folder,
string $mountPoint,
- int $permissions,
- int $quota,
- ?ICacheEntry $cacheEntry = null,
?IStorageFactory $loader = null,
- bool $acl = false,
?IUser $user = null,
?ACLManager $aclManager = null,
array $rootRules = [],
): ?IMountPoint {
- if (!$cacheEntry) {
- // trigger folder creation
- $folder = $this->getFolder($id);
- if ($folder === null) {
- return null;
- }
-
- $cacheEntry = $this->getRootFolder()->getStorage()->getCache()->get($folder->getId());
- if ($cacheEntry === false) {
- return null;
- }
- }
+ $cacheEntry = $folder->rootCacheEntry;
$storage = $this->getRootFolder()->getStorage();
$storage->setOwner($user?->getUID());
- $rootPath = $this->getJailPath($id);
+ $rootPath = $this->getJailPath($folder->id);
- // apply acl before jail
- if ($acl && $user) {
- $inShare = !\OC::$CLI && ($this->getCurrentUID() === null || $this->getCurrentUID() !== $user->getUID());
- $aclManager ??= $this->aclManagerFactory->getACLManager($user, $this->getRootStorageId());
+ if ($aclManager && $folder->acl && $user) {
$aclRootPermissions = $aclManager->getPermissionsForPathFromRules($rootPath, $rootRules);
- $storage = new ACLStorageWrapper([
- 'storage' => $storage,
- 'acl_manager' => $aclManager,
- 'in_share' => $inShare,
- ]);
$cacheEntry['permissions'] &= $aclRootPermissions;
}
- $quotaStorage = $this->getGroupFolderStorage($id, $storage, $user, $rootPath, $quota, $cacheEntry);
+ $quotaStorage = $this->getGroupFolderStorage($folder, $user, $cacheEntry);
$maskedStore = new PermissionsMask([
'storage' => $quotaStorage,
- 'mask' => $permissions,
+ 'mask' => $folder->permissions,
]);
if (!$this->allowRootShare) {
@@ -197,7 +150,7 @@ public function getMount(
}
return new GroupMountPoint(
- $id,
+ $folder->id,
$maskedStore,
$mountPoint,
null,
@@ -206,7 +159,7 @@ public function getMount(
}
public function getTrashMount(
- int $id,
+ FolderDefinitionWithPermissions $folder,
string $mountPoint,
int $quota,
IStorageFactory $loader,
@@ -218,12 +171,10 @@ public function getTrashMount(
$storage->setOwner($user->getUID());
- $trashPath = $this->getRootFolder()->getInternalPath() . '/trash/' . $id;
-
- $trashStorage = $this->getGroupFolderStorage($id, $storage, $user, $trashPath, $quota, $cacheEntry);
+ $trashStorage = $this->getGroupFolderStorage($folder, $user, $cacheEntry, 'trash');
return new GroupMountPoint(
- $id,
+ $folder->id,
$trashStorage,
$mountPoint,
null,
@@ -231,36 +182,35 @@ public function getTrashMount(
);
}
+ /**
+ * @param 'files'|'trash'|'versions' $type
+ */
public function getGroupFolderStorage(
- int $id,
- IStorage $rootStorage,
+ FolderDefinitionWithPermissions $folder,
?IUser $user,
- string $rootPath,
- int $quota,
?ICacheEntry $rootCacheEntry,
+ string $type = 'files',
): IStorage {
+ if ($user) {
+ $inShare = !\OC::$CLI && ($this->getCurrentUID() === null || $this->getCurrentUID() !== $user->getUID());
+ $baseStorage = $this->folderStorageManager->getBaseStorageForFolder($folder->id, $folder, $user, $inShare, $type);
+ } else {
+ $baseStorage = $this->folderStorageManager->getBaseStorageForFolder($folder->id, $folder, null, false, $type);
+ }
if ($this->enableEncryption) {
- $baseStorage = new GroupFolderEncryptionJail([
- 'storage' => $rootStorage,
- 'root' => $rootPath,
- ]);
$quotaStorage = new GroupFolderStorage([
'storage' => $baseStorage,
- 'quota' => $quota,
- 'folder_id' => $id,
+ 'quota' => $folder->quota,
+ 'folder' => $folder,
'rootCacheEntry' => $rootCacheEntry,
'userSession' => $this->userSession,
'mountOwner' => $user,
]);
} else {
- $baseStorage = new Jail([
- 'storage' => $rootStorage,
- 'root' => $rootPath,
- ]);
$quotaStorage = new GroupFolderNoEncryptionStorage([
'storage' => $baseStorage,
- 'quota' => $quota,
- 'folder_id' => $id,
+ 'quota' => $folder->quota,
+ 'folder' => $folder,
'rootCacheEntry' => $rootCacheEntry,
'userSession' => $this->userSession,
'mountOwner' => $user,
diff --git a/lib/Trash/GroupTrashItem.php b/lib/Trash/GroupTrashItem.php
index b18cd4c13..799c6cee8 100644
--- a/lib/Trash/GroupTrashItem.php
+++ b/lib/Trash/GroupTrashItem.php
@@ -10,6 +10,7 @@
use OCA\Files_Trashbin\Trash\ITrashBackend;
use OCA\Files_Trashbin\Trash\TrashItem;
+use OCA\GroupFolders\Folder\FolderDefinition;
use OCP\Files\FileInfo;
use OCP\IUser;
@@ -23,6 +24,7 @@ public function __construct(
IUser $user,
private readonly string $mountPoint,
?IUser $deletedBy,
+ public readonly FolderDefinition $folder,
) {
parent::__construct($backend, $this->mountPoint . '/' . $this->internalOriginalLocation, $deletedTime, $trashPath, $fileInfo, $user, $deletedBy);
}
@@ -54,4 +56,8 @@ public function getInternalPath(): string {
return rtrim($path, '.d' . $this->getDeletedTime());
}
+
+ public function getGroupFolderStorageId(): int {
+ return $this->folder->storageId;
+ }
}
diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php
index 55cc976ab..49607b911 100644
--- a/lib/Trash/TrashBackend.php
+++ b/lib/Trash/TrashBackend.php
@@ -16,7 +16,9 @@
use OCA\Files_Trashbin\Trash\ITrashBackend;
use OCA\Files_Trashbin\Trash\ITrashItem;
use OCA\GroupFolders\ACL\ACLManagerFactory;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCA\GroupFolders\Mount\GroupFolderStorage;
use OCA\GroupFolders\Mount\MountProvider;
use OCA\GroupFolders\Versions\VersionsBackend;
@@ -36,9 +38,6 @@
use OCP\IUserSession;
use Psr\Log\LoggerInterface;
-/**
- * @psalm-import-type InternalFolder from FolderManager
- */
class TrashBackend implements ITrashBackend {
private ?VersionsBackend $versionsBackend = null;
@@ -84,10 +83,10 @@ public function listTrashFolder(ITrashItem $folder): array {
}
$content = $folderNode->getDirectoryListing();
- $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($folder->getPath());
+ $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($folder->getGroupFolderStorageId(), $folder->getPath());
return array_values(array_filter(array_map(function (Node $node) use ($folder, $user): ?GroupTrashItem {
- if (!$this->userHasAccessToPath($user, $this->getUnJailedPath($node))) {
+ if (!$this->userHasAccessToPath($folder->getGroupFolderStorageId(), $user, $this->getUnJailedPath($node))) {
return null;
}
@@ -100,6 +99,7 @@ public function listTrashFolder(ITrashItem $folder): array {
$user,
$folder->getGroupFolderMountPoint(),
$folder->getDeletedBy(),
+ $folder->folder,
);
}, $content)));
}
@@ -120,7 +120,7 @@ public function restoreItem(ITrashItem $item): void {
throw new NotFoundException();
}
- if (!$this->userHasAccessToPath($item->getUser(), $item->getPath(), Constants::PERMISSION_UPDATE)) {
+ if (!$this->userHasAccessToPath($item->getGroupFolderStorageId(), $item->getUser(), $item->getPath(), Constants::PERMISSION_UPDATE)) {
throw new NotPermittedException();
}
@@ -221,7 +221,7 @@ public function removeItem(ITrashItem $item): void {
throw new NotFoundException();
}
- if (!$this->userHasAccessToPath($item->getUser(), $item->getPath(), Constants::PERMISSION_DELETE)) {
+ if (!$this->userHasAccessToPath($item->folder->storageId, $item->getUser(), $item->getPath(), Constants::PERMISSION_DELETE)) {
throw new NotPermittedException();
}
@@ -245,12 +245,13 @@ public function moveToTrash(IStorage $storage, string $internalPath): bool {
/** @var GroupFolderStorage $storage */
$name = basename($internalPath);
$fileEntry = $storage->getCache()->get($internalPath);
+ $folder = $storage->getFolder();
$folderId = $storage->getFolderId();
$user = $this->userSession->getUser();
$owner = $storage->getUser();
- $this->setupTrashFolder($folderId, $owner);
+ $this->setupTrashFolder($folder, $owner);
$trashFolder = $this->rootFolder->get('/' . $owner->getUID() . '/files_trashbin/groupfolders/' . $folderId);
$trashStorage = $trashFolder->getStorage();
@@ -329,19 +330,20 @@ private function moveFromEncryptedStorage(IStorage $sourceStorage, IStorage $tar
private function userHasAccessToFolder(IUser $user, int $folderId): bool {
$folders = $this->folderManager->getFoldersForUser($user);
- $folderIds = array_map(fn (array $folder): int => $folder['folder_id'], $folders);
+ $folderIds = array_map(fn (array $folder): int => $folder->id, $folders);
return in_array($folderId, $folderIds);
}
private function userHasAccessToPath(
+ int $storageId,
IUser $user,
string $path,
int $permission = Constants::PERMISSION_READ,
): bool {
try {
$activePermissions = $this->aclManagerFactory->getACLManager($user)
- ->getACLPermissionsForPath($path);
+ ->getACLPermissionsForPath($storageId, $path);
} catch (\Exception $e) {
$this->logger->warning("Failed to get permissions for {$path}", ['exception' => $e]);
return false;
@@ -351,15 +353,19 @@ private function userHasAccessToPath(
}
private function getNodeForTrashItem(IUser $user, ITrashItem $trashItem): ?Node {
+ if (!($trashItem instanceof GroupTrashItem)) {
+ throw new \LogicException('Trying to remove normal trash item in Team folder trash backend');
+ }
+
[, $folderId, $path] = explode('/', $trashItem->getTrashPath(), 3);
$folderId = (int)$folderId;
$folders = $this->folderManager->getFoldersForUser($user);
foreach ($folders as $groupFolder) {
- if ($groupFolder['folder_id'] === $folderId) {
+ if ($groupFolder->id === $folderId) {
$trashRoot = $this->rootFolder->get('/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId);
try {
$node = $trashRoot->get($path);
- if (!$this->userHasAccessToPath($user, $trashItem->getPath())) {
+ if (!$this->userHasAccessToPath($trashItem->getGroupFolderStorageId(), $user, $trashItem->getPath())) {
return null;
}
@@ -384,13 +390,14 @@ private function getTrashRoot(): Folder {
}
}
- private function setupTrashFolder(int $folderId, ?IUser $user = null): Folder {
+ private function setupTrashFolder(FolderDefinitionWithPermissions $folder, ?IUser $user = null): Folder {
+ $folderId = $folder->id;
if ($user) {
$mountPoint = '/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId;
$mount = $this->mountManager->find($mountPoint);
if ($mount->getMountPoint() !== $mountPoint) {
$trashMount = $this->mountProvider->getTrashMount(
- $folderId,
+ $folder,
$mountPoint,
FileInfo::SPACE_UNLIMITED,
$this->storageFactory,
@@ -425,11 +432,11 @@ private function getUnJailedPath(Node $node): string {
}
/**
- * @param list $folders
+ * @param list $folders
* @return list
*/
private function getTrashForFolders(IUser $user, array $folders): array {
- $folderIds = array_map(fn (array $folder): int => $folder['folder_id'], $folders);
+ $folderIds = array_map(fn (FolderDefinitionWithPermissions $folder): int => $folder->id, $folders);
$rows = $this->trashManager->listTrashForFolders($folderIds);
$indexedRows = [];
$trashItemsByOriginalLocation = [];
@@ -441,17 +448,17 @@ private function getTrashForFolders(IUser $user, array $folders): array {
$items = [];
foreach ($folders as $folder) {
- $folderId = $folder['folder_id'];
- $folderHasAcl = $folder['acl'];
- $mountPoint = $folder['mount_point'];
+ $folderId = $folder->id;
+ $folderHasAcl = $folder->acl;
+ $mountPoint = $folder->mountPoint;
// ensure the trash folder exists
- $this->setupTrashFolder($folderId, $user);
+ $this->setupTrashFolder($folder, $user);
$trashFolder = $this->rootFolder->get('/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId);
$content = $trashFolder->getDirectoryListing();
$userCanManageAcl = $this->folderManager->canManageACL($folderId, $user);
- $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($this->getUnJailedPath($trashFolder));
+ $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($folder->storageId, $this->getUnJailedPath($trashFolder));
foreach ($content as $item) {
/** @var \OC\Files\Node\Node $item */
$pathParts = pathinfo($item->getName());
@@ -468,7 +475,7 @@ private function getTrashForFolders(IUser $user, array $folders): array {
continue;
}
- if (!$this->userHasAccessToPath($user, $this->getUnJailedPath($item))) {
+ if (!$this->userHasAccessToPath($folder->storageId, $user, $this->getUnJailedPath($item))) {
continue;
}
@@ -477,7 +484,7 @@ private function getTrashForFolders(IUser $user, array $folders): array {
$parentTrashItem = $trashItemsByOriginalLocation[$parentOriginalPath];
$relativePath = substr($originalLocation, strlen($parentOriginalPath));
$parentTrashItemPath = "__groupfolders/trash/{$parentTrashItem['folder_id']}/{$parentTrashItem['name']}.d{$parentTrashItem['deleted_time']}";
- if (!$this->userHasAccessToPath($user, $parentTrashItemPath . $relativePath)) {
+ if (!$this->userHasAccessToPath($folder->storageId, $user, $parentTrashItemPath . $relativePath)) {
continue 2;
}
}
@@ -494,6 +501,7 @@ private function getTrashForFolders(IUser $user, array $folders): array {
$user,
$mountPoint,
$this->userManager->get($deletedBy),
+ $folder,
);
}
}
@@ -534,7 +542,7 @@ public function getTrashNodeById(IUser $user, int $fileId): ?Node {
$relativePath = $trashFolder->getRelativePath($absolutePath);
[, $folderId, $nameAndTime] = explode('/', $relativePath);
- if ($this->userHasAccessToFolder($user, (int)$folderId) && $this->userHasAccessToPath($user, $absolutePath)) {
+ if ($this->userHasAccessToFolder($user, (int)$folderId) && $this->userHasAccessToPath($storage->getCache()->getNumericStorageId(), $user, $absolutePath)) {
return $trashFolder->get($relativePath);
} else {
return null;
@@ -544,27 +552,30 @@ public function getTrashNodeById(IUser $user, int $fileId): ?Node {
}
}
- public function cleanTrashFolder(int $folderid): void {
- $trashFolder = $this->setupTrashFolder($folderid);
+ public function cleanTrashFolder(FolderDefinitionWithPermissions $folder): void {
+ $trashFolder = $this->setupTrashFolder($folder);
foreach ($trashFolder->getDirectoryListing() as $node) {
$node->delete();
}
- $this->trashManager->emptyTrashbin($folderid);
+ $this->trashManager->emptyTrashbin($folder->id);
}
public function expire(Expiration $expiration): array {
$size = 0;
$count = 0;
- $folders = $this->folderManager->getAllFoldersWithSize($this->rootFolder->getMountPoint()->getNumericStorageId());
+ $folders = $this->folderManager->getAllFoldersWithSize();
+ $folders = array_map(function (FolderWithMappingsAndCache $folder): FolderDefinitionWithPermissions {
+ return FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL);
+ }, $folders);
foreach ($folders as $folder) {
- $folderId = $folder['id'];
+ $folderId = $folder->id;
$trashItems = $this->trashManager->listTrashForFolders([$folderId]);
// calculate size of trash items
$sizeInTrash = 0;
- $trashFolder = $this->setupTrashFolder($folderId);
+ $trashFolder = $this->setupTrashFolder($folder);
$nodes = []; // cache
foreach ($trashItems as $groupTrashItem) {
$nodeName = $groupTrashItem['name'] . '.d' . $groupTrashItem['deleted_time'];
@@ -578,6 +589,8 @@ public function expire(Expiration $expiration): array {
$sizeInTrash += $node->getSize();
}
+ $size = $folder->rootCacheEntry->getSize();
+
foreach ($trashItems as $groupTrashItem) {
$nodeName = $groupTrashItem['name'] . '.d' . $groupTrashItem['deleted_time'];
if (!isset($nodes[$nodeName])) {
@@ -586,7 +599,7 @@ public function expire(Expiration $expiration): array {
$node = $nodes[$nodeName];
- if ($expiration->isExpired($groupTrashItem['deleted_time'], $folder['quota'] > 0 && $folder['quota'] < ($folder['size'] + $sizeInTrash))) {
+ if ($expiration->isExpired($groupTrashItem['deleted_time'], $folder->quota > 0 && $folder->quota < ($size + $sizeInTrash))) {
$this->logger->debug('expiring ' . $node->getPath());
if ($node->getStorage()->unlink($node->getInternalPath()) === false) {
$this->logger->error('Failed to remove item from trashbin: ' . $node->getPath());
@@ -596,7 +609,7 @@ public function expire(Expiration $expiration): array {
// only count up after checking if removal is possible
$count += 1;
$size += $node->getSize();
- $folder['size'] -= $node->getSize();
+ $size -= $node->getSize();
$node->getStorage()->getCache()->remove($node->getInternalPath());
$this->trashManager->removeItem($folderId, $groupTrashItem['name'], $groupTrashItem['deleted_time']);
if (!is_null($groupTrashItem['file_id']) && !is_null($this->versionsBackend)) {
@@ -615,7 +628,8 @@ public function expire(Expiration $expiration): array {
}
/**
- * Cleanup trashbin of of groupfolders that have been deleted
+ * @param array $existingFolders
+ * Cleanup trashbin of of group folders that have been deleted
*/
private function cleanupDeletedFoldersTrash(array $existingFolders): void {
$trashRoot = $this->getTrashRoot();
@@ -624,8 +638,8 @@ private function cleanupDeletedFoldersTrash(array $existingFolders): void {
if (is_numeric($folderId)) {
$folderId = (int)$folderId;
if (!isset($existingFolders[$folderId])) {
- $this->cleanTrashFolder($folderId);
- $this->setupTrashFolder($folderId)->delete();
+ $this->cleanTrashFolder($existingFolders[$folderId]);
+ $this->setupTrashFolder($existingFolders[$folderId])->delete();
}
}
}
diff --git a/lib/Versions/GroupVersionsExpireManager.php b/lib/Versions/GroupVersionsExpireManager.php
index 8e9878eab..40ba92990 100644
--- a/lib/Versions/GroupVersionsExpireManager.php
+++ b/lib/Versions/GroupVersionsExpireManager.php
@@ -14,14 +14,12 @@
use OCA\GroupFolders\Event\GroupVersionsExpireDeleteVersionEvent;
use OCA\GroupFolders\Event\GroupVersionsExpireEnterFolderEvent;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\FileInfo;
use OCP\IUser;
-/**
- * @psalm-import-type InternalFolderOut from FolderManager
- */
class GroupVersionsExpireManager {
public function __construct(
private readonly FolderManager $folderManager,
@@ -33,13 +31,13 @@ public function __construct(
}
public function expireAll(): void {
- $folders = $this->folderManager->getAllFolders();
- foreach ($folders as $folder) {
- $this->dispatcher->dispatchTyped(new GroupVersionsExpireEnterFolderEvent($folder));
- $this->expireFolder($folder);
- }
+ $folders = $this->folderManager->getAllFoldersWithSize();
+ $this->expireFolders($folders);
}
+ /**
+ * @param FolderWithMappingsAndCache[] $folders
+ */
public function expireFolders(array $folders): void {
foreach ($folders as $folder) {
$this->dispatcher->dispatchTyped(new GroupVersionsExpireEnterFolderEvent($folder));
@@ -47,11 +45,8 @@ public function expireFolders(array $folders): void {
}
}
- /**
- * @param InternalFolderOut $folder
- */
- public function expireFolder(array $folder): void {
- $view = new View('/__groupfolders/versions/' . $folder['id']);
+ public function expireFolder(FolderWithMappingsAndCache $folder): void {
+ $view = new View('/__groupfolders/versions/' . $folder->id);
$files = $this->versionsBackend->getAllVersionedFiles($folder);
/** @var IUser */
$dummyUser = new User('', null, $this->dispatcher);
@@ -75,7 +70,7 @@ public function expireFolder(array $folder): void {
} else {
// source file no longer exists
$this->dispatcher->dispatchTyped(new GroupVersionsExpireDeleteFileEvent($fileId));
- $this->versionsBackend->deleteAllVersionsForFile($folder['id'], $fileId);
+ $this->versionsBackend->deleteAllVersionsForFile($folder->id, $fileId);
}
}
}
diff --git a/lib/Versions/VersionsBackend.php b/lib/Versions/VersionsBackend.php
index d598095dc..0deb641f7 100644
--- a/lib/Versions/VersionsBackend.php
+++ b/lib/Versions/VersionsBackend.php
@@ -16,7 +16,9 @@
use OCA\Files_Versions\Versions\IVersion;
use OCA\Files_Versions\Versions\IVersionBackend;
use OCA\Files_Versions\Versions\IVersionsImporterBackend;
-use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Folder\FolderDefinitionWithMappings;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
+use OCA\GroupFolders\Folder\FolderWithMappingsAndCache;
use OCA\GroupFolders\Mount\GroupFolderStorage;
use OCA\GroupFolders\Mount\GroupMountPoint;
use OCA\GroupFolders\Mount\MountProvider;
@@ -34,9 +36,6 @@
use OCP\IUserSession;
use Psr\Log\LoggerInterface;
-/**
- * @psalm-import-type InternalFolderOut from FolderManager
- */
class VersionsBackend implements IVersionBackend, IMetadataVersionBackend, IDeletableVersionBackend, INeedSyncVersionBackend, IVersionsImporterBackend {
public function __construct(
private readonly IRootFolder $rootFolder,
@@ -245,12 +244,13 @@ public function getVersionFile(IUser $user, FileInfo $sourceFile, $revision): Fi
}
/**
- * @param InternalFolderOut $folder
+ * @param FolderWithMappingsAndCache $folder
* @return array
*/
- public function getAllVersionedFiles(array $folder): array {
- $versionsFolder = $this->getVersionsFolder($folder['id']);
- $mount = $this->mountProvider->getMount($folder['id'], '/dummyuser/files/' . $folder['mount_point'], Constants::PERMISSION_ALL, $folder['quota']);
+ public function getAllVersionedFiles(FolderDefinitionWithMappings $folder): array {
+ $versionsFolder = $this->getVersionsFolder($folder->id);
+ $folderWithPermissions = FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL);
+ $mount = $this->mountProvider->getMount($folderWithPermissions, '/dummyuser/files/' . $folder->mountPoint);
if ($mount === null) {
$this->logger->error('Tried to get all the versioned files from a non existing mountpoint');
return [];
diff --git a/tests/ACL/ACLCacheWrapperTest.php b/tests/ACL/ACLCacheWrapperTest.php
index f1525af69..40d54d4cd 100644
--- a/tests/ACL/ACLCacheWrapperTest.php
+++ b/tests/ACL/ACLCacheWrapperTest.php
@@ -30,8 +30,10 @@ protected function setUp(): void {
$this->aclManager = $this->createMock(ACLManager::class);
$this->aclManager->method('getACLPermissionsForPath')
- ->willReturnCallback(fn (string $path) => $this->aclPermissions[$path] ?? Constants::PERMISSION_ALL);
+ ->willReturnCallback(fn (int $_storageId, string $path) => $this->aclPermissions[$path] ?? Constants::PERMISSION_ALL);
$this->source = $this->createMock(ICache::class);
+ $this->source->method('getNumericStorageId')
+ ->willReturn(1);
$this->cache = new ACLCacheWrapper($this->source, $this->aclManager, false);
}
diff --git a/tests/ACL/ACLManagerTest.php b/tests/ACL/ACLManagerTest.php
index 7c35e8d57..aee68e2f0 100644
--- a/tests/ACL/ACLManagerTest.php
+++ b/tests/ACL/ACLManagerTest.php
@@ -15,8 +15,6 @@
use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager;
use OCA\GroupFolders\Trash\TrashManager;
use OCP\Constants;
-use OCP\Files\IRootFolder;
-use OCP\Files\Mount\IMountPoint;
use OCP\IUser;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
@@ -71,19 +69,12 @@ private function createMapping(string $id): IUserMapping&MockObject {
}
private function getAclManager(bool $perUserMerge = false): ACLManager {
- $rootMountPoint = $this->createMock(IMountPoint::class);
- $rootMountPoint->method('getNumericStorageId')
- ->willReturn(1);
- $rootFolder = $this->createMock(IRootFolder::class);
- $rootFolder->method('getMountPoint')
- ->willReturn($rootMountPoint);
-
- return new ACLManager($this->ruleManager, $this->trashManager, $this->userMappingManager, $this->logger, $this->user, fn (): IRootFolder&MockObject => $rootFolder, null, $perUserMerge);
+ return new ACLManager($this->ruleManager, $this->trashManager, $this->userMappingManager, $this->logger, $this->user, $perUserMerge);
}
public function testGetACLPermissionsForPathNoRules(): void {
$this->rules = [];
- $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath('foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath(0, 'foo'));
}
public function testGetACLPermissionsForPath(): void {
@@ -105,11 +96,11 @@ public function testGetACLPermissionsForPath(): void {
new Rule($this->createMapping('1'), 10, Constants::PERMISSION_READ, 0) // remove read
],
];
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $this->aclManager->getACLPermissionsForPath('foo'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, $this->aclManager->getACLPermissionsForPath('foo/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath('foo/bar/sub'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $this->aclManager->getACLPermissionsForPath('foo/blocked'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $this->aclManager->getACLPermissionsForPath('foo/blocked2'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $this->aclManager->getACLPermissionsForPath(0, 'foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, $this->aclManager->getACLPermissionsForPath(0, 'foo/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath(0, 'foo/bar/sub'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $this->aclManager->getACLPermissionsForPath(0, 'foo/blocked'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $this->aclManager->getACLPermissionsForPath(0, 'foo/blocked2'));
}
public function testGetACLPermissionsForPathInTrashbin(): void {
@@ -137,7 +128,7 @@ public function testGetACLPermissionsForPathInTrashbin(): void {
'original_location' => 'subfolder/subfolder2',
'folder_id' => '1',
]);
- $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath('__groupfolders/trash/1/subfolder2.d1700752274/coucou.md'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $this->aclManager->getACLPermissionsForPath(0, '__groupfolders/trash/1/subfolder2.d1700752274/coucou.md'));
}
@@ -162,11 +153,11 @@ public function testGetACLPermissionsForPathPerUserMerge(): void {
new Rule($this->createMapping('1'), 10, Constants::PERMISSION_READ, 0) // remove read
],
];
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $aclManager->getACLPermissionsForPath('foo'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, $aclManager->getACLPermissionsForPath('foo/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $aclManager->getACLPermissionsForPath('foo/bar/sub'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $aclManager->getACLPermissionsForPath('foo/blocked'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $aclManager->getACLPermissionsForPath('foo/blocked2'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $aclManager->getACLPermissionsForPath(0, 'foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE, $aclManager->getACLPermissionsForPath(0, 'foo/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $aclManager->getACLPermissionsForPath(0, 'foo/bar/sub'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE, $aclManager->getACLPermissionsForPath(0, 'foo/blocked'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_SHARE - Constants::PERMISSION_UPDATE - Constants::PERMISSION_READ, $aclManager->getACLPermissionsForPath(0, 'foo/blocked2'));
}
public function testGetPermissionsForTree(): void {
@@ -183,15 +174,15 @@ public function testGetPermissionsForTree(): void {
new Rule($this->createMapping('2'), 10, Constants::PERMISSION_DELETE, Constants::PERMISSION_DELETE) // re-add delete
],
];
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $this->aclManager->getPermissionsForTree('foo'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $this->aclManager->getPermissionsForTree('foo/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $this->aclManager->getPermissionsForTree(0, 'foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $this->aclManager->getPermissionsForTree(0, 'foo/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo/bar/asd'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo/bar/asd'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getPermissionsForTree('foo'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getPermissionsForTree('foo/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getPermissionsForTree(0, 'foo'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getPermissionsForTree(0, 'foo/bar'));
$this->rules = [
'foo2' => [
@@ -205,12 +196,12 @@ public function testGetPermissionsForTree(): void {
],
];
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo2'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getACLPermissionsForPath('foo2/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo2/bar/asd'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo2'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getACLPermissionsForPath(0, 'foo2/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo2/bar/asd'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree('foo2'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree('foo2/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree(0, 'foo2'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree(0, 'foo2/bar'));
$this->rules = [
'foo3' => [
@@ -224,11 +215,11 @@ public function testGetPermissionsForTree(): void {
],
];
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo3'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getACLPermissionsForPath('foo3/bar'));
- $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath('foo3/bar/asd'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo3'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getACLPermissionsForPath(0, 'foo3/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL, $perUserAclManager->getACLPermissionsForPath(0, 'foo3/bar/asd'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree('foo3'));
- $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree('foo3/bar'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree(0, 'foo3'));
+ $this->assertEquals(Constants::PERMISSION_ALL - Constants::PERMISSION_DELETE, $perUserAclManager->getPermissionsForTree(0, 'foo3/bar'));
}
}
diff --git a/tests/ACL/ACLScannerTest.php b/tests/ACL/ACLScannerTest.php
index 48b5c79d9..73ef65822 100644
--- a/tests/ACL/ACLScannerTest.php
+++ b/tests/ACL/ACLScannerTest.php
@@ -25,7 +25,7 @@ private function getAclManager(array $rules): ACLManager&MockObject {
->disableOriginalConstructor()
->getMock();
$manager->method('getACLPermissionsForPath')
- ->willReturnCallback(fn (string $path) => $rules[$path] ?? Constants::PERMISSION_ALL);
+ ->willReturnCallback(fn (int $_storageId, string $path) => $rules[$path] ?? Constants::PERMISSION_ALL);
return $manager;
}
@@ -55,6 +55,7 @@ public function testScanAclStorage(): void {
'storage' => $baseStorage,
'acl_manager' => $acls,
'in_share' => false,
+ 'storage_id' => $cache->getNumericStorageId(),
]);
$scanner = $aclStorage->getScanner();
diff --git a/tests/ACL/ACLStorageWrapperTest.php b/tests/ACL/ACLStorageWrapperTest.php
index e448d7b17..fd816ed7f 100644
--- a/tests/ACL/ACLStorageWrapperTest.php
+++ b/tests/ACL/ACLStorageWrapperTest.php
@@ -27,12 +27,13 @@ protected function setUp(): void {
$this->aclManager = $this->createMock(ACLManager::class);
$this->aclManager->method('getACLPermissionsForPath')
- ->willReturnCallback(fn (string $path) => $this->aclPermissions[$path] ?? Constants::PERMISSION_ALL);
+ ->willReturnCallback(fn (int $_storageId, string $path) => $this->aclPermissions[$path] ?? Constants::PERMISSION_ALL);
$this->source = new Temporary([]);
$this->storage = new ACLStorageWrapper([
'storage' => $this->source,
'acl_manager' => $this->aclManager,
- 'in_share' => false
+ 'in_share' => false,
+ 'storage_id' => $this->source->getCache()->getNumericStorageId(),
]);
}
diff --git a/tests/ACL/RuleTest.php b/tests/ACL/RuleTest.php
index d932def00..10717959a 100644
--- a/tests/ACL/RuleTest.php
+++ b/tests/ACL/RuleTest.php
@@ -13,7 +13,7 @@
use Test\TestCase;
class RuleTest extends TestCase {
- public function permissionsProvider(): array {
+ public static function permissionsProvider(): array {
return [
[0b00000000, 0b00000000, 0b00000000, 0b00000000],
[0b10101010, 0b00000000, 0b11110000, 0b10101010], //empty mask should have no effect
@@ -31,7 +31,7 @@ public function testApplyPermissions(int $input, int $mask, int $permissions, in
$this->assertEquals($expected, $rule->applyPermissions($input));
}
- public function mergeRulesProvider(): array {
+ public static function mergeRulesProvider(): array {
return [
[[
[0b00001111, 0b00000011],
diff --git a/tests/Event/GroupVersionsExpireDeleteFileEventTest.php b/tests/Event/GroupVersionsExpireDeleteFileEventTest.php
deleted file mode 100644
index 9d58a8864..000000000
--- a/tests/Event/GroupVersionsExpireDeleteFileEventTest.php
+++ /dev/null
@@ -1,20 +0,0 @@
-assertEquals(123, $event->fileId);
- }
-}
diff --git a/tests/Event/GroupVersionsExpireDeleteVersionEventTest.php b/tests/Event/GroupVersionsExpireDeleteVersionEventTest.php
deleted file mode 100644
index aa00bf47e..000000000
--- a/tests/Event/GroupVersionsExpireDeleteVersionEventTest.php
+++ /dev/null
@@ -1,27 +0,0 @@
-createMock(GroupVersion::class);
- $version
- ->expects($this->once())
- ->method('getFolderId')
- ->willReturn(123);
-
- $event = new GroupVersionsExpireDeleteVersionEvent($version);
- $this->assertEquals(123, $event->version->getFolderId());
- }
-}
diff --git a/tests/Event/GroupVersionsExpireEnterFolderEventTest.php b/tests/Event/GroupVersionsExpireEnterFolderEventTest.php
deleted file mode 100644
index 65910a8ba..000000000
--- a/tests/Event/GroupVersionsExpireEnterFolderEventTest.php
+++ /dev/null
@@ -1,20 +0,0 @@
- 'value']);
- $this->assertEquals(['key' => 'value'], $event->folder);
- }
-}
diff --git a/tests/Folder/FolderManagerTest.php b/tests/Folder/FolderManagerTest.php
index ec6554d90..864a406f2 100644
--- a/tests/Folder/FolderManagerTest.php
+++ b/tests/Folder/FolderManagerTest.php
@@ -8,8 +8,12 @@
namespace OCA\GroupFolders\Tests\Folder;
+use OC\Files\Cache\CacheEntry;
use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager;
+use OCA\GroupFolders\Folder\FolderDefinition;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
+use OCA\GroupFolders\Mount\FolderStorageManager;
use OCA\GroupFolders\ResponseDefinitions;
use OCP\Constants;
use OCP\EventDispatcher\IEventDispatcher;
@@ -37,6 +41,7 @@ class FolderManagerTest extends TestCase {
private IEventDispatcher&MockObject $eventDispatcher;
private IConfig&MockObject $config;
private IUserMappingManager&MockObject $userMappingManager;
+ private FolderStorageManager $folderStorageManager;
protected function setUp(): void {
parent::setUp();
@@ -47,6 +52,8 @@ protected function setUp(): void {
$this->eventDispatcher = $this->createMock(IEventDispatcher::class);
$this->config = $this->createMock(IConfig::class);
$this->userMappingManager = $this->createMock(IUserMappingManager::class);
+ $this->folderStorageManager = Server::get(FolderStorageManager::class);
+
$this->manager = new FolderManager(
Server::get(IDBConnection::class),
$this->groupManager,
@@ -55,6 +62,7 @@ protected function setUp(): void {
$this->eventDispatcher,
$this->config,
$this->userMappingManager,
+ $this->folderStorageManager,
);
$this->clean();
}
@@ -68,18 +76,14 @@ private function clean(): void {
}
/**
- * @param list, acl?: bool, quota?: int, size?: int}> $folders
+ * @param list, acl?: bool, quota?: int, size?: int, root_id?: int, storage_id?: int}> $folders
*/
private function assertHasFolders(array $folders): void {
$existingFolders = array_values($this->manager->getAllFolders());
- usort($existingFolders, fn (array $a, array $b): int => strcmp($a['mount_point'], $b['mount_point']));
+ usort($existingFolders, fn (FolderDefinition $a, FolderDefinition $b): int => strcmp($a->mountPoint, $b->mountPoint));
usort($folders, fn (array $a, array $b): int => strcmp($a['mount_point'], $b['mount_point']));
foreach ($folders as &$folder) {
- if (!isset($folder['size'])) {
- $folder['size'] = 0;
- }
-
if (!isset($folder['quota'])) {
$folder['quota'] = FileInfo::SPACE_UNLIMITED;
}
@@ -89,9 +93,12 @@ private function assertHasFolders(array $folders): void {
}
}
- foreach ($existingFolders as &$existingFolder) {
- unset($existingFolder['id']);
- }
+ $existingFolders = array_map(fn (FolderDefinition $existingFolder): array => [
+ 'mount_point' => $existingFolder->mountPoint,
+ 'quota' => $existingFolder->quota,
+ 'acl' => $existingFolder->acl,
+ 'groups' => $existingFolder->groups,
+ ], $existingFolders);
$this->assertEquals($folders, $existingFolders);
}
@@ -328,8 +335,8 @@ public function testGetFoldersForGroup(): void {
$folders = $this->manager->getFoldersForGroup('g1');
$this->assertCount(1, $folders);
$folder = $folders[0];
- $this->assertEquals('foo', $folder['mount_point']);
- $this->assertEquals(2, $folder['permissions']);
+ $this->assertEquals('foo', $folder->mountPoint);
+ $this->assertEquals(2, $folder->permissions);
}
public function testGetFoldersForGroups(): void {
@@ -346,14 +353,14 @@ public function testGetFoldersForGroups(): void {
$folders = $this->manager->getFoldersForGroups(['g1']);
$this->assertCount(1, $folders);
$folder = $folders[0];
- $this->assertEquals('foo', $folder['mount_point']);
- $this->assertEquals(2, $folder['permissions']);
+ $this->assertEquals('foo', $folder->mountPoint);
+ $this->assertEquals(2, $folder->permissions);
}
/**
* @param string[] $groups
*/
- protected function getUser($groups = []): IUser&MockObject {
+ protected function getUser(array $groups = []): IUser&MockObject {
$id = uniqid();
$user = $this->createMock(IUser::class);
$this->groupManager->expects($this->any())
@@ -374,16 +381,22 @@ public function testGetFoldersForUserEmpty(): void {
public function testGetFoldersForUserSimple(): void {
$db = $this->createMock(IDBConnection::class);
$manager = $this->getMockBuilder(FolderManager::class)
- ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager])
+ ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager, $this->folderStorageManager])
->onlyMethods(['getFoldersForGroups'])
->getMock();
- $folder = [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 31,
- 'quota' => FileInfo::SPACE_UNLIMITED,
- ];
+ $cacheEntry = $this->createMock(CacheEntry::class);
+
+ $folder = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 31,
+ );
$manager->expects($this->once())
->method('getFoldersForGroups')
@@ -396,57 +409,80 @@ public function testGetFoldersForUserSimple(): void {
public function testGetFoldersForUserMerge(): void {
$db = $this->createMock(IDBConnection::class);
$manager = $this->getMockBuilder(FolderManager::class)
- ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager])
+ ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager, $this->folderStorageManager])
->onlyMethods(['getFoldersForGroups'])
->getMock();
- $folder1 = [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 3,
- 'quota' => 1000
- ];
- $folder2 = [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 8,
- 'quota' => 1000
- ];
+ $cacheEntry = $this->createMock(CacheEntry::class);
+
+ $folder1 = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 3,
+ );
+ $folder2 = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 8,
+ );
+ $merged = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 8 + 3,
+ );
$manager->expects($this->any())
->method('getFoldersForGroups')
->willReturn([$folder1, $folder2]);
$folders = $manager->getFoldersForUser($this->getUser(['g1', 'g2', 'g3']));
- $this->assertEquals([
- [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 11,
- 'quota' => 1000
- ]
- ], $folders);
+ $this->assertEquals([$merged], $folders);
}
public function testGetFolderPermissionsForUserMerge(): void {
$db = $this->createMock(IDBConnection::class);
$manager = $this->getMockBuilder(FolderManager::class)
- ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager])
+ ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager, $this->folderStorageManager])
->onlyMethods(['getFoldersForGroups'])
->getMock();
- $folder1 = [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 3,
- 'quota' => 1000
- ];
- $folder2 = [
- 'folder_id' => 1,
- 'mount_point' => 'foo',
- 'permissions' => 8,
- 'quota' => 1000
- ];
+ $cacheEntry = $this->createMock(CacheEntry::class);
+
+ $folder1 = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 3,
+ );
+ $folder2 = new FolderDefinitionWithPermissions(
+ 1,
+ 'foo',
+ 1000,
+ false,
+ 1,
+ 2,
+ $cacheEntry,
+ 8,
+ );
$manager->expects($this->any())
->method('getFoldersForGroups')
@@ -470,12 +506,16 @@ public function testQuotaDefaultValue(): void {
return 1024 ** ($exponent++);
});
- /** @var array $folder */
$folder = $this->manager->getFolder($folderId1);
- $this->assertEquals(1024 ** 3, $folder['quota']);
+ if (!$folder) {
+ throw new \Exception('Folder not found');
+ }
+ $this->assertEquals(1024 ** 3, $folder->quota);
- /** @var array $folder */
$folder = $this->manager->getFolder($folderId1);
- $this->assertEquals(1024 ** 4, $folder['quota']);
+ if (!$folder) {
+ throw new \Exception('Folder not found');
+ }
+ $this->assertEquals(1024 ** 4, $folder->quota);
}
}
diff --git a/tests/Trash/TrashBackendTest.php b/tests/Trash/TrashBackendTest.php
index 8dec722e7..742a71aca 100644
--- a/tests/Trash/TrashBackendTest.php
+++ b/tests/Trash/TrashBackendTest.php
@@ -16,6 +16,7 @@
use OCA\GroupFolders\ACL\Rule;
use OCA\GroupFolders\ACL\RuleManager;
use OCA\GroupFolders\ACL\UserMapping\UserMapping;
+use OCA\GroupFolders\Folder\FolderDefinitionWithPermissions;
use OCA\GroupFolders\Folder\FolderManager;
use OCA\GroupFolders\Mount\GroupFolderStorage;
use OCA\GroupFolders\Trash\TrashBackend;
@@ -89,8 +90,12 @@ public function setUp(): void {
}
protected function tearDown(): void {
- $this->trashBackend->cleanTrashFolder($this->folderId);
- $this->folderManager->removeFolder($this->folderId);
+ $folder = $this->folderManager->getFolder($this->folderId);
+ if ($folder) {
+ $folderWithPermissions = FolderDefinitionWithPermissions::fromFolder($folder, $folder->rootCacheEntry, Constants::PERMISSION_ALL);
+ $this->trashBackend->cleanTrashFolder($folderWithPermissions);
+ $this->folderManager->removeFolder($this->folderId);
+ }
/** @var SetupManager $setupManager */
$setupManager = \OCP\Server::get(SetupManager::class);