163 lines
6.1 KiB
PHP
163 lines
6.1 KiB
PHP
<?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'));
|
|
}
|
|
}
|