diff --git a/UPGRADE.md b/UPGRADE.md index 895e05b..aab4132 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -8,6 +8,7 @@ Just click the "update" button or execute the migration command to finish the bu #### Update from Version 3.1.3 to Version 3.1.4 - **[ENHANCEMENT]**: Improving and adding additional Events for Restriction Changes on Entities ([#148](https://github.com/dachcom-digital/pimcore-members/issues/148)) +- **[ENHANCEMENT]**: Update Twig navigation to allow parameters ([@kjkooistra-youwe](https://github.com/dachcom-digital/pimcore-members/pull/147)) #### Update from Version 3.1.2 to Version 3.1.3 - **[ENHANCEMENT]**: Pimcore 6.6.5 ready diff --git a/docs/210_RestrictedNavigation.md b/docs/210_RestrictedNavigation.md index 797301a..c4ec234 100644 --- a/docs/210_RestrictedNavigation.md +++ b/docs/210_RestrictedNavigation.md @@ -2,12 +2,81 @@ Now - since you have restricted some documents to certain groups, you need to manipulate the pimcore navigation renderer. -**Navigation:** Do **not** use the default nav builder extension (`pimcore_build_nav`). Just use the `members_build_nav` to build secure menus. -Otherwise your restricted pages will show up. This twig extension will also handle your navigation cache strategy. +**Navigation:** Do **not** use the default nav builder extension (`pimcore_build_nav`). Just use the `members_build_nav` to build +secure menus. Otherwise your restricted pages will show up. This twig extension will also handle your navigation cache strategy. + +## Usage -### Usage - ```twig -{% set nav = members_build_nav(currentDoc, documentRootDoc, null, true) %} +{% set nav = members_build_nav({ + active: currentDoc, + root: documentRootDoc +}) %} + {{ pimcore_render_nav(nav, 'menu', 'renderMenu', { maxDepth: 2 }) }} ``` + +### Page callback parameter + +The `pageCallback` parameter is 'merged' with the Members bundle restriction access callback, allowing you to set custom data to +the navigation document. Note that the `ElementRestriction` argument is also passed on to the callback function. + +```twig +{% set nav = members_build_nav({ + active: document, + root: rootPage, + pageCallback: navigation_callback() +}) %} +``` + +As you cannot use closures directly within Twig templates, create a function to return one. + +```php +setCustomSetting('key', $page->getKey()); + } + }; + } +} +``` + +To retrieve the setting in the template: + +```twig +{{ page.getCustomSetting('key') }} +``` diff --git a/src/MembersBundle/Twig/Extension/NavigationExtension.php b/src/MembersBundle/Twig/Extension/NavigationExtension.php index f355684..7789013 100644 --- a/src/MembersBundle/Twig/Extension/NavigationExtension.php +++ b/src/MembersBundle/Twig/Extension/NavigationExtension.php @@ -6,6 +6,7 @@ use MembersBundle\Adapter\User\UserInterface; use MembersBundle\Manager\RestrictionManager; use MembersBundle\Manager\RestrictionManagerInterface; +use MembersBundle\Restriction\ElementRestriction; use Pimcore\Model\AbstractModel; use Pimcore\Model\Document; use Pimcore\Navigation\Container; @@ -14,6 +15,9 @@ use Twig\Extension\AbstractExtension; use Twig\TwigFunction; +/** + * @see \Pimcore\Twig\Extension\NavigationExtension + */ class NavigationExtension extends AbstractExtension { /** @@ -57,56 +61,122 @@ public function getFunctions(): array } /** - * @param Document $activeDocument + * @see \Pimcore\Twig\Extension\NavigationExtension::buildNavigation() + * @param array|Document $params config array or active document (legacy mode) * @param Document|null $navigationRootDocument - * @param string|null $htmlMenuPrefix - * @param bool $cache - * + * @param string|null $htmlMenuPrefix + * @param bool|string $cache * @return Container */ public function buildNavigation( - Document $activeDocument, + $params = null, Document $navigationRootDocument = null, string $htmlMenuPrefix = null, $cache = true ): Container { + if (is_array($params)) { + return $this->buildMembersNavigation($params); + } + + // using deprecated argument configuration ($params = navigation root document) + return $this->legacyBuildNavigation( + $params, + $navigationRootDocument, + $htmlMenuPrefix, + $cache + ); + } + + protected function buildMembersNavigation(array $params): Container + { + // Update cache key and page callback + $params['cache'] = $this->getCacheKey($params['cache'] ?? true); + $params['pageCallback'] = $this->getPageCallback($params['pageCallback'] ?? null); + + if (!method_exists($this->navigationHelper, 'build')) { + throw new \Exception( + 'Navigation::build() unavailable, update your Pimcore version to >= 6.5', + 1605864272 + ); + } + + return $this->navigationHelper->build($params); + } + + /** + * @param bool|string $cache + * @return bool|string + */ + protected function getCacheKey($cache) + { $cacheKey = $cache; $user = $this->tokenStorage->getToken() ? $this->tokenStorage->getToken()->getUser() : null; - if (!\Pimcore\Tool::isFrontendRequestByAdmin() && $cacheKey !== false) { - $mergedCacheKey = is_bool($cache) ? '' : $cache; + if (\Pimcore\Tool::isFrontendRequestByAdmin() || $cacheKey === false || !($user instanceof UserInterface)) { + return $cacheKey; + } + + $allowedGroups = $user->getGroups(); + $groupIds = []; + if (!empty($allowedGroups)) { + /** @var GroupInterface $group */ + foreach ($allowedGroups as $group) { + $groupIds[] = $group->getId(); + } + + if (!empty($groupIds)) { + $mergedCacheKey = is_bool($cache) ? '' : $cache; + $cacheKey = ltrim($mergedCacheKey . '-' . implode('-', $groupIds), '-'); + } + } - if ($user instanceof UserInterface) { - $allowedGroups = $user->getGroups(); + return $cacheKey; + } - $groupIds = []; - if (!empty($allowedGroups)) { - /** @var GroupInterface $group */ - foreach ($allowedGroups as $group) { - $groupIds[] = $group->getId(); - } + protected function getPageCallback(?\Closure $additionalClosure = null): \Closure + { + return function (\Pimcore\Navigation\Page\Document $document, AbstractModel $page) use ($additionalClosure) { + $restrictionElement = $this->applyPageRestrictions($document, $page); - if (!empty($groupIds)) { - $cacheKey = ltrim($mergedCacheKey . '-' . implode('-', $groupIds), '-'); - } - } + // Call additional closure if configured and also pass restriction element as additional argument + if ($additionalClosure !== null) { + $additionalClosure->call($this, $document, $page, $restrictionElement); } + + return $page; + }; + } + + protected function applyPageRestrictions(\Pimcore\Navigation\Page\Document $document, AbstractModel $page): ElementRestriction + { + $restrictionElement = $this->restrictionManager->getElementRestrictionStatus($page); + if ($restrictionElement->getSection() !== RestrictionManager::RESTRICTION_SECTION_ALLOWED) { + $document->setActive(false); + $document->setVisible(false); } + return $restrictionElement; + } + + /** + * @param Document $activeDocument + * @param Document|null $navigationRootDocument + * @param string|null $htmlMenuPrefix + * @param bool|string $cache + * @return Container + */ + protected function legacyBuildNavigation( + Document $activeDocument, + ?Document $navigationRootDocument = null, + ?string $htmlMenuPrefix = null, + $cache = true + ): Container { return $this->navigationHelper->buildNavigation( $activeDocument, $navigationRootDocument, $htmlMenuPrefix, - function (\Pimcore\Navigation\Page\Document $document, AbstractModel $page) { - $restrictionElement = $this->restrictionManager->getElementRestrictionStatus($page); - if ($restrictionElement->getSection() !== RestrictionManager::RESTRICTION_SECTION_ALLOWED) { - $document->setActive(false); - $document->setVisible(false); - } - - return $page; - }, - $cacheKey + $this->getPageCallback(), + $this->getCacheKey($cache) ); } } diff --git a/tests/_etc/config.yml b/tests/_etc/config.yml index 899882c..1c95b09 100644 --- a/tests/_etc/config.yml +++ b/tests/_etc/config.yml @@ -8,6 +8,7 @@ setup_files: - { path: app/views/default.html.twig, dest: ./app/Resources/views/Default/default.html.twig } - { path: app/views/snippet.html.twig, dest: ./app/Resources/views/Default/snippet.html.twig } - { path: app/views/staticRoute.html.twig, dest: ./app/Resources/views/Default/staticRoute.html.twig } + - { path: app/views/navigation.html.twig, dest: ./app/Resources/views/Default/navigation.html.twig } preload_files: - { path: Services/TestRestrictedStaticRouteListener.php } additional_composer_packages: diff --git a/tests/_etc/config/app/controller/DefaultController.php b/tests/_etc/config/app/controller/DefaultController.php index b863557..00b9ed3 100644 --- a/tests/_etc/config/app/controller/DefaultController.php +++ b/tests/_etc/config/app/controller/DefaultController.php @@ -24,4 +24,8 @@ public function snippetAction(Request $request) public function staticRouteAction(Request $request) { } + + public function navigationAction(Request $request) + { + } } diff --git a/tests/_etc/config/app/views/navigation.html.twig b/tests/_etc/config/app/views/navigation.html.twig new file mode 100644 index 0000000..3c55a8e --- /dev/null +++ b/tests/_etc/config/app/views/navigation.html.twig @@ -0,0 +1,29 @@ + + +
+ +