diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php index e8da5211583e42fd4a1852ae4e984449722fa10e..ec6f3867f34a205b3a1fa4c2cfd2759a93d8f5bf 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php +++ b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php @@ -23,7 +23,9 @@ use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; use TYPO3\CMS\Extbase\Event\Mvc\BeforeActionCallEvent; use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; +use TYPO3\CMS\Extbase\Mvc\View\GenericViewResolver; use TYPO3\CMS\Extbase\Mvc\View\ViewInterface; +use TYPO3\CMS\Extbase\Mvc\View\ViewResolverInterface; use TYPO3\CMS\Extbase\Mvc\Web\ReferringRequest; use TYPO3\CMS\Extbase\Security\Cryptography\HashService; use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator; @@ -50,6 +52,11 @@ class ActionController implements ControllerInterface */ protected $hashService; + /** + * @var ViewResolverInterface + */ + private $viewResolver; + /** * The current view, as resolved by resolveView() * @@ -103,6 +110,15 @@ class ActionController implements ControllerInterface */ protected $response; + /** + * @param ViewResolverInterface $viewResolver + * @internal + */ + public function injectViewResolver(ViewResolverInterface $viewResolver) + { + $this->viewResolver = $viewResolver; + } + /** * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService */ @@ -371,10 +387,22 @@ class ActionController implements ControllerInterface */ protected function resolveView() { - $view = null; - if ($this->defaultViewObjectName != '') { - /** @var ViewInterface $view */ - $view = $this->objectManager->get($this->defaultViewObjectName); + if ($this->viewResolver instanceof GenericViewResolver) { + /* + * This setter is not part of the ViewResolverInterface as it's only necessary to set + * the default view class from this point when using the generic view resolver which + * must respect the possibly overridden property defaultViewObjectName. + */ + $this->viewResolver->setDefaultViewClass($this->defaultViewObjectName); + } + + $view = $this->viewResolver->resolve( + $this->request->getControllerObjectName(), + $this->request->getControllerActionName(), + $this->request->getFormat() + ); + + if ($view instanceof ViewInterface) { $this->setViewConfiguration($view); if ($view->canRender($this->controllerContext) === false) { $view = null; diff --git a/typo3/sysext/extbase/Classes/Mvc/View/GenericViewResolver.php b/typo3/sysext/extbase/Classes/Mvc/View/GenericViewResolver.php new file mode 100644 index 0000000000000000000000000000000000000000..766c85c4393bddf4f95b8cbd7049016d69a83de3 --- /dev/null +++ b/typo3/sysext/extbase/Classes/Mvc/View/GenericViewResolver.php @@ -0,0 +1,55 @@ +<?php + +declare(strict_types=1); + +namespace TYPO3\CMS\Extbase\Mvc\View; + +/* + * 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\Object\ObjectManager; + +/** + * @internal only to be used within Extbase, not part of TYPO3 Core API. + */ +class GenericViewResolver implements ViewResolverInterface +{ + /** + * @var ObjectManager + */ + private $objectManager; + + /** + * @var string + */ + private $defaultViewClass; + + public function __construct(ObjectManager $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param string $defaultViewClass + * @internal + */ + public function setDefaultViewClass(string $defaultViewClass): void + { + $this->defaultViewClass = $defaultViewClass; + } + + public function resolve(string $controllerObjectName, string $actionName, string $format): ViewInterface + { + return $this->objectManager->get($this->defaultViewClass); + } +} diff --git a/typo3/sysext/extbase/Classes/Mvc/View/ViewResolverInterface.php b/typo3/sysext/extbase/Classes/Mvc/View/ViewResolverInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..27ee2f9c334942b8e5769e77aa776380bbb4719b --- /dev/null +++ b/typo3/sysext/extbase/Classes/Mvc/View/ViewResolverInterface.php @@ -0,0 +1,16 @@ +<?php + +declare(strict_types=1); + +namespace TYPO3\CMS\Extbase\Mvc\View; + +/** + * @internal only to be used within Extbase, not part of TYPO3 Core API. + * + * It's safe to use this interface in TYPO3 10 LTS as it will not be changed or removed in that + * version but this interface is likely to be removed and/or changed in version 11. + */ +interface ViewResolverInterface +{ + public function resolve(string $controllerObjectName, string $actionName, string $format): ViewInterface; +} diff --git a/typo3/sysext/extbase/Configuration/Services.yaml b/typo3/sysext/extbase/Configuration/Services.yaml index e393761fbb7b76e199c62248e54bb51ecee8f4e8..d0c5dfda66bc86bb9ed68ad9261fc82114d16a82 100644 --- a/typo3/sysext/extbase/Configuration/Services.yaml +++ b/typo3/sysext/extbase/Configuration/Services.yaml @@ -83,3 +83,6 @@ services: identifier: 'legacy-slot' method: 'emitAfterPersistObjectSignal' event: TYPO3\CMS\Extbase\Event\Persistence\EntityPersistedEvent + + TYPO3\CMS\Extbase\Mvc\View\GenericViewResolver: ~ + TYPO3\CMS\Extbase\Mvc\View\ViewResolverInterface: '@TYPO3\CMS\Extbase\Mvc\View\GenericViewResolver' diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php index e8fabffe25055cc8173af8a6831fafafca22dc7a..e68796e94346dbb443609336f7a694e9254c982b 100644 --- a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php +++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php @@ -18,6 +18,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Extbase\Tests\Functional\Mvc\Controller; use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\View\JsonView; use TYPO3\CMS\Extbase\Mvc\Web\Request; use TYPO3\CMS\Extbase\Mvc\Web\Response; use TYPO3\CMS\Extbase\Object\ObjectManager; @@ -118,4 +119,29 @@ class ActionControllerTest extends \TYPO3\TestingFramework\Core\Functional\Funct $validators->rewind(); self::assertInstanceOf(NotEmptyValidator::class, $validators->current()); } + + /** + * @test + */ + public function resolveViewRespectsDefaultViewObjectName() + { + // Test setup + $reflectionClass = new \ReflectionClass($this->controller); + $reflectionMethod = $reflectionClass->getProperty('defaultViewObjectName'); + $reflectionMethod->setAccessible(true); + $reflectionMethod->setValue($this->controller, JsonView::class); + + $this->request->setControllerActionName('qux'); + + // Test run + $this->controller->processRequest($this->request, $this->response); + + // Assertions + $reflectionMethod = $reflectionClass->getProperty('view'); + $reflectionMethod->setAccessible(true); + $reflectionMethod->getValue($this->controller); + + $view = $reflectionMethod->getValue($this->controller); + self::assertInstanceOf(JsonView::class, $view); + } } diff --git a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php index 32f9ec608e865efd928e8b347a9bad99c6e46c53..6f2b22aead7c6aab569ea310ed9350ad63422a33 100644 --- a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php +++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/Fixture/Controller/TestController.php @@ -71,4 +71,9 @@ class TestController extends ActionController // return string so we don't need to mock a view return ''; } + + public function quxAction() + { + return ''; + } }