From 9c0e151e311639fbc930ba36f767ec11dfef2bef Mon Sep 17 00:00:00 2001 From: Alexander Schnitzler <git@alexanderschnitzler.de> Date: Wed, 26 Feb 2020 10:24:46 +0100 Subject: [PATCH] [BUGFIX] Automatically use JsonView for json format With https://review.typo3.org/c/Packages/TYPO3.CMS/+/59514/ all possibilities have been taken to define which view class to use based on the format query param. As those format query params are still api and in use, the core must reintegrate the former behaviour: Instantiating a JsonView object for requests with a format query param set to 'json'. The implementation is marked as internal as the overall idea is to remove the format param in the long run. Releases: master Resolves: #90788 Change-Id: I62aaa8f44126edbe1238a5a972044f239f3362eb Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/63438 Tested-by: TYPO3com <noreply@typo3.com> Tested-by: Benni Mack <benni@typo3.org> Tested-by: Susanne Moog <look@susi.dev> Reviewed-by: Georg Ringer <georg.ringer@gmail.com> Reviewed-by: Benni Mack <benni@typo3.org> Reviewed-by: Susanne Moog <look@susi.dev> --- .../Mvc/Controller/ActionController.php | 36 +++++++++++-- .../Classes/Mvc/View/GenericViewResolver.php | 54 +++++++++++++++++++ .../Mvc/View/ViewResolverInterface.php | 15 ++++++ .../extbase/Configuration/Services.yaml | 3 ++ .../Mvc/Controller/ActionControllerTest.php | 47 ++++++++++++++++ .../Fixture/Controller/TestController.php | 5 ++ 6 files changed, 156 insertions(+), 4 deletions(-) create mode 100644 typo3/sysext/extbase/Classes/Mvc/View/GenericViewResolver.php create mode 100644 typo3/sysext/extbase/Classes/Mvc/View/ViewResolverInterface.php diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php index 28b064353142..d78478cb9763 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 000000000000..599d9a27a8f1 --- /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 000000000000..06a7c127a7b5 --- /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 e393761fbb7b..d0c5dfda66bc 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 34c0f0af74e7..660571afe672 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 52889fe1a9af..a936ba460632 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 ''; + } } -- GitLab