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/Utility/TemplatePathUtility.php b/Classes/Utility/TemplatePathUtility.php index 9971910c..748e94cf 100644 --- a/Classes/Utility/TemplatePathUtility.php +++ b/Classes/Utility/TemplatePathUtility.php @@ -33,33 +33,50 @@ 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; } @@ -80,4 +97,16 @@ public static function getAbsolutePaths(string $commaSeparatedPaths): array } 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/Resources/Private/Language/locallang_mask.xlf b/Resources/Private/Language/locallang_mask.xlf index 3eaa3c76..6b8bfd7f 100644 --- a/Resources/Private/Language/locallang_mask.xlf +++ b/Resources/Private/Language/locallang_mask.xlf @@ -31,25 +31,25 @@ 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/Templates2/UpperExists2.html b/Tests/Unit/Fixtures/Templates2/UpperExists2.html new file mode 100644 index 00000000..e69de29b 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',