first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,95 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
/**
* A request handler which can handle web requests.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
abstract class AbstractRequestHandler implements \TYPO3\CMS\Extbase\Mvc\RequestHandlerInterface
{
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @var \TYPO3\CMS\Extbase\Mvc\Dispatcher
*/
protected $dispatcher;
/**
* @var \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder
*/
protected $requestBuilder;
/**
* @var \TYPO3\CMS\Extbase\Service\EnvironmentService
*/
protected $environmentService;
/**
* @param \TYPO3\CMS\Extbase\Mvc\Dispatcher $dispatcher
*/
public function injectDispatcher(\TYPO3\CMS\Extbase\Mvc\Dispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* @param \TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder $requestBuilder
*/
public function injectRequestBuilder(\TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder $requestBuilder)
{
$this->requestBuilder = $requestBuilder;
}
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
*/
public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
{
$this->environmentService = $environmentService;
}
/**
* This request handler can handle any web request.
*
* @return bool If the request is a web request, TRUE otherwise FALSE
*/
public function canHandleRequest()
{
return true;
}
/**
* Returns the priority - how eager the handler is to actually handle the
* request.
*
* @return int The priority of the request handler.
*/
public function getPriority()
{
return 100;
}
}
@@ -0,0 +1,48 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
use TYPO3\CMS\Core\Core\Environment;
/**
* A request handler which can handle web requests invoked by the backend.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class BackendRequestHandler extends AbstractRequestHandler
{
/**
* Handles the web request. The response will automatically be sent to the client.
*
* @return \TYPO3\CMS\Extbase\Mvc\Web\Response
*/
public function handleRequest()
{
$request = $this->requestBuilder->build();
/** @var \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response */
$response = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
$this->dispatcher->dispatch($request, $response);
return $response;
}
/**
* This request handler can handle a web request invoked by the backend.
*
* @return bool If we are in backend mode TRUE otherwise FALSE
*/
public function canHandleRequest()
{
return $this->environmentService->isEnvironmentInBackendMode() && !Environment::isCli();
}
}
@@ -0,0 +1,74 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
/**
* Enforces cHash argument if it is required for a given request
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class CacheHashEnforcer implements SingletonInterface
{
/**
* @var CacheHashCalculator
*/
protected $cacheHashCalculator;
/**
* @var TypoScriptFrontendController
*/
protected $typoScriptFrontendController;
/**
* CacheHashEnforcer constructor.
*
* @param CacheHashCalculator $cacheHashCalculator
* @param TypoScriptFrontendController|null $typoScriptFrontendController
*/
public function __construct(
CacheHashCalculator $cacheHashCalculator,
TypoScriptFrontendController $typoScriptFrontendController = null
) {
$this->cacheHashCalculator = $cacheHashCalculator;
$this->typoScriptFrontendController = $typoScriptFrontendController ?: $GLOBALS['TSFE'];
}
/**
* Checks if cHash is required for the current request and calls
* TypoScriptFrontendController::reqCHash() if so.
* This call will trigger a PageNotFoundException if arguments are required and cHash is not present.
*
* @param Request $request
* @param string $pluginNamespace
*/
public function enforceForRequest(Request $request, string $pluginNamespace)
{
$arguments = $request->getArguments();
if (is_array($arguments) && count($arguments) > 0) {
$parameters = [$pluginNamespace => $arguments];
$parameters['id'] = $this->typoScriptFrontendController->id;
$relevantParameters = $this->cacheHashCalculator->getRelevantParameters(
http_build_query($parameters, '', '&', PHP_QUERY_RFC3986)
);
if (count($relevantParameters) > 0) {
$this->typoScriptFrontendController->reqCHash();
}
}
}
}
@@ -0,0 +1,105 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
/**
* A request handler which can handle web requests invoked by the frontend.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class FrontendRequestHandler extends AbstractRequestHandler
{
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
*/
protected $extensionService;
/**
* @var \TYPO3\CMS\Extbase\Mvc\Web\CacheHashEnforcer
*/
protected $cacheHashEnforcer;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
*/
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
{
$this->extensionService = $extensionService;
}
/**
* @param \TYPO3\CMS\Extbase\Mvc\Web\CacheHashEnforcer $cacheHashEnforcer
*/
public function injectCacheHashEnforcer(\TYPO3\CMS\Extbase\Mvc\Web\CacheHashEnforcer $cacheHashEnforcer)
{
$this->cacheHashEnforcer = $cacheHashEnforcer;
}
/**
* Handles the web request. The response will automatically be sent to the client.
*
* @return \TYPO3\CMS\Extbase\Mvc\ResponseInterface|null
*/
public function handleRequest()
{
$request = $this->requestBuilder->build();
if ($this->extensionService->isActionCacheable(null, null, $request->getControllerName(), $request->getControllerActionName())) {
$request->setIsCached(true);
} else {
$contentObject = $this->configurationManager->getContentObject();
if ($contentObject->getUserObjectType() === \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::OBJECTTYPE_USER) {
$contentObject->convertToUserIntObject();
// \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::convertToUserIntObject() will recreate the object, so we have to stop the request here
return null;
}
$request->setIsCached(false);
}
if ($this->configurationManager->isFeatureEnabled('requireCHashArgumentForActionArguments')) {
$pluginNamespace = $this->extensionService->getPluginNamespace(
$request->getControllerExtensionName(),
$request->getPluginName()
);
$this->cacheHashEnforcer->enforceForRequest($request, $pluginNamespace);
}
/** @var \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response */
$response = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Response::class);
$this->dispatcher->dispatch($request, $response);
return $response;
}
/**
* This request handler can handle any web request.
*
* @return bool If the request is a web request, TRUE otherwise FALSE
*/
public function canHandleRequest()
{
return $this->environmentService->isEnvironmentInFrontendMode();
}
}
@@ -0,0 +1,53 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
/**
* Represents a referring web request.
*/
class ReferringRequest extends Request
{
/**
* Sets the value of the specified argument
*
* @param string $argumentName Name of the argument to set
* @param mixed $value The new value
*/
public function setArgument($argumentName, $value)
{
parent::setArgument($argumentName, $value);
switch ($argumentName) {
case '@extension':
$this->setControllerExtensionName($value);
break;
case '@subpackage':
$this->setControllerSubpackageKey($value);
break;
case '@controller':
$this->setControllerName($value);
break;
case '@action':
$this->setControllerActionName($value);
break;
case '@format':
$this->setFormat($value);
break;
case '@vendor':
$this->setControllerVendorName($value);
break;
}
}
}
@@ -0,0 +1,202 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
/**
* Represents a web request.
*/
class Request extends \TYPO3\CMS\Extbase\Mvc\Request
{
/**
* @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
*/
protected $hashService;
/**
* @var string The requested representation format
*/
protected $format = 'html';
/**
* @var string Contains the request method
*/
protected $method = 'GET';
/**
* @var string
*/
protected $requestUri;
/**
* @var string The base URI for this request - ie. the host and path leading to the index.php
*/
protected $baseUri;
/**
* @var bool TRUE if the current request is cached, false otherwise.
*/
protected $isCached = false;
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Extbase\Service\EnvironmentService
*/
protected $environmentService;
/**
* @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
{
$this->hashService = $hashService;
}
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
{
$this->environmentService = $environmentService;
}
/**
* Sets the request method
*
* @param string $method Name of the request method
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidRequestMethodException if the request method is not supported
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setMethod($method)
{
if ($method === '' || strtoupper($method) !== $method) {
throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidRequestMethodException('The request method "' . $method . '" is not supported.', 1217778382);
}
$this->method = $method;
}
/**
* Returns the name of the request method
*
* @return string Name of the request method
*/
public function getMethod()
{
return $this->method;
}
/**
* Sets the request URI
*
* @param string $requestUri URI of this web request
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setRequestUri($requestUri)
{
$this->requestUri = $requestUri;
}
/**
* Returns the request URI
*
* @return string URI of this web request
*/
public function getRequestUri()
{
return $this->requestUri;
}
/**
* Sets the base URI for this request.
*
* @param string $baseUri New base URI
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setBaseUri($baseUri)
{
$this->baseUri = $baseUri;
}
/**
* Returns the base URI
*
* @return string Base URI of this web request
*/
public function getBaseUri()
{
if ($this->environmentService->isEnvironmentInBackendMode()) {
return $this->baseUri . TYPO3_mainDir;
}
return $this->baseUri;
}
/**
* Set if the current request is cached.
*
* @param bool $isCached
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setIsCached($isCached)
{
$this->isCached = (bool)$isCached;
}
/**
* Return whether the current request is a cached request or not.
*
* @return bool the caching status.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function isCached()
{
return $this->isCached;
}
/**
* Get a freshly built request object pointing to the Referrer.
*
* @return ReferringRequest the referring request, or null if no referrer found
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getReferringRequest()
{
if (isset($this->internalArguments['__referrer']['@request'])) {
$referrerArray = unserialize($this->hashService->validateAndStripHmac($this->internalArguments['__referrer']['@request']), ['allowed_classes' => false]);
$arguments = [];
if (isset($this->internalArguments['__referrer']['arguments'])) {
// This case is kept for compatibility in 7.6 and 6.2, but will be removed in 8
$arguments = unserialize(base64_decode($this->hashService->validateAndStripHmac($this->internalArguments['__referrer']['arguments'])));
}
$referringRequest = new ReferringRequest();
$referringRequest->setArguments(array_replace_recursive($arguments, $referrerArray));
return $referringRequest;
}
return null;
}
}
@@ -0,0 +1,344 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Exception as MvcException;
/**
* Builds a web request.
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
class RequestBuilder implements \TYPO3\CMS\Core\SingletonInterface
{
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* This is the vendor name of the extension
*
* @var string
*/
protected $vendorName;
/**
* This is a unique key for a plugin (not the extension key!)
*
* @var string
*/
protected $pluginName = 'plugin';
/**
* The name of the extension (in UpperCamelCase)
*
* @var string
*/
protected $extensionName;
/**
* The default controller name
*
* @var string
*/
protected $defaultControllerName = '';
/**
* The default format of the response object
*
* @var string
*/
protected $defaultFormat = 'html';
/**
* The allowed actions of the controller. This actions can be called via $_GET and $_POST.
*
* @var array
*/
protected $allowedControllerActions = [];
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
*/
protected $extensionService;
/**
* @var \TYPO3\CMS\Extbase\Service\EnvironmentService
*/
protected $environmentService;
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
*/
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
*/
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
{
$this->extensionService = $extensionService;
}
/**
* @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
*/
public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
{
$this->environmentService = $environmentService;
}
/**
* @throws MvcException
*/
protected function loadDefaultValues()
{
$configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
if (empty($configuration['extensionName'])) {
throw new MvcException('"extensionName" is not properly configured. Request can\'t be dispatched!', 1289843275);
}
if (empty($configuration['pluginName'])) {
throw new MvcException('"pluginName" is not properly configured. Request can\'t be dispatched!', 1289843277);
}
if (!empty($configuration['vendorName'])) {
$this->vendorName = $configuration['vendorName'];
} else {
$this->vendorName = null;
}
$this->extensionName = $configuration['extensionName'];
$this->pluginName = $configuration['pluginName'];
$this->defaultControllerName = (string)current(array_keys($configuration['controllerConfiguration']));
$this->allowedControllerActions = [];
foreach ($configuration['controllerConfiguration'] as $controllerName => $controllerActions) {
$this->allowedControllerActions[$controllerName] = $controllerActions['actions'] ?? null;
}
if (!empty($configuration['format'])) {
$this->defaultFormat = $configuration['format'];
}
}
/**
* Builds a web request object from the raw HTTP information and the configuration
*
* @return \TYPO3\CMS\Extbase\Mvc\Web\Request The web request as an object
*/
public function build()
{
$this->loadDefaultValues();
$pluginNamespace = $this->extensionService->getPluginNamespace($this->extensionName, $this->pluginName);
/** @var \TYPO3\CMS\Core\Http\ServerRequest $typo3Request */
$typo3Request = $GLOBALS['TYPO3_REQUEST'] ?? null;
if ($typo3Request instanceof ServerRequestInterface) {
$queryArguments = $typo3Request->getAttribute('routing');
if ($queryArguments instanceof PageArguments) {
$getParameters = $queryArguments->get($pluginNamespace) ?? [];
} else {
$getParameters = $typo3Request->getQueryParams()[$pluginNamespace] ?? [];
}
$bodyParameters = $typo3Request->getParsedBody()[$pluginNamespace] ?? [];
$bodyParameters = is_array($bodyParameters) ? $bodyParameters : [];
$parameters = is_array($getParameters) ? $getParameters : [];
ArrayUtility::mergeRecursiveWithOverrule($parameters, $bodyParameters);
} else {
$parameters = \TYPO3\CMS\Core\Utility\GeneralUtility::_GPmerged($pluginNamespace);
}
$files = $this->untangleFilesArray($_FILES);
if (is_array($files[$pluginNamespace] ?? null)) {
$parameters = array_replace_recursive($parameters, $files[$pluginNamespace]);
}
$controllerName = $this->resolveControllerName($parameters);
$actionName = $this->resolveActionName($controllerName, $parameters);
/** @var \TYPO3\CMS\Extbase\Mvc\Web\Request $request */
$request = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Request::class);
if ($this->vendorName !== null) {
$request->setControllerVendorName($this->vendorName);
}
$request->setPluginName($this->pluginName);
$request->setControllerExtensionName($this->extensionName);
$request->setControllerName($controllerName);
$request->setControllerActionName($actionName);
// @todo Use Environment
$request->setRequestUri(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL'));
$request->setBaseUri(\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SITE_URL'));
$request->setMethod($this->environmentService->getServerRequestMethod());
if (isset($parameters['format']) && is_string($parameters['format']) && $parameters['format'] !== '') {
$request->setFormat(filter_var($parameters['format'], FILTER_SANITIZE_STRING));
} else {
$request->setFormat($this->defaultFormat);
}
foreach ($parameters as $argumentName => $argumentValue) {
$request->setArgument($argumentName, $argumentValue);
}
return $request;
}
/**
* Returns the current ControllerName extracted from given $parameters.
* If no controller is specified, the defaultControllerName will be returned.
* If that's not available, an exception is thrown.
*
* @param array $parameters
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerNameException
* @throws MvcException if the controller could not be resolved
* @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
* @return string
*/
protected function resolveControllerName(array $parameters)
{
if (!isset($parameters['controller']) || $parameters['controller'] === '') {
if (empty($this->defaultControllerName)) {
throw new MvcException('The default controller for extension "' . $this->extensionName . '" and plugin "' . $this->pluginName . '" can not be determined. Please check for TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1316104317);
}
return $this->defaultControllerName;
}
$allowedControllerNames = array_keys($this->allowedControllerActions);
if (!in_array($parameters['controller'], $allowedControllerNames)) {
$configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
if (isset($configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) && (bool)$configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) {
throw new \TYPO3\CMS\Core\Error\Http\PageNotFoundException('The requested resource was not found', 1313857897);
}
if (isset($configuration['mvc']['callDefaultActionIfActionCantBeResolved']) && (bool)$configuration['mvc']['callDefaultActionIfActionCantBeResolved']) {
return $this->defaultControllerName;
}
throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidControllerNameException(
'The controller "' . $parameters['controller'] . '" is not allowed by plugin "' . $this->pluginName . '". Please check for TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.',
1313855173
);
}
return filter_var($parameters['controller'], FILTER_SANITIZE_STRING);
}
/**
* Returns the current actionName extracted from given $parameters.
* If no action is specified, the defaultActionName will be returned.
* If that's not available or the specified action is not defined in the current plugin, an exception is thrown.
*
* @param string $controllerName
* @param array $parameters
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException
* @throws MvcException
* @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
* @return string
*/
protected function resolveActionName($controllerName, array $parameters)
{
$defaultActionName = is_array($this->allowedControllerActions[$controllerName]) ? current($this->allowedControllerActions[$controllerName]) : '';
if (!isset($parameters['action']) || $parameters['action'] === '') {
if ($defaultActionName === '') {
throw new MvcException('The default action can not be determined for controller "' . $controllerName . '". Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php.', 1295479651);
}
return $defaultActionName;
}
$actionName = $parameters['action'];
$allowedActionNames = $this->allowedControllerActions[$controllerName];
if (!in_array($actionName, $allowedActionNames)) {
$configuration = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
if (isset($configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) && (bool)$configuration['mvc']['throwPageNotFoundExceptionIfActionCantBeResolved']) {
throw new \TYPO3\CMS\Core\Error\Http\PageNotFoundException('The requested resource was not found', 1313857898);
}
if (isset($configuration['mvc']['callDefaultActionIfActionCantBeResolved']) && (bool)$configuration['mvc']['callDefaultActionIfActionCantBeResolved']) {
return $defaultActionName;
}
throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidActionNameException('The action "' . $actionName . '" (controller "' . $controllerName . '") is not allowed by this plugin / module. Please check TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configurePlugin() in your ext_localconf.php / TYPO3\\CMS\\Extbase\\Utility\\ExtensionUtility::configureModule() in your ext_tables.php.', 1313855175);
}
return filter_var($actionName, FILTER_SANITIZE_STRING);
}
/**
* Transforms the convoluted _FILES superglobal into a manageable form.
*
* @param array $convolutedFiles The _FILES superglobal
* @return array Untangled files
*/
protected function untangleFilesArray(array $convolutedFiles)
{
$untangledFiles = [];
$fieldPaths = [];
foreach ($convolutedFiles as $firstLevelFieldName => $fieldInformation) {
if (!is_array($fieldInformation['error'])) {
$fieldPaths[] = [$firstLevelFieldName];
} else {
$newFieldPaths = $this->calculateFieldPaths($fieldInformation['error'], $firstLevelFieldName);
array_walk($newFieldPaths, function (&$value, $key) {
$value = explode('/', $value);
});
$fieldPaths = array_merge($fieldPaths, $newFieldPaths);
}
}
foreach ($fieldPaths as $fieldPath) {
if (count($fieldPath) === 1) {
$fileInformation = $convolutedFiles[$fieldPath[0]];
} else {
$fileInformation = [];
foreach ($convolutedFiles[$fieldPath[0]] as $key => $subStructure) {
try {
$fileInformation[$key] = ArrayUtility::getValueByPath($subStructure, array_slice($fieldPath, 1));
} catch (MissingArrayPathException $e) {
// do nothing if the path is invalid
}
}
}
$untangledFiles = ArrayUtility::setValueByPath($untangledFiles, $fieldPath, $fileInformation);
}
return $untangledFiles;
}
/**
* Returns an array of all possibles "field paths" for the given array.
*
* @param array $structure The array to walk through
* @param string $firstLevelFieldName
* @return array An array of paths (as strings) in the format "key1/key2/key3" ...
*/
protected function calculateFieldPaths(array $structure, $firstLevelFieldName = null)
{
$fieldPaths = [];
if (is_array($structure)) {
foreach ($structure as $key => $subStructure) {
$fieldPath = ($firstLevelFieldName !== null ? $firstLevelFieldName . '/' : '') . $key;
if (is_array($subStructure)) {
foreach ($this->calculateFieldPaths($subStructure) as $subFieldPath) {
$fieldPaths[] = $fieldPath . '/' . $subFieldPath;
}
} else {
$fieldPaths[] = $fieldPath;
}
}
}
return $fieldPaths;
}
}
@@ -0,0 +1,341 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web;
/*
* 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!
*/
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* A web specific response implementation
*/
class Response extends \TYPO3\CMS\Extbase\Mvc\Response
{
/**
* The HTTP headers which will be sent in the response
*
* @var array
*/
protected $headers = [];
/**
* Additional header tags
*
* @var array
*/
protected $additionalHeaderData = [];
/**
* The HTTP status code
*
* @var int
*/
protected $statusCode;
/**
* The HTTP status message
*
* @var string
*/
protected $statusMessage = 'OK';
/**
* The Request which generated the Response
*
* @var \TYPO3\CMS\Extbase\Mvc\Web\Request
*/
protected $request;
/**
* The standardized and other important HTTP Status messages
*
* @var array
*/
protected $statusMessages = [
// INFORMATIONAL CODES
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
103 => 'Early Hints',
// SUCCESS CODES
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-status',
208 => 'Already Reported',
226 => 'IM Used',
// REDIRECTION CODES
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Switch Proxy', // Deprecated
307 => 'Temporary Redirect',
308 => 'Permanent Redirect',
// CLIENT ERROR
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested range not satisfiable',
417 => 'Expectation Failed',
418 => 'I\'m a teapot',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
425 => 'Unordered Collection',
426 => 'Upgrade Required',
428 => 'Precondition Required',
429 => 'Too Many Requests',
431 => 'Request Header Fields Too Large',
451 => 'Unavailable For Legal Reasons',
// SERVER ERROR
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Time-out',
505 => 'HTTP Version not supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
508 => 'Loop Detected',
509 => 'Bandwidth Limit Exceeded',
511 => 'Network Authentication Required',
];
/**
* @var \TYPO3\CMS\Extbase\Service\EnvironmentService
*/
protected $environmentService;
/**
* @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
{
$this->environmentService = $environmentService;
}
/**
* Sets the HTTP status code and (optionally) a customized message.
*
* @param int $code The status code
* @param string $message If specified, this message is sent instead of the standard message
* @throws \InvalidArgumentException if the specified status code is not valid
*/
public function setStatus($code, $message = null)
{
if (!is_int($code)) {
throw new \InvalidArgumentException('The HTTP status code must be of type integer, ' . gettype($code) . ' given.', 1220526013);
}
if ($message === null && !isset($this->statusMessages[$code])) {
throw new \InvalidArgumentException('No message found for HTTP status code "' . $code . '".', 1220526014);
}
$this->statusCode = $code;
$this->statusMessage = $message ?? $this->statusMessages[$code];
}
/**
* Returns status code and status message.
*
* @return string The status code and status message, eg. "404 Not Found
*/
public function getStatus()
{
return $this->statusCode . ' ' . $this->statusMessage;
}
/**
* Returns the status code, if not set, uses the OK status code 200
*
* @return int
* @internal only use for backend module handling
*/
public function getStatusCode()
{
return $this->statusCode ?: 200;
}
/**
* Sets the specified HTTP header
*
* @param string $name Name of the header, for example "Location", "Content-Description" etc.
* @param mixed $value The value of the given header
* @param bool $replaceExistingHeader If a header with the same name should be replaced. Default is TRUE.
* @throws \InvalidArgumentException
*/
public function setHeader($name, $value, $replaceExistingHeader = true)
{
if (stripos($name, 'HTTP') === 0) {
throw new \InvalidArgumentException('The HTTP status header must be set via setStatus().', 1220541963);
}
if ($replaceExistingHeader === true || !isset($this->headers[$name])) {
$this->headers[$name] = [$value];
} else {
$this->headers[$name][] = $value;
}
}
/**
* Returns the HTTP headers - including the status header - of this web response
*
* @return string[] The HTTP headers
*/
public function getHeaders()
{
$preparedHeaders = [];
if ($this->statusCode !== null) {
$protocolVersion = $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0';
$statusHeader = $protocolVersion . ' ' . $this->statusCode . ' ' . $this->statusMessage;
$preparedHeaders[] = $statusHeader;
}
foreach ($this->headers as $name => $values) {
foreach ($values as $value) {
$preparedHeaders[] = $name . ': ' . $value;
}
}
return $preparedHeaders;
}
/**
* Returns the HTTP headers grouped by name without the status header
*
* @return array all headers set for this request
* @internal only used within TYPO3 Core to convert to PSR-7 response headers
*/
public function getUnpreparedHeaders(): array
{
return $this->headers;
}
/**
* Sends the HTTP headers.
*
* If headers have already been sent, this method fails silently.
*/
public function sendHeaders()
{
if (headers_sent() === true) {
return;
}
foreach ($this->getHeaders() as $header) {
header($header);
}
}
/**
* Renders and sends the whole web response
*/
public function send()
{
$this->sendHeaders();
if ($this->content !== null) {
echo $this->getContent();
}
}
/**
* Adds an additional header data (something like
* '<script src="myext/Resources/JavaScript/my.js" type="text/javascript"></script>'
* )
*
* @TODO The workround and the $request member should be removed again, once the PageRender does support non-cached USER_INTs
* @param string $additionalHeaderData The value additional header
* @throws \InvalidArgumentException
*/
public function addAdditionalHeaderData($additionalHeaderData)
{
if (!is_string($additionalHeaderData)) {
throw new \InvalidArgumentException('The additiona header data must be of type String, ' . gettype($additionalHeaderData) . ' given.', 1237370877);
}
if ($this->request->isCached()) {
/** @var PageRenderer $pageRenderer */
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
$pageRenderer->addHeaderData($additionalHeaderData);
} else {
$this->additionalHeaderData[] = $additionalHeaderData;
}
}
/**
* Returns the additional header data
*
* @return array The additional header data
*/
public function getAdditionalHeaderData()
{
return $this->additionalHeaderData;
}
/**
* @param \TYPO3\CMS\Extbase\Mvc\Web\Request $request
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setRequest(\TYPO3\CMS\Extbase\Mvc\Web\Request $request)
{
$this->request = $request;
}
/**
* @return \TYPO3\CMS\Extbase\Mvc\Web\Request
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getRequest()
{
return $this->request;
}
/**
* Sends additional headers and returns the content
*
* @return string|null
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function shutdown()
{
if (!empty($this->getAdditionalHeaderData())) {
$this->getTypoScriptFrontendController()->additionalHeaderData[] = implode(LF, $this->getAdditionalHeaderData());
}
$this->sendHeaders();
return parent::shutdown();
}
/**
* @return TypoScriptFrontendController
*/
protected function getTypoScriptFrontendController()
{
return $GLOBALS['TSFE'];
}
}
@@ -0,0 +1,841 @@
<?php
namespace TYPO3\CMS\Extbase\Mvc\Web\Routing;
/*
* 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!
*/
use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\HttpUtility;
use TYPO3\CMS\Extbase\Mvc\Request;
use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
/**
* An URI Builder
*/
class UriBuilder
{
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
/**
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
*/
protected $extensionService;
/**
* An instance of \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
*
* @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer
*/
protected $contentObject;
/**
* @var Request
*/
protected $request;
/**
* @var array
*/
protected $arguments = [];
/**
* Arguments which have been used for building the last URI
*
* @var array
*/
protected $lastArguments = [];
/**
* @var string
*/
protected $section = '';
/**
* @var bool
*/
protected $createAbsoluteUri = false;
/**
* @var string
*/
protected $absoluteUriScheme;
/**
* @var bool
*/
protected $addQueryString = false;
/**
* @var string
*/
protected $addQueryStringMethod;
/**
* @var array
*/
protected $argumentsToBeExcludedFromQueryString = [];
/**
* @var bool
*/
protected $linkAccessRestrictedPages = false;
/**
* @var int
*/
protected $targetPageUid;
/**
* @var int
*/
protected $targetPageType = 0;
/**
* @var bool
*/
protected $noCache = false;
/**
* @var bool
*/
protected $useCacheHash = true;
/**
* @var string
*/
protected $format = '';
/**
* @var string
*/
protected $argumentPrefix;
/**
* @var \TYPO3\CMS\Extbase\Service\EnvironmentService
*/
protected $environmentService;
/**
* @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectConfigurationManager(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
}
/**
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
{
$this->extensionService = $extensionService;
}
/**
* @param \TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function injectEnvironmentService(\TYPO3\CMS\Extbase\Service\EnvironmentService $environmentService)
{
$this->environmentService = $environmentService;
}
/**
* Life-cycle method that is called by the DI container as soon as this object is completely built
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function initializeObject()
{
$this->contentObject = $this->configurationManager->getContentObject();
}
/**
* Sets the current request
*
* @param Request $request
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* @return Request
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getRequest()
{
return $this->request;
}
/**
* Additional query parameters.
* If you want to "prefix" arguments, you can pass in multidimensional arrays:
* array('prefix1' => array('foo' => 'bar')) gets "&prefix1[foo]=bar"
*
* @param array $arguments
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
/**
* @return array
*/
public function getArguments()
{
return $this->arguments;
}
/**
* If specified, adds a given HTML anchor to the URI (#...)
*
* @param string $section
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setSection($section)
{
$this->section = $section;
return $this;
}
/**
* @return string
*/
public function getSection()
{
return $this->section;
}
/**
* Specifies the format of the target (e.g. "html" or "xml")
*
* @param string $format
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setFormat($format)
{
$this->format = $format;
return $this;
}
/**
* @return string
*/
public function getFormat()
{
return $this->format;
}
/**
* If set, the URI is prepended with the current base URI. Defaults to FALSE.
*
* @param bool $createAbsoluteUri
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setCreateAbsoluteUri($createAbsoluteUri)
{
$this->createAbsoluteUri = $createAbsoluteUri;
return $this;
}
/**
* @return bool
*/
public function getCreateAbsoluteUri()
{
return $this->createAbsoluteUri;
}
/**
* @return string
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getAbsoluteUriScheme()
{
return $this->absoluteUriScheme;
}
/**
* Sets the scheme that should be used for absolute URIs in FE mode
*
* @param string $absoluteUriScheme the scheme to be used for absolute URIs
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setAbsoluteUriScheme($absoluteUriScheme)
{
$this->absoluteUriScheme = $absoluteUriScheme;
return $this;
}
/**
* If set, the current query parameters will be merged with $this->arguments. Defaults to FALSE.
*
* @param bool $addQueryString
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @see TSref/typolink.addQueryString
*/
public function setAddQueryString($addQueryString)
{
$this->addQueryString = (bool)$addQueryString;
return $this;
}
/**
* @return bool
*/
public function getAddQueryString()
{
return $this->addQueryString;
}
/**
* Sets the method to get the addQueryString parameters. Defaults undefined
* which results in using QUERY_STRING.
*
* @param string $addQueryStringMethod
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @see TSref/typolink.addQueryString.method
*/
public function setAddQueryStringMethod($addQueryStringMethod)
{
$this->addQueryStringMethod = $addQueryStringMethod;
return $this;
}
/**
* @return string
*/
public function getAddQueryStringMethod()
{
return (string)$this->addQueryStringMethod;
}
/**
* A list of arguments to be excluded from the query parameters
* Only active if addQueryString is set
*
* @param array $argumentsToBeExcludedFromQueryString
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @see TSref/typolink.addQueryString.exclude
* @see setAddQueryString()
*/
public function setArgumentsToBeExcludedFromQueryString(array $argumentsToBeExcludedFromQueryString)
{
$this->argumentsToBeExcludedFromQueryString = $argumentsToBeExcludedFromQueryString;
return $this;
}
/**
* @return array
*/
public function getArgumentsToBeExcludedFromQueryString()
{
return $this->argumentsToBeExcludedFromQueryString;
}
/**
* Specifies the prefix to be used for all arguments.
*
* @param string $argumentPrefix
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function setArgumentPrefix($argumentPrefix)
{
$this->argumentPrefix = (string)$argumentPrefix;
return $this;
}
/**
* @return string
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getArgumentPrefix()
{
return $this->argumentPrefix;
}
/**
* If set, URIs for pages without access permissions will be created
*
* @param bool $linkAccessRestrictedPages
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setLinkAccessRestrictedPages($linkAccessRestrictedPages)
{
$this->linkAccessRestrictedPages = (bool)$linkAccessRestrictedPages;
return $this;
}
/**
* @return bool
*/
public function getLinkAccessRestrictedPages()
{
return $this->linkAccessRestrictedPages;
}
/**
* Uid of the target page
*
* @param int $targetPageUid
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setTargetPageUid($targetPageUid)
{
$this->targetPageUid = $targetPageUid;
return $this;
}
/**
* returns $this->targetPageUid.
*
* @return int
*/
public function getTargetPageUid()
{
return $this->targetPageUid;
}
/**
* Sets the page type of the target URI. Defaults to 0
*
* @param int $targetPageType
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setTargetPageType($targetPageType)
{
$this->targetPageType = (int)$targetPageType;
return $this;
}
/**
* @return int
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getTargetPageType()
{
return $this->targetPageType;
}
/**
* by default FALSE; if TRUE, &no_cache=1 will be appended to the URI
* This overrules the useCacheHash setting
*
* @param bool $noCache
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setNoCache($noCache)
{
$this->noCache = (bool)$noCache;
return $this;
}
/**
* @return bool
*/
public function getNoCache()
{
return $this->noCache;
}
/**
* by default TRUE; if FALSE, no cHash parameter will be appended to the URI
* If noCache is set, this setting will be ignored.
*
* @param bool $useCacheHash
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function setUseCacheHash($useCacheHash)
{
$this->useCacheHash = (bool)$useCacheHash;
return $this;
}
/**
* @return bool
*/
public function getUseCacheHash()
{
return $this->useCacheHash;
}
/**
* Returns the arguments being used for the last URI being built.
* This is only set after build() / uriFor() has been called.
*
* @return array The last arguments
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function getLastArguments()
{
return $this->lastArguments;
}
/**
* Resets all UriBuilder options to their default value
*
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder the current UriBuilder to allow method chaining
*/
public function reset()
{
$this->arguments = [];
$this->section = '';
$this->format = '';
$this->createAbsoluteUri = false;
$this->addQueryString = false;
$this->addQueryStringMethod = null;
$this->argumentsToBeExcludedFromQueryString = [];
$this->linkAccessRestrictedPages = false;
$this->targetPageUid = null;
$this->targetPageType = 0;
$this->noCache = false;
$this->useCacheHash = true;
$this->argumentPrefix = null;
return $this;
}
/**
* Creates an URI used for linking to an Extbase action.
* Works in Frontend and Backend mode of TYPO3.
*
* @param string $actionName Name of the action to be called
* @param array $controllerArguments Additional query parameters. Will be "namespaced" and merged with $this->arguments.
* @param string $controllerName Name of the target controller. If not set, current ControllerName is used.
* @param string $extensionName Name of the target extension, without underscores. If not set, current ExtensionName is used.
* @param string $pluginName Name of the target plugin. If not set, current PluginName is used.
* @return string the rendered URI
* @see build()
*/
public function uriFor($actionName = null, $controllerArguments = [], $controllerName = null, $extensionName = null, $pluginName = null)
{
if ($actionName !== null) {
$controllerArguments['action'] = $actionName;
}
if ($controllerName !== null) {
$controllerArguments['controller'] = $controllerName;
} else {
$controllerArguments['controller'] = $this->request->getControllerName();
}
if ($extensionName === null) {
$extensionName = $this->request->getControllerExtensionName();
}
if ($pluginName === null && $this->environmentService->isEnvironmentInFrontendMode()) {
$pluginName = $this->extensionService->getPluginNameByAction($extensionName, $controllerArguments['controller'], $controllerArguments['action']);
}
if ($pluginName === null) {
$pluginName = $this->request->getPluginName();
}
if ($this->environmentService->isEnvironmentInFrontendMode() && $this->configurationManager->isFeatureEnabled('skipDefaultArguments')) {
$controllerArguments = $this->removeDefaultControllerAndAction($controllerArguments, $extensionName, $pluginName);
}
if ($this->targetPageUid === null && $this->environmentService->isEnvironmentInFrontendMode()) {
$this->targetPageUid = $this->extensionService->getTargetPidByPlugin($extensionName, $pluginName);
}
if ($this->format !== '') {
$controllerArguments['format'] = $this->format;
}
if ($this->argumentPrefix !== null) {
$prefixedControllerArguments = [$this->argumentPrefix => $controllerArguments];
} else {
$pluginNamespace = $this->extensionService->getPluginNamespace($extensionName, $pluginName);
$prefixedControllerArguments = [$pluginNamespace => $controllerArguments];
}
ArrayUtility::mergeRecursiveWithOverrule($this->arguments, $prefixedControllerArguments);
return $this->build();
}
/**
* This removes controller and/or action arguments from given controllerArguments
* if they are equal to the default controller/action of the target plugin.
* Note: This is only active in FE mode and if feature "skipDefaultArguments" is enabled
*
* @see \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::isFeatureEnabled()
* @param array $controllerArguments the current controller arguments to be modified
* @param string $extensionName target extension name
* @param string $pluginName target plugin name
* @return array
*/
protected function removeDefaultControllerAndAction(array $controllerArguments, $extensionName, $pluginName)
{
$defaultControllerName = $this->extensionService->getDefaultControllerNameByPlugin($extensionName, $pluginName);
if (isset($controllerArguments['action'])) {
$defaultActionName = $this->extensionService->getDefaultActionNameByPluginAndController($extensionName, $pluginName, $controllerArguments['controller']);
if ($controllerArguments['action'] === $defaultActionName) {
unset($controllerArguments['action']);
}
}
if ($controllerArguments['controller'] === $defaultControllerName) {
unset($controllerArguments['controller']);
}
return $controllerArguments;
}
/**
* Builds the URI
* Depending on the current context this calls buildBackendUri() or buildFrontendUri()
*
* @return string The URI
* @see buildBackendUri()
* @see buildFrontendUri()
*/
public function build()
{
if ($this->environmentService->isEnvironmentInBackendMode()) {
return $this->buildBackendUri();
}
return $this->buildFrontendUri();
}
/**
* Builds the URI, backend flavour
* The resulting URI is relative and starts with "index.php".
* The settings pageUid, pageType, noCache, useCacheHash & linkAccessRestrictedPages
* will be ignored in the backend.
*
* @return string The URI
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function buildBackendUri()
{
$arguments = [];
if ($this->addQueryString === true) {
if ($this->addQueryStringMethod) {
switch ($this->addQueryStringMethod) {
case 'GET':
$arguments = GeneralUtility::_GET();
break;
case 'POST':
$arguments = GeneralUtility::_POST();
break;
case 'GET,POST':
$arguments = array_replace_recursive(GeneralUtility::_GET(), GeneralUtility::_POST());
break;
case 'POST,GET':
$arguments = array_replace_recursive(GeneralUtility::_POST(), GeneralUtility::_GET());
break;
default:
// Explode GET vars recursively
parse_str(GeneralUtility::getIndpEnv('QUERY_STRING'), $arguments);
}
} else {
$arguments = GeneralUtility::_GET();
}
foreach ($this->argumentsToBeExcludedFromQueryString as $argumentToBeExcluded) {
$argumentArrayToBeExcluded = [];
parse_str($argumentToBeExcluded, $argumentArrayToBeExcluded);
$arguments = ArrayUtility::arrayDiffAssocRecursive($arguments, $argumentArrayToBeExcluded);
}
} else {
$id = GeneralUtility::_GP('id');
// backwards compatibility: check for M parameter
// @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
$route = GeneralUtility::_GP('route') ?: GeneralUtility::_GP('M');
if ($id !== null) {
$arguments['id'] = $id;
}
if ($route !== null) {
$arguments['route'] = $route;
}
}
ArrayUtility::mergeRecursiveWithOverrule($arguments, $this->arguments);
$arguments = $this->convertDomainObjectsToIdentityArrays($arguments);
$this->lastArguments = $arguments;
$routeName = $arguments['route'] ?? null;
unset($arguments['route'], $arguments['token']);
$backendUriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
try {
if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
$uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
} else {
$uri = (string)$backendUriBuilder->buildUriFromRoutePath($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
}
} catch (ResourceNotFoundException $e) {
try {
if ($this->request instanceof WebRequest && $this->createAbsoluteUri) {
$uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_URL);
} else {
$uri = (string)$backendUriBuilder->buildUriFromRoute($routeName, $arguments, \TYPO3\CMS\Backend\Routing\UriBuilder::ABSOLUTE_PATH);
}
} catch (RouteNotFoundException $e) {
$uri = '';
}
}
if ($this->section !== '') {
$uri .= '#' . $this->section;
}
return $uri;
}
/**
* Builds the URI, frontend flavour
*
* @return string The URI
* @see buildTypolinkConfiguration()
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function buildFrontendUri()
{
$typolinkConfiguration = $this->buildTypolinkConfiguration();
if ($this->createAbsoluteUri === true) {
$typolinkConfiguration['forceAbsoluteUrl'] = true;
if ($this->absoluteUriScheme !== null) {
$typolinkConfiguration['forceAbsoluteUrl.']['scheme'] = $this->absoluteUriScheme;
}
}
$uri = $this->contentObject->typoLink_URL($typolinkConfiguration);
return $uri;
}
/**
* Builds a TypoLink configuration array from the current settings
*
* @return array typolink configuration array
* @see TSref/typolink
*/
protected function buildTypolinkConfiguration()
{
$typolinkConfiguration = [];
$typolinkConfiguration['parameter'] = $this->targetPageUid ?? $GLOBALS['TSFE']->id;
if ($this->targetPageType !== 0) {
$typolinkConfiguration['parameter'] .= ',' . $this->targetPageType;
} elseif ($this->format !== '') {
$targetPageType = $this->extensionService->getTargetPageTypeByFormat($this->request->getControllerExtensionName(), $this->format);
$typolinkConfiguration['parameter'] .= ',' . $targetPageType;
}
if (!empty($this->arguments)) {
$arguments = $this->convertDomainObjectsToIdentityArrays($this->arguments);
$this->lastArguments = $arguments;
$typolinkConfiguration['additionalParams'] = HttpUtility::buildQueryString($arguments, '&');
}
if ($this->addQueryString === true) {
$typolinkConfiguration['addQueryString'] = 1;
if (!empty($this->argumentsToBeExcludedFromQueryString)) {
$typolinkConfiguration['addQueryString.'] = [
'exclude' => implode(',', $this->argumentsToBeExcludedFromQueryString)
];
}
if ($this->addQueryStringMethod) {
$typolinkConfiguration['addQueryString.']['method'] = $this->addQueryStringMethod;
}
}
if ($this->noCache === true) {
$typolinkConfiguration['no_cache'] = 1;
} elseif ($this->useCacheHash) {
$typolinkConfiguration['useCacheHash'] = 1;
}
if ($this->section !== '') {
$typolinkConfiguration['section'] = $this->section;
}
if ($this->linkAccessRestrictedPages === true) {
$typolinkConfiguration['linkAccessRestrictedPages'] = 1;
}
return $typolinkConfiguration;
}
/**
* Recursively iterates through the specified arguments and turns instances of type \TYPO3\CMS\Extbase\DomainObject\AbstractEntity
* into an arrays containing the uid of the domain object.
*
* @param array $arguments The arguments to be iterated
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException
* @return array The modified arguments array
*/
protected function convertDomainObjectsToIdentityArrays(array $arguments)
{
foreach ($arguments as $argumentKey => $argumentValue) {
// if we have a LazyLoadingProxy here, make sure to get the real instance for further processing
if ($argumentValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
$argumentValue = $argumentValue->_loadRealInstance();
// also update the value in the arguments array, because the lazyLoaded object could be
// hidden and thus the $argumentValue would be NULL.
$arguments[$argumentKey] = $argumentValue;
}
if ($argumentValue instanceof \Iterator) {
$argumentValue = $this->convertIteratorToArray($argumentValue);
}
if ($argumentValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
if ($argumentValue->getUid() !== null) {
$arguments[$argumentKey] = $argumentValue->getUid();
} elseif ($argumentValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
$arguments[$argumentKey] = $this->convertTransientObjectToArray($argumentValue);
} else {
throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException('Could not serialize Domain Object ' . get_class($argumentValue) . '. It is neither an Entity with identity properties set, nor a Value Object.', 1260881688);
}
} elseif (is_array($argumentValue)) {
$arguments[$argumentKey] = $this->convertDomainObjectsToIdentityArrays($argumentValue);
}
}
return $arguments;
}
/**
* @param \Iterator $iterator
* @return array
*/
protected function convertIteratorToArray(\Iterator $iterator)
{
if (method_exists($iterator, 'toArray')) {
$array = $iterator->toArray();
} else {
$array = iterator_to_array($iterator);
}
return $array;
}
/**
* Converts a given object recursively into an array.
*
* @param \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject $object
* @return array
* @todo Refactore this into convertDomainObjectsToIdentityArrays()
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
public function convertTransientObjectToArray(\TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject $object)
{
$result = [];
foreach ($object->_getProperties() as $propertyName => $propertyValue) {
if ($propertyValue instanceof \Iterator) {
$propertyValue = $this->convertIteratorToArray($propertyValue);
}
if ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject) {
if ($propertyValue->getUid() !== null) {
$result[$propertyName] = $propertyValue->getUid();
} else {
$result[$propertyName] = $this->convertTransientObjectToArray($propertyValue);
}
} elseif (is_array($propertyValue)) {
$result[$propertyName] = $this->convertDomainObjectsToIdentityArrays($propertyValue);
} else {
$result[$propertyName] = $propertyValue;
}
}
return $result;
}
}