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,162 @@
<?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\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Backend\Context\PageContextFactory;
use TYPO3\CMS\Backend\Module\ModuleInterface;
use TYPO3\CMS\Backend\Routing\Route;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
/**
* Initializes PageContext for backend modules that work with pages.
*
* Creates a PageContext with resolved language information and stores it
* in the request attribute 'pageContext' for use by controllers.
*
* This middleware only runs for modules that use the page tree navigation
* component ('@typo3/backend/tree/page-tree-element'). This includes modules
* like web_layout, records, and others under the 'content' parent, as well
* as standalone modules that explicitly set the navigation component.
*
* The middleware:
* - Checks if the module or any of its parents use the page tree component
* - Extracts the page ID from request (query or body parameter 'id')
* - Defaults to page ID 0 (NullSite/root level) if not found
* - Creates PageContext with language resolution
*
* If PageContext creation fails (e.g. no site found, no page access),
* the middleware logs a warning but continues without breaking the request,
* allowing controllers to handle the missing context or create it manually.
*
* @internal
*/
readonly class PageContextInitialization implements MiddlewareInterface
{
public function __construct(
protected PageContextFactory $pageContextFactory,
protected LoggerInterface $logger,
) {}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
if (!$this->requiresPageContext($request)) {
return $handler->handle($request);
}
$backendUser = $request->getAttribute('backend.user', $GLOBALS['BE_USER']);
// Only process if user is authenticated in the backend
if (!($backendUser instanceof BackendUserAuthentication) || !$backendUser->user) {
return $handler->handle($request);
}
$pageId = $this->determinePageId($request);
try {
$request = $request->withAttribute(
'pageContext',
$this->pageContextFactory->createFromRequest($request, $pageId, $backendUser)
);
} catch (\Exception $e) {
// If PageContext creation fails, log the error and continue without it.
// Controllers can fall back to manual creation if needed.
$this->logger->warning(
'Failed to create PageContext in middleware for page {page}: {error}',
[
'page' => $pageId,
'error' => $e->getMessage(),
'exception' => $e,
]
);
}
return $handler->handle($request);
}
private function determinePageId(ServerRequestInterface $request): int
{
$id = $request->getParsedBody()['id'] ?? $request->getQueryParams()['id'] ?? null;
if ($id !== null) {
return (int)$id;
}
$editStatement = $request->getParsedBody()['edit'] ?? $request->getQueryParams()['edit'] ?? null;
if (is_array($editStatement)) {
$table = key($editStatement);
$uidAndAction = current($editStatement);
$uid = (int)key($uidAndAction);
$action = current($uidAndAction);
if ($action === 'edit') {
return $this->getPageIdByRecord($table, $uid);
}
if ($action === 'new') {
return $this->getPageIdByRecord($table, $uid, true);
}
}
$commandStatement = $request->getParsedBody()['cmd'] ?? $request->getQueryParams()['cmd'] ?? null;
if (is_array($commandStatement)) {
$table = key($commandStatement);
$uidActionAndTarget = current($commandStatement);
$uid = (int)key($uidActionAndTarget);
$actionAndTarget = current($uidActionAndTarget);
$action = key($actionAndTarget);
$target = current($actionAndTarget);
if ($action === 'delete') {
return $this->getPageIdByRecord($table, $uid);
}
if ($action === 'copy' || $action === 'move') {
return $this->getPageIdByRecord($table, (int)($target['target'] ?? $target), true);
}
}
return 0;
}
private function getPageIdByRecord(string $table, int $id, bool $ignoreTable = false): int
{
$pageId = 0;
if ($table && $id) {
if (($ignoreTable || $table === 'pages') && $id >= 0) {
$pageId = $id;
} else {
$record = BackendUtility::getRecordWSOL($table, abs($id), '*', '', false);
$pageId = (int)($record['pid'] ?? 0);
}
}
return $pageId;
}
/**
* Check if the current request requires PageContext initialization.
*
* PageContext is required when:
* 1. Module uses the page tree navigation component
* 2. Route explicitly has 'requestPageContext' option set to true
*/
private function requiresPageContext(ServerRequestInterface $request): bool
{
return ((($module = $request->getAttribute('module')) instanceof ModuleInterface) && $module->getNavigationComponent() === '@typo3/backend/tree/page-tree-element')
|| ((($route = $request->getAttribute('route')) instanceof Route) && $route->getOption('requestPageContext'));
}
}