Merge pull request #56251 from nextcloud/carl/template-manager-cleanup

refactor(template-manager): Modernize template manager API
This commit is contained in:
Andy Scherzinger 2025-12-04 17:26:53 +01:00 committed by GitHub
commit 1ef465f804
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 272 additions and 124 deletions

View file

@ -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{

View file

@ -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"
}
}
},

View file

@ -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<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
$required = require __DIR__ . '/installed.php';
self::$installed = $required;

View file

@ -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.

View file

@ -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(),

View file

@ -16,6 +16,7 @@ use OC\Files\Utils\PathHelper;
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\ISearchOperator;
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 @@ class Folder extends Node implements \OCP\Files\Folder {
$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.');
}
}
}
}

View file

@ -14,6 +14,7 @@ use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotPermittedException;
use Override;
/**
* Class LazyFolder
@ -138,6 +139,11 @@ class LazyFolder implements Folder {
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
*/

View file

@ -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<callable(): TemplateFileCreator> */
private array $registeredTypes = [];
/** @var list<TemplateFileCreator> */
private array $types = [];
/** @var array<class-string<ICustomTemplateProvider>, 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<class-string<ICustomTemplateProvider>, 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<TemplateFileCreator>
*/
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;
@ -364,12 +355,7 @@ class TemplateManager implements ITemplateManager {
}
}
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 @@ 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)) {

View file

@ -65,6 +65,15 @@ interface Folder extends Node {
*/
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
*

View file

@ -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
*/

View file

@ -1912,7 +1912,8 @@
"mime",
"size",
"type",
"hasPreview"
"hasPreview",
"permissions"
],
"properties": {
"basename": {
@ -1937,14 +1938,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"
}
}
},

View file

@ -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 @@ use OCP\Files\NotPermittedException;
*/
#[\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 @@ class FileTest extends NodeTestCase {
}
}
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';
}

View file

@ -17,6 +17,7 @@ use OC\Files\Mount\MountPoint;
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\ISearchBinaryOperator;
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 @@ use PHPUnit\Framework\MockObject\MockObject;
*/
#[\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 @@ class FolderTest extends NodeTestCase {
}
}
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 @@ class FolderTest extends NodeTestCase {
->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 @@ class FolderTest extends NodeTestCase {
$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 @@ class FolderTest extends NodeTestCase {
}, $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);
}
}

View file

@ -92,30 +92,24 @@ abstract class NodeTestCase extends \Test\TestCase {
return $view;
}
abstract protected function createTestNode(IRootFolder $root, View&MockObject $view, string $path, array $data = [], string $internalPath = '', ?IStorage $storage = null): Node;
/**
* @param IRootFolder $root
* @param View $view
* @param string $path
* @return Node
* @return class-string<Node>
*/
abstract protected function createTestNode($root, $view, $path, array $data = [], $internalPath = '', $storage = null);
abstract protected function getNodeClass(): string;
/**
* @return class-string<Node>
*/
abstract protected function getNonExistingNodeClass(): string;
/**
* @return string
*/
abstract protected function getNodeClass();
abstract protected function getViewDeleteMethod(): string;
/**
* @return string
*/
abstract protected function getNonExistingNodeClass();
/**
* @return string
*/
abstract protected function getViewDeleteMethod();
protected function getMockStorage() {
protected function getMockStorage(): IStorage&MockObject {
$storage = $this->getMockBuilder(IStorage::class)
->disableOriginalConstructor()
->getMock();
@ -125,7 +119,7 @@ abstract class NodeTestCase extends \Test\TestCase {
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);

View file

@ -23,8 +23,7 @@ use OCP\IDBConnection;
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 @@ 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(