diff --git a/apps/files/lib/ResponseDefinitions.php b/apps/files/lib/ResponseDefinitions.php index c5d094e7bd8..f5ed2ba8ce2 100644 --- a/apps/files/lib/ResponseDefinitions.php +++ b/apps/files/lib/ResponseDefinitions.php @@ -17,9 +17,10 @@ namespace OCA\Files; * 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 df34a0daf1f..c848532366f 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 2052022fd8e..8d3f8acd3ff 100644 --- a/lib/composer/composer/InstalledVersions.php +++ b/lib/composer/composer/InstalledVersions.php @@ -277,7 +277,7 @@ class InstalledVersions 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 @@ class InstalledVersions 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 f27399a042d..62ecfd8d004 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 cd89ef10785..ab3dd57b356 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/Template/TemplateManager.php b/lib/private/Files/Template/TemplateManager.php index 294ace256da..4d66ed8f9c8 100644 --- a/lib/private/Files/Template/TemplateManager.php +++ b/lib/private/Files/Template/TemplateManager.php @@ -11,16 +11,15 @@ namespace OC\Files\Template; 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\RegisterTemplateCreatorEvent; 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 @@ class TemplateManager implements ITemplateManager { return $this->providers; } - public function getTypes(): array { + /** + * @return list + */ + private function getTypes(): array { if (!empty($this->types)) { return $this->types; } @@ -112,6 +103,7 @@ class TemplateManager implements ITemplateManager { return $this->types; } + #[Override] public function listCreators(): array { $types = $this->getTypes(); usort($types, function (TemplateFileCreator $a, TemplateFileCreator $b) { @@ -120,6 +112,7 @@ class TemplateManager implements ITemplateManager { return $types; } + #[Override] public function listTemplates(): array { return array_values(array_map(function (TemplateFileCreator $entry) { return array_merge($entry->jsonSerialize(), [ @@ -128,6 +121,7 @@ class TemplateManager implements ITemplateManager { }, $this->listCreators())); } + #[Override] public function listTemplateFields(int $fileId): array { foreach ($this->listCreators() as $creator) { $fields = $this->getTemplateFields($creator, $fileId); @@ -141,13 +135,7 @@ class TemplateManager implements ITemplateManager { 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 @@ class TemplateManager implements ITemplateManager { 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 @@ class TemplateManager implements ITemplateManager { $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 @@ class TemplateManager implements ITemplateManager { } /** - * @return Folder * @throws \OCP\Files\NotFoundException * @throws \OCP\Files\NotPermittedException * @throws \OC\User\NoUserException @@ -245,6 +235,9 @@ class TemplateManager implements ITemplateManager { 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 @@ class TemplateManager implements ITemplateManager { $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 @@ class TemplateManager implements ITemplateManager { $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 @@ class TemplateManager implements ITemplateManager { 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; @@ -365,8 +356,10 @@ class TemplateManager implements ITemplateManager { } try { + /** @var Folder $folder */ $folder = $userFolder->get($userTemplatePath); } catch (NotFoundException $e) { + /** @var Folder $folder */ $folder = $userFolder->get(dirname($userTemplatePath)); $folder = $folder->newFolder(basename($userTemplatePath)); } @@ -407,7 +400,7 @@ class TemplateManager implements ITemplateManager { 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/Template/ITemplateManager.php b/lib/public/Files/Template/ITemplateManager.php index df81bc5604e..c838bfff20d 100644 --- a/lib/public/Files/Template/ITemplateManager.php +++ b/lib/public/Files/Template/ITemplateManager.php @@ -8,11 +8,25 @@ declare(strict_types=1); */ 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 @@ interface ITemplateManager { * @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 e0c9d77d2e8..f7cefda9f7d 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/Template/TemplateManagerTest.php b/tests/lib/Files/Template/TemplateManagerTest.php index be65657aba9..f0c5acaec90 100644 --- a/tests/lib/Files/Template/TemplateManagerTest.php +++ b/tests/lib/Files/Template/TemplateManagerTest.php @@ -25,6 +25,7 @@ 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 +64,12 @@ class TemplateManagerTest extends TestCase { $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(