diff --git a/lib/Mount/GroupFolderStorage.php b/lib/Mount/GroupFolderStorage.php index 26518772f..8a650ffc5 100644 --- a/lib/Mount/GroupFolderStorage.php +++ b/lib/Mount/GroupFolderStorage.php @@ -25,6 +25,7 @@ use OC\Files\ObjectStore\ObjectStoreScanner; use OC\Files\ObjectStore\ObjectStoreStorage; use OC\Files\Storage\Wrapper\Quota; +use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; use OCP\IUser; use OCP\IUserSession; @@ -33,8 +34,8 @@ class GroupFolderStorage extends Quota { private int $folderId; private ?ICacheEntry $rootEntry; private IUserSession $userSession; - private ?IUser $mountOwner = null; - /** @var RootEntryCache|null */ + private ?IUser $mountOwner; + /** @var ICache|null */ public $cache = null; public function __construct($parameters) { diff --git a/lib/Mount/MountProvider.php b/lib/Mount/MountProvider.php index 8a0fb4625..e60df4943 100644 --- a/lib/Mount/MountProvider.php +++ b/lib/Mount/MountProvider.php @@ -279,7 +279,7 @@ public function getTrashMount( $storage = $this->getRootFolder()->getStorage(); - $storage->setOwner($user?->getUID()); + $storage->setOwner($user->getUID()); $trashPath = $this->getRootFolder()->getInternalPath() . '/trash/' . $id; diff --git a/lib/Trash/TrashBackend.php b/lib/Trash/TrashBackend.php index 328663cce..14692853d 100644 --- a/lib/Trash/TrashBackend.php +++ b/lib/Trash/TrashBackend.php @@ -22,6 +22,7 @@ namespace OCA\GroupFolders\Trash; use OC\Encryption\Exceptions\DecryptionFailedException; +use OC\Files\ObjectStore\ObjectStoreStorage; use OC\Files\Storage\Wrapper\Encryption; use OC\Files\Storage\Wrapper\Jail; use OCA\Files_Trashbin\Expiration; @@ -309,9 +310,11 @@ private function moveFromEncryptedStorage(IStorage $sourceStorage, IStorage $tar $sourceStorage = $sourceStorage->getWrapperStorage(); } + /** @psalm-suppress TooManyArguments */ $result = $targetStorage->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true); if ($result) { - if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) { + // hacky workaround to make sure we don't rely on a newer minor version + if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && is_callable([$sourceStorage, 'setPreserveCacheOnDelete'])) { /** @var ObjectStoreStorage $sourceStorage */ $sourceStorage->setPreserveCacheOnDelete(true); } @@ -322,7 +325,7 @@ private function moveFromEncryptedStorage(IStorage $sourceStorage, IStorage $tar $result = $sourceStorage->unlink($sourceInternalPath); } } finally { - if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class)) { + if ($sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && is_callable([$sourceStorage, 'setPreserveCacheOnDelete'])) { /** @var ObjectStoreStorage $sourceStorage */ $sourceStorage->setPreserveCacheOnDelete(false); } @@ -355,6 +358,7 @@ private function getNodeForTrashItem(IUser $user, ITrashItem $trashItem): ?Node $folders = $this->folderManager->getFoldersForUser($user); foreach ($folders as $groupFolder) { if ($groupFolder['folder_id'] === $folderId) { + /** @var Folder $trashRoot */ $trashRoot = $this->rootFolder->get('/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId); try { $node = $trashRoot->get($path); @@ -421,6 +425,8 @@ private function getTrashForFolders(IUser $user, array $folders): array { // ensure the trash folder exists $this->getTrashFolder($folderId); + + /** @var Folder $trashFolder */ $trashFolder = $this->rootFolder->get('/' . $user->getUID() . '/files_trashbin/groupfolders/' . $folderId); $content = $trashFolder->getDirectoryListing(); $this->aclManagerFactory->getACLManager($user)->preloadRulesForFolder($this->getUnJailedPath($trashFolder)); diff --git a/tests/stub.phpstub b/tests/stub.phpstub index 4b7c5644d..eb5a07b6c 100644 --- a/tests/stub.phpstub +++ b/tests/stub.phpstub @@ -279,6 +279,9 @@ namespace OCA\Files_Trashbin { */ public function isExpired($timestamp, $quotaExceeded = false) {} } + + class Storage extends \OC\Files\Storage\Wrapper\Wrapper { + } } @@ -1500,7 +1503,7 @@ namespace OC\Files\Storage\Wrapper{ * @param string $targetInternalPath * @return bool */ - public function copyFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) + public function copyFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) { } /** @@ -1561,109 +1564,702 @@ namespace OC\Files\Storage\Wrapper{ class Jail extends Wrapper { protected $rootPath; - public function getUnjailedPath(string $path): string {} - public function getUnjailedStorage(): IStorage {} - } - - class Quota extends Wrapper { - public function getQuota() {} - } - - class PermissionsMask extends Wrapper { - public function getQuota() {} - } -} - -namespace OC\Files\ObjectStore { - use OC\Files\Storage\Wrapper\Wrapper; - class ObjectStoreStorage extends Wrapper {} -} - -namespace OCA\Circles { - use OCA\Circles\Model\Circle; - use OCA\Circles\Model\FederatedUser; - use OCA\Circles\Model\Probes\CircleProbe; - use OCA\Circles\Model\Probes\DataProbe; - use OCP\DB\QueryBuilder\ICompositeExpression; - use OCP\DB\QueryBuilder\IQueryBuilder; - - interface IFederatedUser { - - } - - class CirclesManager { - public function startSession(?FederatedUser $federatedUser = null): void {} - public function probeCircles(?CircleProbe $circleProbe = null, ?DataProbe $dataProbe = null): array {} - public function getQueryHelper(): CirclesQueryHelper {} - public function getLocalFederatedUser(string $userId): FederatedUser {} - public function startSuperSession(): void {} - public function stopSession(): void {} - public function getCircle(string $singleId, ?CircleProbe $probe = null): Circle {} - } - - class CirclesQueryHelper{ - public function addCircleDetails(string $alias, string $field): void {} - public function getQueryBuilder(): IQueryBuilder {} - public function extractCircle(array $data): Circle {} - public function limitToInheritedMembers(string $alias, string $field, IFederatedUser $federatedUser, bool $fullDetails = false): ICompositeExpression {} - } -} - -namespace OCA\Circles\Exceptions { - class CircleNotFoundException extends \Exception { - } -} - -namespace OCA\Circles\Model { - use OCA\Circles\IFederatedUser; - - class FederatedUser implements IFederatedUser { - } - class CircleProbe { - } - class DataProbe { - } - class Circle { - public function getDisplayName(): string {} - public function getSingleId(): string {} - } -} - -namespace OCA\Circles\Model\Probes { - class CircleProbe { - public function includeSystemCircles(bool $include = true): self {} - public function includeSingleCircles(bool $include = true): self {} - } -} - -namespace OCA\Circles\Events { - use OCA\Circles\Model\Circle; - - class CircleDestroyedEvent extends \OCP\EventDispatcher\Event { - public function getCircle(): Circle {} - } -} - - -namespace OCA\DAV\Connector\Sabre\Exception { - class Forbidden extends \Sabre\DAV\Exception\Forbidden { - public const NS_OWNCLOUD = 'http://owncloud.org/ns'; - /** - * @param string $message - * @param bool $retry - * @param \Exception $previous + * @param array $arguments ['storage' => $storage, 'root' => $root] + * + * $storage: The storage that will be wrapper + * $root: The folder in the wrapped storage that will become the root folder of the wrapped storage */ - public function __construct($message, $retry = false, \Exception $previous = null) {} - + public function __construct($arguments) + { + } + public function getUnjailedPath($path) + { + } /** - * This method allows the exception to include additional information - * into the WebDAV error response + * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper + */ + public function getUnjailedStorage() + { + } + public function getJailedPath($path) + { + } + public function getId() + { + } + /** + * see https://www.php.net/manual/en/function.mkdir.php * - * @param \Sabre\DAV\Server $server - * @param \DOMElement $errorNode - * @return void + * @param string $path + * @return bool */ - public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) {} - } + public function mkdir($path) + { + } + /** + * see https://www.php.net/manual/en/function.rmdir.php + * + * @param string $path + * @return bool + */ + public function rmdir($path) + { + } + /** + * see https://www.php.net/manual/en/function.opendir.php + * + * @param string $path + * @return resource|false + */ + public function opendir($path) + { + } + /** + * see https://www.php.net/manual/en/function.is_dir.php + * + * @param string $path + * @return bool + */ + public function is_dir($path) + { + } + /** + * see https://www.php.net/manual/en/function.is_file.php + * + * @param string $path + * @return bool + */ + public function is_file($path) + { + } + /** + * see https://www.php.net/manual/en/function.stat.php + * only the following keys are required in the result: size and mtime + * + * @param string $path + * @return array|bool + */ + public function stat($path) + { + } + /** + * see https://www.php.net/manual/en/function.filetype.php + * + * @param string $path + * @return bool + */ + public function filetype($path) + { + } + /** + * see https://www.php.net/manual/en/function.filesize.php + * The result for filesize when called on a folder is required to be 0 + */ + public function filesize($path) : false|int|float + { + } + /** + * check if a file can be created in $path + * + * @param string $path + * @return bool + */ + public function isCreatable($path) + { + } + /** + * check if a file can be read + * + * @param string $path + * @return bool + */ + public function isReadable($path) + { + } + /** + * check if a file can be written to + * + * @param string $path + * @return bool + */ + public function isUpdatable($path) + { + } + /** + * check if a file can be deleted + * + * @param string $path + * @return bool + */ + public function isDeletable($path) + { + } + /** + * check if a file can be shared + * + * @param string $path + * @return bool + */ + public function isSharable($path) + { + } + /** + * get the full permissions of a path. + * Should return a combination of the PERMISSION_ constants defined in lib/public/constants.php + * + * @param string $path + * @return int + */ + public function getPermissions($path) + { + } + /** + * see https://www.php.net/manual/en/function.file_exists.php + * + * @param string $path + * @return bool + */ + public function file_exists($path) + { + } + /** + * see https://www.php.net/manual/en/function.filemtime.php + * + * @param string $path + * @return int|bool + */ + public function filemtime($path) + { + } + /** + * see https://www.php.net/manual/en/function.file_get_contents.php + * + * @param string $path + * @return string|false + */ + public function file_get_contents($path) + { + } + /** + * see https://www.php.net/manual/en/function.file_put_contents.php + * + * @param string $path + * @param mixed $data + * @return int|float|false + */ + public function file_put_contents($path, $data) + { + } + /** + * see https://www.php.net/manual/en/function.unlink.php + * + * @param string $path + * @return bool + */ + public function unlink($path) + { + } + /** + * see https://www.php.net/manual/en/function.rename.php + * + * @param string $source + * @param string $target + * @return bool + */ + public function rename($source, $target) + { + } + /** + * see https://www.php.net/manual/en/function.copy.php + * + * @param string $source + * @param string $target + * @return bool + */ + public function copy($source, $target) + { + } + /** + * see https://www.php.net/manual/en/function.fopen.php + * + * @param string $path + * @param string $mode + * @return resource|bool + */ + public function fopen($path, $mode) + { + } + /** + * get the mimetype for a file or folder + * The mimetype for a folder is required to be "httpd/unix-directory" + * + * @param string $path + * @return string|bool + */ + public function getMimeType($path) + { + } + /** + * see https://www.php.net/manual/en/function.hash.php + * + * @param string $type + * @param string $path + * @param bool $raw + * @return string|bool + */ + public function hash($type, $path, $raw = false) + { + } + /** + * see https://www.php.net/manual/en/function.free_space.php + * + * @param string $path + * @return int|float|bool + */ + public function free_space($path) + { + } + /** + * search for occurrences of $query in file names + * + * @param string $query + * @return array|bool + */ + public function search($query) + { + } + /** + * see https://www.php.net/manual/en/function.touch.php + * If the backend does not support the operation, false should be returned + * + * @param string $path + * @param int $mtime + * @return bool + */ + public function touch($path, $mtime = null) + { + } + /** + * get the path to a local version of the file. + * The local version of the file can be temporary and doesn't have to be persistent across requests + * + * @param string $path + * @return string|false + */ + public function getLocalFile($path) + { + } + /** + * check if a file or folder has been updated since $time + * + * @param string $path + * @param int $time + * @return bool + * + * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. + * returning true for other changes in the folder is optional + */ + public function hasUpdated($path, $time) + { + } + /** + * get a cache instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage|null (optional) the storage to pass to the cache + * @return \OC\Files\Cache\Cache + */ + public function getCache($path = '', $storage = null) + { + } + /** + * get the user id of the owner of a file or folder + * + * @param string $path + * @return string + */ + public function getOwner($path) + { + } + /** + * get a watcher instance for the cache + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Watcher + */ + public function getWatcher($path = '', $storage = null) + { + } + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string|false + */ + public function getETag($path) + { + } + public function getMetaData($path) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + * @throws \OCP\Lock\LockedException + */ + public function acquireLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + */ + public function releaseLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * @param string $path + * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE + * @param \OCP\Lock\ILockingProvider $provider + */ + public function changeLock($path, $type, \OCP\Lock\ILockingProvider $provider) + { + } + /** + * Resolve the path for the source of the share + * + * @param string $path + * @return array + */ + public function resolvePath($path) + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function copyFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function moveFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) + { + } + public function getPropagator($storage = null) + { + } + public function writeStream(string $path, $stream, ?int $size = null) : int + { + } + public function getDirectoryContent($directory) : \Traversable + { + } + } + class Quota extends \OC\Files\Storage\Wrapper\Wrapper + { + /** @var callable|null */ + protected $quotaCallback; + /** @var int|float|null int on 64bits, float on 32bits for bigint */ + protected int|float|null $quota; + protected string $sizeRoot; + private \OC\SystemConfig $config; + private bool $quotaIncludeExternalStorage; + /** + * @param array $parameters + */ + public function __construct($parameters) + { + } + /** + * @return int|float quota value + */ + public function getQuota() : int|float + { + } + private function hasQuota() : bool + { + } + /** + * @param string $path + * @param IStorage $storage + * @return int|float + */ + protected function getSize($path, $storage = null) + { + } + /** + * Get free space as limited by the quota + * + * @param string $path + * @return int|float|bool + */ + public function free_space($path) + { + } + /** + * see https://www.php.net/manual/en/function.file_put_contents.php + * + * @param string $path + * @param mixed $data + * @return int|float|false + */ + public function file_put_contents($path, $data) + { + } + /** + * see https://www.php.net/manual/en/function.copy.php + * + * @param string $source + * @param string $target + * @return bool + */ + public function copy($source, $target) + { + } + /** + * see https://www.php.net/manual/en/function.fopen.php + * + * @param string $path + * @param string $mode + * @return resource|bool + */ + public function fopen($path, $mode) + { + } + /** + * Checks whether the given path is a part file + * + * @param string $path Path that may identify a .part file + * @return bool + * @note this is needed for reusing keys + */ + private function isPartFile($path) + { + } + /** + * Only apply quota for files, not metadata, trash or others + */ + private function shouldApplyQuota(string $path) : bool + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function copyFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) + { + } + /** + * @param IStorage $sourceStorage + * @param string $sourceInternalPath + * @param string $targetInternalPath + * @return bool + */ + public function moveFromStorage(\OCP\Files\Storage\IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) + { + } + public function mkdir($path) + { + } + public function touch($path, $mtime = null) + { + } + } + /** + * Mask the permissions of a storage + * + * This can be used to restrict update, create, delete and/or share permissions of a storage + * + * Note that the read permissions can't be masked + */ + class PermissionsMask extends \OC\Files\Storage\Wrapper\Wrapper + { + /** + * @var int the permissions bits we want to keep + */ + private $mask; + /** + * @param array $arguments ['storage' => $storage, 'mask' => $mask] + * + * $storage: The storage the permissions mask should be applied on + * $mask: The permission bits that should be kept, a combination of the \OCP\Constant::PERMISSION_ constants + */ + public function __construct($arguments) + { + } + private function checkMask($permissions) + { + } + public function isUpdatable($path) + { + } + public function isCreatable($path) + { + } + public function isDeletable($path) + { + } + public function isSharable($path) + { + } + public function getPermissions($path) + { + } + public function rename($source, $target) + { + } + public function copy($source, $target) + { + } + public function touch($path, $mtime = null) + { + } + public function mkdir($path) + { + } + public function rmdir($path) + { + } + public function unlink($path) + { + } + public function file_put_contents($path, $data) + { + } + public function fopen($path, $mode) + { + } + /** + * get a cache instance for the storage + * + * @param string $path + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the cache + * @return \OC\Files\Cache\Cache + */ + public function getCache($path = '', $storage = null) + { + } + public function getMetaData($path) + { + } + public function getScanner($path = '', $storage = null) + { + } + public function getDirectoryContent($directory) : \Traversable + { + } + } + class Encryption extends \OC\Files\Storage\Wrapper\Wrapper { + } +} + +namespace OC\Files\ObjectStore { + use OC\Files\Storage\Wrapper\Wrapper; + class ObjectStoreStorage extends Wrapper {} +} + +namespace OCA\Circles { + use OCA\Circles\Model\Circle; + use OCA\Circles\Model\FederatedUser; + use OCA\Circles\Model\Probes\CircleProbe; + use OCA\Circles\Model\Probes\DataProbe; + use OCP\DB\QueryBuilder\ICompositeExpression; + use OCP\DB\QueryBuilder\IQueryBuilder; + + interface IFederatedUser { + + } + + class CirclesManager { + public function startSession(?FederatedUser $federatedUser = null): void {} + public function probeCircles(?CircleProbe $circleProbe = null, ?DataProbe $dataProbe = null): array {} + public function getQueryHelper(): CirclesQueryHelper {} + public function getLocalFederatedUser(string $userId): FederatedUser {} + public function startSuperSession(): void {} + public function stopSession(): void {} + public function getCircle(string $singleId, ?CircleProbe $probe = null): Circle {} + } + + class CirclesQueryHelper{ + public function addCircleDetails(string $alias, string $field): void {} + public function getQueryBuilder(): IQueryBuilder {} + public function extractCircle(array $data): Circle {} + public function limitToInheritedMembers(string $alias, string $field, IFederatedUser $federatedUser, bool $fullDetails = false): ICompositeExpression {} + } +} + +namespace OCA\Circles\Exceptions { + class CircleNotFoundException extends \Exception { + } +} + +namespace OCA\Circles\Model { + use OCA\Circles\IFederatedUser; + + class FederatedUser implements IFederatedUser { + } + class CircleProbe { + } + class DataProbe { + } + class Circle { + public function getDisplayName(): string {} + public function getSingleId(): string {} + } +} + +namespace OCA\Circles\Model\Probes { + class CircleProbe { + public function includeSystemCircles(bool $include = true): self {} + public function includeSingleCircles(bool $include = true): self {} + } +} + +namespace OCA\Circles\Events { + use OCA\Circles\Model\Circle; + + class CircleDestroyedEvent extends \OCP\EventDispatcher\Event { + public function getCircle(): Circle {} + } +} + + +namespace OCA\DAV\Connector\Sabre\Exception { + class Forbidden extends \Sabre\DAV\Exception\Forbidden { + public const NS_OWNCLOUD = 'http://owncloud.org/ns'; + + /** + * @param string $message + * @param bool $retry + * @param \Exception $previous + */ + public function __construct($message, $retry = false, \Exception $previous = null) {} + + /** + * This method allows the exception to include additional information + * into the WebDAV error response + * + * @param \Sabre\DAV\Server $server + * @param \DOMElement $errorNode + * @return void + */ + public function serialize(\Sabre\DAV\Server $server, \DOMElement $errorNode) {} + } +} + +namespace OC\Encryption\Exceptions { + class DecryptionFailedException extends \Exception {} }