TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:00 +02:00
commit f9941541b7
1178 changed files with 135377 additions and 0 deletions
@@ -0,0 +1,93 @@
<?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\Backend\Sidebar;
use TYPO3\CMS\Backend\Attribute\AsSidebarComponent;
use TYPO3\CMS\Backend\Module\MenuModule;
use TYPO3\CMS\Backend\Module\ModuleProvider;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\View\BackendViewFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Module menu sidebar component.
*
* Renders the main backend module navigation menu in the sidebar.
*
* @internal
*/
#[AsSidebarComponent(identifier: 'module-menu')]
final readonly class ModuleMenuSidebarComponent implements SidebarComponentInterface
{
public function __construct(
private ModuleProvider $moduleProvider,
private BackendViewFactory $viewFactory,
private UriBuilder $uriBuilder,
) {}
public function hasAccess(SidebarComponentContext $context): bool
{
return !empty($this->moduleProvider->getModulesForModuleMenu($context->user));
}
public function getResult(SidebarComponentContext $context): SidebarComponentResult
{
$modules = $this->moduleProvider->getModulesForModuleMenu($context->user);
if ($modules === []) {
return new SidebarComponentResult('', '');
}
$view = $this->viewFactory->create($context->request);
$view->assignMultiple([
'modules' => $modules,
'modulesInformation' => GeneralUtility::jsonEncodeForHtmlAttribute(
$this->getModulesInformation($context),
false
),
]);
return new SidebarComponentResult(
identifier: 'module-menu',
html: $view->render('Backend/ModuleMenu'),
module: '@typo3/backend/module-menu.js',
);
}
/**
* Get module information for JavaScript.
*
* @return array<string, mixed>
*/
private function getModulesInformation(SidebarComponentContext $context): array
{
$modules = [];
foreach ($this->moduleProvider->getModules($context->user, true, false) as $identifier => $module) {
$menuModule = new MenuModule(clone $module);
$modules[$identifier] = [
'name' => $identifier,
'aliases' => $module->getAliases(),
'component' => $menuModule->getComponent(),
'navigationComponentId' => $menuModule->getNavigationComponent(),
'parent' => $menuModule->hasParentModule() ? $menuModule->getParentIdentifier() : '',
'link' => $menuModule->getShouldBeLinked() ? (string)$this->uriBuilder->buildUriFromRoute($module->getIdentifier()) : '',
];
}
return $modules;
}
}
+78
View File
@@ -0,0 +1,78 @@
<?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\Backend\Sidebar;
/**
* Renders the complete backend sidebar with all registered components.
*
* @internal
*/
final readonly class Sidebar
{
/**
* @param array<string, SidebarComponentInterface> $components Components keyed by identifier
*/
public function __construct(
private array $components,
private SidebarComponentContext $context,
) {}
/**
* @return array{
* html: string,
* modules: list<string>,
* accessible: bool,
* expanded: bool
* }
*/
public function render(): array
{
$html = [];
$modules = [];
foreach ($this->components as $identifier => $component) {
$result = $component->getResult($this->context);
$html[] = '<div class="sidebar-component" data-identifier="' . htmlspecialchars($identifier) . '">' . $result->html . '</div>';
if ($result->module) {
$modules[] = $result->module;
}
}
return [
'html' => $html === [] ? '' : '<div class="sidebar-container">' . implode(LF, $html) . '</div>',
'modules' => $modules,
'accessible' => $this->isAccessible(),
'expanded' => $this->isExpanded(),
];
}
public function isAccessible(): bool
{
return $this->components !== [];
}
public function isExpanded(): bool
{
$collapseState = $this->context->user->uc['BackendComponents']['States']['typo3-sidebar']['collapsed'] ?? false;
return $collapseState !== true && $collapseState !== 'true';
}
public function getComponentByIdentifier(string $identifier): ?SidebarComponentInterface
{
return $this->components[$identifier] ?? null;
}
}
@@ -0,0 +1,34 @@
<?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\Backend\Sidebar;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
/**
* Context object for sidebar components
*
* @internal
*/
final readonly class SidebarComponentContext
{
public function __construct(
public ServerRequestInterface $request,
public BackendUserAuthentication $user,
) {}
}
@@ -0,0 +1,39 @@
<?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\Backend\Sidebar;
/**
* Interface for backend sidebar components.
*
* Use the AsSidebarComponent attribute to register sidebar components
* and configure their identifier and ordering.
*
* @internal
*/
interface SidebarComponentInterface
{
/**
* Check if the current user has access to this sidebar component.
*/
public function hasAccess(SidebarComponentContext $context): bool;
/**
* Render the sidebar component HTML.
*/
public function getResult(SidebarComponentContext $context): SidebarComponentResult;
}
@@ -0,0 +1,44 @@
<?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\Backend\Sidebar;
/**
* Result object for sidebar component rendering.
*
* This class provides a structured response format that can be consistently
* evaluated on both PHP and JavaScript sides.
*
* @internal
*/
final readonly class SidebarComponentResult implements \JsonSerializable
{
public function __construct(
public string $identifier,
public string $html,
public string $module = '',
) {}
public function jsonSerialize(): array
{
return [
'identifier' => $this->identifier,
'html' => $this->html,
'module' => $this->module,
];
}
}
@@ -0,0 +1,75 @@
<?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\Backend\Sidebar;
/**
* Registry for sidebar components.
*
* Components are ordered by before/after dependencies during container compilation.
*
* @internal
*/
class SidebarComponentsRegistry
{
/**
* All registered components in their ordered positions.
*
* @var array<string, SidebarComponentInterface>
*/
private array $components = [];
/**
* @param array<string, SidebarComponentInterface> $sidebarComponents Pre-ordered components (injected by SidebarComponentsPass)
*/
public function __construct(array $sidebarComponents = [])
{
foreach ($sidebarComponents as $identifier => $component) {
$this->components[$identifier] = $component;
}
}
/**
* Get all sidebar components.
*
* @return array<string, SidebarComponentInterface>
*/
public function getComponents(): array
{
return $this->components;
}
public function hasComponent(string $identifier): bool
{
return isset($this->components[$identifier]);
}
/**
* @throws \InvalidArgumentException if component does not exist
*/
public function getComponent(string $identifier): SidebarComponentInterface
{
if (!$this->hasComponent($identifier)) {
throw new \InvalidArgumentException(
sprintf('Sidebar component with identifier "%s" is not registered', $identifier),
1765923035
);
}
return $this->components[$identifier];
}
}
+41
View File
@@ -0,0 +1,41 @@
<?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\Backend\Sidebar;
/**
* Factory to create request-scoped Sidebar instances.
*
* @internal
*/
final readonly class SidebarFactory
{
public function __construct(
private SidebarComponentsRegistry $registry,
) {}
public function create(SidebarComponentContext $context): Sidebar
{
$components = [];
foreach ($this->registry->getComponents() as $identifier => $component) {
if ($component->hasAccess($context)) {
$components[$identifier] = $component;
}
}
return new Sidebar($components, $context);
}
}