|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de> 00006 * All rights reserved 00007 * 00008 * This class is a backport of the corresponding class of FLOW3. 00009 * All credits go to the v5 team. 00010 * 00011 * This script is part of the TYPO3 project. The TYPO3 project is 00012 * free software; you can redistribute it and/or modify 00013 * it under the terms of the GNU General Public License as published by 00014 * the Free Software Foundation; either version 2 of the License, or 00015 * (at your option) any later version. 00016 * 00017 * The GNU General Public License can be found at 00018 * http://www.gnu.org/copyleft/gpl.html. 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00027 00028 /** 00029 * A multi action controller. This is by far the most common base class for Controllers. 00030 * 00031 * @package Extbase 00032 * @subpackage MVC\Controller 00033 * @version $ID:$ 00034 * @api 00035 */ 00036 class Tx_Extbase_MVC_Controller_ActionController extends Tx_Extbase_MVC_Controller_AbstractController { 00037 00038 /** 00039 * @var Tx_Extbase_Reflection_Service 00040 */ 00041 protected $reflectionService; 00042 00043 /** 00044 * The current view, as resolved by resolveView() 00045 * 00046 * @var Tx_Extbase_MVC_View_ViewInterface 00047 * @api 00048 */ 00049 protected $view = NULL; 00050 00051 /** 00052 * Pattern after which the view object name is built if no Fluid template 00053 * is found. 00054 * @var string 00055 * @api 00056 */ 00057 protected $viewObjectNamePattern = 'Tx_@extension_View_@controller_@action@format'; 00058 00059 /** 00060 * A list of formats and object names of the views which should render them. 00061 * 00062 * Example: 00063 * 00064 * array('html' => 'Tx_MyExtension_View_MyHtmlView', 'json' => 'F3... 00065 * 00066 * @var array 00067 */ 00068 protected $viewFormatToObjectNameMap = array(); 00069 00070 /** 00071 * The default view object to use if none of the resolved views can render 00072 * a response for the current request. 00073 * 00074 * @var string 00075 * @api 00076 */ 00077 protected $defaultViewObjectName = 'Tx_Fluid_View_TemplateView'; 00078 00079 /** 00080 * Name of the action method 00081 * @var string 00082 * @api 00083 */ 00084 protected $actionMethodName = 'indexAction'; 00085 00086 /** 00087 * Name of the special error action method which is called in case of errors 00088 * @var string 00089 * @api 00090 */ 00091 protected $errorMethodName = 'errorAction'; 00092 00093 /** 00094 * Injects the reflection service 00095 * 00096 * @param Tx_Extbase_Reflection_Service $reflectionService 00097 * @return void 00098 */ 00099 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) { 00100 $this->reflectionService = $reflectionService; 00101 } 00102 00103 /** 00104 * Checks if the current request type is supported by the controller. 00105 * 00106 * If your controller only supports certain request types, either 00107 * replace / modify the supporteRequestTypes property or override this 00108 * method. 00109 * 00110 * @param Tx_Extbase_MVC_Request $request The current request 00111 * @return boolean TRUE if this request type is supported, otherwise FALSE 00112 */ 00113 public function canProcessRequest(Tx_Extbase_MVC_RequestInterface $request) { 00114 return parent::canProcessRequest($request); 00115 00116 } 00117 00118 /** 00119 * Handles a request. The result output is returned by altering the given response. 00120 * 00121 * @param Tx_Extbase_MVC_Request $request The request object 00122 * @param Tx_Extbase_MVC_Response $response The response, modified by this handler 00123 * @return void 00124 */ 00125 public function processRequest(Tx_Extbase_MVC_RequestInterface $request, Tx_Extbase_MVC_ResponseInterface $response) { 00126 if (!$this->canProcessRequest($request)) { 00127 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); 00128 } 00129 00130 $this->request = $request; 00131 $this->request->setDispatched(TRUE); 00132 $this->response = $response; 00133 00134 $this->uriBuilder = $this->objectManager->create('Tx_Extbase_MVC_Web_Routing_UriBuilder'); 00135 $this->uriBuilder->setRequest($request); 00136 00137 $this->actionMethodName = $this->resolveActionMethodName(); 00138 00139 $this->initializeActionMethodArguments(); 00140 $this->initializeActionMethodValidators(); 00141 00142 $this->initializeAction(); 00143 $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName); 00144 if (method_exists($this, $actionInitializationMethodName)) { 00145 call_user_func(array($this, $actionInitializationMethodName)); 00146 } 00147 00148 $this->mapRequestArgumentsToControllerArguments(); 00149 $this->checkRequestHash(); 00150 $this->controllerContext = $this->buildControllerContext(); 00151 $this->view = $this->resolveView(); 00152 if ($this->view !== NULL) { 00153 $this->initializeView($this->view); 00154 } 00155 $this->callActionMethod(); 00156 } 00157 00158 /** 00159 * Implementation of the arguments initilization in the action controller: 00160 * Automatically registers arguments of the current action 00161 * 00162 * Don't override this method - use initializeAction() instead. 00163 * 00164 * @return void 00165 * @see initializeArguments() 00166 */ 00167 protected function initializeActionMethodArguments() { 00168 $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->actionMethodName); 00169 00170 foreach ($methodParameters as $parameterName => $parameterInfo) { 00171 $dataType = NULL; 00172 if (isset($parameterInfo['type'])) { 00173 $dataType = $parameterInfo['type']; 00174 } elseif ($parameterInfo['array']) { 00175 $dataType = 'array'; 00176 } 00177 if ($dataType === NULL) throw new Tx_Extbase_MVC_Exception_InvalidArgumentType('The argument type for parameter "' . $parameterName . '" could not be detected.', 1253175643); 00178 00179 $defaultValue = (isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL); 00180 00181 $this->arguments->addNewArgument($parameterName, $dataType, ($parameterInfo['optional'] === FALSE), $defaultValue); 00182 } 00183 } 00184 00185 /** 00186 * Adds the needed valiators to the Arguments: 00187 * - Validators checking the data type from the @param annotation 00188 * - Custom validators specified with @validate. 00189 * 00190 * In case @dontvalidate is NOT set for an argument, the following two 00191 * validators are also added: 00192 * - Model-based validators (@validate annotations in the model) 00193 * - Custom model validator classes 00194 * 00195 * @return void 00196 */ 00197 protected function initializeActionMethodValidators() { 00198 $parameterValidators = $this->validatorResolver->buildMethodArgumentsValidatorConjunctions(get_class($this), $this->actionMethodName); 00199 00200 $dontValidateAnnotations = array(); 00201 $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName); 00202 if (isset($methodTagsValues['dontvalidate'])) { 00203 $dontValidateAnnotations = $methodTagsValues['dontvalidate']; 00204 } 00205 00206 foreach ($this->arguments as $argument) { 00207 $validator = $parameterValidators[$argument->getName()]; 00208 00209 if (array_search('$' . $argument->getName(), $dontValidateAnnotations) === FALSE) { 00210 $baseValidatorConjunction = $this->validatorResolver->getBaseValidatorConjunction($argument->getDataType()); 00211 if ($baseValidatorConjunction !== NULL) { 00212 $validator->addValidator($baseValidatorConjunction); 00213 } 00214 } 00215 $argument->setValidator($validator); 00216 } 00217 } 00218 00219 /** 00220 * Resolves and checks the current action method name 00221 * 00222 * @return string Method name of the current action 00223 * @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). 00224 */ 00225 protected function resolveActionMethodName() { 00226 $actionMethodName = $this->request->getControllerActionName() . 'Action'; 00227 if (!method_exists($this, $actionMethodName)) throw new Tx_Extbase_MVC_Exception_NoSuchAction('An action "' . $actionMethodName . '" does not exist in controller "' . get_class($this) . '".', 1186669086); 00228 return $actionMethodName; 00229 } 00230 00231 /** 00232 * Calls the specified action method and passes the arguments. 00233 * 00234 * If the action returns a string, it is appended to the content in the 00235 * response object. If the action doesn't return anything and a valid 00236 * view exists, the view is rendered automatically. 00237 * 00238 * @param string $actionMethodName Name of the action method to call 00239 * @return void 00240 * @api 00241 */ 00242 protected function callActionMethod() { 00243 $argumentsAreValid = TRUE; 00244 $preparedArguments = array(); 00245 foreach ($this->arguments as $argument) { 00246 $preparedArguments[] = $argument->getValue(); 00247 } 00248 00249 if ($this->argumentsMappingResults->hasErrors()) { 00250 $actionResult = call_user_func(array($this, $this->errorMethodName)); 00251 } else { 00252 $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments); 00253 } 00254 if ($actionResult === NULL && $this->view instanceof Tx_Extbase_MVC_View_ViewInterface) { 00255 $this->response->appendContent($this->view->render()); 00256 } elseif (is_string($actionResult) && strlen($actionResult) > 0) { 00257 $this->response->appendContent($actionResult); 00258 } 00259 } 00260 00261 /** 00262 * Prepares a view for the current action and stores it in $this->view. 00263 * By default, this method tries to locate a view with a name matching 00264 * the current action. 00265 * 00266 * @return void 00267 * @api 00268 */ 00269 protected function resolveView() { 00270 $viewObjectName = $this->resolveViewObjectName(); 00271 if ($viewObjectName !== FALSE) { 00272 $view = $this->objectManager->create($viewObjectName); 00273 $this->setViewConfiguration($view); 00274 if ($view->canRender($this->controllerContext) === FALSE) { 00275 unset($view); 00276 } 00277 } 00278 if (!isset($view) && $this->defaultViewObjectName != '') { 00279 $view = $this->objectManager->create($this->defaultViewObjectName); 00280 $this->setViewConfiguration($view); 00281 if ($view->canRender($this->controllerContext) === FALSE) { 00282 unset($view); 00283 } 00284 } 00285 if (!isset($view)) { 00286 $view = $this->objectManager->create('Tx_Extbase_MVC_View_NotFoundView'); 00287 $view->assign('errorMessage', 'No template was found. View could not be resolved for action "' . $this->request->getControllerActionName() . '"'); 00288 } 00289 $view->setControllerContext($this->controllerContext); 00290 00291 if (method_exists($view, 'injectSettings')) { 00292 $view->injectSettings($this->settings); 00293 } 00294 $view->initializeView(); // In FLOW3, solved through Object Lifecycle methods, we need to call it explicitely 00295 $view->assign('settings', $this->settings); // same with settings injection. 00296 return $view; 00297 } 00298 00299 /** 00300 * @param Tx_Extbase_MVC_View_ViewInterface $view 00301 * @return void 00302 */ 00303 protected function setViewConfiguration(Tx_Extbase_MVC_View_ViewInterface $view) { 00304 // Template Path Override 00305 $extbaseFrameworkConfiguration = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); 00306 if (isset($extbaseFrameworkConfiguration['view']['templateRootPath']) 00307 && strlen($extbaseFrameworkConfiguration['view']['templateRootPath']) > 0 00308 && method_exists($view, 'setTemplateRootPath')) { 00309 $view->setTemplateRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['templateRootPath'])); 00310 } 00311 if (isset($extbaseFrameworkConfiguration['view']['layoutRootPath']) 00312 && strlen($extbaseFrameworkConfiguration['view']['layoutRootPath']) > 0 00313 && method_exists($view, 'setLayoutRootPath')) { 00314 $view->setLayoutRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['layoutRootPath'])); 00315 } 00316 if (isset($extbaseFrameworkConfiguration['view']['partialRootPath']) 00317 && strlen($extbaseFrameworkConfiguration['view']['partialRootPath']) > 0 00318 && method_exists($view, 'setPartialRootPath')) { 00319 $view->setPartialRootPath(t3lib_div::getFileAbsFileName($extbaseFrameworkConfiguration['view']['partialRootPath'])); 00320 } 00321 } 00322 00323 /** 00324 * Determines the fully qualified view object name. 00325 * 00326 * @return mixed The fully qualified view object name or FALSE if no matching view could be found. 00327 * @api 00328 */ 00329 protected function resolveViewObjectName() { 00330 $possibleViewName = $this->viewObjectNamePattern; 00331 $extensionName = $this->request->getControllerExtensionName(); 00332 $possibleViewName = str_replace('@extension', $extensionName, $possibleViewName); 00333 $possibleViewName = str_replace('@controller', $this->request->getControllerName(), $possibleViewName); 00334 $possibleViewName = str_replace('@action', ucfirst($this->request->getControllerActionName()), $possibleViewName); 00335 $format = $this->request->getFormat(); 00336 00337 $viewObjectName = str_replace('@format', ucfirst($this->request->getFormat()), $possibleViewName); 00338 if (class_exists($viewObjectName) === FALSE) { 00339 $viewObjectName = str_replace('@format', '', $possibleViewName); 00340 } 00341 if (class_exists($viewObjectName) === FALSE && isset($this->viewFormatToObjectNameMap[$format])) { 00342 $viewObjectName = $this->viewFormatToObjectNameMap[$format]; 00343 } 00344 return class_exists($viewObjectName) ? $viewObjectName : FALSE; 00345 } 00346 00347 /** 00348 * Initializes the view before invoking an action method. 00349 * 00350 * Override this method to solve assign variables common for all actions 00351 * or prepare the view in another way before the action is called. 00352 * 00353 * @param Tx_Extbase_View_ViewInterface $view The view to be initialized 00354 * @return void 00355 * @api 00356 */ 00357 protected function initializeView(Tx_Extbase_MVC_View_ViewInterface $view) { 00358 } 00359 00360 /** 00361 * Initializes the controller before invoking an action method. 00362 * 00363 * Override this method to solve tasks which all actions have in 00364 * common. 00365 * 00366 * @return void 00367 * @api 00368 */ 00369 protected function initializeAction() { 00370 } 00371 00372 /** 00373 * A special action which is called if the originally intended action could 00374 * not be called, for example if the arguments were not valid. 00375 * 00376 * The default implementation sets a flash message, request errors and forwards back 00377 * to the originating action. This is suitable for most actions dealing with form input. 00378 * 00379 * We clear the page cache by default on an error as well, as we need to make sure the 00380 * data is re-evaluated when the user changes something. 00381 * 00382 * @return string 00383 * @api 00384 */ 00385 protected function errorAction() { 00386 $this->request->setErrors($this->argumentsMappingResults->getErrors()); 00387 $this->clearCacheOnError(); 00388 00389 $errorFlashMessage = $this->getErrorFlashMessage(); 00390 if ($errorFlashMessage !== FALSE) { 00391 $this->flashMessages->add($errorFlashMessage, '', t3lib_FlashMessage::ERROR); 00392 } 00393 00394 if ($this->request->hasArgument('__referrer')) { 00395 $referrer = $this->request->getArgument('__referrer'); 00396 $this->forward($referrer['actionName'], $referrer['controllerName'], $referrer['extensionName'], $this->request->getArguments()); 00397 } 00398 00399 $message = 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL; 00400 foreach ($this->argumentsMappingResults->getErrors() as $error) { 00401 $message .= 'Error: ' . $error->getMessage() . PHP_EOL; 00402 } 00403 foreach ($this->argumentsMappingResults->getWarnings() as $warning) { 00404 $message .= 'Warning: ' . $warning->getMessage() . PHP_EOL; 00405 } 00406 return $message; 00407 } 00408 00409 /** 00410 * A template method for displaying custom error flash messages, or to 00411 * display no flash message at all on errors. Override this to customize 00412 * the flash message in your action controller. 00413 * 00414 * @return string|boolean The flash message or FALSE if no flash message should be set 00415 * @api 00416 */ 00417 protected function getErrorFlashMessage() { 00418 return 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '()'; 00419 } 00420 00421 /** 00422 * Checks the request hash (HMAC), if arguments have been touched by the property mapper. 00423 * 00424 * In case the @dontverifyrequesthash-Annotation has been set, this suppresses the exception. 00425 * 00426 * @return void 00427 * @throws Tx_Extbase_MVC_Exception_InvalidOrNoRequestHash In case request hash checking failed 00428 * @author Sebastian Kurfürst <sebastian@typo3.org> 00429 */ 00430 protected function checkRequestHash() { 00431 if (!($this->request instanceof Tx_Extbase_MVC_Web_Request)) return; // We only want to check it for now for web requests. 00432 if ($this->request->isHmacVerified()) return; // all good 00433 00434 $verificationNeeded = FALSE; 00435 foreach ($this->arguments as $argument) { 00436 if ($argument->getOrigin() == Tx_Extbase_MVC_Controller_Argument::ORIGIN_NEWLY_CREATED 00437 || $argument->getOrigin() == Tx_Extbase_MVC_Controller_Argument::ORIGIN_PERSISTENCE_AND_MODIFIED) { 00438 $verificationNeeded = TRUE; 00439 } 00440 } 00441 if ($verificationNeeded) { 00442 $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName); 00443 if (!isset($methodTagsValues['dontverifyrequesthash'])) { 00444 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); 00445 } 00446 } 00447 } 00448 00449 /** 00450 * Clear cache of current page on error. Needed because we want a re-evaluation of the data. 00451 * Better would be just do delete the cache for the error action, but that is not possible right now. 00452 * 00453 * @return void 00454 */ 00455 protected function clearCacheOnError() { 00456 $extbaseSettings = $this->configurationManager->getConfiguration(Tx_Extbase_Configuration_ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); 00457 if (isset($extbaseSettings['persistence']['enableAutomaticCacheClearing']) && $extbaseSettings['persistence']['enableAutomaticCacheClearing'] === '1') { 00458 if (isset($GLOBALS['TSFE'])) { 00459 $pageUid = $GLOBALS['TSFE']->id; 00460 Tx_Extbase_Utility_Cache::clearPageCache(array($pageUid)); 00461 } 00462 } 00463 } 00464 00465 } 00466 ?>
1.8.0