[TASK] Use FQCN's when registering plugins/modules
[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 // this functionality is actually not needed any more.
127 // the extension name is known during the creation of request objects and if the extension name was
128 // needed, then it were to be fetched by the request object. The controller object shouldn't care about
129 // the extension that uses it.
130 // todo: decide whether to drop this now or along with the property or if to keep that to not break the public api
131
132 $className = static::class;
133 $classNameParts = explode('\\', $className, 4);
134 // Skip vendor and product name for core classes
135 if (strpos($className, 'TYPO3\\CMS\\') === 0) {
136 $this->extensionName = $classNameParts[2];
137 } else {
138 $this->extensionName = $classNameParts[1];
139 }
140 }
141
142 /**
143 * @param ConfigurationManagerInterface $configurationManager
144 */
145 public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
146 {
147 $this->configurationManager = $configurationManager;
148 $this->settings = $this->configurationManager->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS);
149 }
150
151 /**
152 * Injects the object manager
153 *
154 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
155 */
156 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
157 {
158 $this->objectManager = $objectManager;
159 $this->arguments = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\Arguments::class);
160 }
161
162 /**
163 * Creates a Message object and adds it to the FlashMessageQueue.
164 *
165 * @param string $messageBody The message
166 * @param string $messageTitle Optional message title
167 * @param int $severity Optional severity, must be one of \TYPO3\CMS\Core\Messaging\FlashMessage constants
168 * @param bool $storeInSession Optional, defines whether the message should be stored in the session (default) or not
169 * @throws \InvalidArgumentException if the message body is no string
170 * @see \TYPO3\CMS\Core\Messaging\FlashMessage
171 */
172 public function addFlashMessage($messageBody, $messageTitle = '', $severity = \TYPO3\CMS\Core\Messaging\AbstractMessage::OK, $storeInSession = true)
173 {
174 if (!is_string($messageBody)) {
175 throw new \InvalidArgumentException('The message body must be of type string, "' . gettype($messageBody) . '" given.', 1243258395);
176 }
177 /* @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
178 $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
179 \TYPO3\CMS\Core\Messaging\FlashMessage::class,
180 (string)$messageBody,
181 (string)$messageTitle,
182 $severity,
183 $storeInSession
184 );
185 $this->controllerContext->getFlashMessageQueue()->enqueue($flashMessage);
186 }
187
188 /**
189 * Checks if the current request type is supported by the controller.
190 *
191 * If your controller only supports certain request types, either
192 * replace / modify the supportedRequestTypes property or override this
193 * method.
194 *
195 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The current request
196 * @return bool TRUE if this request type is supported, otherwise FALSE
197 */
198 public function canProcessRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request)
199 {
200 foreach ($this->supportedRequestTypes as $supportedRequestType) {
201 if ($request instanceof $supportedRequestType) {
202 return true;
203 }
204 }
205 return false;
206 }
207
208 /**
209 * Processes a general request. The result can be returned by altering the given response.
210 *
211 * @param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request The request object
212 * @param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response The response, modified by this handler
213 * @throws UnsupportedRequestTypeException if the controller doesn't support the current request type
214 */
215 public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response)
216 {
217 if (!$this->canProcessRequest($request)) {
218 throw new UnsupportedRequestTypeException(static::class . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes), 1187701132);
219 }
220 if ($response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response && $request instanceof WebRequest) {
221 $response->setRequest($request);
222 }
223 $this->request = $request;
224 $this->request->setDispatched(true);
225 $this->response = $response;
226 $this->uriBuilder = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder::class);
227 $this->uriBuilder->setRequest($request);
228 $this->initializeControllerArgumentsBaseValidators();
229 $this->mapRequestArgumentsToControllerArguments();
230 $this->controllerContext = $this->buildControllerContext();
231 }
232
233 /**
234 * Initialize the controller context
235 *
236 * @return \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext ControllerContext to be passed to the view
237 */
238 protected function buildControllerContext()
239 {
240 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext */
241 $controllerContext = $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext::class);
242 $controllerContext->setRequest($this->request);
243 $controllerContext->setResponse($this->response);
244 if ($this->arguments !== null) {
245 $controllerContext->setArguments($this->arguments);
246 }
247 $controllerContext->setUriBuilder($this->uriBuilder);
248
249 return $controllerContext;
250 }
251
252 /**
253 * Forwards the request to another action and / or controller.
254 *
255 * Request is directly transferred to the other action / controller
256 * without the need for a new request.
257 *
258 * @param string $actionName Name of the action to forward to
259 * @param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
260 * @param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
261 * @param array $arguments Arguments to pass to the target action
262 * @throws StopActionException
263 * @see redirect()
264 */
265 public function forward($actionName, $controllerName = null, $extensionName = null, array $arguments = null)
266 {
267 $this->request->setDispatched(false);
268 if ($this->request instanceof WebRequest) {
269 $this->request->setControllerActionName($actionName);
270 if ($controllerName !== null) {
271 $this->request->setControllerName($controllerName);
272 }
273 if ($extensionName !== null) {
274 $this->request->setControllerExtensionName($extensionName);
275 }
276 }
277 if ($arguments !== null) {
278 $this->request->setArguments($arguments);
279 }
280 throw new StopActionException('forward', 1476045801);
281 }
282
283 /**
284 * Redirects the request to another action and / or controller.
285 *
286 * Redirect will be sent to the client which then performs another request to the new URI.
287 *
288 * NOTE: This method only supports web requests and will thrown an exception
289 * if used with other request types.
290 *
291 * @param string $actionName Name of the action to forward to
292 * @param string $controllerName Unqualified object name of the controller to forward to. If not specified, the current controller is used.
293 * @param string $extensionName Name of the extension containing the controller to forward to. If not specified, the current extension is assumed.
294 * @param array $arguments Arguments to pass to the target action
295 * @param int $pageUid Target page uid. If NULL, the current page uid is used
296 * @param int $delay (optional) The delay in seconds. Default is no delay.
297 * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
298 * @throws UnsupportedRequestTypeException If the request is not a web request
299 * @throws StopActionException
300 * @see forward()
301 */
302 protected function redirect($actionName, $controllerName = null, $extensionName = null, array $arguments = null, $pageUid = null, $delay = 0, $statusCode = 303)
303 {
304 if (!$this->request instanceof WebRequest) {
305 throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539734);
306 }
307 if ($controllerName === null) {
308 $controllerName = $this->request->getControllerName();
309 }
310 $this->uriBuilder->reset()->setTargetPageUid($pageUid)->setCreateAbsoluteUri(true);
311 if (\TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('TYPO3_SSL')) {
312 $this->uriBuilder->setAbsoluteUriScheme('https');
313 }
314 $uri = $this->uriBuilder->uriFor($actionName, $arguments, $controllerName, $extensionName);
315 $this->redirectToUri($uri, $delay, $statusCode);
316 }
317
318 /**
319 * Redirects the web request to another uri.
320 *
321 * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
322 *
323 * @param mixed $uri A string representation of a URI
324 * @param int $delay (optional) The delay in seconds. Default is no delay.
325 * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other
326 * @throws UnsupportedRequestTypeException If the request is not a web request
327 * @throws StopActionException
328 */
329 protected function redirectToUri($uri, $delay = 0, $statusCode = 303)
330 {
331 if (!$this->request instanceof WebRequest) {
332 throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1220539735);
333 }
334
335 $this->objectManager->get(\TYPO3\CMS\Extbase\Service\CacheService::class)->clearCachesOfRegisteredPageIds();
336
337 $uri = $this->addBaseUriIfNecessary($uri);
338 $escapedUri = htmlentities($uri, ENT_QUOTES, 'utf-8');
339 $this->response->setContent('<html><head><meta http-equiv="refresh" content="' . (int)$delay . ';url=' . $escapedUri . '"/></head></html>');
340 if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
341 $this->response->setStatus($statusCode);
342 $this->response->setHeader('Location', (string)$uri);
343 }
344 throw new StopActionException('redirectToUri', 1476045828);
345 }
346
347 /**
348 * Adds the base uri if not already in place.
349 *
350 * @param string $uri The URI
351 * @return string
352 */
353 protected function addBaseUriIfNecessary($uri)
354 {
355 return \TYPO3\CMS\Core\Utility\GeneralUtility::locationHeaderUrl((string)$uri);
356 }
357
358 /**
359 * Sends the specified HTTP status immediately.
360 *
361 * NOTE: This method only supports web requests and will thrown an exception if used with other request types.
362 *
363 * @param int $statusCode The HTTP status code
364 * @param string $statusMessage A custom HTTP status message
365 * @param string $content Body content which further explains the status
366 * @throws UnsupportedRequestTypeException If the request is not a web request
367 * @throws StopActionException
368 */
369 public function throwStatus($statusCode, $statusMessage = null, $content = null)
370 {
371 if (!$this->request instanceof WebRequest) {
372 throw new UnsupportedRequestTypeException('throwStatus() only supports web requests.', 1220539739);
373 }
374 if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
375 $this->response->setStatus($statusCode, $statusMessage);
376 if ($content === null) {
377 $content = $this->response->getStatus();
378 }
379 }
380 $this->response->setContent($content);
381 throw new StopActionException('throwStatus', 1476045871);
382 }
383
384 /**
385 * Collects the base validators which were defined for the data type of each
386 * controller argument and adds them to the argument's validator chain.
387 */
388 public function initializeControllerArgumentsBaseValidators()
389 {
390 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
391 foreach ($this->arguments as $argument) {
392 $validator = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
393 if ($validator !== null) {
394 $argument->setValidator($validator);
395 }
396 }
397 }
398
399 /**
400 * Maps arguments delivered by the request object to the local controller arguments.
401 *
402 * @throws Exception\RequiredArgumentMissingException
403 */
404 protected function mapRequestArgumentsToControllerArguments()
405 {
406 /** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
407 foreach ($this->arguments as $argument) {
408 $argumentName = $argument->getName();
409 if ($this->request->hasArgument($argumentName)) {
410 $argument->setValue($this->request->getArgument($argumentName));
411 } elseif ($argument->isRequired()) {
412 throw new \TYPO3\CMS\Extbase\Mvc\Controller\Exception\RequiredArgumentMissingException('Required argument "' . $argumentName . '" is not set for ' . $this->request->getControllerObjectName() . '->' . $this->request->getControllerActionName() . '.', 1298012500);
413 }
414 }
415 }
416 }