first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
@@ -0,0 +1,418 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
|
||||
|
||||
/**
|
||||
* An abstract base class for Controllers
|
||||
*/
|
||||
abstract class AbstractController implements ControllerInterface
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
|
||||
*/
|
||||
protected $signalSlotDispatcher;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
|
||||
*/
|
||||
protected $uriBuilder;
|
||||
|
||||
/**
|
||||
* @var string Key of the extension this controller belongs to
|
||||
*/
|
||||
protected $extensionName;
|
||||
|
||||
/**
|
||||
* Contains the settings of the current extension
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $settings;
|
||||
|
||||
/**
|
||||
* The current request.
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\RequestInterface
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The response which will be returned by this action controller
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\ResponseInterface
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver
|
||||
*/
|
||||
protected $validatorResolver;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Controller\Arguments Arguments passed to the controller
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
|
||||
*/
|
||||
public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
|
||||
{
|
||||
$this->signalSlotDispatcher = $signalSlotDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver
|
||||
*/
|
||||
public function injectValidatorResolver(\TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver)
|
||||
{
|
||||
$this->validatorResolver = $validatorResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* An array of supported request types. By default only web requests are supported.
|
||||
* Modify or replace this array if your specific controller supports certain
|
||||
* (additional) request types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $supportedRequestTypes = [\TYPO3\CMS\Extbase\Mvc\Request::class];
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
|
||||
*/
|
||||
protected $controllerContext;
|
||||
|
||||
/**
|
||||
* @return ControllerContext
|
||||
*/
|
||||
public function getControllerContext()
|
||||
{
|
||||
return $this->controllerContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var ConfigurationManagerInterface
|
||||
*/
|
||||
protected $configurationManager;
|
||||
|
||||
/**
|
||||
* Constructs the controller.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$className = static::class;
|
||||
$classNameParts = explode('\\', $className, 4);
|
||||
// Skip vendor and product name for core classes
|
||||
if (strpos($className, 'TYPO3\\CMS\\') === 0) {
|
||||
$this->extensionName = $classNameParts[2];
|
||||
} else {
|
||||
$this->extensionName = $classNameParts[1];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ConfigurationManagerInterface $configurationManager
|
||||
*/
|
||||
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
|
||||
{
|
||||
$this->configurationManager = $configurationManager;
|
||||
$this->settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects the object manager
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
$this->arguments = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Message object and adds it to the FlashMessageQueue.
|
||||
*
|
||||
* @param string $messageBody The message
|
||||
* @param string $messageTitle Optional message title
|
||||
* @param int $severity Optional severity, must be one of \TYPO3\CMS\Core\Messaging\FlashMessage constants
|
||||
* @param bool $storeInSession Optional, defines whether the message should be stored in the session (default) or not
|
||||
* @throws \InvalidArgumentException if the message body is no string
|
||||
* @see \TYPO3\CMS\Core\Messaging\FlashMessage
|
||||
*/
|
||||
public function addFlashMessage($messageBody, $messageTitle = '', $severity = \TYPO3\CMS\Core\Messaging\AbstractMessage::OK, $storeInSession = true)
|
||||
{
|
||||
if (!is_string($messageBody)) {
|
||||
throw new \InvalidArgumentException('The message body must be of type string, "' . gettype($messageBody) . '" given.', 1243258395);
|
||||
}
|
||||
/* @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
|
||||
$flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
|
||||
\TYPO3\CMS\Core\Messaging\FlashMessage::class,
|
||||
(string)$messageBody,
|
||||
(string)$messageTitle,
|
||||
$severity,
|
||||
$storeInSession
|
||||
);
|
||||
$this->controllerContext->getFlashMessageQueue()->enqueue($flashMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current request type is supported by the controller.
|
||||
*
|
||||
* If your controller only supports certain request types, either
|
||||
* replace / modify the supportedRequestTypes property or override this
|
||||
* method.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
|
||||
* @return bool TRUE if this request type is supported, otherwise FALSE
|
||||
*/
|
||||
public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
|
||||
{
|
||||
foreach ($this->supportedRequestTypes as $supportedRequestType) {
|
||||
if ($request instanceof $supportedRequestType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a general request. The result can be returned by altering the given response.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler
|
||||
* @throws UnsupportedRequestTypeException if the controller doesn't support the current request type
|
||||
*/
|
||||
public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
|
||||
{
|
||||
if (!$this->canProcessRequest($request)) {
|
||||
throw new UnsupportedRequestTypeException(static::class . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes), 1187701132);
|
||||
}
|
||||
if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
|
||||
$response->setRequest($request);
|
||||
}
|
||||
$this->request = $request;
|
||||
$this->request->setDispatched(true);
|
||||
$this->response = $response;
|
||||
$this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
|
||||
$this->uriBuilder->setRequest($request);
|
||||
$this->initializeControllerArgumentsBaseValidators();
|
||||
$this->mapRequestArgumentsToControllerArguments();
|
||||
$this->controllerContext = $this->buildControllerContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the controller context
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext ControllerContext to be passed to the view
|
||||
*/
|
||||
protected function buildControllerContext()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext */
|
||||
$controllerContext = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext::class);
|
||||
$controllerContext->setRequest($this->request);
|
||||
$controllerContext->setResponse($this->response);
|
||||
if ($this->arguments !== null) {
|
||||
$controllerContext->setArguments($this->arguments);
|
||||
}
|
||||
$controllerContext->setUriBuilder($this->uriBuilder);
|
||||
|
||||
return $controllerContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the request to another action and / or controller.
|
||||
*
|
||||
* Request is directly transferred to the other action / controller
|
||||
* without the need for a new request.
|
||||
*
|
||||
* @param string $actionName Name of the action to forward to
|
||||
* @param string|null $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
|
||||
* @param string|null $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
|
||||
* @param array|null $arguments Arguments to pass to the target action
|
||||
* @throws StopActionException
|
||||
* @see redirect()
|
||||
*/
|
||||
public function forward($actionName, $controllerName = null, $extensionName = null, array $arguments = null)
|
||||
{
|
||||
$this->request->setDispatched(false);
|
||||
if ($this->request instanceof WebRequest) {
|
||||
$this->request->setControllerActionName($actionName);
|
||||
if ($controllerName !== null) {
|
||||
$this->request->setControllerName($controllerName);
|
||||
}
|
||||
if ($extensionName !== null) {
|
||||
$this->request->setControllerExtensionName($extensionName);
|
||||
}
|
||||
}
|
||||
if ($arguments !== null) {
|
||||
$this->request->setArguments($arguments);
|
||||
}
|
||||
throw new StopActionException('forward', 1476045801);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the request to another action and / or controller.
|
||||
*
|
||||
* Redirect will be sent to the client which then performs another request to the new URI.
|
||||
*
|
||||
* NOTE: This method only supports web requests and will thrown an exception
|
||||
* if used with other request types.
|
||||
*
|
||||
* @param string $actionName Name of the action to forward to
|
||||
* @param string|null $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
|
||||
* @param string|null $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
|
||||
* @param array|null $arguments Arguments to pass to the target action
|
||||
* @param int|null $pageUid Target page uid. If NULL, the current page uid is used
|
||||
* @param int $delay (optional) The delay in seconds. Default is no delay.
|
||||
* @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
|
||||
* @throws UnsupportedRequestTypeException If the request is not a web request
|
||||
* @throws StopActionException
|
||||
* @see forward()
|
||||
*/
|
||||
protected function redirect($actionName, $controllerName = null, $extensionName = null, array $arguments = null, $pageUid = null, $delay = 0, $statusCode = 303)
|
||||
{
|
||||
if (!$this->request instanceof WebRequest) {
|
||||
throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539734);
|
||||
}
|
||||
if ($controllerName === null) {
|
||||
$controllerName = $this->request->getControllerName();
|
||||
}
|
||||
$this->uriBuilder->reset()->setTargetPageUid($pageUid)->setCreateAbsoluteUri(true);
|
||||
if (\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SSL')) {
|
||||
$this->uriBuilder->setAbsoluteUriScheme('https');
|
||||
}
|
||||
$uri = $this->uriBuilder->uriFor($actionName, $arguments, $controllerName, $extensionName);
|
||||
$this->redirectToUri($uri, $delay, $statusCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the web request to another uri.
|
||||
*
|
||||
* NOTE: This method only supports web requests and will thrown an exception if used with other request types.
|
||||
*
|
||||
* @param mixed $uri A string representation of a URI
|
||||
* @param int $delay (optional) The delay in seconds. Default is no delay.
|
||||
* @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
|
||||
* @throws UnsupportedRequestTypeException If the request is not a web request
|
||||
* @throws StopActionException
|
||||
*/
|
||||
protected function redirectToUri($uri, $delay = 0, $statusCode = 303)
|
||||
{
|
||||
if (!$this->request instanceof WebRequest) {
|
||||
throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539735);
|
||||
}
|
||||
|
||||
$this->objectManager->get(\TYPO3\CMS\Extbase\Service\CacheService::class)->clearCachesOfRegisteredPageIds();
|
||||
|
||||
$uri = $this->addBaseUriIfNecessary($uri);
|
||||
$escapedUri = htmlentities($uri, ENT_QUOTES, 'utf-8');
|
||||
$this->response->setContent('<html><head><meta http-equiv="refresh" content="' . (int)$delay . ';url=' . $escapedUri . '"/></head></html>');
|
||||
if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
|
||||
$this->response->setStatus($statusCode);
|
||||
$this->response->setHeader('Location', (string)$uri);
|
||||
}
|
||||
// Avoid caching the plugin when we issue a redirect response
|
||||
// This means that even when an action is configured as cachable
|
||||
// we avoid the plugin to be cached, but keep the page cache untouched
|
||||
$contentObject = $this->configurationManager->getContentObject();
|
||||
if ($contentObject->getUserObjectType() === \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::OBJECTTYPE_USER) {
|
||||
$contentObject->convertToUserIntObject();
|
||||
}
|
||||
|
||||
throw new StopActionException('redirectToUri', 1476045828);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the base uri if not already in place.
|
||||
*
|
||||
* @param string $uri The URI
|
||||
* @return string
|
||||
*/
|
||||
protected function addBaseUriIfNecessary($uri)
|
||||
{
|
||||
return \TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl((string)$uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the specified HTTP status immediately.
|
||||
*
|
||||
* NOTE: This method only supports web requests and will thrown an exception if used with other request types.
|
||||
*
|
||||
* @param int $statusCode The HTTP status code
|
||||
* @param string $statusMessage A custom HTTP status message
|
||||
* @param string $content Body content which further explains the status
|
||||
* @throws UnsupportedRequestTypeException If the request is not a web request
|
||||
* @throws StopActionException
|
||||
*/
|
||||
public function throwStatus($statusCode, $statusMessage = null, $content = null)
|
||||
{
|
||||
if (!$this->request instanceof WebRequest) {
|
||||
throw new UnsupportedRequestTypeException('throwStatus() only supports web requests.', 1220539739);
|
||||
}
|
||||
if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
|
||||
$this->response->setStatus($statusCode, $statusMessage);
|
||||
if ($content === null) {
|
||||
$content = $this->response->getStatus();
|
||||
}
|
||||
}
|
||||
$this->response->setContent($content);
|
||||
throw new StopActionException('throwStatus', 1476045871);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the base validators which were defined for the data type of each
|
||||
* controller argument and adds them to the argument's validator chain.
|
||||
*/
|
||||
public function initializeControllerArgumentsBaseValidators()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
$validator = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
|
||||
if ($validator !== null) {
|
||||
$argument->setValidator($validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps arguments delivered by the request object to the local controller arguments.
|
||||
*
|
||||
* @throws Exception\RequiredArgumentMissingException
|
||||
*/
|
||||
protected function mapRequestArgumentsToControllerArguments()
|
||||
{
|
||||
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
$argumentName = $argument->getName();
|
||||
if ($this->request->hasArgument($argumentName)) {
|
||||
$argument->setValue($this->request->getArgument($argumentName));
|
||||
} elseif ($argument->isRequired()) {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException('Required argument "' . $argumentName . '" is not set for ' . $this->request->getControllerObjectName() . '->' . $this->request->getControllerActionName() . '.', 1298012500);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
|
||||
use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator;
|
||||
use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface;
|
||||
use TYPO3Fluid\Fluid\View\TemplateView;
|
||||
|
||||
/**
|
||||
* A multi action controller. This is by far the most common base class for Controllers.
|
||||
*/
|
||||
class ActionController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
|
||||
*/
|
||||
protected $reflectionService;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Service\CacheService
|
||||
*/
|
||||
protected $cacheService;
|
||||
|
||||
/**
|
||||
* The current view, as resolved by resolveView()
|
||||
*
|
||||
* @var ViewInterface
|
||||
*/
|
||||
protected $view;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $namespacesViewObjectNamePattern = '@vendor\@extension\View\@controller\@action@format';
|
||||
|
||||
/**
|
||||
* A list of formats and object names of the views which should render them.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* array('html' => 'Tx_MyExtension_View_MyHtmlView', 'json' => 'F3...
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $viewFormatToObjectNameMap = [];
|
||||
|
||||
/**
|
||||
* The default view object to use if none of the resolved views can render
|
||||
* a response for the current request.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $defaultViewObjectName = \TYPO3\CMS\Fluid\View\TemplateView::class;
|
||||
|
||||
/**
|
||||
* Name of the action method
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $actionMethodName = 'indexAction';
|
||||
|
||||
/**
|
||||
* Name of the special error action method which is called in case of errors
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $errorMethodName = 'errorAction';
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService
|
||||
*/
|
||||
protected $mvcPropertyMappingConfigurationService;
|
||||
|
||||
/**
|
||||
* The current request.
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* The response which will be returned by this action controller
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
|
||||
*/
|
||||
public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService)
|
||||
{
|
||||
$this->reflectionService = $reflectionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Service\CacheService $cacheService
|
||||
*/
|
||||
public function injectCacheService(\TYPO3\CMS\Extbase\Service\CacheService $cacheService)
|
||||
{
|
||||
$this->cacheService = $cacheService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService
|
||||
*/
|
||||
public function injectMvcPropertyMappingConfigurationService(\TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfigurationService $mvcPropertyMappingConfigurationService)
|
||||
{
|
||||
$this->mvcPropertyMappingConfigurationService = $mvcPropertyMappingConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a request. The result output is returned by altering the given response.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler
|
||||
*
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException
|
||||
*/
|
||||
public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
|
||||
{
|
||||
if (!$this->canProcessRequest($request)) {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException(static::class . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes), 1187701131);
|
||||
}
|
||||
|
||||
if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
|
||||
$response->setRequest($request);
|
||||
}
|
||||
$this->request = $request;
|
||||
$this->request->setDispatched(true);
|
||||
$this->response = $response;
|
||||
$this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
|
||||
$this->uriBuilder->setRequest($request);
|
||||
$this->actionMethodName = $this->resolveActionMethodName();
|
||||
$this->initializeActionMethodArguments();
|
||||
$this->initializeActionMethodValidators();
|
||||
$this->mvcPropertyMappingConfigurationService->initializePropertyMappingConfigurationFromRequest($request, $this->arguments);
|
||||
$this->initializeAction();
|
||||
$actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
|
||||
if (method_exists($this, $actionInitializationMethodName)) {
|
||||
call_user_func([$this, $actionInitializationMethodName]);
|
||||
}
|
||||
$this->mapRequestArgumentsToControllerArguments();
|
||||
$this->controllerContext = $this->buildControllerContext();
|
||||
$this->view = $this->resolveView();
|
||||
if ($this->view !== null) {
|
||||
$this->initializeView($this->view);
|
||||
}
|
||||
$this->callActionMethod();
|
||||
$this->renderAssetsForRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method which initializes assets that should be attached to the response
|
||||
* for the given $request, which contains parameters that an override can
|
||||
* use to determine which assets to add via PageRenderer.
|
||||
*
|
||||
* This default implementation will attempt to render the sections "HeaderAssets"
|
||||
* and "FooterAssets" from the template that is being rendered, inserting the
|
||||
* rendered content into either page header or footer, as appropriate. Both
|
||||
* sections are optional and can be used one or both in combination.
|
||||
*
|
||||
* You can add assets with this method without worrying about duplicates, if
|
||||
* for example you do this in a plugin that gets used multiple time on a page.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
|
||||
*/
|
||||
protected function renderAssetsForRequest($request)
|
||||
{
|
||||
if (!$this->view instanceof TemplateView) {
|
||||
// Only TemplateView (from Fluid engine, so this includes all TYPO3 Views based
|
||||
// on TYPO3's AbstractTemplateView) supports renderSection(). The method is not
|
||||
// declared on ViewInterface - so we must assert a specific class. We silently skip
|
||||
// asset processing if the View doesn't match, so we don't risk breaking custom Views.
|
||||
return;
|
||||
}
|
||||
$pageRenderer = $this->objectManager->get(PageRenderer::class);
|
||||
$variables = ['request' => $request, 'arguments' => $this->arguments];
|
||||
$headerAssets = $this->view->renderSection('HeaderAssets', $variables, true);
|
||||
$footerAssets = $this->view->renderSection('FooterAssets', $variables, true);
|
||||
if (!empty(trim($headerAssets))) {
|
||||
$pageRenderer->addHeaderData($headerAssets);
|
||||
}
|
||||
if (!empty(trim($footerAssets))) {
|
||||
$pageRenderer->addFooterData($footerAssets);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the arguments initilization in the action controller:
|
||||
* Automatically registers arguments of the current action
|
||||
*
|
||||
* Don't override this method - use initializeAction() instead.
|
||||
*
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException
|
||||
* @see initializeArguments()
|
||||
*/
|
||||
protected function initializeActionMethodArguments()
|
||||
{
|
||||
$methodParameters = $this->reflectionService
|
||||
->getClassSchema(static::class)
|
||||
->getMethod($this->actionMethodName)['params'] ?? [];
|
||||
|
||||
foreach ($methodParameters as $parameterName => $parameterInfo) {
|
||||
$dataType = null;
|
||||
if (isset($parameterInfo['type'])) {
|
||||
$dataType = $parameterInfo['type'];
|
||||
} elseif ($parameterInfo['array']) {
|
||||
$dataType = 'array';
|
||||
}
|
||||
if ($dataType === null) {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException('The argument type for parameter $' . $parameterName . ' of method ' . static::class . '->' . $this->actionMethodName . '() could not be detected.', 1253175643);
|
||||
}
|
||||
$defaultValue = $parameterInfo['hasDefaultValue'] === true ? $parameterInfo['defaultValue'] : null;
|
||||
$this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the needed validators to the Arguments:
|
||||
*
|
||||
* - Validators checking the data type from the @param annotation
|
||||
* - Custom validators specified with validate annotations.
|
||||
* - Model-based validators (validate annotations in the model)
|
||||
* - Custom model validator classes
|
||||
*/
|
||||
protected function initializeActionMethodValidators()
|
||||
{
|
||||
if ($this->arguments->count() === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$classSchema = $this->reflectionService->getClassSchema(static::class);
|
||||
|
||||
$ignoreValidationAnnotations = array_unique(array_flip(
|
||||
$classSchema->getMethod($this->actionMethodName)['tags']['ignorevalidation'] ?? []
|
||||
));
|
||||
|
||||
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
if (isset($ignoreValidationAnnotations[$argument->getName()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$validator = $this->objectManager->get(ConjunctionValidator::class);
|
||||
$validatorDefinitions = $classSchema->getMethod($this->actionMethodName)['params'][$argument->getName()]['validators'] ?? [];
|
||||
|
||||
foreach ($validatorDefinitions as $validatorDefinition) {
|
||||
/** @var ValidatorInterface $validatorInstance */
|
||||
$validatorInstance = $this->objectManager->get(
|
||||
$validatorDefinition['className'],
|
||||
$validatorDefinition['options']
|
||||
);
|
||||
|
||||
$validator->addValidator(
|
||||
$validatorInstance
|
||||
);
|
||||
}
|
||||
|
||||
$baseValidatorConjunction = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
|
||||
if ($baseValidatorConjunction->count() > 0) {
|
||||
$validator->addValidator($baseValidatorConjunction);
|
||||
}
|
||||
$argument->setValidator($validator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and checks the current action method name
|
||||
*
|
||||
* @return string Method name of the current action
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchActionException if the action specified in the request object does not exist (and if there's no default action either).
|
||||
*/
|
||||
protected function resolveActionMethodName()
|
||||
{
|
||||
$actionMethodName = $this->request->getControllerActionName() . 'Action';
|
||||
if (!method_exists($this, $actionMethodName)) {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchActionException('An action "' . $actionMethodName . '" does not exist in controller "' . static::class . '".', 1186669086);
|
||||
}
|
||||
return $actionMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified action method and passes the arguments.
|
||||
*
|
||||
* If the action returns a string, it is appended to the content in the
|
||||
* response object. If the action doesn't return anything and a valid
|
||||
* view exists, the view is rendered automatically.
|
||||
*/
|
||||
protected function callActionMethod()
|
||||
{
|
||||
$preparedArguments = [];
|
||||
/** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
$preparedArguments[] = $argument->getValue();
|
||||
}
|
||||
$validationResult = $this->arguments->validate();
|
||||
if (!$validationResult->hasErrors()) {
|
||||
$this->emitBeforeCallActionMethodSignal($preparedArguments);
|
||||
$actionResult = $this->{$this->actionMethodName}(...$preparedArguments);
|
||||
} else {
|
||||
$actionResult = $this->{$this->errorMethodName}();
|
||||
}
|
||||
|
||||
if ($actionResult === null && $this->view instanceof ViewInterface) {
|
||||
$this->response->appendContent($this->view->render());
|
||||
} elseif (is_string($actionResult) && $actionResult !== '') {
|
||||
$this->response->appendContent($actionResult);
|
||||
} elseif (is_object($actionResult) && method_exists($actionResult, '__toString')) {
|
||||
$this->response->appendContent((string)$actionResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits a signal before the current action is called
|
||||
*
|
||||
* @param array $preparedArguments
|
||||
*/
|
||||
protected function emitBeforeCallActionMethodSignal(array $preparedArguments)
|
||||
{
|
||||
$this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeCallActionMethod', [static::class, $this->actionMethodName, $preparedArguments]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a view for the current action.
|
||||
* By default, this method tries to locate a view with a name matching the current action.
|
||||
*
|
||||
* @return ViewInterface
|
||||
*/
|
||||
protected function resolveView()
|
||||
{
|
||||
$viewObjectName = $this->resolveViewObjectName();
|
||||
if ($viewObjectName !== false) {
|
||||
/** @var ViewInterface $view */
|
||||
$view = $this->objectManager->get($viewObjectName);
|
||||
$this->setViewConfiguration($view);
|
||||
if ($view->canRender($this->controllerContext) === false) {
|
||||
unset($view);
|
||||
}
|
||||
}
|
||||
if (!isset($view) && $this->defaultViewObjectName != '') {
|
||||
/** @var ViewInterface $view */
|
||||
$view = $this->objectManager->get($this->defaultViewObjectName);
|
||||
$this->setViewConfiguration($view);
|
||||
if ($view->canRender($this->controllerContext) === false) {
|
||||
unset($view);
|
||||
}
|
||||
}
|
||||
if (!isset($view)) {
|
||||
$view = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\View\NotFoundView::class);
|
||||
$view->assign('errorMessage', 'No template was found. View could not be resolved for action "'
|
||||
. $this->request->getControllerActionName() . '" in class "' . $this->request->getControllerObjectName() . '"');
|
||||
}
|
||||
$view->setControllerContext($this->controllerContext);
|
||||
if (method_exists($view, 'injectSettings')) {
|
||||
$view->injectSettings($this->settings);
|
||||
}
|
||||
$view->initializeView();
|
||||
// In TYPO3.Flow, solved through Object Lifecycle methods, we need to call it explicitly
|
||||
$view->assign('settings', $this->settings);
|
||||
// same with settings injection.
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ViewInterface $view
|
||||
*/
|
||||
protected function setViewConfiguration(ViewInterface $view)
|
||||
{
|
||||
// Template Path Override
|
||||
$extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(
|
||||
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK
|
||||
);
|
||||
|
||||
// set TemplateRootPaths
|
||||
$viewFunctionName = 'setTemplateRootPaths';
|
||||
if (method_exists($view, $viewFunctionName)) {
|
||||
$setting = 'templateRootPaths';
|
||||
$parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
|
||||
// no need to bother if there is nothing to set
|
||||
if ($parameter) {
|
||||
$view->$viewFunctionName($parameter);
|
||||
}
|
||||
}
|
||||
|
||||
// set LayoutRootPaths
|
||||
$viewFunctionName = 'setLayoutRootPaths';
|
||||
if (method_exists($view, $viewFunctionName)) {
|
||||
$setting = 'layoutRootPaths';
|
||||
$parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
|
||||
// no need to bother if there is nothing to set
|
||||
if ($parameter) {
|
||||
$view->$viewFunctionName($parameter);
|
||||
}
|
||||
}
|
||||
|
||||
// set PartialRootPaths
|
||||
$viewFunctionName = 'setPartialRootPaths';
|
||||
if (method_exists($view, $viewFunctionName)) {
|
||||
$setting = 'partialRootPaths';
|
||||
$parameter = $this->getViewProperty($extbaseFrameworkConfiguration, $setting);
|
||||
// no need to bother if there is nothing to set
|
||||
if ($parameter) {
|
||||
$view->$viewFunctionName($parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the path resolving for *rootPath(s)
|
||||
*
|
||||
* numerical arrays get ordered by key ascending
|
||||
*
|
||||
* @param array $extbaseFrameworkConfiguration
|
||||
* @param string $setting parameter name from TypoScript
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getViewProperty($extbaseFrameworkConfiguration, $setting)
|
||||
{
|
||||
$values = [];
|
||||
if (
|
||||
!empty($extbaseFrameworkConfiguration['view'][$setting])
|
||||
&& is_array($extbaseFrameworkConfiguration['view'][$setting])
|
||||
) {
|
||||
$values = $extbaseFrameworkConfiguration['view'][$setting];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the fully qualified view object name.
|
||||
*
|
||||
* @return mixed The fully qualified view object name or FALSE if no matching view could be found.
|
||||
*/
|
||||
protected function resolveViewObjectName()
|
||||
{
|
||||
$vendorName = $this->request->getControllerVendorName();
|
||||
if ($vendorName === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$possibleViewName = str_replace(
|
||||
[
|
||||
'@vendor',
|
||||
'@extension',
|
||||
'@controller',
|
||||
'@action'
|
||||
],
|
||||
[
|
||||
$vendorName,
|
||||
$this->request->getControllerExtensionName(),
|
||||
$this->request->getControllerName(),
|
||||
ucfirst($this->request->getControllerActionName())
|
||||
],
|
||||
$this->namespacesViewObjectNamePattern
|
||||
);
|
||||
$format = $this->request->getFormat();
|
||||
$viewObjectName = str_replace('@format', ucfirst($format), $possibleViewName);
|
||||
if (class_exists($viewObjectName) === false) {
|
||||
$viewObjectName = str_replace('@format', '', $possibleViewName);
|
||||
}
|
||||
if (isset($this->viewFormatToObjectNameMap[$format]) && class_exists($viewObjectName) === false) {
|
||||
$viewObjectName = $this->viewFormatToObjectNameMap[$format];
|
||||
}
|
||||
return class_exists($viewObjectName) ? $viewObjectName : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the view before invoking an action method.
|
||||
*
|
||||
* Override this method to solve assign variables common for all actions
|
||||
* or prepare the view in another way before the action is called.
|
||||
*
|
||||
* @param ViewInterface $view The view to be initialized
|
||||
*/
|
||||
protected function initializeView(ViewInterface $view)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the controller before invoking an action method.
|
||||
*
|
||||
* Override this method to solve tasks which all actions have in
|
||||
* common.
|
||||
*/
|
||||
protected function initializeAction()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* A special action which is called if the originally intended action could
|
||||
* not be called, for example if the arguments were not valid.
|
||||
*
|
||||
* The default implementation sets a flash message, request errors and forwards back
|
||||
* to the originating action. This is suitable for most actions dealing with form input.
|
||||
*
|
||||
* We clear the page cache by default on an error as well, as we need to make sure the
|
||||
* data is re-evaluated when the user changes something.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function errorAction()
|
||||
{
|
||||
$this->clearCacheOnError();
|
||||
$this->addErrorFlashMessage();
|
||||
$this->forwardToReferringRequest();
|
||||
|
||||
return $this->getFlattenedValidationErrorMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache of current page on error. Needed because we want a re-evaluation of the data.
|
||||
* Better would be just do delete the cache for the error action, but that is not possible right now.
|
||||
*/
|
||||
protected function clearCacheOnError()
|
||||
{
|
||||
$extbaseSettings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
|
||||
if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') {
|
||||
if (isset($GLOBALS['TSFE'])) {
|
||||
$pageUid = $GLOBALS['TSFE']->id;
|
||||
$this->cacheService->clearPageCache([$pageUid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If an error occurred during this request, this adds a flash message describing the error to the flash
|
||||
* message container.
|
||||
*/
|
||||
protected function addErrorFlashMessage()
|
||||
{
|
||||
$errorFlashMessage = $this->getErrorFlashMessage();
|
||||
if ($errorFlashMessage !== false) {
|
||||
$this->addFlashMessage($errorFlashMessage, '', FlashMessage::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A template method for displaying custom error flash messages, or to
|
||||
* display no flash message at all on errors. Override this to customize
|
||||
* the flash message in your action controller.
|
||||
*
|
||||
* @return string The flash message or FALSE if no flash message should be set
|
||||
*/
|
||||
protected function getErrorFlashMessage()
|
||||
{
|
||||
return 'An error occurred while trying to call ' . static::class . '->' . $this->actionMethodName . '()';
|
||||
}
|
||||
|
||||
/**
|
||||
* If information on the request before the current request was sent, this method forwards back
|
||||
* to the originating request. This effectively ends processing of the current request, so do not
|
||||
* call this method before you have finished the necessary business logic!
|
||||
*
|
||||
* @throws StopActionException
|
||||
*/
|
||||
protected function forwardToReferringRequest()
|
||||
{
|
||||
$referringRequest = $this->request->getReferringRequest();
|
||||
if ($referringRequest !== null) {
|
||||
$originalRequest = clone $this->request;
|
||||
$this->request->setOriginalRequest($originalRequest);
|
||||
$this->request->setOriginalRequestMappingResults($this->arguments->validate());
|
||||
$this->forward(
|
||||
$referringRequest->getControllerActionName(),
|
||||
$referringRequest->getControllerName(),
|
||||
$referringRequest->getControllerExtensionName(),
|
||||
$referringRequest->getArguments()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string with a basic error message about validation failure.
|
||||
* We may add all validation error messages to a log file in the future,
|
||||
* but for security reasons (@see #54074) we do not return these here.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getFlattenedValidationErrorMessage()
|
||||
{
|
||||
$outputMessage = 'Validation failed while trying to call ' . static::class . '->' . $this->actionMethodName . '().' . PHP_EOL;
|
||||
return $outputMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of action method names and their parameters.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*
|
||||
* @return array Array of method parameters by action name
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public static function getActionMethodParameters($objectManager)
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
$reflectionService = $objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class);
|
||||
|
||||
$result = [];
|
||||
|
||||
$className = get_called_class();
|
||||
$methodNames = get_class_methods($className);
|
||||
foreach ($methodNames as $methodName) {
|
||||
if (strlen($methodName) > 6 && strpos($methodName, 'Action', strlen($methodName) - 6) !== false) {
|
||||
$result[$methodName] = $reflectionService->getMethodParameters($className, $methodName);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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\Extbase\Error\Result;
|
||||
use TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException;
|
||||
use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
|
||||
|
||||
/**
|
||||
* A controller argument
|
||||
*/
|
||||
class Argument
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Property\PropertyMapper
|
||||
*/
|
||||
protected $propertyMapper;
|
||||
|
||||
/**
|
||||
* @var MvcPropertyMappingConfiguration
|
||||
*/
|
||||
protected $propertyMappingConfiguration;
|
||||
|
||||
/**
|
||||
* Name of this argument
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name = '';
|
||||
|
||||
/**
|
||||
* Short name of this argument
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $shortName;
|
||||
|
||||
/**
|
||||
* Data type of this argument's value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dataType;
|
||||
|
||||
/**
|
||||
* TRUE if this argument is required
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $isRequired = false;
|
||||
|
||||
/**
|
||||
* Actual value of this argument
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $value;
|
||||
|
||||
/**
|
||||
* Default value. Used if argument is optional.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $defaultValue;
|
||||
|
||||
/**
|
||||
* A custom validator, used supplementary to the base validation
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface
|
||||
*/
|
||||
protected $validator;
|
||||
|
||||
/**
|
||||
* The validation results. This can be asked if the argument has errors.
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Error\Result
|
||||
*/
|
||||
protected $validationResults;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $hasBeenValidated = false;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper
|
||||
*/
|
||||
public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper)
|
||||
{
|
||||
$this->propertyMapper = $propertyMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration $propertyMappingConfiguration
|
||||
*/
|
||||
public function injectPropertyMappingConfiguration(MvcPropertyMappingConfiguration $propertyMappingConfiguration)
|
||||
{
|
||||
$this->propertyMappingConfiguration = $propertyMappingConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs this controller argument
|
||||
*
|
||||
* @param string $name Name of this argument
|
||||
* @param string $dataType The data type of this argument
|
||||
* @throws \InvalidArgumentException if $name is not a string or empty
|
||||
*/
|
||||
public function __construct($name, $dataType)
|
||||
{
|
||||
if (!is_string($name)) {
|
||||
throw new \InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
|
||||
}
|
||||
if ($name === '') {
|
||||
throw new \InvalidArgumentException('$name must be a non-empty string.', 1232551853);
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->dataType = TypeHandlingUtility::normalizeType($dataType);
|
||||
|
||||
$this->validationResults = new Result();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this argument
|
||||
*
|
||||
* @return string This argument's name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the short name of this argument.
|
||||
*
|
||||
* @param string $shortName A "short name" - a single character
|
||||
* @throws \InvalidArgumentException if $shortName is not a character
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
|
||||
*/
|
||||
public function setShortName($shortName)
|
||||
{
|
||||
if ($shortName !== null && (!is_string($shortName) || strlen($shortName) !== 1)) {
|
||||
throw new \InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
|
||||
}
|
||||
$this->shortName = $shortName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the short name of this argument
|
||||
*
|
||||
* @return string This argument's short name
|
||||
*/
|
||||
public function getShortName()
|
||||
{
|
||||
return $this->shortName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data type of this argument's value
|
||||
*
|
||||
* @return string The data type
|
||||
*/
|
||||
public function getDataType()
|
||||
{
|
||||
return $this->dataType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this argument to be required
|
||||
*
|
||||
* @param bool $required TRUE if this argument should be required
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
|
||||
*/
|
||||
public function setRequired($required)
|
||||
{
|
||||
$this->isRequired = (bool)$required;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if this argument is required
|
||||
*
|
||||
* @return bool TRUE if this argument is required
|
||||
*/
|
||||
public function isRequired()
|
||||
{
|
||||
return $this->isRequired;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value of the argument
|
||||
*
|
||||
* @param mixed $defaultValue Default value
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
|
||||
*/
|
||||
public function setDefaultValue($defaultValue)
|
||||
{
|
||||
$this->defaultValue = $defaultValue;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default value of this argument
|
||||
*
|
||||
* @return mixed The default value
|
||||
*/
|
||||
public function getDefaultValue()
|
||||
{
|
||||
return $this->defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom validator which is used supplementary to the base validation
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator The actual validator object
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument Returns $this (used for fluent interface)
|
||||
*/
|
||||
public function setValidator(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator)
|
||||
{
|
||||
$this->validator = $validator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set validator
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface The set validator, NULL if none was set
|
||||
*/
|
||||
public function getValidator()
|
||||
{
|
||||
return $this->validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this argument.
|
||||
*
|
||||
* @param mixed $rawValue The value of this argument
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument
|
||||
* @throws \TYPO3\CMS\Extbase\Property\Exception
|
||||
*/
|
||||
public function setValue($rawValue)
|
||||
{
|
||||
if ($rawValue === null) {
|
||||
$this->value = null;
|
||||
return $this;
|
||||
}
|
||||
if (is_object($rawValue) && $rawValue instanceof $this->dataType) {
|
||||
$this->value = $rawValue;
|
||||
return $this;
|
||||
}
|
||||
try {
|
||||
$this->value = $this->propertyMapper->convert($rawValue, $this->dataType, $this->propertyMappingConfiguration);
|
||||
} catch (TargetNotFoundException $e) {
|
||||
// for optional arguments no exeption is thrown.
|
||||
if ($this->isRequired()) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
$this->validationResults->merge($this->propertyMapper->getMessages());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this argument
|
||||
*
|
||||
* @return mixed The value of this argument - if none was set, NULL is returned
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
if ($this->value === null) {
|
||||
return $this->defaultValue;
|
||||
}
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Property Mapping Configuration used for this argument; can be used by the initialize*action to modify the Property Mapping.
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration
|
||||
*/
|
||||
public function getPropertyMappingConfiguration()
|
||||
{
|
||||
return $this->propertyMappingConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool TRUE if the argument is valid, FALSE otherwise
|
||||
*/
|
||||
public function isValid(): bool
|
||||
{
|
||||
return !$this->validate()->hasErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Extbase\Error\Result Validation errors which have occurred.
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getValidationResults()
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
return $this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this argument's value
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return (string)$this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Extbase\Error\Result
|
||||
*/
|
||||
public function validate(): \TYPO3\CMS\Extbase\Error\Result
|
||||
{
|
||||
if ($this->hasBeenValidated) {
|
||||
return $this->validationResults;
|
||||
}
|
||||
|
||||
if ($this->validator !== null) {
|
||||
$validationMessages = $this->validator->validate($this->value);
|
||||
$this->validationResults->merge($validationMessages);
|
||||
}
|
||||
|
||||
$this->hasBeenValidated = true;
|
||||
return $this->validationResults;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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 composite of controller arguments
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
class Arguments extends \ArrayObject
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var array Names of the arguments contained by this object
|
||||
*/
|
||||
protected $argumentNames = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $argumentShortNames = [];
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor. If this one is removed, reflection breaks.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or replaces the argument specified by $value. The argument's name is taken from the
|
||||
* argument object itself, therefore the $offset does not have any meaning in this context.
|
||||
*
|
||||
* @param mixed $offset Offset - not used here
|
||||
* @param mixed $value The argument
|
||||
* @throws \InvalidArgumentException if the argument is not a valid Controller Argument object
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
if (!$value instanceof Argument) {
|
||||
throw new \InvalidArgumentException('Controller arguments must be valid TYPO3\\CMS\\Extbase\\Mvc\\Controller\\Argument objects.', 1187953786);
|
||||
}
|
||||
$argumentName = $value->getName();
|
||||
parent::offsetSet($argumentName, $value);
|
||||
$this->argumentNames[$argumentName] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an argument, aliased to offsetSet()
|
||||
*
|
||||
* @param mixed $value The value
|
||||
* @throws \InvalidArgumentException if the argument is not a valid Controller Argument object
|
||||
*/
|
||||
public function append($value)
|
||||
{
|
||||
if (!$value instanceof Argument) {
|
||||
throw new \InvalidArgumentException('Controller arguments must be valid TYPO3\\CMS\\Extbase\\Mvc\\Controller\\Argument objects.', 1187953787);
|
||||
}
|
||||
$this->offsetSet(null, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an argument
|
||||
*
|
||||
* @param mixed $offset Offset
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
$translatedOffset = $this->translateToLongArgumentName($offset);
|
||||
parent::offsetUnset($translatedOffset);
|
||||
unset($this->argumentNames[$translatedOffset]);
|
||||
if ($offset != $translatedOffset) {
|
||||
unset($this->argumentShortNames[$offset]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the requested index exists
|
||||
*
|
||||
* @param mixed $offset Offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
$translatedOffset = $this->translateToLongArgumentName($offset);
|
||||
return parent::offsetExists($translatedOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at the specified index
|
||||
*
|
||||
* @param mixed $offset Offset
|
||||
* @return Argument The requested argument object
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException if the argument does not exist
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$translatedOffset = $this->translateToLongArgumentName($offset);
|
||||
if ($translatedOffset === '') {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException('The argument "' . $offset . '" does not exist.', 1216909923);
|
||||
}
|
||||
return parent::offsetGet($translatedOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates, adds and returns a new controller argument to this composite object.
|
||||
* If an argument with the same name exists already, it will be replaced by the
|
||||
* new argument object.
|
||||
*
|
||||
* @param string $name Name of the argument
|
||||
* @param string $dataType Name of one of the built-in data types
|
||||
* @param bool $isRequired TRUE if this argument should be marked as required
|
||||
* @param mixed $defaultValue Default value of the argument. Only makes sense if $isRequired==FALSE
|
||||
* @return Argument The new argument
|
||||
*/
|
||||
public function addNewArgument($name, $dataType = 'Text', $isRequired = false, $defaultValue = null)
|
||||
{
|
||||
/** @var Argument $argument */
|
||||
$argument = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Argument::class, $name, $dataType);
|
||||
$argument->setRequired($isRequired);
|
||||
$argument->setDefaultValue($defaultValue);
|
||||
$this->addArgument($argument);
|
||||
return $argument;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified controller argument to this composite object.
|
||||
* If an argument with the same name exists already, it will be replaced by the
|
||||
* new argument object.
|
||||
*
|
||||
* Note that the argument will be cloned, not referenced.
|
||||
*
|
||||
* @param Argument $argument The argument to add
|
||||
*/
|
||||
public function addArgument(Argument $argument)
|
||||
{
|
||||
$this->offsetSet(null, $argument);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an argument specified by name
|
||||
*
|
||||
* @param string $argumentName Name of the argument to retrieve
|
||||
* @return Argument
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException
|
||||
*/
|
||||
public function getArgument($argumentName)
|
||||
{
|
||||
if (!$this->offsetExists($argumentName)) {
|
||||
throw new \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException('An argument "' . $argumentName . '" does not exist.', 1195815178);
|
||||
}
|
||||
return $this->offsetGet($argumentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an argument with the specified name exists
|
||||
*
|
||||
* @param string $argumentName Name of the argument to check for
|
||||
* @return bool TRUE if such an argument exists, otherwise FALSE
|
||||
* @see offsetExists()
|
||||
*/
|
||||
public function hasArgument($argumentName)
|
||||
{
|
||||
return $this->offsetExists($argumentName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all arguments contained in this object
|
||||
*
|
||||
* @return array Argument names
|
||||
*/
|
||||
public function getArgumentNames()
|
||||
{
|
||||
return array_keys($this->argumentNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the short names of all arguments contained in this object that have one.
|
||||
*
|
||||
* @return array Argument short names
|
||||
*/
|
||||
public function getArgumentShortNames()
|
||||
{
|
||||
$argumentShortNames = [];
|
||||
/** @var Argument $argument */
|
||||
foreach ($this as $argument) {
|
||||
$argumentShortNames[$argument->getShortName()] = true;
|
||||
}
|
||||
return array_keys($argumentShortNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic setter method for the argument values. Each argument
|
||||
* value can be set by just calling the setArgumentName() method.
|
||||
*
|
||||
* @param string $methodName Name of the method
|
||||
* @param array $arguments Method arguments
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __call($methodName, array $arguments)
|
||||
{
|
||||
if (strpos($methodName, 'set') !== 0) {
|
||||
throw new \LogicException('Unknown method "' . $methodName . '".', 1210858451);
|
||||
}
|
||||
$firstLowerCaseArgumentName = $this->translateToLongArgumentName(strtolower($methodName[3]) . substr($methodName, 4));
|
||||
$firstUpperCaseArgumentName = $this->translateToLongArgumentName(ucfirst(substr($methodName, 3)));
|
||||
if (in_array($firstLowerCaseArgumentName, $this->getArgumentNames())) {
|
||||
$argument = parent::offsetGet($firstLowerCaseArgumentName);
|
||||
$argument->setValue($arguments[0]);
|
||||
} elseif (in_array($firstUpperCaseArgumentName, $this->getArgumentNames())) {
|
||||
$argument = parent::offsetGet($firstUpperCaseArgumentName);
|
||||
$argument->setValue($arguments[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a short argument name to its corresponding long name. If the
|
||||
* specified argument name is a real argument name already, it will be returned again.
|
||||
*
|
||||
* If an argument with the specified name or short name does not exist, an empty
|
||||
* string is returned.
|
||||
*
|
||||
* @param string $argumentName argument name
|
||||
* @return string long argument name or empty string
|
||||
*/
|
||||
protected function translateToLongArgumentName($argumentName)
|
||||
{
|
||||
if (in_array($argumentName, $this->getArgumentNames())) {
|
||||
return $argumentName;
|
||||
}
|
||||
/** @var Argument $argument */
|
||||
foreach ($this as $argument) {
|
||||
if ($argumentName === $argument->getShortName()) {
|
||||
return $argument->getName();
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all arguments and resets this object
|
||||
*/
|
||||
public function removeAll()
|
||||
{
|
||||
foreach ($this->argumentNames as $argumentName => $booleanValue) {
|
||||
parent::offsetUnset($argumentName);
|
||||
}
|
||||
$this->argumentNames = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all property mapping / validation errors
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Error\Result
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
|
||||
*/
|
||||
public function getValidationResults()
|
||||
{
|
||||
trigger_error(
|
||||
'Method ' . __METHOD__ . ' is deprecated and will be removed in TYPO3 v10.0.',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
|
||||
return $this->validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Extbase\Error\Result
|
||||
*/
|
||||
public function validate(): \TYPO3\CMS\Extbase\Error\Result
|
||||
{
|
||||
$results = new \TYPO3\CMS\Extbase\Error\Result();
|
||||
/** @var Argument $argument */
|
||||
foreach ($this as $argument) {
|
||||
$argumentValidationResults = $argument->validate();
|
||||
if ($argumentValidationResults === null) {
|
||||
continue;
|
||||
}
|
||||
$results->forProperty($argument->getName())->merge($argumentValidationResults);
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition;
|
||||
use TYPO3\CMS\Extbase\Mvc\Cli\ConsoleOutput;
|
||||
use TYPO3\CMS\Extbase\Mvc\Cli\Request;
|
||||
use TYPO3\CMS\Extbase\Mvc\Cli\Response;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
|
||||
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
use TYPO3\CMS\Extbase\Mvc\ResponseInterface;
|
||||
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
|
||||
use TYPO3\CMS\Extbase\Reflection\ReflectionService;
|
||||
|
||||
/**
|
||||
* A controller which processes requests from the command line
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use symfony/console commands instead.
|
||||
*/
|
||||
class CommandController implements CommandControllerInterface
|
||||
{
|
||||
/**
|
||||
* @var Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var Arguments
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
/**
|
||||
* Name of the command method
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $commandMethodName = '';
|
||||
|
||||
/**
|
||||
* Whether the command needs admin access to perform its job
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $requestAdminPermissions = false;
|
||||
|
||||
/**
|
||||
* @var ReflectionService
|
||||
*/
|
||||
protected $reflectionService;
|
||||
|
||||
/**
|
||||
* @var ObjectManagerInterface
|
||||
*/
|
||||
protected $objectManager;
|
||||
|
||||
/**
|
||||
* @var ConsoleOutput
|
||||
*/
|
||||
protected $output;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
trigger_error('Extbase Command Controllers will be removed in TYPO3 v10.0. Migrate to symfony/console commands instead.', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
|
||||
*/
|
||||
public function injectObjectManager(ObjectManagerInterface $objectManager)
|
||||
{
|
||||
$this->objectManager = $objectManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
|
||||
*/
|
||||
public function injectReflectionService(ReflectionService $reflectionService)
|
||||
{
|
||||
$this->reflectionService = $reflectionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current request type is supported by the controller.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
|
||||
* @return bool TRUE if this request type is supported, otherwise FALSE
|
||||
*/
|
||||
public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
|
||||
{
|
||||
return $request instanceof Request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a command line request.
|
||||
*
|
||||
* @param RequestInterface $request The request object
|
||||
* @param ResponseInterface $response The response, modified by this handler
|
||||
* @throws UnsupportedRequestTypeException if the controller doesn't support the current request type
|
||||
*/
|
||||
public function processRequest(RequestInterface $request, ResponseInterface $response)
|
||||
{
|
||||
if (!$this->canProcessRequest($request)) {
|
||||
throw new UnsupportedRequestTypeException(sprintf('%s only supports command line requests – requests of type "%s" given.', static::class, get_class($request)), 1300787096);
|
||||
}
|
||||
|
||||
$this->request = $request;
|
||||
$this->request->setDispatched(true);
|
||||
$this->response = $response;
|
||||
|
||||
$this->commandMethodName = $this->resolveCommandMethodName();
|
||||
$this->output = $this->objectManager->get(ConsoleOutput::class);
|
||||
$this->arguments = $this->objectManager->get(Arguments::class);
|
||||
$this->initializeCommandMethodArguments();
|
||||
$this->mapRequestArgumentsToControllerArguments();
|
||||
$this->initializeBackendAuthentication();
|
||||
$this->callCommandMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and checks the current command method name
|
||||
*
|
||||
* Note: The resulting command method name might not have the correct case, which isn't a problem because PHP is
|
||||
* case insensitive regarding method names.
|
||||
*
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchCommandException
|
||||
* @return string Method name of the current command
|
||||
* @throws NoSuchCommandException
|
||||
*/
|
||||
protected function resolveCommandMethodName()
|
||||
{
|
||||
$commandMethodName = $this->request->getControllerCommandName() . 'Command';
|
||||
if (!is_callable([$this, $commandMethodName])) {
|
||||
throw new NoSuchCommandException(sprintf('A command method "%s()" does not exist in controller "%s".', $commandMethodName, static::class), 1300902143);
|
||||
}
|
||||
return $commandMethodName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the arguments array of this controller by creating an empty argument object for each of the
|
||||
* method arguments found in the designated command method.
|
||||
*
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentTypeException
|
||||
* @throws InvalidArgumentTypeException
|
||||
*/
|
||||
protected function initializeCommandMethodArguments()
|
||||
{
|
||||
$methodParameters = $this->reflectionService
|
||||
->getClassSchema(static::class)
|
||||
->getMethod($this->commandMethodName)['params'] ?? [];
|
||||
|
||||
foreach ($methodParameters as $parameterName => $parameterInfo) {
|
||||
$dataType = null;
|
||||
if (isset($parameterInfo['type'])) {
|
||||
$dataType = $parameterInfo['type'];
|
||||
} elseif ($parameterInfo['array']) {
|
||||
$dataType = 'array';
|
||||
}
|
||||
if ($dataType === null) {
|
||||
throw new InvalidArgumentTypeException(sprintf('The argument type for parameter $%s of method %s->%s() could not be detected.', $parameterName, static::class, $this->commandMethodName), 1306755296);
|
||||
}
|
||||
$defaultValue = ($parameterInfo['defaultValue'] ?? null);
|
||||
$this->arguments->addNewArgument($parameterName, $dataType, $parameterInfo['optional'] === false, $defaultValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps arguments delivered by the request object to the local controller arguments.
|
||||
*/
|
||||
protected function mapRequestArgumentsToControllerArguments()
|
||||
{
|
||||
/** @var Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
$argumentName = $argument->getName();
|
||||
if ($this->request->hasArgument($argumentName)) {
|
||||
$argument->setValue($this->request->getArgument($argumentName));
|
||||
continue;
|
||||
}
|
||||
if (!$argument->isRequired()) {
|
||||
continue;
|
||||
}
|
||||
$argumentValue = null;
|
||||
$commandArgumentDefinition = $this->objectManager->get(CommandArgumentDefinition::class, $argumentName, true, null);
|
||||
while ($argumentValue === null) {
|
||||
$argumentValue = $this->output->ask(sprintf('<comment>Please specify the required argument "%s":</comment> ', $commandArgumentDefinition->getDashedName()));
|
||||
}
|
||||
$argument->setValue($argumentValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes and ensures authenticated backend access
|
||||
*/
|
||||
protected function initializeBackendAuthentication()
|
||||
{
|
||||
$backendUserAuthentication = $this->getBackendUserAuthentication();
|
||||
if ($backendUserAuthentication !== null) {
|
||||
$backendUserAuthentication->backendCheckLogin();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forwards the request to another command and / or CommandController.
|
||||
*
|
||||
* Request is directly transferred to the other command / controller
|
||||
* without the need for a new request.
|
||||
*
|
||||
* @param string $commandName
|
||||
* @param string $controllerObjectName
|
||||
* @param array $arguments
|
||||
* @throws StopActionException
|
||||
*/
|
||||
protected function forward($commandName, $controllerObjectName = null, array $arguments = [])
|
||||
{
|
||||
$this->request->setDispatched(false);
|
||||
$this->request->setControllerCommandName($commandName);
|
||||
if ($controllerObjectName !== null) {
|
||||
$this->request->setControllerObjectName($controllerObjectName);
|
||||
}
|
||||
$this->request->setArguments($arguments);
|
||||
|
||||
$this->arguments->removeAll();
|
||||
throw new StopActionException('forward', 1476107661);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the specified command method and passes the arguments.
|
||||
*
|
||||
* If the command returns a string, it is appended to the content in the
|
||||
* response object. If the command doesn't return anything and a valid
|
||||
* view exists, the view is rendered automatically.
|
||||
*/
|
||||
protected function callCommandMethod()
|
||||
{
|
||||
$preparedArguments = [];
|
||||
/** @var Argument $argument */
|
||||
foreach ($this->arguments as $argument) {
|
||||
$preparedArguments[] = $argument->getValue();
|
||||
}
|
||||
$commandResult = call_user_func_array([$this, $this->commandMethodName], $preparedArguments);
|
||||
if (is_string($commandResult) && $commandResult !== '') {
|
||||
$this->response->appendContent($commandResult);
|
||||
} elseif (is_object($commandResult) && method_exists($commandResult, '__toString')) {
|
||||
$this->response->appendContent((string)$commandResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs specified text to the console window
|
||||
* You can specify arguments that will be passed to the text via sprintf
|
||||
*
|
||||
* @see http://www.php.net/sprintf
|
||||
* @param string $text Text to output
|
||||
* @param array $arguments Optional arguments to use for sprintf
|
||||
*/
|
||||
protected function output($text, array $arguments = [])
|
||||
{
|
||||
$this->output->output($text, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs specified text to the console window and appends a line break
|
||||
*
|
||||
* @param string $text Text to output
|
||||
* @param array $arguments Optional arguments to use for sprintf
|
||||
* @see output()
|
||||
*/
|
||||
protected function outputLine($text = '', array $arguments = [])
|
||||
{
|
||||
$this->output->outputLine($text, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given text to fit into MAXIMUM_LINE_LENGTH and outputs it to the
|
||||
* console window
|
||||
*
|
||||
* @param string $text Text to output
|
||||
* @param array $arguments Optional arguments to use for sprintf
|
||||
* @param int $leftPadding The number of spaces to use for indentation
|
||||
* @see outputLine()
|
||||
*/
|
||||
protected function outputFormatted($text = '', array $arguments = [], $leftPadding = 0)
|
||||
{
|
||||
$this->output->outputFormatted($text, $arguments, $leftPadding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exits the CLI through the dispatcher
|
||||
* An exit status code can be specified @see http://www.php.net/exit
|
||||
*
|
||||
* @param int $exitCode Exit code to return on exit
|
||||
* @throws StopActionException
|
||||
*/
|
||||
protected function quit($exitCode = 0)
|
||||
{
|
||||
$this->response->setExitCode($exitCode);
|
||||
throw new StopActionException('quit', 1476107681);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the response and exits the CLI without any further code execution
|
||||
* Should be used for commands that flush code caches.
|
||||
*
|
||||
* @param int $exitCode Exit code to return on exit
|
||||
*/
|
||||
protected function sendAndExit($exitCode = 0)
|
||||
{
|
||||
$this->response->send();
|
||||
exit($exitCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global BackendUserAuthentication object.
|
||||
*
|
||||
* @return BackendUserAuthentication|null
|
||||
*/
|
||||
protected function getBackendUserAuthentication()
|
||||
{
|
||||
return $GLOBALS['BE_USER'] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for command controllers
|
||||
*
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0. Use symfony/console commands instead.
|
||||
*/
|
||||
interface CommandControllerInterface extends \TYPO3\CMS\Extbase\Mvc\Controller\ControllerInterface
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* The controller context contains information from the controller
|
||||
*/
|
||||
class ControllerContext
|
||||
{
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Response
|
||||
*/
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Controller\Arguments
|
||||
*/
|
||||
protected $arguments;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
|
||||
*/
|
||||
protected $uriBuilder;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $flashMessageQueueDefaultIdentifier;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Core\Messaging\FlashMessageService
|
||||
*/
|
||||
protected $flashMessageService;
|
||||
|
||||
/**
|
||||
* @var \TYPO3\CMS\Extbase\Service\ExtensionService
|
||||
*/
|
||||
protected $extensionService;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService
|
||||
*/
|
||||
public function injectFlashMessageService(\TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService)
|
||||
{
|
||||
$this->flashMessageService = $flashMessageService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Service\ExtensionService $extensionService
|
||||
*/
|
||||
public function injectExtensionService(\TYPO3\CMS\Extbase\Service\ExtensionService $extensionService)
|
||||
{
|
||||
$this->extensionService = $extensionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request of the controller
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Request $request
|
||||
*/
|
||||
public function setRequest(\TYPO3\CMS\Extbase\Mvc\Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the request of the controller
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Request
|
||||
*/
|
||||
public function getRequest()
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response of the controller
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Response $response
|
||||
*/
|
||||
public function setResponse(\TYPO3\CMS\Extbase\Mvc\Response $response)
|
||||
{
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response of the controller
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Response
|
||||
*/
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the arguments of the controller
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Controller\Arguments $arguments
|
||||
*/
|
||||
public function setArguments(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments $arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the arguments of the controller
|
||||
*
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Controller\Arguments
|
||||
*/
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder $uriBuilder
|
||||
*/
|
||||
public function setUriBuilder(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder $uriBuilder)
|
||||
{
|
||||
$this->uriBuilder = $uriBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
|
||||
*/
|
||||
public function getUriBuilder()
|
||||
{
|
||||
return $this->uriBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $identifier Queue-identifier
|
||||
* @return \TYPO3\CMS\Core\Messaging\FlashMessageQueue
|
||||
*/
|
||||
public function getFlashMessageQueue($identifier = null)
|
||||
{
|
||||
if ($identifier === null) {
|
||||
if ($this->flashMessageQueueDefaultIdentifier === null) {
|
||||
// cache the default-identifier for performance-reasons
|
||||
$this->flashMessageQueueDefaultIdentifier = 'extbase.flashmessages.' . $this->extensionService->getPluginNamespace($this->request->getControllerExtensionName(), $this->request->getPluginName());
|
||||
}
|
||||
$identifier = $this->flashMessageQueueDefaultIdentifier;
|
||||
}
|
||||
return $this->flashMessageService->getMessageQueueByIdentifier($identifier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for controllers
|
||||
*/
|
||||
interface ControllerInterface
|
||||
{
|
||||
/**
|
||||
* Checks if the current request type is supported by the controller.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
|
||||
* @return bool TRUE if this request type is supported, otherwise FALSE
|
||||
*/
|
||||
public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request);
|
||||
|
||||
/**
|
||||
* Processes a general request. The result can be returned by altering the given response.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by the controller
|
||||
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException if the controller doesn't support the current request type
|
||||
*/
|
||||
public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response);
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller\Exception;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* An "Invalid Argument Name" exception
|
||||
*/
|
||||
class RequiredArgumentMissingException extends \TYPO3\CMS\Extbase\Mvc\Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* The default property mapping configuration is available
|
||||
* inside the Argument-object.
|
||||
*/
|
||||
class MvcPropertyMappingConfiguration extends \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
|
||||
{
|
||||
/**
|
||||
* Allow creation of a certain sub property
|
||||
*
|
||||
* @param string $propertyPath
|
||||
*/
|
||||
public function allowCreationForSubProperty($propertyPath)
|
||||
{
|
||||
$this->forProperty($propertyPath)->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow modification for a given property path
|
||||
*
|
||||
* @param string $propertyPath
|
||||
*/
|
||||
public function allowModificationForSubProperty($propertyPath)
|
||||
{
|
||||
$this->forProperty($propertyPath)->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the target type for a certain property. Especially useful
|
||||
* if there is an object which has a nested object which is abstract,
|
||||
* and you want to instanciate a concrete object instead.
|
||||
*
|
||||
* @param string $propertyPath
|
||||
* @param string $targetType
|
||||
*/
|
||||
public function setTargetTypeForSubProperty($propertyPath, $targetType)
|
||||
{
|
||||
$this->forProperty($propertyPath)->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_TARGET_TYPE, $targetType);
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Extbase\Mvc\Controller;
|
||||
|
||||
/*
|
||||
* 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\Error\Http\BadRequestException;
|
||||
use TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException;
|
||||
use TYPO3\CMS\Extbase\Security\Exception\InvalidHashException;
|
||||
|
||||
/**
|
||||
* This is a Service which can generate a request hash and check whether the currently given arguments
|
||||
* fit to the request hash.
|
||||
*
|
||||
* It is used when forms are generated and submitted:
|
||||
* After a form has been generated, the method "generateRequestHash" is called with the names of all form fields.
|
||||
* It cleans up the array of form fields and creates another representation of it, which is then serialized and hashed.
|
||||
*
|
||||
* Both serialized form field list and the added hash form the request hash, which will be sent over the wire (as an argument __hmac).
|
||||
*
|
||||
* On the validation side, the validation happens in two steps:
|
||||
* 1) Check if the request hash is consistent (the hash value fits to the serialized string)
|
||||
* 2) Check that _all_ GET/POST parameters submitted occur inside the form field list of the request hash.
|
||||
*
|
||||
* Note: It is crucially important that a private key is computed into the hash value! This is done inside the HashService.
|
||||
*
|
||||
* @internal only to be used within Extbase, not part of TYPO3 Core API.
|
||||
*/
|
||||
class MvcPropertyMappingConfigurationService implements \TYPO3\CMS\Core\SingletonInterface
|
||||
{
|
||||
/**
|
||||
* The hash service class to use
|
||||
*
|
||||
* @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService
|
||||
*/
|
||||
protected $hashService;
|
||||
|
||||
/**
|
||||
* @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService
|
||||
*/
|
||||
public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService)
|
||||
{
|
||||
$this->hashService = $hashService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a request hash for a list of form fields
|
||||
*
|
||||
* @param array $formFieldNames Array of form fields
|
||||
* @param string $fieldNamePrefix
|
||||
*
|
||||
* @return string trusted properties token
|
||||
* @throws \TYPO3\CMS\EXTBASE\Security\Exception\InvalidArgumentForHashGenerationException
|
||||
*/
|
||||
public function generateTrustedPropertiesToken($formFieldNames, $fieldNamePrefix = '')
|
||||
{
|
||||
$formFieldArray = [];
|
||||
foreach ($formFieldNames as $formField) {
|
||||
$formFieldParts = explode('[', $formField);
|
||||
$currentPosition = &$formFieldArray;
|
||||
$formFieldPartsCount = count($formFieldParts);
|
||||
for ($i = 0; $i < $formFieldPartsCount; $i++) {
|
||||
$formFieldPart = $formFieldParts[$i];
|
||||
$formFieldPart = rtrim($formFieldPart, ']');
|
||||
if (!is_array($currentPosition)) {
|
||||
throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is declared as array, but it collides with a previous form field of the same name which declared the field as string. This is an inconsistency you need to fix inside your Fluid form. (String overridden by Array)', 1255072196);
|
||||
}
|
||||
if ($i === $formFieldPartsCount - 1) {
|
||||
if (isset($currentPosition[$formFieldPart]) && is_array($currentPosition[$formFieldPart])) {
|
||||
throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is declared as string, but it collides with a previous form field of the same name which declared the field as array. This is an inconsistency you need to fix inside your Fluid form. (Array overridden by String)', 1255072587);
|
||||
}
|
||||
// Last iteration - add a string
|
||||
if ($formFieldPart === '') {
|
||||
$currentPosition[] = 1;
|
||||
} else {
|
||||
$currentPosition[$formFieldPart] = 1;
|
||||
}
|
||||
} else {
|
||||
if ($formFieldPart === '') {
|
||||
throw new \TYPO3\CMS\Extbase\Security\Exception\InvalidArgumentForHashGenerationException('The form field "' . $formField . '" is invalid. Reason: "[]" used not as last argument, but somewhere in the middle (like foo[][bar]).', 1255072832);
|
||||
}
|
||||
if (!isset($currentPosition[$formFieldPart])) {
|
||||
$currentPosition[$formFieldPart] = [];
|
||||
}
|
||||
$currentPosition = &$currentPosition[$formFieldPart];
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($fieldNamePrefix !== '') {
|
||||
$formFieldArray = ($formFieldArray[$fieldNamePrefix] ?? []);
|
||||
}
|
||||
return $this->serializeAndHashFormFieldArray($formFieldArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize and hash the form field array
|
||||
*
|
||||
* @param array $formFieldArray form field array to be serialized and hashed
|
||||
*
|
||||
* @return string Hash
|
||||
*/
|
||||
protected function serializeAndHashFormFieldArray(array $formFieldArray)
|
||||
{
|
||||
$serializedFormFieldArray = serialize($formFieldArray);
|
||||
return $this->hashService->appendHmac($serializedFormFieldArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the property mapping configuration in $controllerArguments if
|
||||
* the trusted properties are set inside the request.
|
||||
*
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Request $request
|
||||
* @param \TYPO3\CMS\Extbase\Mvc\Controller\Arguments $controllerArguments
|
||||
* @throws BadRequestException
|
||||
*/
|
||||
public function initializePropertyMappingConfigurationFromRequest(\TYPO3\CMS\Extbase\Mvc\Request $request, \TYPO3\CMS\Extbase\Mvc\Controller\Arguments $controllerArguments)
|
||||
{
|
||||
$trustedPropertiesToken = $request->getInternalArgument('__trustedProperties');
|
||||
if (!is_string($trustedPropertiesToken)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$serializedTrustedProperties = $this->hashService->validateAndStripHmac($trustedPropertiesToken);
|
||||
} catch (InvalidHashException | InvalidArgumentForHashGenerationException $e) {
|
||||
throw new BadRequestException('The HMAC of the form could not be validated.', 1581862822);
|
||||
}
|
||||
$trustedProperties = unserialize($serializedTrustedProperties, ['allowed_classes' => false]);
|
||||
foreach ($trustedProperties as $propertyName => $propertyConfiguration) {
|
||||
if (!$controllerArguments->hasArgument($propertyName)) {
|
||||
continue;
|
||||
}
|
||||
$propertyMappingConfiguration = $controllerArguments->getArgument($propertyName)->getPropertyMappingConfiguration();
|
||||
$this->modifyPropertyMappingConfiguration($propertyConfiguration, $propertyMappingConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the passed $propertyMappingConfiguration according to the $propertyConfiguration which
|
||||
* has been generated by Fluid. In detail, if the $propertyConfiguration contains
|
||||
* an __identity field, we allow modification of objects; else we allow creation.
|
||||
*
|
||||
* All other properties are specified as allowed properties.
|
||||
*
|
||||
* @param array $propertyConfiguration
|
||||
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration
|
||||
*/
|
||||
protected function modifyPropertyMappingConfiguration($propertyConfiguration, \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration)
|
||||
{
|
||||
if (!is_array($propertyConfiguration)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset($propertyConfiguration['__identity'])) {
|
||||
$propertyMappingConfiguration->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true);
|
||||
unset($propertyConfiguration['__identity']);
|
||||
} else {
|
||||
$propertyMappingConfiguration->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true);
|
||||
}
|
||||
|
||||
foreach ($propertyConfiguration as $innerKey => $innerValue) {
|
||||
if (is_array($innerValue)) {
|
||||
$this->modifyPropertyMappingConfiguration($innerValue, $propertyMappingConfiguration->forProperty($innerKey));
|
||||
}
|
||||
$propertyMappingConfiguration->allowProperties($innerKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user