diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php index 28b0643531421dd4119f83ea145cbb5d53740934..d78478cb9763730c2582b8df00d6a5f5eda19c03 100644 --- a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php +++ b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php @@ -22,7 +22,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; @@ -49,6 +51,11 @@ class ActionController implements ControllerInterface */ protected $hashService; + /** + * @var ViewResolverInterface + */ + private $viewResolver; + /** * The current view, as resolved by resolveView() * @@ -102,6 +109,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 */ @@ -370,10 +386,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..599d9a27a8f165a7986df3e9b124d577a7cfc50a --- /dev/null +++ b/typo3/sysext/extbase/Classes/Mvc/View/GenericViewResolver.php @@ -0,0 +1,54 @@ +<?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($format === 'json' ? JsonView::class : $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..06a7c127a7b5dceb7cd508d04fb5caf05e0da3bc --- /dev/null +++ b/typo3/sysext/extbase/Classes/Mvc/View/ViewResolverInterface.php @@ -0,0 +1,15 @@ +<?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 34c0f0af74e7f54c0d27574925efba69f508a1b1..660571afe672322bd0288ca3f20af9c55627510d 100644 --- a/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php +++ b/typo3/sysext/extbase/Tests/Functional/Mvc/Controller/ActionControllerTest.php @@ -16,6 +16,7 @@ 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; @@ -116,4 +117,50 @@ class ActionControllerTest extends \TYPO3\TestingFramework\Core\Functional\Funct $validators->rewind(); self::assertInstanceOf(NotEmptyValidator::class, $validators->current()); } + + /** + * @test + */ + public function resolveViewRespectsRequestedFormat() + { + // Test setup + $this->request->setControllerActionName('qux'); + $this->request->setFormat('json'); + + // Test run + $this->controller->processRequest($this->request, $this->response); + + // Assertions + $reflectionClass = new \ReflectionClass($this->controller); + $reflectionMethod = $reflectionClass->getProperty('view'); + $reflectionMethod->setAccessible(true); + + $view = $reflectionMethod->getValue($this->controller); + self::assertInstanceOf(JsonView::class, $view); + } + + /** + * @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 52889fe1a9af6861c5d25d8ac5cf0f6afb4eddf4..a936ba4606322508dc0dbb1b962b52df143a8e45 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 @@ -69,4 +69,9 @@ class TestController extends ActionController // return string so we don't need to mock a view return ''; } + + public function quxAction() + { + return ''; + } }