PageRenderTime 42ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/typo3/sysext/extbase/Classes/MVC/Controller/ActionController.php

https://bitbucket.org/linxpinx/mercurial
PHP | 427 lines | 186 code | 44 blank | 197 comment | 39 complexity | a6c97de32ba04381de31574fd93562a4 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Unlicense, LGPL-2.1, Apache-2.0
  1. <?php
  2. /***************************************************************
  3. * Copyright notice
  4. *
  5. * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
  6. * All rights reserved
  7. *
  8. * This class is a backport of the corresponding class of FLOW3.
  9. * All credits go to the v5 team.
  10. *
  11. * This script is part of the TYPO3 project. The TYPO3 project is
  12. * free software; you can redistribute it and/or modify
  13. * it under the terms of the GNU General Public License as published by
  14. * the Free Software Foundation; either version 2 of the License, or
  15. * (at your option) any later version.
  16. *
  17. * The GNU General Public License can be found at
  18. * http://www.gnu.org/copyleft/gpl.html.
  19. *
  20. * This script is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * This copyright notice MUST APPEAR in all copies of the script!
  26. ***************************************************************/
  27. /**
  28. * A multi action controller. This is by far the most common base class for Controllers.
  29. *
  30. * @package Extbase
  31. * @subpackage MVC\Controller
  32. * @version $ID:$
  33. * @api
  34. */
  35. class Tx_Extbase_MVC_Controller_ActionController extends Tx_Extbase_MVC_Controller_AbstractController {
  36. /**
  37. * @var Tx_Extbase_Reflection_Service
  38. */
  39. protected $reflectionService;
  40. /**
  41. * By default a Fluid TemplateView is provided, if a template is available,
  42. * then a view with the same name as the current action will be looked up.
  43. * If none is available the $defaultViewObjectName will be used and finally
  44. * an EmptyView will be created.
  45. * @var Tx_Extbase_MVC_View_ViewInterface
  46. * @api
  47. */
  48. protected $view = NULL;
  49. /**
  50. * Pattern after which the view object name is built if no Fluid template
  51. * is found.
  52. * @var string
  53. * @api
  54. */
  55. protected $viewObjectNamePattern = 'Tx_@extension_View_@controller_@action@format';
  56. /**
  57. * The default view object to use if neither a Fluid template nor an action
  58. * specific view object could be found.
  59. * @var string
  60. * @api
  61. */
  62. protected $defaultViewObjectName = NULL;
  63. /**
  64. * Name of the action method
  65. * @var string
  66. * @api
  67. */
  68. protected $actionMethodName = 'indexAction';
  69. /**
  70. * Name of the special error action method which is called in case of errors
  71. * @var string
  72. * @api
  73. */
  74. protected $errorMethodName = 'errorAction';
  75. /**
  76. * Injects the reflection service
  77. *
  78. * @param Tx_Extbase_Reflection_Service $reflectionService
  79. * @return void
  80. */
  81. public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
  82. $this->reflectionService = $reflectionService;
  83. }
  84. /**
  85. * Checks if the current request type is supported by the controller.
  86. *
  87. * If your controller only supports certain request types, either
  88. * replace / modify the supporteRequestTypes property or override this
  89. * method.
  90. *
  91. * @param Tx_Extbase_MVC_Request $request The current request
  92. * @return boolean TRUE if this request type is supported, otherwise FALSE
  93. */
  94. public function canProcessRequest(Tx_Extbase_MVC_Request $request) {
  95. return parent::canProcessRequest($request);
  96. }
  97. /**
  98. * Handles a request. The result output is returned by altering the given response.
  99. *
  100. * @param Tx_Extbase_MVC_Request $request The request object
  101. * @param Tx_Extbase_MVC_Response $response The response, modified by this handler
  102. * @return void
  103. */
  104. public function processRequest(Tx_Extbase_MVC_Request $request, Tx_Extbase_MVC_Response $response) {
  105. if (!$this->canProcessRequest($request)) throw new Tx_Extbase_MVC_Exception_UnsupportedRequestType(get_class($this) . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes) , 1187701131);
  106. $this->request = $request;
  107. $this->request->setDispatched(TRUE);
  108. $this->response = $response;
  109. $this->uriBuilder = t3lib_div::makeInstance('Tx_Extbase_MVC_Web_Routing_UriBuilder');
  110. $this->uriBuilder->setRequest($request);
  111. $this->actionMethodName = $this->resolveActionMethodName();
  112. $this->initializeActionMethodArguments();
  113. $this->initializeActionMethodValidators();
  114. $this->initializeAction();
  115. $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
  116. if (method_exists($this, $actionInitializationMethodName)) {
  117. call_user_func(array($this, $actionInitializationMethodName));
  118. }
  119. $this->mapRequestArgumentsToControllerArguments();
  120. $this->checkRequestHash();
  121. $this->view = $this->resolveView();
  122. if ($this->view !== NULL) $this->initializeView($this->view);
  123. $this->callActionMethod();
  124. }
  125. /**
  126. * Implementation of the arguments initilization in the action controller:
  127. * Automatically registers arguments of the current action
  128. *
  129. * Don't override this method - use initializeAction() instead.
  130. *
  131. * @return void
  132. * @see initializeArguments()
  133. */
  134. protected function initializeActionMethodArguments() {
  135. $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->actionMethodName);
  136. foreach ($methodParameters as $parameterName => $parameterInfo) {
  137. $dataType = NULL;
  138. if (isset($parameterInfo['type'])) {
  139. $dataType = $parameterInfo['type'];
  140. } elseif ($parameterInfo['array']) {
  141. $dataType = 'array';
  142. }
  143. if ($dataType === NULL) throw new Tx_Extbase_MVC_Exception_InvalidArgumentType('The argument type for parameter "' . $parameterName . '" could not be detected.', 1253175643);
  144. $defaultValue = (isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL);
  145. $this->arguments->addNewArgument($parameterName, $dataType, ($parameterInfo['optional'] === FALSE), $defaultValue);
  146. }
  147. }
  148. /**
  149. * Adds the needed valiators to the Arguments:
  150. * - Validators checking the data type from the @param annotation
  151. * - Custom validators specified with @validate.
  152. *
  153. * In case @dontvalidate is NOT set for an argument, the following two
  154. * validators are also added:
  155. * - Model-based validators (@validate annotations in the model)
  156. * - Custom model validator classes
  157. *
  158. * @return void
  159. */
  160. protected function initializeActionMethodValidators() {
  161. $parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName);
  162. $dontValidateAnnotations = array();
  163. $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
  164. if (isset($methodTagsValues['dontvalidate'])) {
  165. $dontValidateAnnotations = $methodTagsValues['dontvalidate'];
  166. }
  167. foreach ($this->arguments as $argument) {
  168. $validator = $parameterValidators[$argument->getName()];
  169. if (array_search('$' . $argument->getName(), $dontValidateAnnotations) === FALSE) {
  170. $baseValidatorConjunction = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType());
  171. if ($baseValidatorConjunction !== NULL) {
  172. $validator->addValidator($baseValidatorConjunction);
  173. }
  174. }
  175. $argument->setValidator($validator);
  176. }
  177. }
  178. /**
  179. * Resolves and checks the current action method name
  180. *
  181. * @return string Method name of the current action
  182. * @throws Tx_Extbase_MVC_Exception_NoSuchAction if the action specified in the request object does not exist (and if there's no default action either).
  183. */
  184. protected function resolveActionMethodName() {
  185. $actionMethodName = $this->request->getControllerActionName() . 'Action';
  186. if (!method_exists($this, $actionMethodName)) throw new Tx_Extbase_MVC_Exception_NoSuchAction('An action "' . $actionMethodName . '" does not exist in controller "' . get_class($this) . '".', 1186669086);
  187. return $actionMethodName;
  188. }
  189. /**
  190. * Calls the specified action method and passes the arguments.
  191. *
  192. * If the action returns a string, it is appended to the content in the
  193. * response object. If the action doesn't return anything and a valid
  194. * view exists, the view is rendered automatically.
  195. *
  196. * @param string $actionMethodName Name of the action method to call
  197. * @return void
  198. * @api
  199. */
  200. protected function callActionMethod() {
  201. $argumentsAreValid = TRUE;
  202. $preparedArguments = array();
  203. foreach ($this->arguments as $argument) {
  204. $preparedArguments[] = $argument->getValue();
  205. }
  206. if ($this->argumentsMappingResults->hasErrors()) {
  207. $actionResult = call_user_func(array($this, $this->errorMethodName));
  208. } else {
  209. $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
  210. }
  211. if ($actionResult === NULL && $this->view instanceof Tx_Extbase_MVC_View_ViewInterface) {
  212. $this->response->appendContent($this->view->render());
  213. } elseif (is_string($actionResult) && strlen($actionResult) > 0) {
  214. $this->response->appendContent($actionResult);
  215. }
  216. }
  217. /**
  218. * Prepares a view for the current action and stores it in $this->view.
  219. * By default, this method tries to locate a view with a name matching
  220. * the current action.
  221. *
  222. * @return void
  223. * @api
  224. */
  225. protected function resolveView() {
  226. $view = $this->objectManager->getObject('Tx_Fluid_View_TemplateView');
  227. $controllerContext = $this->buildControllerContext();
  228. $view->setControllerContext($controllerContext);
  229. // Template Path Override
  230. $extbaseFrameworkConfiguration = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
  231. if (isset($extbaseFrameworkConfiguration['view']['templateRootPath']) && strlen($extbaseFrameworkConfiguration['view']['templateRootPath']) > 0) {
  232. $view->setTemplateRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['templateRootPath']));
  233. }
  234. if (isset($extbaseFrameworkConfiguration['view']['layoutRootPath']) && strlen($extbaseFrameworkConfiguration['view']['layoutRootPath']) > 0) {
  235. $view->setLayoutRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['layoutRootPath']));
  236. }
  237. if (isset($extbaseFrameworkConfiguration['view']['partialRootPath']) && strlen($extbaseFrameworkConfiguration['view']['partialRootPath']) > 0) {
  238. $view->setPartialRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['partialRootPath']));
  239. }
  240. if ($view->hasTemplate() === FALSE) {
  241. $viewObjectName = $this->resolveViewObjectName();
  242. if (class_exists($viewObjectName) === FALSE) $viewObjectName = 'Tx_Extbase_MVC_View_EmptyView';
  243. $view = $this->objectManager->getObject($viewObjectName);
  244. $view->setControllerContext($controllerContext);
  245. }
  246. if (method_exists($view, 'injectSettings')) {
  247. $view->injectSettings($this->settings);
  248. }
  249. $view->initializeView(); // In FLOW3, solved through Object Lifecycle methods, we need to call it explicitely
  250. $view->assign('settings', $this->settings); // same with settings injection.
  251. return $view;
  252. }
  253. /**
  254. * Determines the fully qualified view object name.
  255. *
  256. * @return mixed The fully qualified view object name or FALSE if no matching view could be found.
  257. * @api
  258. */
  259. protected function resolveViewObjectName() {
  260. $possibleViewName = $this->viewObjectNamePattern;
  261. $extensionName = $this->request->getControllerExtensionName();
  262. $possibleViewName = str_replace('@extension', $extensionName, $possibleViewName);
  263. $possibleViewName = str_replace('@controller', $this->request->getControllerName(), $possibleViewName);
  264. $possibleViewName = str_replace('@action', ucfirst($this->request->getControllerActionName()), $possibleViewName);
  265. $viewObjectName = str_replace('@format', ucfirst($this->request->getFormat()), $possibleViewName);
  266. if (class_exists($viewObjectName) === FALSE) {
  267. $viewObjectName = str_replace('@format', '', $possibleViewName);
  268. }
  269. if (class_exists($viewObjectName) === FALSE && $this->defaultViewObjectName !== NULL) {
  270. $viewObjectName = $this->defaultViewObjectName;
  271. }
  272. return $viewObjectName;
  273. }
  274. /**
  275. * Initializes the view before invoking an action method.
  276. *
  277. * Override this method to solve assign variables common for all actions
  278. * or prepare the view in another way before the action is called.
  279. *
  280. * @param Tx_Extbase_View_ViewInterface $view The view to be initialized
  281. * @return void
  282. * @api
  283. */
  284. protected function initializeView(Tx_Extbase_MVC_View_ViewInterface $view) {
  285. }
  286. /**
  287. * Initializes the controller before invoking an action method.
  288. *
  289. * Override this method to solve tasks which all actions have in
  290. * common.
  291. *
  292. * @return void
  293. * @api
  294. */
  295. protected function initializeAction() {
  296. }
  297. /**
  298. * A special action which is called if the originally intended action could
  299. * not be called, for example if the arguments were not valid.
  300. *
  301. * The default implementation sets a flash message, request errors and forwards back
  302. * to the originating action. This is suitable for most actions dealing with form input.
  303. *
  304. * We clear the page cache by default on an error as well, as we need to make sure the
  305. * data is re-evaluated when the user changes something.
  306. *
  307. * @return string
  308. * @api
  309. */
  310. protected function errorAction() {
  311. $this->request->setErrors($this->argumentsMappingResults->getErrors());
  312. $this->clearCacheOnError();
  313. $errorFlashMessage = $this->getErrorFlashMessage();
  314. if ($errorFlashMessage !== FALSE) {
  315. $this->flashMessages->add($errorFlashMessage);
  316. }
  317. if ($this->request->hasArgument('__referrer')) {
  318. $referrer = $this->request->getArgument('__referrer');
  319. $this->forward($referrer['actionName'], $referrer['controllerName'], $referrer['extensionName'], $this->request->getArguments());
  320. }
  321. $message = 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL;
  322. foreach ($this->argumentsMappingResults->getErrors() as $error) {
  323. $message .= 'Error: ' . $error->getMessage() . PHP_EOL;
  324. }
  325. foreach ($this->argumentsMappingResults->getWarnings() as $warning) {
  326. $message .= 'Warning: ' . $warning->getMessage() . PHP_EOL;
  327. }
  328. return $message;
  329. }
  330. /**
  331. * A template method for displaying custom error flash messages, or to
  332. * display no flash message at all on errors. Override this to customize
  333. * the flash message in your action controller.
  334. *
  335. * @return string|boolean The flash message or FALSE if no flash message should be set
  336. * @api
  337. */
  338. protected function getErrorFlashMessage() {
  339. return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()';
  340. }
  341. /**
  342. * Checks the request hash (HMAC), if arguments have been touched by the property mapper.
  343. *
  344. * In case the @dontverifyrequesthash-Annotation has been set, this suppresses the exception.
  345. *
  346. * @return void
  347. * @throws Tx_Extbase_MVC_Exception_InvalidOrNoRequestHash In case request hash checking failed
  348. * @author Sebastian Kurf?rst <sebastian@typo3.org>
  349. */
  350. protected function checkRequestHash() {
  351. if (!($this->request instanceof Tx_Extbase_MVC_Web_Request)) return; // We only want to check it for now for web requests.
  352. if ($this->request->isHmacVerified()) return; // all good
  353. $verificationNeeded = FALSE;
  354. foreach ($this->arguments as $argument) {
  355. if ($argument->getOrigin() == Tx_Extbase_MVC_Controller_Argument::ORIGIN_NEWLY_CREATED
  356. || $argument->getOrigin() == Tx_Extbase_MVC_Controller_Argument::ORIGIN_PERSISTENCE_AND_MODIFIED) {
  357. $verificationNeeded = TRUE;
  358. }
  359. }
  360. if ($verificationNeeded) {
  361. $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
  362. if (!isset($methodTagsValues['dontverifyrequesthash'])) {
  363. throw new Tx_Extbase_MVC_Exception_InvalidOrNoRequestHash('Request hash (HMAC) checking failed. The parameter __hmac was invalid or not set, and objects were modified.', 1255082824);
  364. }
  365. }
  366. }
  367. /**
  368. * Clear cache of current page on error. Needed because we want a re-evaluation of the data.
  369. * Better would be just do delete the cache for the error action, but that is not possible right now.
  370. *
  371. * @return void
  372. */
  373. protected function clearCacheOnError() {
  374. $extbaseSettings = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
  375. if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') {
  376. if (isset($GLOBALS['TSFE'])) {
  377. $pageUid = $GLOBALS['TSFE']->id;
  378. Tx_Extbase_Utility_Cache::clearPageCache(array($pageUid));
  379. }
  380. }
  381. }
  382. }
  383. ?>