diff --git a/.editorconfig b/.editorconfig index e68d366a..d9bb78d6 100644 --- a/.editorconfig +++ b/.editorconfig @@ -23,6 +23,9 @@ indent_size = 2 [*.json] indent_style = tab +[Makefile] +indent_style = tab + # ReST-Files [*.rst] indent_size = 3 diff --git a/Build/phpstan/phpstan-baseline.neon b/Build/phpstan/phpstan-baseline.neon index 2584f8e3..ccd9654d 100644 --- a/Build/phpstan/phpstan-baseline.neon +++ b/Build/phpstan/phpstan-baseline.neon @@ -1230,6 +1230,11 @@ parameters: count: 1 path: ../../Tests/Unit/Loader/JsonSplitLoaderTest.php + - + message: "#^Method MASK\\\\Mask\\\\Tests\\\\Unit\\\\Loader\\\\JsonSplitLoaderTest\\:\\:getExpectedConfigurationArray2\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: ../../Tests/Unit/Loader/JsonSplitLoaderTest.php + - message: "#^Method MASK\\\\Mask\\\\Test\\\\Utility\\\\OverrideFieldsUtilityTest\\:\\:restructuringFieldsWorks\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" count: 1 @@ -1284,4 +1289,3 @@ parameters: message: "#^Method MASK\\\\Mask\\\\Tests\\\\Unit\\\\TemplatePathUtilityTest\\:\\:getTemplatePathDataProvider\\(\\) return type has no value type specified in iterable type iterable\\.$#" count: 1 path: ../../Tests/Unit/Utility/TemplatePathUtilityTest.php - diff --git a/Classes/CodeGenerator/TyposcriptCodeGenerator.php b/Classes/CodeGenerator/TyposcriptCodeGenerator.php index 9053bba4..c6f09808 100644 --- a/Classes/CodeGenerator/TyposcriptCodeGenerator.php +++ b/Classes/CodeGenerator/TyposcriptCodeGenerator.php @@ -214,21 +214,24 @@ public function generateSetupTyposcript(): string // for base paths to fluid templates configured in extension settings $paths = []; if ($this->maskExtensionConfiguration['content'] ?? false) { - $paths['templateRootPaths'] = [ - 10 => $this->maskExtensionConfiguration['content'], - ]; + $paths['templateRootPaths'] = $this->getTyposcriptPathArray( + $this->maskExtensionConfiguration['content'], + 10 + ); } if ($this->maskExtensionConfiguration['partials'] ?? false) { - $paths['partialRootPaths'] = [ - 10 => $this->maskExtensionConfiguration['partials'], - ]; + $paths['partialRootPaths'] = $this->getTyposcriptPathArray( + $this->maskExtensionConfiguration['partials'], + 10 + ); } if ($this->maskExtensionConfiguration['layouts'] ?? false) { - $paths['layoutRootPaths'] = [ - 10 => $this->maskExtensionConfiguration['layouts'], - ]; + $paths['layoutRootPaths'] = $this->getTyposcriptPathArray( + $this->maskExtensionConfiguration['layouts'], + 10 + ); } $setupContent[] = ArrayToTypoScriptConverter::convert($paths, 'lib.maskContentElement'); @@ -250,4 +253,19 @@ public function generateSetupTyposcript(): string return implode("\n\n", $setupContent) . "\n\n"; } + + /** + * @return string[] + */ + protected function getTyposcriptPathArray(string $commaSeparatedPaths, int $startKey): array + { + $paths = TemplatePathUtility::getPaths($commaSeparatedPaths); + if (count($paths) === 0) { + return []; + } + return array_combine( + range($startKey, $startKey + count($paths) - 1), + $paths + ); + } } diff --git a/Classes/Controller/AjaxController.php b/Classes/Controller/AjaxController.php index 7d1ed51e..545031dc 100644 --- a/Classes/Controller/AjaxController.php +++ b/Classes/Controller/AjaxController.php @@ -1128,12 +1128,21 @@ protected function getMissingFolders(): array if (!isset($this->maskExtensionConfiguration[$key])) { continue; } - $path = GeneralUtility::getFileAbsFileName($this->maskExtensionConfiguration[$key]); - if ($path === '') { + $origPaths = TemplatePathUtility::getPaths($this->maskExtensionConfiguration[$key]); + if (count($origPaths) === 0) { continue; } - if (!file_exists($path)) { - $missingFolders[$key] = $this->maskExtensionConfiguration[$key]; + foreach ($origPaths as $origPath) { + $path = GeneralUtility::getFileAbsFileName($origPath); + if (!file_exists($path)) { + $suffix = ''; + $num = 1; + while (isset($missingFolders[$key . $suffix])) { + $num++; + $suffix = ' #' . $num; + } + $missingFolders[$key . $suffix] = $origPath; + } } } diff --git a/Classes/EventListeners/MaskBackendPreviewEventListener.php b/Classes/EventListeners/MaskBackendPreviewEventListener.php index 76c72924..c5077751 100644 --- a/Classes/EventListeners/MaskBackendPreviewEventListener.php +++ b/Classes/EventListeners/MaskBackendPreviewEventListener.php @@ -93,12 +93,12 @@ public function __invoke(PageContentPreviewRenderingEvent $event): void $view = GeneralUtility::makeInstance(StandaloneView::class, $renderingContext); $view->setTemplatePathAndFilename($templatePathAndFilename); if (!empty($this->maskExtensionConfiguration['layouts_backend'])) { - $layoutRootPath = GeneralUtility::getFileAbsFileName($this->maskExtensionConfiguration['layouts_backend']); - $view->setLayoutRootPaths([$layoutRootPath]); + $layoutRootPaths = TemplatePathUtility::getAbsolutePaths($this->maskExtensionConfiguration['layouts_backend']); + $view->setLayoutRootPaths($layoutRootPaths); } if (!empty($this->maskExtensionConfiguration['partials_backend'])) { - $partialRootPath = GeneralUtility::getFileAbsFileName($this->maskExtensionConfiguration['partials_backend']); - $view->setPartialRootPaths([$partialRootPath]); + $partialRootPaths = TemplatePathUtility::getAbsolutePaths($this->maskExtensionConfiguration['partials_backend']); + $view->setPartialRootPaths($partialRootPaths); } // Fetch and assign some useful variables diff --git a/Classes/Imaging/PreviewIconResolver.php b/Classes/Imaging/PreviewIconResolver.php index cf8e5d46..bf5ed352 100644 --- a/Classes/Imaging/PreviewIconResolver.php +++ b/Classes/Imaging/PreviewIconResolver.php @@ -17,6 +17,7 @@ namespace MASK\Mask\Imaging; +use MASK\Mask\Utility\TemplatePathUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\PathUtility; @@ -43,20 +44,23 @@ public function isPreviewIconAvailable(string $key): bool public function getPreviewIconPath(string $key): string { - if (!($this->maskExtensionConfiguration['preview'] ?? false)) { + $previewPaths = TemplatePathUtility::getPaths($this->maskExtensionConfiguration['preview']); + if (!count($previewPaths)) { return ''; } // search a fitting png or svg file in this path $fileExtensions = ['png', 'svg']; - $previewPath = rtrim($this->maskExtensionConfiguration['preview'], '/'); - foreach ($fileExtensions as $fileExtension) { - $extPathToIcon = $previewPath . '/' . $key . '.' . $fileExtension; - $absolutePathToIcon = GeneralUtility::getFileAbsFileName($extPathToIcon); - if ($absolutePathToIcon === '' || !file_exists($absolutePathToIcon)) { - continue; + foreach ($previewPaths as $previewPath) { + $previewPath = rtrim($previewPath, '/'); + foreach ($fileExtensions as $fileExtension) { + $extPathToIcon = $previewPath . '/' . $key . '.' . $fileExtension; + $absolutePathToIcon = GeneralUtility::getFileAbsFileName($extPathToIcon); + if ($absolutePathToIcon === '' || !file_exists($absolutePathToIcon)) { + continue; + } + $resource = PathUtility::getPublicResourceWebPath($extPathToIcon); + return '/' . ltrim($resource, '/'); } - $resource = PathUtility::getPublicResourceWebPath($extPathToIcon); - return '/' . ltrim($resource, '/'); } return ''; diff --git a/Classes/Loader/JsonSplitLoader.php b/Classes/Loader/JsonSplitLoader.php index 7de78f2d..b9d36e39 100644 --- a/Classes/Loader/JsonSplitLoader.php +++ b/Classes/Loader/JsonSplitLoader.php @@ -27,6 +27,7 @@ use MASK\Mask\Definition\TcaFieldDefinition; use MASK\Mask\Enumeration\FieldType; use MASK\Mask\Migrations\MigrationManager; +use MASK\Mask\Utility\TemplatePathUtility; use Symfony\Component\Finder\Finder; use TYPO3\CMS\Core\Configuration\Features; use TYPO3\CMS\Core\Utility\ArrayUtility; @@ -63,17 +64,21 @@ public function load(): TableDefinitionCollection $this->tableDefinitionCollection = new TableDefinitionCollection(); $definitionArray = []; - $contentElementsFolder = $this->validateFolderPath('tt_content'); - if (file_exists($contentElementsFolder)) { - $definitionArray = $this->mergeElementDefinitions($definitionArray, $contentElementsFolder); + $contentElementsFolders = $this->validateFolderPaths('tt_content'); + foreach ($contentElementsFolders as $contentElementsFolder) { + if (file_exists($contentElementsFolder)) { + $definitionArray = $this->mergeElementDefinitions($definitionArray, $contentElementsFolder); + } } // If optional backendLayoutsFolder is not empty, validate the path. - $backendLayoutsFolder = $this->getAbsolutePath('pages'); - if ($backendLayoutsFolder !== '') { - $backendLayoutsFolder = $this->validateFolderPath('pages'); - if (file_exists($backendLayoutsFolder)) { - $definitionArray = $this->mergeElementDefinitions($definitionArray, $backendLayoutsFolder); + $backendLayoutsFolders = $this->getAbsolutePaths('pages'); + if (count($backendLayoutsFolders)) { + $backendLayoutsFolders = $this->validateFolderPaths('pages'); + foreach ($backendLayoutsFolders as $backendLayoutsFolder) { + if (file_exists($backendLayoutsFolder)) { + $definitionArray = $this->mergeElementDefinitions($definitionArray, $backendLayoutsFolder); + } } } @@ -91,7 +96,7 @@ public function write(TableDefinitionCollection $tableDefinitionCollection): voi { // Write content elements and backend layouts (if a folder is defined). $this->writeElementsForTable($tableDefinitionCollection, 'tt_content'); - if ($this->getAbsolutePath('pages') !== '') { + if (count($this->getAbsolutePaths('pages'))) { $this->writeElementsForTable($tableDefinitionCollection, 'pages'); } @@ -99,14 +104,17 @@ public function write(TableDefinitionCollection $tableDefinitionCollection): voi $this->tableDefinitionCollection = $tableDefinitionCollection; } - protected function validateFolderPath(string $table): string + /** + * @return string[] + */ + protected function validateFolderPaths(string $table): array { - $path = $this->getAbsolutePath($table); - if ($path === '' && isset($this->maskExtensionConfiguration[self::FOLDER_KEYS[$table]]) && $this->maskExtensionConfiguration[self::FOLDER_KEYS[$table]] !== '') { - throw new \InvalidArgumentException('Expected ' . self::FOLDER_KEYS[$table] . ' to be a correct file system path. The value "' . $path . '" was given.', 1639218892); + $paths = $this->getAbsolutePaths($table); + if (count($paths) === 0 && isset($this->maskExtensionConfiguration[self::FOLDER_KEYS[$table]]) && $this->maskExtensionConfiguration[self::FOLDER_KEYS[$table]] !== '') { + throw new \InvalidArgumentException('Expected ' . self::FOLDER_KEYS[$table] . ' to be a correct file system path. The value "" was given.', 1639218892); } - return $path; + return $paths; } protected function getPath(string $table): string @@ -114,9 +122,12 @@ protected function getPath(string $table): string return $this->maskExtensionConfiguration[self::FOLDER_KEYS[$table]] ?? ''; } - protected function getAbsolutePath(string $table): string + /** + * @return string[] + */ + protected function getAbsolutePaths(string $table): array { - return GeneralUtility::getFileAbsFileName($this->getPath($table)); + return TemplatePathUtility::getAbsolutePaths($this->getPath($table)); } protected function mergeElementDefinitions(array &$definitionArray, string $folder): array @@ -138,10 +149,12 @@ protected function writeElementsForTable(TableDefinitionCollection $tableDefinit } $overrideSharedFields = $this->features->isFeatureEnabled('overrideSharedFields'); - $absolutePath = $this->validateFolderPath($table); + $absoluteFolderPaths = $this->validateFolderPaths($table); - if (!file_exists($absolutePath)) { - GeneralUtility::mkdir_deep($absolutePath); + foreach ($absoluteFolderPaths as $absoluteFolderPath) { + if (!file_exists($absoluteFolderPath)) { + GeneralUtility::mkdir_deep($absoluteFolderPath); + } } $elements = []; @@ -150,7 +163,7 @@ protected function writeElementsForTable(TableDefinitionCollection $tableDefinit } // Delete removed elements - foreach ((new Finder())->files()->in($absolutePath) as $file) { + foreach ((new Finder())->files()->in($absoluteFolderPaths) as $file) { if ($file->getFileInfo()->getExtension() !== 'json') { continue; } @@ -219,7 +232,20 @@ protected function writeElementsForTable(TableDefinitionCollection $tableDefinit if ($overrideSharedFields) { $elementTableDefinitionCollection->setRestructuringDone(); } - $filePath = $absolutePath . '/' . $element->key . '.json'; + $fileName = $element->key . '.json'; + $filePath = null; + foreach ($absoluteFolderPaths as $absolutePath) { + $filePath = $absolutePath . '/' . $fileName; + if (file_exists($filePath)) { + break; + } + $filePath = null; + } + if ($filePath === null) { + //use the first folder for new files + $filePath = $absoluteFolderPaths[0] . '/' . $fileName; + } + $result = GeneralUtility::writeFile($filePath, json_encode($elementTableDefinitionCollection->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . "\n"); if (!$result) { diff --git a/Classes/Utility/TemplatePathUtility.php b/Classes/Utility/TemplatePathUtility.php index b346c6a1..748e94cf 100644 --- a/Classes/Utility/TemplatePathUtility.php +++ b/Classes/Utility/TemplatePathUtility.php @@ -33,36 +33,80 @@ public static function getTemplatePath( array $settings, string $elementKey, bool $onlyTemplateName = false, - ?string $path = null, + ?string $commaSeparatedPaths = null, bool $removeExtension = false ): string { - if ($path === null) { - $path = GeneralUtility::getFileAbsFileName(rtrim($settings['content'] ?? '', '/') . '/'); + if ($commaSeparatedPaths === null) { + $paths = static::getAbsolutePaths($settings['content']); + } else { + $paths = static::getAbsolutePaths($commaSeparatedPaths); } - if ($path === '' || $elementKey === '') { + if (count($paths) === 0 || $elementKey === '') { return ''; } - $path = rtrim($path, '/') . '/'; - $fileExtension = '.html'; - // check if a html file with underscores exist - if (file_exists($path . GeneralUtility::underscoredToUpperCamelCase($elementKey) . $fileExtension)) { - $fileName = GeneralUtility::underscoredToUpperCamelCase($elementKey); - } elseif (file_exists($path . ucfirst($elementKey) . $fileExtension)) { - $fileName = ucfirst($elementKey); - } elseif (file_exists($path . $elementKey . $fileExtension)) { - $fileName = $elementKey; - } else { - $fileName = GeneralUtility::underscoredToUpperCamelCase($elementKey); - } + foreach ($paths as $path) { + $path = rtrim($path, '/') . '/'; + $fileExtension = '.html'; + + // check if a html file with underscores exist + $exists = false; + if (file_exists($path . GeneralUtility::underscoredToUpperCamelCase($elementKey) . $fileExtension)) { + $fileName = GeneralUtility::underscoredToUpperCamelCase($elementKey); + $exists = true; + } elseif (file_exists($path . ucfirst($elementKey) . $fileExtension)) { + $fileName = ucfirst($elementKey); + $exists = true; + } elseif (file_exists($path . $elementKey . $fileExtension)) { + $fileName = $elementKey; + $exists = true; + } else { + $fileName = GeneralUtility::underscoredToUpperCamelCase($elementKey); + } - if ($removeExtension) { - $fileExtension = ''; + if ($removeExtension) { + $fileExtension = ''; + } + + if ($exists) { + if ($onlyTemplateName) { + return $fileName . $fileExtension; + } + return $path . $fileName . $fileExtension; + } } + //non-existing template file if ($onlyTemplateName) { return $fileName . $fileExtension; } return $path . $fileName . $fileExtension; } + + /** + * Split a string of comma-separated paths and make them absolute. + * Remove empty paths. + * + * @return string[] + */ + public static function getAbsolutePaths(string $commaSeparatedPaths): array + { + $paths = GeneralUtility::trimExplode(',', $commaSeparatedPaths); + foreach ($paths as $key => $path) { + $paths[$key] = GeneralUtility::getFileAbsFileName($path); + } + return array_filter($paths); + } + + /** + * Split a string of comma-separated paths into an array. + * Remove empty values. + * + * @return string[] + */ + public static function getPaths(string $commaSeparatedPaths): array + { + $paths = GeneralUtility::trimExplode(',', $commaSeparatedPaths); + return array_filter($paths); + } } diff --git a/Makefile b/Makefile index e4842089..b0d17b19 100644 --- a/Makefile +++ b/Makefile @@ -14,3 +14,17 @@ test-docs: ## Test the documentation rendering mkdir -p Documentation-GENERATED-temp docker run --rm --pull always -v "$(shell pwd)":/project -t ghcr.io/typo3-documentation/render-guides:latest --config=Documentation --no-progress --fail-on-log + +.PHONY: test +test: test-cs test-unit + +.PHONY: test-cs +test-cs: + Build/Scripts/runTests.sh -s cgl -n + Build/Scripts/runTests.sh -s cglHeader -n + XDEBUG_MODE="off" .Build/bin/phpstan analyse -c Build/phpstan/phpstan.neon --no-progress --no-interaction --memory-limit 4G + +.PHONE: test-unit +test-unit: + composer install --prefer-dist --no-progress + Build/Scripts/runTests.sh -s unit -p 8.4 diff --git a/Resources/Private/Language/locallang_mask.xlf b/Resources/Private/Language/locallang_mask.xlf index a8fd36c4..6b8bfd7f 100644 --- a/Resources/Private/Language/locallang_mask.xlf +++ b/Resources/Private/Language/locallang_mask.xlf @@ -22,34 +22,34 @@ File with project-specific mask configuration (JsonLoader) - Folder in which to store content element definitions (JsonSplitLoader) + Folder in which to store content element definitions (JsonSplitLoader): Multiple folders comma-separated - Folder in which to store backend layout definitions (JsonSplitLoader) + Folder in which to store backend layout definitions (JsonSplitLoader): Multiple folders comma-separated Override shared fields per content element.:Careful! As soon as this option is enabled and you save content elements, the fields are not synced anymore and this action can not be undone! Please back up your Mask element definitions before. - Folder for Content Fluid Templates + Folder for Content Fluid Templates: Multiple folders comma-separated - Folder for Backend Preview Templates + Folder for Backend Preview Templates: Multiple folders comma-separated - Folder for 32x32px png/svg content element preview icons (e.g. mykey.(png|svg)) + Folder for content element preview icons: 32x32px png/svg files, e.g. mykey.(png|svg). Multiple folders comma-separated - Folder for Content Fluid Layouts + Folder for Content Fluid Layouts: Multiple folders comma-separated - Folder for Content Fluid Partials + Folder for Content Fluid Partials: Multiple folders comma-separated - Folder for Backend Preview Layouts + Folder for Backend Preview Layouts: Multiple folders comma-separated - Folder for Backend Preview Partials + Folder for Backend Preview Partials: Multiple folders comma-separated PageIds from where the in PageTS defined backend layouts should be loaded (comma separated) diff --git a/Tests/Unit/CodeGenerator/TypoScriptCodeGeneratorTest.php b/Tests/Unit/CodeGenerator/TypoScriptCodeGeneratorTest.php index d89fb41a..7cac7278 100644 --- a/Tests/Unit/CodeGenerator/TypoScriptCodeGeneratorTest.php +++ b/Tests/Unit/CodeGenerator/TypoScriptCodeGeneratorTest.php @@ -74,6 +74,51 @@ public static function generateSetupTyposcriptDataProvider(): iterable templateName = Element1 } +', + ]; + + yield 'comma-separated paths' => [ + 'json' => [ + 'tt_content' => [ + 'elements' => [ + 'element1' => [ + 'label' => 'Element 1', + 'key' => 'element1', + ], + 'element2' => [ + 'label' => 'Element 2', + 'key' => 'element2', + 'hidden' => true, + ], + ], + ], + ], + 'configuration' => [ + 'content' => 'EXT:sitepackage/Resources/Private/Mask/Templates,EXT:sitepackage/Resources/Private/Mask/Templates2', + 'layouts' => 'EXT:sitepackage/Resources/Private/Mask/Layouts,EXT:sitepackage/Resources/Private/Mask/Layouts2', + 'partials' => 'EXT:sitepackage/Resources/Private/Mask/Partials,EXT:sitepackage/Resources/Private/Mask/Partials2', + ], + 'expected' => +'lib.maskContentElement { + templateRootPaths { + 10 = EXT:sitepackage/Resources/Private/Mask/Templates + 11 = EXT:sitepackage/Resources/Private/Mask/Templates2 + } + partialRootPaths { + 10 = EXT:sitepackage/Resources/Private/Mask/Partials + 11 = EXT:sitepackage/Resources/Private/Mask/Partials2 + } + layoutRootPaths { + 10 = EXT:sitepackage/Resources/Private/Mask/Layouts + 11 = EXT:sitepackage/Resources/Private/Mask/Layouts2 + } +} + +tt_content.mask_element1 =< lib.maskContentElement +tt_content.mask_element1 { + templateName = Element1 +} + ', ]; diff --git a/Tests/Unit/Fixtures/Configuration/ContentElements2/f.json b/Tests/Unit/Fixtures/Configuration/ContentElements2/f.json new file mode 100644 index 00000000..63961f97 --- /dev/null +++ b/Tests/Unit/Fixtures/Configuration/ContentElements2/f.json @@ -0,0 +1,55 @@ +{ + "tt_content": { + "elements": { + "f": { + "key": "f", + "label": "F", + "description": "", + "shortLabel": "", + "color": "#000000", + "icon": "", + "columns": [ + "header", + "tx_mask_file" + ], + "columnsOverride": [], + "labels": [ + "Header", + "File" + ], + "descriptions": [ + "", + "only images are allowed" + ], + "sorting": 2 + } + }, + "sql": { + "tx_mask_file": { + "tt_content": { + "tx_mask_file": "int(11) unsigned DEFAULT '0' NOT NULL" + } + } + }, + "tca": { + "tx_mask_file": { + "config": { + "appearance": { + "fileUploadAllowed": 1 + } + }, + "type": "file", + "key": "file", + "fullKey": "tx_mask_file", + "imageoverlayPalette": 1 + }, + "header": { + "coreField": 1, + "type": "string", + "key": "header", + "fullKey": "header" + } + }, + "palettes": [] + } +} diff --git a/Tests/Unit/Fixtures/Templates2/UpperExists2.html b/Tests/Unit/Fixtures/Templates2/UpperExists2.html new file mode 100644 index 00000000..e69de29b diff --git a/Tests/Unit/Loader/JsonSplitLoaderTest.php b/Tests/Unit/Loader/JsonSplitLoaderTest.php index faadc352..68adc3ef 100644 --- a/Tests/Unit/Loader/JsonSplitLoaderTest.php +++ b/Tests/Unit/Loader/JsonSplitLoaderTest.php @@ -54,6 +54,30 @@ public function load(): void self::assertEquals($this->getExpectedConfigurationArray(), $jsonSplitLoader->load()->toArray(false)); } + /** + * @test + */ + public function loadCommaSeparated(): void + { + $this->registerPackageManager(); + $jsonSplitLoader = new JsonSplitLoader( + [ + 'content_elements_folder' => 'EXT:mask/Tests/Unit/Fixtures/Configuration/ContentElements,EXT:mask/Tests/Unit/Fixtures/Configuration/ContentElements2', + 'backend_layouts_folder' => 'EXT:mask/Tests/Unit/Fixtures/Configuration/BackendLayouts', + ], + new MigrationManager([]), + new Features() + ); + + self::assertEquals( + array_merge_recursive( + $this->getExpectedConfigurationArray(), + $this->getExpectedConfigurationArray2() + ), + $jsonSplitLoader->load()->toArray(false) + ); + } + /** * @test */ @@ -637,6 +661,40 @@ public function write(): void self::assertEquals($configurationE, json_decode(file_get_contents($contentElementsPath . '/e.json'), true)['tables']); } + /** + * @test + */ + public function writeCommaSeparated(): void + { + $this->registerPackageManager(); + + $GLOBALS['TCA']['tt_content']['columns']['header']['config']['type'] = 'input'; + $jsonSplitLoader = new JsonSplitLoader( + [ + 'content_elements_folder' => 'EXT:mask/var/ContentElements,EXT:mask/var/ContentElements2', + 'backend_layouts_folder' => 'EXT:mask/var/BackendLayouts', + ], + new MigrationManager([]), + new Features() + ); + + $jsonSplitLoader->write(TableDefinitionCollection::createFromArray($this->getExpectedConfigurationArray())); + + $contentElementsPath = GeneralUtility::getFileAbsFileName('EXT:mask/var/ContentElements'); + $contentElementsPath2 = GeneralUtility::getFileAbsFileName('EXT:mask/var/ContentElements2'); + self::assertFileExists($contentElementsPath . '/a.json'); + self::assertFileExists($contentElementsPath . '/b.json'); + self::assertFileExists($contentElementsPath . '/c.json'); + self::assertFileExists($contentElementsPath . '/d.json'); + self::assertFileExists($contentElementsPath . '/e.json'); + + rename($contentElementsPath . '/a.json', $contentElementsPath2 . '/a.json'); + + $jsonSplitLoader->write(TableDefinitionCollection::createFromArray($this->getExpectedConfigurationArray())); + self::assertFileDoesNotExist($contentElementsPath . '/a.json'); + self::assertFileExists($contentElementsPath2 . '/a.json'); + } + protected function getExpectedConfigurationArray(): array { return [ @@ -1110,6 +1168,39 @@ protected function getExpectedConfigurationArray(): array ]; } + protected function getExpectedConfigurationArray2(): array + { + return [ + 'tt_content' => [ + 'elements' => [ + 'f' => [ + 'key' => 'f', + 'label' => 'F', + 'description' => '', + 'shortLabel' => '', + 'color' => '#000000', + 'icon' => '', + 'columns' => [ + 'header', + 'tx_mask_file', + ], + 'labels' => [ + 'Header', + 'File', + ], + 'descriptions' => [ + '', + 'only images are allowed', + ], + 'sorting' => 2, + 'colorOverlay' => '', + 'iconOverlay' => '', + ], + ], + ], + ]; + } + /** * @test */ diff --git a/Tests/Unit/Utility/TemplatePathUtilityTest.php b/Tests/Unit/Utility/TemplatePathUtilityTest.php index 13f398cb..728126b9 100644 --- a/Tests/Unit/Utility/TemplatePathUtilityTest.php +++ b/Tests/Unit/Utility/TemplatePathUtilityTest.php @@ -36,6 +36,14 @@ public static function getTemplatePathDataProvider(): iterable false, Environment::getProjectPath() . '/Tests/Unit/Fixtures/Templates/UpperExists.html', ], + 'Comma-separated content path' => [ + ['content' => 'EXT:mask/Tests/Unit/Fixtures/Templates/,EXT:mask/Tests/Unit/Fixtures/Templates2/'], + 'upper_exists2', + false, + null, + false, + Environment::getProjectPath() . '/Tests/Unit/Fixtures/Templates2/UpperExists2.html', + ], 'File does not exist' => [ ['content' => 'EXT:mask/Tests/Unit/Fixtures/Templates/'], 'noelement', @@ -66,7 +74,7 @@ public static function getTemplatePathDataProvider(): iterable false, 'typo3conf/ext/mask/Tests/Unit/Fixtures/Templates/', false, - 'typo3conf/ext/mask/Tests/Unit/Fixtures/Templates/UpperExists.html', + Environment::getProjectPath() . '/.Build/Web/typo3conf/ext/mask/Tests/Unit/Fixtures/Templates/UpperExists.html', ], 'Manually configured absolute path works' => [ ['content' => ''], @@ -76,6 +84,14 @@ public static function getTemplatePathDataProvider(): iterable false, Environment::getProjectPath() . '/Tests/Unit/Fixtures/Templates/UpperExists.html', ], + 'Manually configured comma-separated path works' => [ + ['content' => ''], + 'upper_exists2', + false, + 'typo3conf/ext/mask/Tests/Unit/Fixtures/Templates/,EXT:mask/Tests/Unit/Fixtures/Templates2/', + false, + Environment::getProjectPath() . '/Tests/Unit/Fixtures/Templates2/UpperExists2.html', + ], 'Only template is returned' => [ ['content' => 'EXT:mask/Tests/Unit/Fixtures/Templates/'], 'upper_exists',