diff --git a/apps/files/lib/ResponseDefinitions.php b/apps/files/lib/ResponseDefinitions.php index c5d094e7bd8cf..f5ed2ba8ce238 100644 --- a/apps/files/lib/ResponseDefinitions.php +++ b/apps/files/lib/ResponseDefinitions.php @@ -17,9 +17,10 @@ * filename: ?string, * lastmod: int, * mime: string, - * size: int, + * size: int|float, * type: string, * hasPreview: bool, + * permissions: int, * } * * @psalm-type FilesTemplateField = array{ diff --git a/apps/files/openapi.json b/apps/files/openapi.json index df34a0daf1f29..c848532366f28 100644 --- a/apps/files/openapi.json +++ b/apps/files/openapi.json @@ -323,7 +323,8 @@ "mime", "size", "type", - "hasPreview" + "hasPreview", + "permissions" ], "properties": { "basename": { @@ -348,14 +349,26 @@ "type": "string" }, "size": { - "type": "integer", - "format": "int64" + "anyOf": [ + { + "type": "integer", + "format": "int64" + }, + { + "type": "number", + "format": "double" + } + ] }, "type": { "type": "string" }, "hasPreview": { "type": "boolean" + }, + "permissions": { + "type": "integer", + "format": "int64" } } }, diff --git a/lib/composer/composer/InstalledVersions.php b/lib/composer/composer/InstalledVersions.php index 2052022fd8e1e..8d3f8acd3ff38 100644 --- a/lib/composer/composer/InstalledVersions.php +++ b/lib/composer/composer/InstalledVersions.php @@ -277,7 +277,7 @@ public static function getRawData() if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { + if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) { self::$installed = include __DIR__ . '/installed.php'; } else { self::$installed = array(); @@ -378,7 +378,7 @@ private static function getInstalled() if (null === self::$installed) { // only require the installed.php file if this file is loaded from its dumped location, // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { + if (substr(__DIR__, -8, 1) !== 'C' && is_file(__DIR__ . '/installed.php')) { /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ $required = require __DIR__ . '/installed.php'; self::$installed = $required; diff --git a/lib/composer/composer/LICENSE b/lib/composer/composer/LICENSE index f27399a042d95..62ecfd8d0046b 100644 --- a/lib/composer/composer/LICENSE +++ b/lib/composer/composer/LICENSE @@ -1,4 +1,3 @@ - Copyright (c) Nils Adermann, Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/lib/composer/composer/installed.php b/lib/composer/composer/installed.php index cd89ef10785d8..ab3dd57b35611 100644 --- a/lib/composer/composer/installed.php +++ b/lib/composer/composer/installed.php @@ -3,7 +3,7 @@ 'name' => '__root__', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3fce359f4c606737b21b1b4213efd5bc5536e867', + 'reference' => '8c12590cf6f93ce7aa41f17817b3791e524da39e', 'type' => 'library', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), @@ -13,7 +13,7 @@ '__root__' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '3fce359f4c606737b21b1b4213efd5bc5536e867', + 'reference' => '8c12590cf6f93ce7aa41f17817b3791e524da39e', 'type' => 'library', 'install_path' => __DIR__ . '/../../../', 'aliases' => array(), diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index b747406e4dfa0..3558664758816 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -16,6 +16,7 @@ use OC\User\LazyUser; use OCP\Files\Cache\ICacheEntry; use OCP\Files\FileInfo; +use OCP\Files\Folder as IFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\Node as INode; use OCP\Files\NotFoundException; @@ -26,8 +27,9 @@ use OCP\Files\Search\ISearchOrder; use OCP\Files\Search\ISearchQuery; use OCP\IUserManager; +use Override; -class Folder extends Node implements \OCP\Files\Folder { +class Folder extends Node implements IFolder { private ?IUserManager $userManager = null; @@ -480,4 +482,28 @@ private function recreateIfNeeded(): void { $this->wasDeleted = false; } } + + #[Override] + public function getOrCreateFolder(string $path, int $maxRetries = 5): IFolder { + $i = 0; + while (true) { + $path = $i === 0 ? $path : $path . ' (' . $i . ')'; + try { + $folder = $this->get($path); + if ($folder instanceof IFolder) { + return $folder; + } + } catch (NotFoundException) { + $folder = dirname($path) === '.' ? $this : $this->get(dirname($path)); + if (!($folder instanceof Folder)) { + throw new NotPermittedException("Unable to create folder $path. Parent is not a directory."); + } + return $folder->newFolder(basename($path)); + } + $i++; + if ($i === $maxRetries) { + throw new NotPermittedException('Unable to load or create folder.'); + } + } + } } diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php index ceadc4cad1e9c..c4188b80ee5ec 100644 --- a/lib/private/Files/Node/LazyFolder.php +++ b/lib/private/Files/Node/LazyFolder.php @@ -14,6 +14,7 @@ use OCP\Files\IRootFolder; use OCP\Files\Mount\IMountPoint; use OCP\Files\NotPermittedException; +use Override; /** * Class LazyFolder @@ -138,6 +139,11 @@ public function get($path) { return $this->getRootFolder()->get($this->getFullPath($path)); } + #[Override] + public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder { + return $this->getRootFolder()->getOrCreateFolder($this->getFullPath($path), $maxRetries); + } + /** * @inheritDoc */ diff --git a/lib/private/Files/Template/TemplateManager.php b/lib/private/Files/Template/TemplateManager.php index 294ace256da36..155be9303b1f2 100644 --- a/lib/private/Files/Template/TemplateManager.php +++ b/lib/private/Files/Template/TemplateManager.php @@ -11,16 +11,15 @@ use OC\AppFramework\Bootstrap\Coordinator; use OC\Files\Cache\Scanner; use OC\Files\Filesystem; +use OCA\Files\ResponseDefinitions; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; use OCP\Files\Folder; use OCP\Files\GenericFileException; use OCP\Files\IFilenameValidator; use OCP\Files\IRootFolder; -use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\Template\BeforeGetTemplatesEvent; -use OCP\Files\Template\Field; use OCP\Files\Template\FileCreatedFromTemplateEvent; use OCP\Files\Template\ICustomTemplateProvider; use OCP\Files\Template\ITemplateManager; @@ -28,65 +27,54 @@ use OCP\Files\Template\Template; use OCP\Files\Template\TemplateFileCreator; use OCP\IConfig; +use OCP\IL10N; use OCP\IPreview; -use OCP\IServerContainer; use OCP\IUserManager; use OCP\IUserSession; use OCP\L10N\IFactory; +use Override; +use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +/** + * @psalm-import-type FilesTemplateFile from ResponseDefinitions + */ class TemplateManager implements ITemplateManager { - private $registeredTypes = []; - private $types = []; - - /** @var array|null */ - private $providers = null; - - private $serverContainer; - private $eventDispatcher; - private $rootFolder; - private $userManager; - private $previewManager; - private $config; - private $l10n; - private $logger; - private $userId; - private $l10nFactory; - /** @var Coordinator */ - private $bootstrapCoordinator; + /** @var list */ + private array $registeredTypes = []; + /** @var list */ + private array $types = []; + /** @var array, ICustomTemplateProvider>|null */ + private ?array $providers = null; + private IL10n $l10n; + private ?string $userId; public function __construct( - IServerContainer $serverContainer, - IEventDispatcher $eventDispatcher, - Coordinator $coordinator, - IRootFolder $rootFolder, + private readonly ContainerInterface $serverContainer, + private readonly IEventDispatcher $eventDispatcher, + private readonly Coordinator $bootstrapCoordinator, + private readonly IRootFolder $rootFolder, IUserSession $userSession, - IUserManager $userManager, - IPreview $previewManager, - IConfig $config, - IFactory $l10nFactory, - LoggerInterface $logger, - private IFilenameValidator $filenameValidator, + private readonly IUserManager $userManager, + private readonly IPreview $previewManager, + private readonly IConfig $config, + private readonly IFactory $l10nFactory, + private readonly LoggerInterface $logger, + private readonly IFilenameValidator $filenameValidator, ) { - $this->serverContainer = $serverContainer; - $this->eventDispatcher = $eventDispatcher; - $this->bootstrapCoordinator = $coordinator; - $this->rootFolder = $rootFolder; - $this->userManager = $userManager; - $this->previewManager = $previewManager; - $this->config = $config; - $this->l10nFactory = $l10nFactory; $this->l10n = $l10nFactory->get('lib'); - $this->logger = $logger; - $user = $userSession->getUser(); - $this->userId = $user ? $user->getUID() : null; + $this->userId = $userSession->getUser()?->getUID(); } + #[Override] public function registerTemplateFileCreator(callable $callback): void { $this->registeredTypes[] = $callback; } - public function getRegisteredProviders(): array { + /** + * @return array, ICustomTemplateProvider> + */ + private function getRegisteredProviders(): array { if ($this->providers !== null) { return $this->providers; } @@ -101,7 +89,10 @@ public function getRegisteredProviders(): array { return $this->providers; } - public function getTypes(): array { + /** + * @return list + */ + private function getTypes(): array { if (!empty($this->types)) { return $this->types; } @@ -112,6 +103,7 @@ public function getTypes(): array { return $this->types; } + #[Override] public function listCreators(): array { $types = $this->getTypes(); usort($types, function (TemplateFileCreator $a, TemplateFileCreator $b) { @@ -120,6 +112,7 @@ public function listCreators(): array { return $types; } + #[Override] public function listTemplates(): array { return array_values(array_map(function (TemplateFileCreator $entry) { return array_merge($entry->jsonSerialize(), [ @@ -128,6 +121,7 @@ public function listTemplates(): array { }, $this->listCreators())); } + #[Override] public function listTemplateFields(int $fileId): array { foreach ($this->listCreators() as $creator) { $fields = $this->getTemplateFields($creator, $fileId); @@ -141,13 +135,7 @@ public function listTemplateFields(int $fileId): array { return []; } - /** - * @param string $filePath - * @param string $templateId - * @param array $templateFields - * @return array - * @throws GenericFileException - */ + #[Override] public function createFromTemplate(string $filePath, string $templateId = '', string $templateType = 'user', array $templateFields = []): array { $userFolder = $this->rootFolder->getUserFolder($this->userId); try { @@ -159,6 +147,7 @@ public function createFromTemplate(string $filePath, string $templateId = '', st if (!$userFolder->nodeExists(dirname($filePath))) { throw new GenericFileException($this->l10n->t('Invalid path')); } + /** @var Folder $folder */ $folder = $userFolder->get(dirname($filePath)); $template = null; if ($templateType === 'user' && $templateId !== '') { @@ -178,7 +167,9 @@ public function createFromTemplate(string $filePath, string $templateId = '', st $targetFile = $folder->newFile($filename, ($template instanceof File ? $template->fopen('rb') : null)); $this->eventDispatcher->dispatchTyped(new FileCreatedFromTemplateEvent($template, $targetFile, $templateFields)); - return $this->formatFile($userFolder->get($filePath)); + /** @var File $file */ + $file = $userFolder->get($filePath); + return $this->formatFile($file); } catch (\Exception $e) { $this->logger->error($e->getMessage(), ['exception' => $e]); throw new GenericFileException($this->l10n->t('Failed to create file from template')); @@ -186,7 +177,6 @@ public function createFromTemplate(string $filePath, string $templateId = '', st } /** - * @return Folder * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException * @throws \OC\User\NoUserException @@ -245,6 +235,9 @@ private function getUserTemplates(TemplateFileCreator $type): array { foreach ($type->getMimetypes() as $mimetype) { foreach ($userTemplateFolder->searchByMime($mimetype) as $templateFile) { + if (!($templateFile instanceof File)) { + continue; + } $template = new Template( 'user', $this->rootFolder->getUserFolder($this->userId)->getRelativePath($templateFile->getPath()), @@ -267,9 +260,7 @@ private function getTemplateFields(TemplateFileCreator $type, int $fileId): arra $matchedTemplates = array_filter( array_merge($providerTemplates, $userTemplates), - function (Template $template) use ($fileId) { - return $template->jsonSerialize()['fileid'] === $fileId; - }); + fn (Template $template): bool => $template->jsonSerialize()['fileid'] === $fileId); if (empty($matchedTemplates)) { return []; @@ -277,22 +268,19 @@ function (Template $template) use ($fileId) { $this->eventDispatcher->dispatchTyped(new BeforeGetTemplatesEvent($matchedTemplates, true)); - return array_values(array_map(function (Template $template) { - return $template->jsonSerialize()['fields'] ?? []; - }, $matchedTemplates)); + return array_values(array_map(static fn (Template $template): array => $template->jsonSerialize()['fields'] ?? [], $matchedTemplates)); } /** - * @param Node|File $file - * @return array + * @return FilesTemplateFile * @throws NotFoundException * @throws \OCP\Files\InvalidPathException */ - private function formatFile(Node $file): array { + private function formatFile(File $file): array { return [ 'basename' => $file->getName(), 'etag' => $file->getEtag(), - 'fileid' => $file->getId(), + 'fileid' => $file->getId() ?? -1, 'filename' => $this->rootFolder->getUserFolder($this->userId)->getRelativePath($file->getPath()), 'lastmod' => $file->getMTime(), 'mime' => $file->getMimetype(), @@ -312,14 +300,17 @@ public function hasTemplateDirectory(): bool { return false; } + #[Override] public function setTemplatePath(string $path): void { $this->config->setUserValue($this->userId, 'core', 'templateDirectory', $path); } + #[Override] public function getTemplatePath(): string { return $this->config->getUserValue($this->userId, 'core', 'templateDirectory', ''); } + #[Override] public function initializeTemplateDirectory(?string $path = null, ?string $userId = null, $copyTemplates = true): string { if ($userId !== null) { $this->userId = $userId; @@ -364,12 +355,7 @@ public function initializeTemplateDirectory(?string $path = null, ?string $userI } } - try { - $folder = $userFolder->get($userTemplatePath); - } catch (NotFoundException $e) { - $folder = $userFolder->get(dirname($userTemplatePath)); - $folder = $folder->newFolder(basename($userTemplatePath)); - } + $folder = $userFolder->getOrCreateFolder($userTemplatePath); $folderIsEmpty = count($folder->getDirectoryListing()) === 0; @@ -407,7 +393,7 @@ public function initializeTemplateDirectory(?string $path = null, ?string $userI return $this->getTemplatePath(); } - private function getLocalizedTemplatePath(string $skeletonTemplatePath, string $userLang) { + private function getLocalizedTemplatePath(string $skeletonTemplatePath, string $userLang): string { $localizedSkeletonTemplatePath = str_replace('{lang}', $userLang, $skeletonTemplatePath); if (!file_exists($localizedSkeletonTemplatePath)) { diff --git a/lib/public/Files/Folder.php b/lib/public/Files/Folder.php index a35d2d78bc90e..163d362ecb474 100644 --- a/lib/public/Files/Folder.php +++ b/lib/public/Files/Folder.php @@ -65,6 +65,15 @@ public function getDirectoryListing(); */ public function get($path); + /** + * Get or create new folder if the folder does not already exist. + * + * @param string $path relative path of the file or folder + * @throw \OCP\Files\NotPermittedException + * @since 33.0.0 + */ + public function getOrCreateFolder(string $path, int $maxRetries = 5): Folder; + /** * Check if a file or folder exists in the folder * diff --git a/lib/public/Files/Template/ITemplateManager.php b/lib/public/Files/Template/ITemplateManager.php index df81bc5604ec2..c838bfff20de3 100644 --- a/lib/public/Files/Template/ITemplateManager.php +++ b/lib/public/Files/Template/ITemplateManager.php @@ -8,11 +8,25 @@ */ namespace OCP\Files\Template; +use OCP\AppFramework\Attribute\Consumable; use OCP\Files\GenericFileException; /** * @since 21.0.0 + * @psalm-type FilesTemplateFile = array{ + * basename: string, + * etag: string, + * fileid: int, + * filename: ?string, + * lastmod: int, + * mime: string, + * size: int|float, + * type: string, + * hasPreview: bool, + * permissions: int, + * } */ +#[Consumable(since: '21.0.0')] interface ITemplateManager { /** * Register a template type support @@ -78,7 +92,7 @@ public function initializeTemplateDirectory(?string $path = null, ?string $userI * @param string $templateId * @param string $templateType * @param array $templateFields Since 30.0.0 - * @return array + * @return FilesTemplateFile * @throws GenericFileException * @since 21.0.0 */ diff --git a/openapi.json b/openapi.json index e0c9d77d2e883..f7cefda9f7d99 100644 --- a/openapi.json +++ b/openapi.json @@ -1908,7 +1908,8 @@ "mime", "size", "type", - "hasPreview" + "hasPreview", + "permissions" ], "properties": { "basename": { @@ -1933,14 +1934,26 @@ "type": "string" }, "size": { - "type": "integer", - "format": "int64" + "anyOf": [ + { + "type": "integer", + "format": "int64" + }, + { + "type": "number", + "format": "double" + } + ] }, "type": { "type": "string" }, "hasPreview": { "type": "boolean" + }, + "permissions": { + "type": "integer", + "format": "int64" } } }, diff --git a/tests/lib/Files/Node/FileTest.php b/tests/lib/Files/Node/FileTest.php index 027f48498eab9..36a410887cc67 100644 --- a/tests/lib/Files/Node/FileTest.php +++ b/tests/lib/Files/Node/FileTest.php @@ -9,9 +9,14 @@ namespace Test\Files\Node; use OC\Files\Node\File; +use OC\Files\Node\NonExistingFile; use OC\Files\Node\Root; +use OC\Files\View; use OCP\Constants; +use OCP\Files\IRootFolder; use OCP\Files\NotPermittedException; +use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\MockObject\MockObject; /** * Class FileTest @@ -21,7 +26,7 @@ */ #[\PHPUnit\Framework\Attributes\Group('DB')] class FileTest extends NodeTestCase { - protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): File { if ($data || $internalPath || $storage) { return new File($root, $view, $path, $this->getFileInfo($data, $internalPath, $storage)); } else { @@ -29,15 +34,15 @@ protected function createTestNode($root, $view, $path, array $data = [], $intern } } - protected function getNodeClass() { - return '\OC\Files\Node\File'; + protected function getNodeClass(): string { + return File::class; } - protected function getNonExistingNodeClass() { - return '\OC\Files\Node\NonExistingFile'; + protected function getNonExistingNodeClass(): string { + return NonExistingFile::class; } - protected function getViewDeleteMethod() { + protected function getViewDeleteMethod(): string { return 'unlink'; } diff --git a/tests/lib/Files/Node/FolderTest.php b/tests/lib/Files/Node/FolderTest.php index 8ba0745d4e191..9801f05d6108e 100644 --- a/tests/lib/Files/Node/FolderTest.php +++ b/tests/lib/Files/Node/FolderTest.php @@ -17,6 +17,7 @@ use OC\Files\Node\File; use OC\Files\Node\Folder; use OC\Files\Node\Node; +use OC\Files\Node\NonExistingFolder; use OC\Files\Node\Root; use OC\Files\Search\SearchBinaryOperator; use OC\Files\Search\SearchComparison; @@ -37,6 +38,7 @@ use OCP\Files\Search\ISearchComparison; use OCP\Files\Search\ISearchOrder; use OCP\Files\Storage\IStorage; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; /** @@ -47,7 +49,7 @@ */ #[\PHPUnit\Framework\Attributes\Group('DB')] class FolderTest extends NodeTestCase { - protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null) { + protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): Folder { $view->expects($this->any()) ->method('getRoot') ->willReturn(''); @@ -58,23 +60,20 @@ protected function createTestNode($root, $view, $path, array $data = [], $intern } } - protected function getNodeClass() { - return '\OC\Files\Node\Folder'; + protected function getNodeClass(): string { + return Folder::class; } - protected function getNonExistingNodeClass() { - return '\OC\Files\Node\NonExistingFolder'; + protected function getNonExistingNodeClass(): string { + return NonExistingFolder::class; } - protected function getViewDeleteMethod() { + protected function getViewDeleteMethod(): string { return 'rmdir'; } public function testGetDirectoryContent(): void { $manager = $this->createMock(Manager::class); - /** - * @var View|\PHPUnit\Framework\MockObject\MockObject $view - */ $root = $this->getMockBuilder(Root::class) ->setConstructorArgs([$manager, $this->view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig]) ->getMock(); @@ -299,7 +298,6 @@ public function testSearch(): void { ->getMock(); $root->method('getUser') ->willReturn($this->user); - /** @var Storage\IStorage&MockObject $storage */ $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::1'); $cache = new Cache($storage); @@ -349,7 +347,6 @@ public function testSearchInRoot(): void { $root->expects($this->any()) ->method('getUser') ->willReturn($this->user); - /** @var \PHPUnit\Framework\MockObject\MockObject|Storage $storage */ $storage = $this->createMock(IStorage::class); $storage->method('getId')->willReturn('test::2'); $cache = new Cache($storage); @@ -1041,4 +1038,87 @@ public function testSearchSubStoragesLimitOffset(int $offset, int $limit, array }, $result); $this->assertEquals($expectedPaths, $ids); } + + public static function dataGetOrCreateFolder(): \Generator { + yield 'Create new folder' => [0]; + yield 'Get existing folder' => [1]; + yield 'Create new folder while a file with the same name already exists' => [2]; + } + + #[DataProvider('dataGetOrCreateFolder')] + public function testGetOrCreateFolder(int $case): void { + $folderName = 'asd'; + + $view = $this->getRootViewMock(); + $manager = $this->createMock(Manager::class); + $root = $this->getMockBuilder(Root::class) + ->setConstructorArgs([$manager, $view, $this->user, $this->userMountCache, $this->logger, $this->userManager, $this->eventDispatcher, $this->cacheFactory, $this->appConfig]) + ->getMock(); + $root->expects($this->any()) + ->method('getUser') + ->willReturn($this->user); + + $view->method('getFileInfo') + ->willReturnCallback(function (string $path) use ($folderName) { + if ($path === '/bar/foo' || $path === '/bar/foo/' . $folderName) { + return $this->getFileInfo(['permissions' => Constants::PERMISSION_ALL]); + } + $this->fail('Trying to get ' . $path); + }); + + $view->method('mkdir') + ->willReturn(true); + + $view->method('touch') + ->with('/bar/foo/asd') + ->willReturn(true); + + $node = new Folder($root, $view, '/bar/foo'); + + switch ($case) { + case 0: + $child = new Folder($root, $view, '/bar/foo/' . $folderName, null, $node); + + $root->expects($this->any()) + ->method('get') + ->willReturnCallback(function (string $path) use ($root, $view, $folderName) { + if ($path === '/bar/foo/') { + return new Folder($root, $view, '/bar/foo/'); + } elseif ($path === '/bar/foo/' . $folderName) { + throw new NotFoundException(); + } + $this->fail('Trying to get ' . $path); + }); + + break; // do nothing + case 1: + $child = new Folder($root, $view, '/bar/foo/' . $folderName, null, $node); + + $root->expects($this->any()) + ->method('get') + ->with('/bar/foo/' . $folderName) + ->willReturn($child); + $node->newFolder($folderName); + break; + case 2: + $child = new Folder($root, $view, '/bar/foo/' . $folderName . ' (1)', null, $node); + $root->expects($this->any()) + ->method('get') + ->willReturnCallback(function (string $path) use ($root, $view, $folderName) { + if ($path === '/bar/foo/') { + return new Folder($root, $view, '/bar/foo/'); + } elseif ($path === '/bar/foo/' . $folderName) { + return new File($root, $view, '/bar/foo/asd'); + } elseif ($path === '/bar/foo/' . $folderName . ' (1)') { + throw new NotFoundException(); + } + $this->fail('Trying to get ' . $path); + }); + $node->newFile($folderName); + break; + } + + $result = $node->getOrCreateFolder($folderName); + $this->assertEquals($child, $result); + } } diff --git a/tests/lib/Files/Node/NodeTestCase.php b/tests/lib/Files/Node/NodeTestCase.php index 90f7c2c0f65b3..56a20c3d7148b 100644 --- a/tests/lib/Files/Node/NodeTestCase.php +++ b/tests/lib/Files/Node/NodeTestCase.php @@ -92,30 +92,24 @@ protected function getRootViewMock() { return $view; } - /** - * @param IRootFolder $root - * @param View $view - * @param string $path - * @return Node - */ - abstract protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null); + abstract protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): Node; /** - * @return string + * @return class-string */ - abstract protected function getNodeClass(); + abstract protected function getNodeClass(): string; /** - * @return string + * @return class-string */ - abstract protected function getNonExistingNodeClass(); + abstract protected function getNonExistingNodeClass(): string; /** * @return string */ - abstract protected function getViewDeleteMethod(); + abstract protected function getViewDeleteMethod(): string; - protected function getMockStorage() { + protected function getMockStorage(): IStorage&MockObject { $storage = $this->getMockBuilder(IStorage::class) ->disableOriginalConstructor() ->getMock(); @@ -125,7 +119,7 @@ protected function getMockStorage() { return $storage; } - protected function getFileInfo($data, $internalPath = '', $storage = null) { + protected function getFileInfo($data, $internalPath = '', ?IStorage $storage = null) { $mount = $this->createMock(IMountPoint::class); $mount->method('getStorage') ->willReturn($storage); diff --git a/tests/lib/Files/Template/TemplateManagerTest.php b/tests/lib/Files/Template/TemplateManagerTest.php index be65657aba967..7b3f0beff429d 100644 --- a/tests/lib/Files/Template/TemplateManagerTest.php +++ b/tests/lib/Files/Template/TemplateManagerTest.php @@ -23,8 +23,7 @@ use OCP\IL10N; use OCP\IPreview; use OCP\IServerContainer; -use OCP\IUserManager; -use OCP\IUserSession; +use OCP\IUser; use OCP\L10N\IFactory; use Psr\Log\NullLogger; use Test\TestCase; @@ -63,8 +62,12 @@ protected function setUp(): void { $this->bootstrapCoordinator->method('getRegistrationContext') ->willReturn(new RegistrationContext($logger)); $this->rootFolder = $this->createMock(IRootFolder::class); - $userSession = $this->createMock(IUserSession::class); - $userManager = $this->createMock(IUserManager::class); + $user = $this->createMock(IUser::class); + $user->method('getUID')->willReturn('user1'); + $userSession = $this->createMock(\OCP\IUserSession::class); + $userSession->method('getUser') + ->willReturn($user); + $userManager = $this->createMock(\OCP\IUserManager::class); $previewManager = $this->createMock(IPreview::class); $this->templateManager = new TemplateManager(