diff --git a/Classes/CacheHelper.php b/Classes/CacheHelper.php index 4577209..7d42e79 100644 --- a/Classes/CacheHelper.php +++ b/Classes/CacheHelper.php @@ -73,7 +73,7 @@ protected function getAllPageIdsFromItems(array $pages): array foreach ($pages as $page) { $pageIds[] = (int)$page['uid']; if (!empty($page['subpages'])) { - $pageIds[] = array_replace($pageIds, $this->getAllPageIdsFromItems($page['subpages'])); + $pageIds = array_merge($pageIds, $this->getAllPageIdsFromItems($page['subpages'])); } } return $pageIds; diff --git a/Classes/Compiler/AbstractMenuCompiler.php b/Classes/Compiler/AbstractMenuCompiler.php index ce92a45..7b2a3a0 100644 --- a/Classes/Compiler/AbstractMenuCompiler.php +++ b/Classes/Compiler/AbstractMenuCompiler.php @@ -68,8 +68,11 @@ protected function generateCacheIdentifierForMenu(string $prefix, array $configu { $groupIds = $this->context->getAspect('frontend.user')->getGroupIds(); $language = $this->context->getAspect('language')->getId(); - return $prefix . '-language-' . $language . '-groups-' . implode('_', $groupIds) . '-' . GeneralUtility::shortMD5(json_encode($configuration)); + $root = $this->getCurrentSite()->getRootPageId(); + $identifier = $prefix . '-root-' . $root . '-language-' . $language . '-groups-' . implode('_', $groupIds) . '-' . GeneralUtility::shortMD5(json_encode($configuration)); + return $identifier; } + protected function getCurrentSite(): ?SiteInterface { diff --git a/Classes/Compiler/LanguageMenuCompiler.php b/Classes/Compiler/LanguageMenuCompiler.php index f821acc..14fc248 100644 --- a/Classes/Compiler/LanguageMenuCompiler.php +++ b/Classes/Compiler/LanguageMenuCompiler.php @@ -10,6 +10,7 @@ * of the License, or any later version. */ +use TYPO3\CMS\Core\Context\Context; use TYPO3\CMS\Core\Context\LanguageAspectFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; @@ -22,13 +23,18 @@ class LanguageMenuCompiler extends AbstractMenuCompiler public function compile(ContentObjectRenderer $contentObjectRenderer, array $configuration): array { $cacheIdentifier = $this->generateCacheIdentifierForMenu('list', $configuration); - return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration) { - $excludedLanguages = $contentObjectRenderer->stdWrap($configuration['excludeLanguages'] ?? '', $configuration['excludeLanguages.']); - $excludedLanguages = GeneralUtility::trimExplode(',', $excludedLanguages); - $targetPage = $contentObjectRenderer->stdWrap($configuration['pointToPage'] ?? $GLOBALS['TSFE']->id, $configuration['pointToPage.']); - $targetPage = (int)$targetPage; + + $excludedLanguages = $contentObjectRenderer->stdWrap($configuration['excludeLanguages'] ?? '', $configuration['excludeLanguages.']); + $excludedLanguages = GeneralUtility::trimExplode(',', $excludedLanguages); + $targetPage = $contentObjectRenderer->stdWrap($configuration['pointToPage'] ?? $GLOBALS['TSFE']->id, $configuration['pointToPage.']); + $targetPage = (int)$targetPage; + + $cacheIdentifier .= '-' . GeneralUtility::shortMD5(json_encode([$excludedLanguages, $targetPage])); + + return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration, $excludedLanguages, $targetPage) { $site = $this->getCurrentSite(); + $context = GeneralUtility::makeInstance(Context::class); $pages = []; foreach ($site->getLanguages() as $language) { if (in_array($language->getTwoLetterIsoCode(), $excludedLanguages, true)) { @@ -38,13 +44,17 @@ public function compile(ContentObjectRenderer $contentObjectRenderer, array $con continue; } $languageAspect = LanguageAspectFactory::createFromSiteLanguage($language); - $page = $this->menuRepository->getPageInLanguage($targetPage, $languageAspect); + $context->setAspect('language', $languageAspect); + $page = $this->menuRepository->getPageInLanguage($targetPage, $context); if (!empty($page)) { - // @todo: we need to have this prefixed with "language_" - $page = array_merge($page, $language->toArray()); + $page['language'] = $language->toArray(); + if (!empty($page['_PAGES_OVERLAY']) && $page['_PAGES_OVERLAY'] === true) { + $page['uid'] = $page['_PAGES_OVERLAY_UID']; + } $pages[] = $page; } } + return $pages; }); } } diff --git a/Classes/Compiler/ListMenuCompiler.php b/Classes/Compiler/ListMenuCompiler.php index 40aced2..37ba6c7 100644 --- a/Classes/Compiler/ListMenuCompiler.php +++ b/Classes/Compiler/ListMenuCompiler.php @@ -21,9 +21,13 @@ class ListMenuCompiler extends AbstractMenuCompiler public function compile(ContentObjectRenderer $contentObjectRenderer, array $configuration): array { $cacheIdentifier = $this->generateCacheIdentifierForMenu('list', $configuration); - return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration) { - $pageIds = $contentObjectRenderer->stdWrap($configuration['pages'] ?? $this->getCurrentSite()->getRootPageId(), $configuration['pages.']); - $pageIds = GeneralUtility::intExplode(',', $pageIds); + + $pageIds = $contentObjectRenderer->stdWrap($configuration['pages'] ?? $this->getCurrentSite()->getRootPageId(), $configuration['pages.']); + $pageIds = GeneralUtility::intExplode(',', $pageIds); + + $cacheIdentifier .= '-' . GeneralUtility::shortMD5(json_encode([$pageIds])); + + return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration, $pageIds) { $pages = []; foreach ($pageIds as $pageId) { diff --git a/Classes/Compiler/TreeMenuCompiler.php b/Classes/Compiler/TreeMenuCompiler.php index 4086704..172a185 100644 --- a/Classes/Compiler/TreeMenuCompiler.php +++ b/Classes/Compiler/TreeMenuCompiler.php @@ -21,11 +21,15 @@ class TreeMenuCompiler extends AbstractMenuCompiler public function compile(ContentObjectRenderer $contentObjectRenderer, array $configuration): array { $cacheIdentifier = $this->generateCacheIdentifierForMenu('tree', $configuration); - return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration) { - $includeStartPageIds = $contentObjectRenderer->stdWrap($configuration['includeRootPages'] ?? false, $configuration['includeRootPages.']); - $startPageIds = $contentObjectRenderer->stdWrap($configuration['entryPoints'] ?? $this->getCurrentSite()->getRootPageId(), $configuration['entryPoints.']); - $startPageIds = GeneralUtility::intExplode(',', $startPageIds); - $depth = (int)$contentObjectRenderer->stdWrap($configuration['depth'] ?? 1, $configuration['depth.']); + + $includeStartPageIds = $contentObjectRenderer->stdWrap($configuration['includeRootPages'] ?? false, $configuration['includeRootPages.']); + $startPageIds = $contentObjectRenderer->stdWrap($configuration['entryPoints'] ?? $this->getCurrentSite()->getRootPageId(), $configuration['entryPoints.']); + $startPageIds = GeneralUtility::intExplode(',', $startPageIds); + $depth = (int)$contentObjectRenderer->stdWrap($configuration['depth'] ?? 1, $configuration['depth.']); + + $cacheIdentifier .= '-' . GeneralUtility::shortMD5(json_encode([$includeStartPageIds, $startPageIds, $depth])); + + return $this->cache->get($cacheIdentifier, function() use ($contentObjectRenderer, $configuration, $includeStartPageIds, $startPageIds, $depth) { $tree = []; foreach ($startPageIds as $startPageId) { diff --git a/Classes/DataProcessing/AbstractMenu.php b/Classes/DataProcessing/AbstractMenu.php new file mode 100644 index 0000000..4f08250 --- /dev/null +++ b/Classes/DataProcessing/AbstractMenu.php @@ -0,0 +1,59 @@ +contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class); + } + + /** + * Process additional data processors + * + * @param array $page + * @param array $processorConfiguration + * @return array + */ + protected function processAdditionalDataProcessors(&$page, $processorConfiguration) + { + if (is_array($page['subpages'])) { + foreach ($page['subpages'] as &$item) { + $this->processAdditionalDataProcessors($item, $processorConfiguration); + } + } + + /** @var ContentObjectRenderer $recordContentObjectRenderer */ + $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); + $recordContentObjectRenderer->start($page, 'pages'); + $page = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $page); + return $page; + } +} diff --git a/Classes/DataProcessing/BreadcrumbsMenu.php b/Classes/DataProcessing/BreadcrumbsMenu.php index ee3471e..a9a741b 100644 --- a/Classes/DataProcessing/BreadcrumbsMenu.php +++ b/Classes/DataProcessing/BreadcrumbsMenu.php @@ -19,7 +19,7 @@ /** * DataProcessor to retrieve a list of all pages of the current rootline to build a breadcrumb menu. */ -class BreadcrumbsMenu implements DataProcessorInterface +class BreadcrumbsMenu extends AbstractMenu { /** * @var MenuRepository @@ -28,6 +28,7 @@ class BreadcrumbsMenu implements DataProcessorInterface public function __construct() { + parent::__construct(); $this->menuRepository = GeneralUtility::makeInstance(MenuRepository::class); } @@ -36,11 +37,17 @@ public function __construct() */ public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData) { + if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { + return $processedData; + } $pages = $this->menuRepository->getBreadcrumbsMenu($GLOBALS['TSFE']->rootLine); $rootLevelCount = count($pages); foreach ($pages as $page) { PageStateMarker::markStates($page, $rootLevelCount--); } + foreach ($pages as &$page) { + $this->processAdditionalDataProcessors($page, $processorConfiguration); + } $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'breadcrumbs'); $processedData[$targetVariableName] = $pages; return $processedData; diff --git a/Classes/DataProcessing/LanguageMenu.php b/Classes/DataProcessing/LanguageMenu.php index 0e3cdca..dcf1b9f 100644 --- a/Classes/DataProcessing/LanguageMenu.php +++ b/Classes/DataProcessing/LanguageMenu.php @@ -15,18 +15,20 @@ use TYPO3\CMS\Core\Site\Entity\SiteLanguage; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; -use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; /** * DataProcessor to retrieve a list of a all available languages. */ -class LanguageMenu implements DataProcessorInterface +class LanguageMenu extends AbstractMenu { /** * @inheritDoc */ public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData) { + if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { + return $processedData; + } $pages = GeneralUtility::makeInstance(LanguageMenuCompiler::class)->compile($cObj, $processorConfiguration); $currentLanguage = $this->getCurrentSiteLanguage(); foreach ($pages as &$page) { @@ -35,13 +37,19 @@ public function process(ContentObjectRenderer $cObj, array $contentObjectConfigu $page['isActiveLanguage'] = true; } } + foreach ($pages as &$page) { + $this->processAdditionalDataProcessors($page, $processorConfiguration); + } $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration); $processedData[$targetVariableName] = $pages; return $processedData; } + /** + * @return null|SiteLanguage + */ protected function getCurrentSiteLanguage(): ?SiteLanguage { - $GLOBALS['TYPO3_REQUEST']->getAttribute('language'); + return $GLOBALS['TYPO3_REQUEST']->getAttribute('language'); } } diff --git a/Classes/DataProcessing/ListMenu.php b/Classes/DataProcessing/ListMenu.php index b88f611..8991662 100644 --- a/Classes/DataProcessing/ListMenu.php +++ b/Classes/DataProcessing/ListMenu.php @@ -19,17 +19,24 @@ /** * DataProcessor to retrieve a list of pages. */ -class ListMenu implements DataProcessorInterface +class ListMenu extends AbstractMenu { /** * @inheritDoc */ public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData) { + if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { + return $processedData; + } + $pages = GeneralUtility::makeInstance(ListMenuCompiler::class)->compile($cObj, $processorConfiguration); foreach ($pages as &$page) { PageStateMarker::markStates($page); } + foreach ($pages as &$page) { + $this->processAdditionalDataProcessors($page, $processorConfiguration); + } $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration); $processedData[$targetVariableName] = $pages; return $processedData; diff --git a/Classes/DataProcessing/TreeMenu.php b/Classes/DataProcessing/TreeMenu.php index fca023b..71697a0 100644 --- a/Classes/DataProcessing/TreeMenu.php +++ b/Classes/DataProcessing/TreeMenu.php @@ -14,24 +14,32 @@ use B13\Menus\PageStateMarker; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; -use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; + /** * DataProcessor to render a tree-based menu of pages and subpages. */ -class TreeMenu implements DataProcessorInterface +class TreeMenu extends AbstractMenu { + /** * @inheritDoc */ public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData) { + if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { + return $processedData; + } $pages = GeneralUtility::makeInstance(TreeMenuCompiler::class)->compile($cObj, $processorConfiguration); foreach ($pages as &$page) { PageStateMarker::markStatesRecursively($page, 1); } + foreach ($pages as &$page) { + $this->processAdditionalDataProcessors($page, $processorConfiguration); + } $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration); $processedData[$targetVariableName] = $pages; return $processedData; } + } diff --git a/Classes/Domain/Repository/MenuRepository.php b/Classes/Domain/Repository/MenuRepository.php index 057857d..7c5d328 100644 --- a/Classes/Domain/Repository/MenuRepository.php +++ b/Classes/Domain/Repository/MenuRepository.php @@ -69,10 +69,13 @@ public function getPage(int $pageId, array $configuration): array $this->populateAdditionalKeysForPage($page); return $page; } - public function getPageInLanguage(int $pageId, LanguageAspect $languageAspect): array + + + public function getPageInLanguage(int $pageId, Context $context): array { - $page = $this->pageRepository->getPage($pageId); - if (!$this->pageRepository->isPageSuitableForLanguage($page, $languageAspect)) { + $pageRepository = GeneralUtility::makeInstance(PageRepository::class, $context); + $page = $pageRepository->getPage($pageId); + if (!$pageRepository->isPageSuitableForLanguage($page, $context->getAspect('language'))) { return []; } $this->populateAdditionalKeysForPage($page); @@ -81,6 +84,7 @@ public function getPageInLanguage(int $pageId, LanguageAspect $languageAspect): public function getPageTree(int $startPageId, int $depth, array $configuration): array { + $page = $this->pageRepository->getPage($startPageId); $languageAspect = $this->context->getAspect('language'); if (!$this->pageRepository->isPageSuitableForLanguage($page, $languageAspect)) { @@ -107,7 +111,8 @@ public function getSubPagesOfPage(int $pageId, int $depth, array $configuration) $pageId, '*', 'sorting', - 'AND doktype NOT IN (' . implode(',', $excludedDoktypes) . ') AND nav_hide=0 ' . $whereClause + 'AND doktype NOT IN (' . implode(',', $excludedDoktypes) . ') AND nav_hide=0 ' . $whereClause, + false ); /** @var LanguageAspect $languageAspect */ $languageAspect = $this->context->getAspect('language'); diff --git a/Documentation/FluidMigration.md b/Documentation/FluidMigration.md new file mode 100644 index 0000000..79ca6df --- /dev/null +++ b/Documentation/FluidMigration.md @@ -0,0 +1,51 @@ +Migrate TypoScript for Fluid-based solution +------------------------------------------- + +general Menu + + - dataProcessors.10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor + - dataProcessors.10.level = 2 + + dataProcessors.10 = B13\Menus\DataProcessing\TreeMenu + + dataProcessors.10.depth = 2 + +special directory + + - dataProcessors.10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor + - dataProcessors.special = directory + - dataProcessors.special.value = 34 + + dataProcessors.dataProcessors.10 = B13\Menus\DataProcessing\TreeMenu + + dataProcessors.entryPoints = 34 + +special list + + - dataProcessors.10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor + - dataProcessors.special = list + - dataProcessors.special.value = 34,22 + + dataProcessors.10 = B13\Menus\DataProcessing\ListMenu + + dataProcessors.pages = 34,22 + +special language + + - dataProcessors.10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor + - dataProcessors.special = language + + dataProcessors.10 = B13\Menus\DataProcessing\LanguageMenu + +special breadcrumbs + + - dataProcessors.10 = TYPO3\CMS\Frontend\DataProcessing\MenuProcessor + - dataProcessors.10.special = rootline + + dataProcessors.10 = B13\Menus\DataProcessing\BreadcrumbsMenu + + +Migrate Templates for Fluid-based solution +------------------------------------------- + +* menuItem.children -> menuItem.hasSubpages or menuItem.subpages +* menuItem.data -> is dropped, properties direct in menuItem +* menuItem.link -> is dropped, use Page-Link-VH with menuItem.uid +* menuItem.spacer -> menuItem.isSpacer +* menuItem.current -> menuItem.isCurrentPage +* menuItem.active -> menuItem.isInRootLine +* menuItem.title -> menuItem.nav_title + + diff --git a/README.md b/README.md index 7b58c3a..be49bb7 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Pure TypoScript-based solution: page.10 = TREEMENU # a list of page IDs, rootpageID is used if none given page.10.entryPoints = 23,13 - # the number of levels to fetch from the database + # the number of levels to fetch from the database (1 if empty) page.10.depth = 3 page.10.excludePages = 4,51 page.10.renderObj.level1 = TEXT @@ -92,11 +92,11 @@ Usage in Fluid: