Commit 29db88c3 authored by Oliver Bartsch's avatar Oliver Bartsch Committed by Benni Mack
Browse files

[!!!][FEATURE] New Module Registration API

This change introduces a new way to register
TYPO3 Backend Modules.

Each module is now registered during build-time
in a dedicated registry. Therefore, The registration
is moved from ext_tables.php to the
Configuration/Backend/Modules.php file.

Accessing the registered modules can be done via
a central API, the ModuleProvider class. This class
takes care of access permissions and further
processing (e.g. module menu generation).

$GLOBALS[TBE_MODULES] is fully removed as no sane
backwards-compatibility (for a use-case) is available.

A new PSR-14 event BeforeModuleCreationEvent is added,
which allows to manipulate a module configuration, before
it is used to create and register the module.

Existing registrations do no longer work. However, to support
extenion authors in maintaining multiple versions, the previously
used API methods (e.g. `addModule` and `registerModule`) will
stay until TYPO3 v13.0. No trigger_error() is used, so extension
authors use the Extension Scanner to detect usages.

Executed commands:

   composer u typo3/cms-styleguide

Resolves: #96733
Releases: main
Change-Id: I07f4bc417e6effb1215cf0e373cc60f2b13ba8ad
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/73058


Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Christian Kuhn's avatarChristian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent d7c5cb24
......@@ -105,11 +105,6 @@ parameters:
count: 1
path: ../../typo3/sysext/backend/Classes/Controller/Wizard/SuggestWizardController.php
-
message: "#^Variable \\$subEntries in empty\\(\\) always exists and is not falsy\\.$#"
count: 1
path: ../../typo3/sysext/backend/Classes/Domain/Repository/Module/BackendModuleRepository.php
-
message: "#^Method TYPO3\\\\CMS\\\\Backend\\\\Form\\\\AbstractNode\\:\\:initializeResultArray\\(\\) should return array\\{string\\: array\\<int, string\\>, requireJsModules\\: array\\<int, TYPO3\\\\CMS\\\\Core\\\\Page\\\\JavaScriptModuleInstruction\\>\\} but returns array\\{additionalJavaScriptPost\\: array\\{\\}, additionalHiddenFields\\: array\\{\\}, additionalInlineLanguageLabelFiles\\: array\\{\\}, stylesheetFiles\\: array\\{\\}, requireJsModules\\: array\\{\\}, inlineData\\: array\\{\\}, html\\: ''\\}\\.$#"
count: 1
......@@ -305,26 +300,6 @@ parameters:
count: 1
path: ../../typo3/sysext/backend/Classes/Middleware/BackendUserAuthenticator.php
-
message: "#^Call to function is_array\\(\\) with int\\|string will always evaluate to false\\.$#"
count: 2
path: ../../typo3/sysext/backend/Classes/Module/ModuleLoader.php
-
message: "#^Else branch is unreachable because previous condition is always true\\.$#"
count: 1
path: ../../typo3/sysext/backend/Classes/Module/ModuleLoader.php
-
message: "#^Offset 'default' on array\\{\\} in empty\\(\\) does not exist\\.$#"
count: 1
path: ../../typo3/sysext/backend/Classes/Module/ModuleLoader.php
-
message: "#^Result of \\|\\| is always true\\.$#"
count: 1
path: ../../typo3/sysext/backend/Classes/Module/ModuleLoader.php
-
message: "#^Method TYPO3\\\\CMS\\\\Backend\\\\Routing\\\\PreviewUriBuilder\\:\\:create\\(\\) should return static\\(TYPO3\\\\CMS\\\\Backend\\\\Routing\\\\PreviewUriBuilder\\) but returns object\\.$#"
count: 1
......@@ -5600,11 +5575,6 @@ parameters:
count: 2
path: ../../typo3/sysext/seo/Tests/Functional/XmlSitemap/XmlSitemapRecordsTest.php
-
message: "#^Elseif condition is always true\\.$#"
count: 1
path: ../../typo3/sysext/setup/Classes/Controller/SetupModuleController.php
-
message: "#^If condition is always true\\.$#"
count: 1
......
......@@ -8980,12 +8980,12 @@
"source": {
"type": "git",
"url": "https://github.com/TYPO3/styleguide.git",
"reference": "e8ff67e717d0f28c7a9ba30083139a257cb04947"
"reference": "35761f537f11109f082762439bb24e81fcfd0976"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/TYPO3/styleguide/zipball/e8ff67e717d0f28c7a9ba30083139a257cb04947",
"reference": "e8ff67e717d0f28c7a9ba30083139a257cb04947",
"url": "https://api.github.com/repos/TYPO3/styleguide/zipball/35761f537f11109f082762439bb24e81fcfd0976",
"reference": "35761f537f11109f082762439bb24e81fcfd0976",
"shasum": ""
},
"require-dev": {
......@@ -8993,11 +8993,11 @@
"codeception/module-asserts": "^1.2",
"codeception/module-cli": "^1.1",
"codeception/module-webdriver": "^1.1",
"phpstan/phpstan": "^1.2.0",
"phpstan/phpstan": "^1.4.3",
"typo3/cms-core": "~12.0@dev",
"typo3/cms-frontend": "~12.0@dev",
"typo3/cms-install": "~12.0@dev",
"typo3/coding-standards": "^0.3.0",
"typo3/coding-standards": "^0.5.0",
"typo3/tailor": "^1.2",
"typo3/testing-framework": "dev-main"
},
......@@ -9045,7 +9045,7 @@
"issues": "https://github.com/TYPO3/styleguide/issues",
"source": "https://github.com/TYPO3/styleguide/tree/main"
},
"time": "2021-12-07T14:13:05+00:00"
"time": "2022-02-04T14:19:51+00:00"
},
{
"name": "typo3/testing-framework",
......
......@@ -18,7 +18,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\Backend\Shortcut;
use Symfony\Component\Routing\Route;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Utility\BackendUtility;
......@@ -52,19 +52,11 @@ class ShortcutRepository
protected array $shortcutGroups;
protected ConnectionPool $connectionPool;
protected IconFactory $iconFactory;
protected ModuleLoader $moduleLoader;
public function __construct(ConnectionPool $connectionPool, IconFactory $iconFactory, ModuleLoader $moduleLoader)
{
$this->connectionPool = $connectionPool;
$this->iconFactory = $iconFactory;
$this->moduleLoader = $moduleLoader;
$this->moduleLoader->load($GLOBALS['TBE_MODULES']);
public function __construct(
protected readonly ConnectionPool $connectionPool,
protected readonly IconFactory $iconFactory,
protected readonly ModuleProvider $moduleProvider,
) {
$this->shortcutGroups = $this->initShortcutGroups();
$this->shortcuts = $this->initShortcuts();
}
......@@ -415,7 +407,7 @@ class ShortcutRepository
// Check if the user has access to this module
// @todo Hack for EditDocumentController / FormEngine, see issues #91368 and #91210
if ($routeIdentifier !== 'record_edit' && !is_array($this->moduleLoader->checkMod($moduleName))) {
if ($routeIdentifier !== 'record_edit' && !$this->moduleProvider->accessGranted($moduleName, $backendUser)) {
continue;
}
......@@ -549,18 +541,12 @@ class ShortcutRepository
break;
default:
$iconIdentifier = '';
if (str_contains($moduleName, '_')) {
[$mainModule, $subModule] = explode('_', $moduleName, 2);
$iconIdentifier = $this->moduleLoader->getModules()[$mainModule]['sub'][$subModule]['iconIdentifier'] ?? '';
} elseif ($moduleName !== '') {
$iconIdentifier = $this->moduleLoader->getModules()[$moduleName]['iconIdentifier'] ?? '';
if ($module = $this->moduleProvider->getModule($moduleName, null, false)) {
$iconIdentifier = $module->getIconIdentifier();
}
if (!$iconIdentifier) {
if ($iconIdentifier === '') {
$iconIdentifier = 'empty-empty';
}
$icon = $this->iconFactory->getIcon($iconIdentifier, Icon::SIZE_SMALL)->render();
}
......
......@@ -17,9 +17,10 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
use TYPO3\CMS\Backend\Domain\Model\Module\BackendModule;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Module\MenuModule;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
......@@ -28,14 +29,13 @@ use TYPO3\CMS\Fluid\View\BackendTemplateView;
*/
class HelpToolbarItem implements ToolbarItemInterface
{
protected ?BackendModule $helpModuleMenu = null;
protected ?MenuModule $helpModule = null;
public function __construct(
BackendModuleRepository $backendModuleRepository
) {
$helpModuleMenu = $backendModuleRepository->findByModuleName('help');
if ($helpModuleMenu && $helpModuleMenu->getChildren()->count() > 0) {
$this->helpModuleMenu = $helpModuleMenu;
public function __construct(ModuleProvider $moduleProvider)
{
$helpModule = $moduleProvider->getModuleForMenu('help', $this->getBackendUser());
if ($helpModule && $helpModule->hasSubModules()) {
$this->helpModule = $helpModule;
}
}
......@@ -44,7 +44,7 @@ class HelpToolbarItem implements ToolbarItemInterface
*/
public function checkAccess(): bool
{
return (bool)$this->helpModuleMenu;
return $this->helpModule !== null;
}
/**
......@@ -60,12 +60,12 @@ class HelpToolbarItem implements ToolbarItemInterface
*/
public function getDropDown(): string
{
if (!$this->helpModuleMenu instanceof BackendModule) {
if ($this->helpModule === null) {
// checkAccess() is called before and prevents call to getDropDown() if there is no help.
throw new \RuntimeException('No HelpModuleMenu found.', 1641993564);
}
$view = $this->getFluidTemplateObject();
$view->assign('modules', $this->helpModuleMenu->getChildren());
$view->assign('modules', $this->helpModule->getSubModules());
return $view->render('ToolbarItems/HelpToolbarItemDropDown');
}
......@@ -100,4 +100,9 @@ class HelpToolbarItem implements ToolbarItemInterface
$view->setTemplateRootPaths(['EXT:backend/Resources/Private/Templates']);
return $view;
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
......@@ -17,8 +17,9 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\BackendTemplateView;
......@@ -27,12 +28,8 @@ use TYPO3\CMS\Fluid\View\BackendTemplateView;
*/
class LiveSearchToolbarItem implements ToolbarItemInterface
{
protected BackendModuleRepository $backendModuleRepository;
public function __construct(
BackendModuleRepository $backendModuleRepository
) {
$this->backendModuleRepository = $backendModuleRepository;
public function __construct(protected readonly ModuleProvider $moduleProvider)
{
}
/**
......@@ -41,8 +38,7 @@ class LiveSearchToolbarItem implements ToolbarItemInterface
*/
public function checkAccess(): bool
{
$listModule = $this->backendModuleRepository->findByModuleName('web_list');
return $listModule !== null && $listModule !== false;
return $this->moduleProvider->accessGranted('web_list', $this->getBackendUser());
}
/**
......@@ -86,4 +82,9 @@ class LiveSearchToolbarItem implements ToolbarItemInterface
{
return 90;
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
......@@ -17,7 +17,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\Connection;
......@@ -32,11 +32,8 @@ use TYPO3\CMS\Fluid\View\BackendTemplateView;
*/
class UserToolbarItem implements ToolbarItemInterface
{
protected BackendModuleRepository $backendModuleRepository;
public function __construct(BackendModuleRepository $backendModuleRepository)
public function __construct(protected readonly ModuleProvider $moduleProvider)
{
$this->backendModuleRepository = $backendModuleRepository;
}
/**
......@@ -102,8 +99,8 @@ class UserToolbarItem implements ToolbarItemInterface
}
$modules = null;
if ($userModule = $this->backendModuleRepository->findByModuleName('user')) {
$modules = $userModule->getChildren();
if ($userModule = $this->moduleProvider->getModuleForMenu('user', $backendUser)) {
$modules = $userModule->getSubModules();
}
$view = $this->getFluidTemplateObject();
$view->assignMultiple([
......
......@@ -19,8 +19,9 @@ namespace TYPO3\CMS\Backend\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Information\Typo3Information;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\Package\PackageManager;
......@@ -37,12 +38,10 @@ class AboutController
public function __construct(
protected readonly Typo3Version $version,
protected readonly Typo3Information $typo3Information,
protected readonly ModuleLoader $moduleLoader,
protected readonly ModuleProvider $moduleProvider,
protected readonly PackageManager $packageManager,
protected readonly ModuleTemplateFactory $moduleTemplateFactory
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
) {
$this->moduleLoader->observeWorkspaces = true;
$this->moduleLoader->load($GLOBALS['TBE_MODULES']);
}
/**
......@@ -66,55 +65,11 @@ class AboutController
'loadedExtensions' => $this->getLoadedExtensions(),
'copyRightNotice' => $this->typo3Information->getCopyrightNotice(),
'warnings' => $warnings,
'modules' => $this->getModulesData(),
'modules' => $this->moduleProvider->getModules($this->getBackendUser()),
]);
return $view->renderResponse('About/Index');
}
/**
* Create array with data of all main modules (Web, File, ...)
* and its nested sub modules.
*/
protected function getModulesData(): array
{
$mainModulesData = [];
foreach ($this->moduleLoader->getModules() as $moduleName => $moduleInfo) {
$moduleLabels = $this->moduleLoader->getLabelsForModule($moduleName);
$mainModuleData = [
'name' => $moduleName,
'label' => $moduleLabels['title'],
];
if (is_array($moduleInfo['sub'] ?? null) && !empty($moduleInfo['sub'])) {
$mainModuleData['subModules'] = $this->getSubModuleData((string)$moduleName);
}
$mainModulesData[] = $mainModuleData;
}
return $mainModulesData;
}
/**
* Create array with data of all subModules of a specific main module
*/
protected function getSubModuleData(string $moduleName): array
{
if (empty($this->moduleLoader->getModules()[$moduleName]['sub'])) {
return [];
}
$subModulesData = [];
foreach ($this->moduleLoader->getModules()[$moduleName]['sub'] ?? [] as $subModuleName => $subModuleInfo) {
$moduleLabels = $this->moduleLoader->getLabelsForModule($moduleName . '_' . $subModuleName);
$subModuleData = [];
$subModuleData['name'] = $subModuleName;
$subModuleData['icon'] = $subModuleInfo['icon'] ?? null;
$subModuleData['iconIdentifier'] = $subModuleInfo['iconIdentifier'] ?? null;
$subModuleData['label'] = $moduleLabels['title'] ?? null;
$subModuleData['shortDescription'] = $moduleLabels['shortdescription'] ?? null;
$subModuleData['longDescription'] = $moduleLabels['description'] ?? null;
$subModulesData[] = $subModuleData;
}
return $subModulesData;
}
/**
* Fetches a list of all active (loaded) extensions in the current system
*/
......@@ -134,4 +89,9 @@ class AboutController
}
return $extensions;
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}
......@@ -17,8 +17,8 @@ namespace TYPO3\CMS\Backend\Controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Module\ModuleInterface;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\Router;
use TYPO3\CMS\Backend\Routing\RouteRedirect;
use TYPO3\CMS\Backend\Routing\UriBuilder;
......@@ -49,21 +49,24 @@ class BackendController
use PageRendererBackendSetupTrait;
protected string $css = '';
protected \SplObjectStorage $moduleStorage;
/**
* @var ModuleInterface[]
*/
protected array $modules;
public function __construct(
protected Typo3Version $typo3Version,
protected UriBuilder $uriBuilder,
protected PageRenderer $pageRenderer,
protected ModuleLoader $moduleLoader,
protected BackendModuleRepository $backendModuleRepository,
protected ToolbarItemsRegistry $toolbarItemsRegistry,
protected ExtensionConfiguration $extensionConfiguration,
protected BackendViewFactory $viewFactory,
protected readonly Typo3Version $typo3Version,
protected readonly UriBuilder $uriBuilder,
protected readonly PageRenderer $pageRenderer,
protected readonly ModuleProvider $moduleProvider,
protected readonly ToolbarItemsRegistry $toolbarItemsRegistry,
protected readonly ExtensionConfiguration $extensionConfiguration,
protected readonly BackendViewFactory $viewFactory,
) {
// @todo: This hook is essentially useless.
$this->executeHook('constructPostProcess');
$this->moduleStorage = $this->backendModuleRepository->loadAllowedModules(['user', 'help']);
$this->modules = $this->moduleProvider->getModulesForModuleMenu($this->getBackendUser());
}
/**
......@@ -129,13 +132,12 @@ class BackendController
$typo3Version = 'TYPO3 CMS ' . $this->typo3Version->getVersion();
$title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [' . $typo3Version . ']' : $typo3Version;
$pageRenderer->setTitle($title);
$hasModules = count($this->moduleStorage) > 0;
$moduleMenuCollapsed = $this->getCollapseStateOfMenu();
$view = $this->viewFactory->create($request, 'typo3/cms-backend');
$this->assignTopbarDetailsToView($view);
$view->assignMultiple([
'modules' => $this->moduleStorage,
'modules' => $this->modules,
'startupModule' => $this->getStartupModule($request),
'stateTracker' => (string)$this->uriBuilder->buildUriFromRoute('state-tracker'),
'sitename' => $title,
......@@ -143,7 +145,7 @@ class BackendController
]);
$content = $view->render('Backend/Main');
$this->executeHook('renderPostProcess', ['content' => &$content]);
$bodyTag = '<body class="scaffold t3js-scaffold' . (!$moduleMenuCollapsed && $hasModules ? ' scaffold-modulemenu-expanded' : '') . '">';
$bodyTag = '<body class="scaffold t3js-scaffold' . (!$moduleMenuCollapsed && $this->modules ? ' scaffold-modulemenu-expanded' : '') . '">';
$pageRenderer->addBodyContent($bodyTag . $content);
return new HtmlResponse($pageRenderer->render());
}
......@@ -156,7 +158,7 @@ class BackendController
public function getModuleMenu(ServerRequestInterface $request): ResponseInterface
{
$view = $this->viewFactory->create($request, 'typo3/cms-backend');
$view->assign('modules', $this->moduleStorage);
$view->assign('modules', $this->modules);
return new JsonResponse(['menu' => $view->render('Backend/ModuleMenu')]);
}
......@@ -212,7 +214,7 @@ class BackendController
$logoHeight /= 2;
}
}
$view->assign('hasModules', count($this->moduleStorage) > 0);
$view->assign('hasModules', (bool)$this->modules);
$view->assign('logoUrl', PathUtility::getAbsoluteWebPath($logoPath));
$view->assign('logoWidth', $logoWidth);
$view->assign('logoHeight', $logoHeight);
......@@ -289,10 +291,6 @@ class BackendController
*/
protected function getStartupModule(ServerRequestInterface $request): array
{
$moduleLoader = $this->moduleLoader;
$moduleLoader->observeWorkspaces = true;
$moduleLoader->load($GLOBALS['TBE_MODULES']);
$startModule = null;
$moduleParameters = [];
try {
......@@ -312,10 +310,10 @@ class BackendController
$startModule = $backendUser->uc['startModuleOnFirstLogin'];
unset($backendUser->uc['startModuleOnFirstLogin']);
$backendUser->writeUC();
} elseif ($moduleLoader->checkMod($backendUser->uc['startModule'] ?? '') !== 'notFound') {
} elseif (isset($backendUser->uc['startModule']) && $this->moduleProvider->accessGranted($backendUser->uc['startModule'], $backendUser)) {
$startModule = $backendUser->uc['startModule'];
} else {
$startModule = $this->determineFirstAvailableBackendModule($moduleLoader);
} elseif ($firstAccessibleModule = $this->moduleProvider->getFirstAccessibleModule($backendUser)) {
$startModule = $firstAccessibleModule->getIdentifier();
}
// check if the start module has additional parameters, so a redirect to a specific
......@@ -342,24 +340,6 @@ class BackendController
return [null, null];
}
protected function determineFirstAvailableBackendModule(ModuleLoader $moduleLoader): string
{
foreach ($moduleLoader->getModules() as $modData) {
$hasSubmodules = !empty($modData['sub']) && is_array($modData['sub']);
$isStandalone = $modData['standalone'] ?? false;
if ($isStandalone) {
return $modData['name'];
}
if ($hasSubmodules) {
$firstSubmodule = reset($modData['sub']);
return $firstSubmodule['name'];
}
}
return '';
}
/**
* Executes defined hooks functions for the given identifier.
*
......
......@@ -23,7 +23,7 @@ use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Clipboard\Clipboard;
use TYPO3\CMS\Backend\Controller\Event\ModifyPageLayoutContentEvent;
use TYPO3\CMS\Backend\Domain\Model\Element\ImmediateActionElement;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\PreviewUriBuilder;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
......@@ -156,28 +156,17 @@ class PageLayoutController
*/
protected $context;
protected IconFactory $iconFactory;
protected PageRenderer $pageRenderer;
protected UriBuilder $uriBuilder;
protected PageRepository $pageRepository;
protected ModuleTemplateFactory $moduleTemplateFactory;
protected EventDispatcherInterface $eventDispatcher;
public function __construct(
IconFactory $iconFactory,
PageRenderer $pageRenderer,
UriBuilder $uriBuilder,
PageRepository $pageRepository,
ModuleTemplateFactory $moduleTemplateFactory,
EventDispatcherInterface $eventDispatcher
protected readonly IconFactory $iconFactory,
protected readonly PageRenderer $pageRenderer,
protected readonly UriBuilder $uriBuilder,
protected readonly PageRepository $pageRepository,
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
protected readonly EventDispatcherInterface $eventDispatcher,
protected readonly ModuleProvider $moduleProvider,
) {
$this->iconFactory = $iconFactory;
$this->pageRenderer = $pageRenderer;
$this->uriBuilder = $uriBuilder;
$this->pageRepository = $pageRepository;
$this->moduleTemplateFactory = $moduleTemplateFactory;
$this->eventDispatcher = $eventDispatcher;
}
/**
* Injects the request object for the current request or subrequest
* As this controller goes only through the main() method, it is rather simple for now
......@@ -386,10 +375,7 @@ class PageLayoutController
// If page is a folder
if ($this->pageinfo['doktype'] == PageRepository::DOKTYPE_SYSFOLDER) {
$moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
$moduleLoader->load($GLOBALS['TBE_MODULES']);
$modules = $moduleLoader->getModules();
if (is_array($modules['web']['sub']['list'] ?? null)) {
if ($this->moduleProvider->accessGranted('web_list', $this->getBackendUser())) {
$title = $lang->getLL('goToListModule');
$message = '<p>' . $lang->getLL('goToListModuleMessage') . '</p>';
$message .= '<a class="btn btn-info" data-dispatch-action="TYPO3.ModuleMenu.showModule" data-dispatch-args-list="web_list">'
......
......@@ -21,8 +21,6 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Backend\Shortcut\ShortcutRepository;
use TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem;
use TYPO3\CMS\Backend\Module\ModuleLoader;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\JsonResponse;