a42db6ae1dc4de18786648bebcd915f37745d545
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Mvc / Controller / AbstractController.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Mvc\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
18 use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
19 use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
20 use TYPO3\CMS\Extbase\Mvc\Web\Request as WebRequest;
21
22 /**
23 * An abstract base class for Controllers
24 */
25 abstract class AbstractController implements ControllerInterface
26 {
27 /**
28 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
29 */
30 protected $signalSlotDispatcher;
31
32 /**
33 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
34 */
35 protected $objectManager;
36
37 /**
38 * @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder
39 */
40 protected $uriBuilder;
41
42 /**
43 * @var string Key of the extension this controller belongs to
44 */
45 protected $extensionName;
46
47 /**
48 * Contains the settings of the current extension
49 *
50 * @var array
51 */
52 protected $settings;
53
54 /**
55 * The current request.
56 *
57 * @var \TYPO3\CMS\Extbase\Mvc\RequestInterface
58 */
59 protected $request;
60
61 /**
62 * The response which will be returned by this action controller
63 *
64 * @var \TYPO3\CMS\Extbase\Mvc\ResponseInterface
65 */
66 protected $response;
67
68 /**
69 * @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver
70 */
71 protected $validatorResolver;
72
73 /**
74 * @var \TYPO3\CMS\Extbase\Mvc\Controller\Arguments Arguments passed to the controller
75 */
76 protected $arguments;
77
78 /**
79 * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher
80 */
81 public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher)
82 {
83 $this->signalSlotDispatcher = $signalSlotDispatcher;
84 }
85
86 /**
87 * @param \TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver
88 */
89 public function injectValidatorResolver(\TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver)
90 {
91 $this->validatorResolver = $validatorResolver;
92 }
93
94 /**
95 * An array of supported request types. By default only web requests are supported.
96 * Modify or replace this array if your specific controller supports certain
97 * (additional) request types.
98 *
99 * @var array
100 */
101 protected $supportedRequestTypes = [\TYPO3\CMS\Extbase\Mvc\Request::class];
102
103 /**
104 * @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext
105 */
106 protected $controllerContext;
107
108 /**
109 * @return ControllerContext
110 */
111 public function getControllerContext()
112 {
113 return $this->controllerContext;
114 }
115
116 /**
117 * @var ConfigurationManagerInterface
118 */
119 protected $configurationManager;
120
121 /**
122 * Constructs the controller.
123 */
124 public function __construct()
125 {
126 $className = static::class;
127 $classNameParts = explode('\\', $className, 4);
128 // Skip vendor and product name for core classes
129 if (strpos($className, 'TYPO3\\CMS\\') === 0) {
130 $this->extensionName = $classNameParts[2];
131 } else {
132 $this->extensionName = $classNameParts[1];
133 }
134 }
135
136 /**
137 * @param ConfigurationManagerInterface $configurationManager
138 */
139 public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
140 {
141 $this->configurationManager = $configurationManager;
142 $this->settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
143 }
144
145 /**
146 * Injects the object manager
147 *
148 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
149 */
150 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
151 {
152 $this->objectManager = $objectManager;
153 $this->arguments = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class);
154 }
155
156 /**
157 * Creates a Message object and adds it to the FlashMessageQueue.
158 *
159 * @param string $messageBody The message
160 * @param string $messageTitle Optional message title
161 * @param int $severity Optional severity, must be one of \TYPO3\CMS\Core\Messaging\FlashMessage constants
162 * @param bool $storeInSession Optional, defines whether the message should be stored in the session (default) or not
163 * @throws \InvalidArgumentException if the message body is no string
164 * @see \TYPO3\CMS\Core\Messaging\FlashMessage
165 */
166 public function addFlashMessage($messageBody, $messageTitle = '', $severity = \TYPO3\CMS\Core\Messaging\AbstractMessage::OK, $storeInSession = true)
167 {
168 if (!is_string($messageBody)) {
169 throw new \InvalidArgumentException('The message body must be of type string, "' . gettype($messageBody) . '" given.', 1243258395);
170 }
171 /* @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
172 $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
173 \TYPO3\CMS\Core\Messaging\FlashMessage::class,
174 (string)$messageBody,
175 (string)$messageTitle,
176 $severity,
177 $storeInSession
178 );
179 $this->controllerContext->getFlashMessageQueue()->enqueue($flashMessage);
180 }
181
182 /**
183 * Checks if the current request type is supported by the controller.
184 *
185 * If your controller only supports certain request types, either
186 * replace / modify the supportedRequestTypes property or override this
187 * method.
188 *
189 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
190 * @return bool TRUE if this request type is supported, otherwise FALSE
191 */
192 public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
193 {
194 foreach ($this->supportedRequestTypes as $supportedRequestType) {
195 if ($request instanceof $supportedRequestType) {
196 return true;
197 }
198 }
199 return false;
200 }
201
202 /**
203 * Processes a general request. The result can be returned by altering the given response.
204 *
205 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
206 * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler
207 * @throws UnsupportedRequestTypeException if the controller doesn't support the current request type
208 */
209 public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
210 {
211 if (!$this->canProcessRequest($request)) {
212 throw new UnsupportedRequestTypeException(static::class . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes), 1187701132);
213 }
214 if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
215 $response->setRequest($request);
216 }
217 $this->request = $request;
218 $this->request->setDispatched(true);
219 $this->response = $response;
220 $this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
221 $this->uriBuilder->setRequest($request);
222 $this->initializeControllerArgumentsBaseValidators();
223 $this->mapRequestArgumentsToControllerArguments();
224 $this->controllerContext = $this->buildControllerContext();
225 }
226
227 /**
228 * Initialize the controller context
229 *
230 * @return \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext ControllerContext to be passed to the view
231 */
232 protected function buildControllerContext()
233 {
234 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext */
235 $controllerContext = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext::class);
236 $controllerContext->setRequest($this->request);
237 $controllerContext->setResponse($this->response);
238 if ($this->arguments !== null) {
239 $controllerContext->setArguments($this->arguments);
240 }
241 $controllerContext->setUriBuilder($this->uriBuilder);
242
243 return $controllerContext;
244 }
245
246 /**
247 * Forwards the request to another action and / or controller.
248 *
249 * Request is directly transferred to the other action / controller
250 * without the need for a new request.
251 *
252 * @param string $actionName Name of the action to forward to
253 * @param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
254 * @param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
255 * @param array $arguments Arguments to pass to the target action
256 * @throws StopActionException
257 * @see redirect()
258 */
259 public function forward($actionName, $controllerName = null, $extensionName = null, array $arguments = null)
260 {
261 $this->request->setDispatched(false);
262 if ($this->request instanceof WebRequest) {
263 $this->request->setControllerActionName($actionName);
264 if ($controllerName !== null) {
265 $this->request->setControllerName($controllerName);
266 }
267 if ($extensionName !== null) {
268 $this->request->setControllerExtensionName($extensionName);
269 }
270 }
271 if ($arguments !== null) {
272 $this->request->setArguments($arguments);
273 }
274 throw new StopActionException('forward', 1476045801);
275 }
276
277 /**
278 * Redirects the request to another action and / or controller.
279 *
280 * Redirect will be sent to the client which then performs another request to the new URI.
281 *
282 * NOTE: This method only supports web requests and will thrown an exception
283 * if used with other request types.
284 *
285 * @param string $actionName Name of the action to forward to
286 * @param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
287 * @param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
288 * @param array $arguments Arguments to pass to the target action
289 * @param int $pageUid Target page uid. If NULL, the current page uid is used
290 * @param int $delay (optional) The delay in seconds. Default is no delay.
291 * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
292 * @throws UnsupportedRequestTypeException If the request is not a web request
293 * @throws StopActionException
294 * @see forward()
295 */
296 protected function redirect($actionName, $controllerName = null, $extensionName = null, array $arguments = null, $pageUid = null, $delay = 0, $statusCode = 303)
297 {
298 if (!$this->request instanceof WebRequest) {
299 throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539734);
300 }
301 if ($controllerName === null) {
302 $controllerName = $this->request->getControllerName();
303 }
304 $this->uriBuilder->reset()->setTargetPageUid($pageUid)->setCreateAbsoluteUri(true);
305 if (\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SSL')) {
306 $this->uriBuilder->setAbsoluteUriScheme('https');
307 }
308 $uri = $this->uriBuilder->uriFor($actionName, $arguments, $controllerName, $extensionName);
309 $this->redirectToUri($uri, $delay, $statusCode);
310 }
311
312 /**
313 * Redirects the web request to another uri.
314 *
315 * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
316 *
317 * @param mixed $uri A string representation of a URI
318 * @param int $delay (optional) The delay in seconds. Default is no delay.
319 * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
320 * @throws UnsupportedRequestTypeException If the request is not a web request
321 * @throws StopActionException
322 */
323 protected function redirectToUri($uri, $delay = 0, $statusCode = 303)
324 {
325 if (!$this->request instanceof WebRequest) {
326 throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539735);
327 }
328
329 $this->objectManager->get(\TYPO3\CMS\Extbase\Service\CacheService::class)->clearCachesOfRegisteredPageIds();
330
331 $uri = $this->addBaseUriIfNecessary($uri);
332 $escapedUri = htmlentities($uri, ENT_QUOTES, 'utf-8');
333 $this->response->setContent('<html><head><meta http-equiv="refresh" content="' . (int)$delay . ';url=' . $escapedUri . '"/></head></html>');
334 if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
335 $this->response->setStatus($statusCode);
336 $this->response->setHeader('Location', (string)$uri);
337 }
338 throw new StopActionException('redirectToUri', 1476045828);
339 }
340
341 /**
342 * Adds the base uri if not already in place.
343 *
344 * @param string $uri The URI
345 * @return string
346 */
347 protected function addBaseUriIfNecessary($uri)
348 {
349 return \TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl((string)$uri);
350 }
351
352 /**
353 * Sends the specified HTTP status immediately.
354 *
355 * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
356 *
357 * @param int $statusCode The HTTP status code
358 * @param string $statusMessage A custom HTTP status message
359 * @param string $content Body content which further explains the status
360 * @throws UnsupportedRequestTypeException If the request is not a web request
361 * @throws StopActionException
362 */
363 public function throwStatus($statusCode, $statusMessage = null, $content = null)
364 {
365 if (!$this->request instanceof WebRequest) {
366 throw new UnsupportedRequestTypeException('throwStatus() only supports web requests.', 1220539739);
367 }
368 if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
369 $this->response->setStatus($statusCode, $statusMessage);
370 if ($content === null) {
371 $content = $this->response->getStatus();
372 }
373 }
374 $this->response->setContent($content);
375 throw new StopActionException('throwStatus', 1476045871);
376 }
377
378 /**
379 * Collects the base validators which were defined for the data type of each
380 * controller argument and adds them to the argument's validator chain.
381 */
382 public function initializeControllerArgumentsBaseValidators()
383 {
384 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
385 foreach ($this->arguments as $argument) {
386 $validator = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
387 if ($validator !== null) {
388 $argument->setValidator($validator);
389 }
390 }
391 }
392
393 /**
394 * Maps arguments delivered by the request object to the local controller arguments.
395 *
396 * @throws Exception\RequiredArgumentMissingException
397 */
398 protected function mapRequestArgumentsToControllerArguments()
399 {
400 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
401 foreach ($this->arguments as $argument) {
402 $argumentName = $argument->getName();
403 if ($this->request->hasArgument($argumentName)) {
404 $argument->setValue($this->request->getArgument($argumentName));
405 } elseif ($argument->isRequired()) {
406 throw new \TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException('Required argument "' . $argumentName . '" is not set for ' . $this->request->getControllerObjectName() . '->' . $this->request->getControllerActionName() . '.', 1298012500);
407 }
408 }
409 }
410 }