Commit 45ae6943 authored by Benni Mack's avatar Benni Mack Committed by Georg Ringer
Browse files

[!!!][FEATURE] Add new Event for filtering HMENU items

This change introduces a new PSR-14 event for manipulating
menu items in a menu.

The hook
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages']
is removed, and AbstractMenuFilterPagesHookInterface deprecated.

Resolves: #92508
Releases: main
Change-Id: I2960e5021b2ba49e1b26e68f2bb0cbed809d60a7
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72955

Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
parent a883ba13
.. include:: ../../Includes.txt
=========================================================
Breaking: #92508 - Removed hook for filtering HMENU items
=========================================================
See :issue:`92508`
Description
===========
The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages']`
has been removed in favor of a new PSR-14 Event :php:`TYPO3\CMS\Frontend\Event\FilterMenuItemsEvent`.
The event is called with all menu items instead of operating on
one single item.
Impact
======
Any hook implementation registered is not executed anymore
in TYPO3 v12.0+.
Affected Installations
======================
TYPO3 installations with custom menus using this hook.
Migration
=========
The hook is removed without deprecation in order to allow extensions
to work with TYPO3 v11 (using the hook) and v12+ (using the new Event)
when implementing the Event as well without any further deprecations.
Use the :doc:`PSR-14 Event <../12.0/Feature-92508-PSR-14EventForModifyingMenuItems.rst>`
to allow greater influence in the functionality.
.. index:: Frontend, FullyScanned, ext:frontend
.. include:: ../../Includes.txt
===============================================================
Deprecation: #92508 - Unused Interface for filterMenuPages hook
===============================================================
See :issue:`92508`
Description
===========
The hook :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages']`
required hook implementations to implement :php:`AbstractMenuFilterPagesHookInterface`.
Since the mentioned hook is :doc:`removed <../12.0/Breaking-92508-RemovedHookForFilteringHMENUItems.rst>`,
the interface is not in use anymore and has been marked as deprecated.
Impact
======
The extension scanner will now notify any extension, which might
still use the PHP interface.
Affected Installations
======================
TYPO3 installations using the PHP interface in custom extension code.
Migration
=========
The PHP interface is still available for TYPO3 v12.x, so extensions can
provide a version which is compatible with TYPO3 v11 (using the hook)
and TYPO3 v12.x (using the new Event), at the same time.
Remove any usage of the PHP interface and use the new PSR-14
Event to avoid any further problems in TYPO3 v13+.
.. index:: Frontend, FullyScanned, ext:frontend
.. include:: ../../Includes.txt
=======================================================
Feature: #92508 - PSR-14 Event for modifying menu items
=======================================================
See :issue:`92508`
Description
===========
A new PSR-14 Event :php:`TYPO3\CMS\Frontend\Event\FilterMenuItemsEvent` has been
introduced which serves as a more powerful and flexible alternative
for the now removed hook
:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages']`.
The new PSR-14 Event has a variety of properties and getters, along with
:php:`->getFilteredMenuItems()` and :php:`->setFilteredMenuItems()`. Those
methods can be used to change the items of a menu, which has been generated
with :typoscript:`HMENU`.
Impact
======
The main advantage of the PSR-14 Event is that it is fired after TYPO3 has
filtered all menu items. The menu can then be adjusted by adding, removing
or modifing the menu items. Also changing the order is possible.
Additionally, more information about the currently rendered menu, such as the
menu items which were filtered out, is available in the PSR-14 Event.
.. index:: Frontend, ext:frontend
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
namespace TYPO3\CMS\Frontend\ContentObject\Menu; namespace TYPO3\CMS\Frontend\ContentObject\Menu;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LogLevel; use Psr\Log\LogLevel;
use TYPO3\CMS\Core\Cache\CacheManager; use TYPO3\CMS\Core\Cache\CacheManager;
...@@ -32,6 +33,7 @@ use TYPO3\CMS\Core\Utility\MathUtility; ...@@ -32,6 +33,7 @@ use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException; use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3\CMS\Frontend\Event\FilterMenuItemsEvent;
use TYPO3\CMS\Frontend\Typolink\PageLinkBuilder; use TYPO3\CMS\Frontend\Typolink\PageLinkBuilder;
/** /**
...@@ -389,22 +391,21 @@ abstract class AbstractMenuContentObject ...@@ -389,22 +391,21 @@ abstract class AbstractMenuContentObject
$maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf) : $maxItems; $maxItems = is_array($maxItemsConf) ? $this->parent_cObj->stdWrap($maxItems, $maxItemsConf) : $maxItems;
$beginConf = $this->mconf['begin.'] ?? $this->conf['begin.'] ?? null; $beginConf = $this->mconf['begin.'] ?? $this->conf['begin.'] ?? null;
$begin = is_array($beginConf) ? $this->parent_cObj->stdWrap($begin, $beginConf) : $begin; $begin = is_array($beginConf) ? $this->parent_cObj->stdWrap($begin, $beginConf) : $begin;
$banUidArray = $this->getBannedUids();
// Fill in the menuArr with elements that should go into the menu:
$this->menuArr = []; $this->menuArr = [];
foreach ($menuItems as $data) { foreach ($menuItems as &$data) {
$isSpacerPage = (int)($data['doktype'] ?? 0) === PageRepository::DOKTYPE_SPACER || ($data['ITEM_STATE'] ?? '') === 'SPC'; $data = $this->determineOriginalShortcutPage($data);
// if item is a spacer, $spacer is set $data['isSpacer'] = ($data['isSpacer'] ?? false) || (int)($data['doktype'] ?? 0) === PageRepository::DOKTYPE_SPACER || ($data['ITEM_STATE'] ?? '') === 'SPC';
if ($this->filterMenuPages($data, $banUidArray, $isSpacerPage)) { }
$c_b++; $menuItems = $this->removeInaccessiblePages($menuItems);
// If the beginning item has been reached. // Fill in the menuArr with elements that should go into the menu
if ($begin <= $c_b) { foreach ($menuItems as $menuItem) {
$this->menuArr[$c] = $this->determineOriginalShortcutPage($data); $c_b++;
$this->menuArr[$c]['isSpacer'] = $isSpacerPage; // If the beginning item has been reached, add the items.
$c++; if ($begin <= $c_b) {
if ($maxItems && $c >= $maxItems) { $this->menuArr[$c] = $menuItem;
break; $c++;
} if ($maxItems && $c >= $maxItems) {
break;
} }
} }
} }
...@@ -470,20 +471,30 @@ abstract class AbstractMenuContentObject ...@@ -470,20 +471,30 @@ abstract class AbstractMenuContentObject
/** /**
* Gets an array of page rows and removes all, which are not accessible * Gets an array of page rows and removes all, which are not accessible
*
* @param array $pages
* @return array
*/ */
protected function removeInaccessiblePages(array $pages) protected function removeInaccessiblePages(array $pages): array
{ {
$banned = $this->getBannedUids(); $banned = $this->getBannedUids();
$filteredPages = []; $filteredPages = [];
foreach ($pages as $aPage) { foreach ($pages as $aPage) {
if ($this->filterMenuPages($aPage, $banned, (int)$aPage['doktype'] === PageRepository::DOKTYPE_SPACER)) { $isSpacerPage = ((int)($aPage['doktype'] ?? 0) === PageRepository::DOKTYPE_SPACER) || ($aPage['isSpacer'] ?? false);
$filteredPages[$aPage['uid']] = $aPage; if ($this->filterMenuPages($aPage, $banned, $isSpacerPage)) {
$filteredPages[] = $aPage;
} }
} }
return $filteredPages; $event = new FilterMenuItemsEvent(
$pages,
$filteredPages,
$this->mconf,
$this->conf,
$banned,
$this->excludedDoktypes,
$this->getCurrentSite(),
$this->getTypoScriptFrontendController()->getContext(),
$this->getTypoScriptFrontendController()->page
);
$event = GeneralUtility::getContainer()->get(EventDispatcherInterface::class)->dispatch($event);
return $event->getFilteredMenuItems();
} }
/** /**
...@@ -1087,17 +1098,6 @@ abstract class AbstractMenuContentObject ...@@ -1087,17 +1098,6 @@ abstract class AbstractMenuContentObject
*/ */
public function filterMenuPages(&$data, $banUidArray, $isSpacerPage) public function filterMenuPages(&$data, $banUidArray, $isSpacerPage)
{ {
$includePage = true;
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/tslib/class.tslib_menu.php']['filterMenuPages'] ?? [] as $className) {
$hookObject = GeneralUtility::makeInstance($className);
if (!$hookObject instanceof AbstractMenuFilterPagesHookInterface) {
throw new \UnexpectedValueException($className . ' must implement interface ' . AbstractMenuFilterPagesHookInterface::class, 1269877402);
}
$includePage = $includePage && $hookObject->processFilter($data, $banUidArray, $isSpacerPage, $this);
}
if (!$includePage) {
return false;
}
if ($data['_SAFE'] ?? false) { if ($data['_SAFE'] ?? false) {
return true; return true;
} }
......
...@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Frontend\ContentObject\Menu; ...@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Frontend\ContentObject\Menu;
/** /**
* interface for classes which hook into AbstractMenuContentObject * interface for classes which hook into AbstractMenuContentObject
* @deprecated in favor of FilterMenuItemsEvent - not in use anymore, and only available for compatibility reasons, will be removed in TYPO3 v13.0
*/ */
interface AbstractMenuFilterPagesHookInterface interface AbstractMenuFilterPagesHookInterface
{ {
......
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Frontend\Event;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Site\Entity\Site;
/**
* Listeners to this Event will be able to modify items for a menu generated with HMENU
*/
final class FilterMenuItemsEvent
{
private array $allMenuItems;
private array $filteredMenuItems;
private array $menuConfiguration;
private array $itemConfiguration;
private array $bannedMenuItems;
private array $excludedDoktypes;
private Site $site;
private Context $context;
private array $currentPage;
public function __construct(
array $allMenuItems,
array $filteredMenuItems,
array $menuConfiguration,
array $itemConfiguration,
array $bannedMenuItems,
array $excludedDoktypes,
Site $site,
Context $context,
array $currentPage
) {
$this->allMenuItems = $allMenuItems;
$this->filteredMenuItems = $filteredMenuItems;
$this->menuConfiguration = $menuConfiguration;
$this->itemConfiguration = $itemConfiguration;
$this->bannedMenuItems = $bannedMenuItems;
$this->excludedDoktypes = $excludedDoktypes;
$this->site = $site;
$this->context = $context;
$this->currentPage = $currentPage;
}
public function getAllMenuItems(): array
{
return $this->allMenuItems;
}
public function getFilteredMenuItems(): array
{
return $this->filteredMenuItems;
}
public function setFilteredMenuItems(array $filteredMenuItems): void
{
$this->filteredMenuItems = $filteredMenuItems;
}
public function getMenuConfiguration(): array
{
return $this->menuConfiguration;
}
public function getItemConfiguration(): array
{
return $this->itemConfiguration;
}
public function getBannedMenuItems(): array
{
return $this->bannedMenuItems;
}
public function getExcludedDoktypes(): array
{
return $this->excludedDoktypes;
}
public function getSite(): Site
{
return $this->site;
}
public function getContext(): Context
{
return $this->context;
}
public function getCurrentPage(): array
{
return $this->currentPage;
}
}
...@@ -565,4 +565,10 @@ return [ ...@@ -565,4 +565,10 @@ return [
'Breaking-96333-AutoConfigurationOfContextMenuItemProviders.rst', 'Breaking-96333-AutoConfigurationOfContextMenuItemProviders.rst',
], ],
], ],
'$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'cms/tslib/class.tslib_menu.php\'][\'filterMenuPages\']' => [
'restFiles' => [
'Breaking-92508-RemovedHookForFilteringHMENUItems.rst',
'Feature-92508-PSR-14EventForModifyingMenuItems.rst',
],
],
]; ];
...@@ -1885,4 +1885,11 @@ return [ ...@@ -1885,4 +1885,11 @@ return [
'Breaking-96107-DeprecatedFunctionalityRemoved.rst', 'Breaking-96107-DeprecatedFunctionalityRemoved.rst',
], ],
], ],
'TYPO3\CMS\Core\Frontend\ContentObject\Menu\AbstractMenuFilterPagesHookInterface' => [
'restFiles' => [
'Deprecation-92508-UnusedInterfaceForFilterMenuPagesHook.rst',
'Breaking-92508-RemovedHookForFilteringHMENUItems.rst',
'Feature-92508-PSR-14EventForModifyingMenuItems.rst',
],
],
]; ];
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment