Skip to content

Commit

Permalink
[TASK] Improve redirect handling
Browse files Browse the repository at this point in the history
Replace default SiteBaseRedirectResolver
with headless' one in order to handle proper redirects with headless approach.
  • Loading branch information
twoldanski committed Aug 30, 2023
1 parent e7c1fe8 commit 188931c
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 1 deletion.
50 changes: 50 additions & 0 deletions Classes/Middleware/SiteBaseRedirectResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

/*
* This file is part of the "headless" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/

declare(strict_types=1);

namespace FriendsOfTYPO3\Headless\Middleware;

use FriendsOfTYPO3\Headless\Utility\UrlUtility;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class SiteBaseRedirectResolver extends \TYPO3\CMS\Frontend\Middleware\SiteBaseRedirectResolver
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = parent::process($request, $handler);

$site = $request->getAttribute('site');
$siteConf = $site->getConfiguration();

if (!($siteConf['headless'] ?? false)) {
return $response;
}

if ($response instanceof RedirectResponse) {
$urlUtility = GeneralUtility::makeInstance(UrlUtility::class)->withRequest($request);
return new JsonResponse([
'redirectUrl' => $urlUtility->prepareRelativeUrlIfPossible(
$urlUtility->getFrontendUrlWithSite(
$response->getHeader('location')[0] ?? '',
$site
)
),
'statusCode' => $response->getStatusCode(),
]);
}

return $response;
}
}
6 changes: 5 additions & 1 deletion Configuration/RequestMiddlewares.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use FriendsOfTYPO3\Headless\Middleware\ElementBodyResponseMiddleware;
use FriendsOfTYPO3\Headless\Middleware\RedirectHandler;
use FriendsOfTYPO3\Headless\Middleware\ShortcutAndMountPointRedirect;
use FriendsOfTYPO3\Headless\Middleware\SiteBaseRedirectResolver;
use FriendsOfTYPO3\Headless\Middleware\UserIntMiddleware;
use TYPO3\CMS\Core\Configuration\Features;
use TYPO3\CMS\Core\Utility\GeneralUtility;
Expand All @@ -31,7 +32,7 @@
if ($features->isFeatureEnabled('headless.elementBodyResponse')) {
$middlewares['frontend']['headless/cms-frontend/element-body-response'] = [
'after' => [
'typo3/cms-adminpanel/data-persister',
'typo3/cms-frontend/content-length-headers',
],
'target' => ElementBodyResponseMiddleware::class,
];
Expand Down Expand Up @@ -60,6 +61,9 @@
'typo3/cms-frontend/shortcut-and-mountpoint-redirect' => [
'disabled' => true,
],
'typo3/cms-frontend/base-redirect-resolver' => [
'target' => SiteBaseRedirectResolver::class,
],
'headless/cms-redirects/redirecthandler' => [
'target' => RedirectHandler::class,
'before' => [
Expand Down
155 changes: 155 additions & 0 deletions Tests/Unit/Middleware/SiteBaseRedirectResolverTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<?php

/*
* This file is part of the "headless" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/

declare(strict_types=1);

namespace FriendsOfTYPO3\Headless\Tests\Unit\Middleware;

use FriendsOfTYPO3\Headless\Middleware\SiteBaseRedirectResolver;
use FriendsOfTYPO3\Headless\Utility\UrlUtility;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\DependencyInjection\Container;
use TYPO3\CMS\Core\ExpressionLanguage\Resolver;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Http\ServerRequest;
use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Core\Routing\SiteRouteResult;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\ErrorController;
use TYPO3\CMS\Frontend\Http\RequestHandler;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

use function json_decode;

class SiteBaseRedirectResolverTest extends UnitTestCase
{
use ProphecyTrait;
protected bool $resetSingletonInstances = true;

public function testJsonRedirect()
{
$site = new Site('test', 1, [
'base' => 'https://www.typo3.org',
'headless' => true,
'languages' => [
[
'title' => 'English',
'enabled' => true,
'languageId' => 0,
'base' => '/en-us',
'typo3Language' => 'default',
'locale' => 'en_US.UTF-8',
'iso-639-1' => 'en',
'navigationTitle' => 'English',
'hreflang' => 'en-us',
'direction' => 'ltr',
'flag' => 'us',
],
]]);

$siteFinder = $this->prophesize(SiteFinder::class);
$siteFinder->getSiteByPageId(1)->willReturn($site);

$container = new Container();
$urlUtility = GeneralUtility::makeInstance(UrlUtility::class, null, $this->prophesize(Resolver::class)->reveal(), $siteFinder->reveal());
$container->set(UrlUtility::class, $urlUtility);

GeneralUtility::setContainer($container);

$resolver = new SiteBaseRedirectResolver();

$request = new ServerRequest();
$request = $request->withAttribute('site', $site);

$uri = new Uri('https://www.typo3.org/');

$request = $request->withUri($uri);
$request = $request->withAttribute('routing', new SiteRouteResult($uri, $site));

$response = $resolver->process($request, $this->prophesize(RequestHandler::class)->reveal());

self::assertSame(['redirectUrl' => 'https://www.typo3.org/en-us', 'statusCode' => 307], json_decode($response->getBody()->getContents(), true));

// language resolved
$uri = new Uri('https://www.typo3.org/en-us/');

$request = $request->withUri($uri);
$request = $request->withAttribute('routing', new SiteRouteResult($uri, $site));
$request = $request->withAttribute('language', new SiteLanguage(0, 'en', new Uri('/en-us'), ['enabled' => true]));

$handler = $this->prophesize(RequestHandler::class);
$handler->handle($request)->willReturn(new JsonResponse(['nextMiddleware' => true]));

$response = $resolver->process($request, $handler->reveal());

self::assertSame(['nextMiddleware' => true], json_decode($response->getBody()->getContents(), true));

// handle initial data
$uri = new Uri('https://www.typo3.org/en-us/?type=834');

$request = $request->withUri($uri);
$request = $request->withAttribute('language', null);

$handler = $this->prophesize(RequestHandler::class);
$handler->handle($request)->willReturn(new JsonResponse(['nextMiddleware' => true]));

$response = $resolver->process($request, $handler->reveal());

self::assertSame(['redirectUrl' => 'https://www.typo3.org/en-us', 'statusCode' => 307], json_decode($response->getBody()->getContents(), true));

// handle non-headless domain
$site = new Site('test', 1, [
'base' => 'https://www.typo3.org',
'headless' => false,
'languages' => [
[
'title' => 'English',
'enabled' => true,
'languageId' => 0,
'base' => '/en-us',
'typo3Language' => 'default',
'locale' => 'en_US.UTF-8',
'iso-639-1' => 'en',
'navigationTitle' => 'English',
'hreflang' => 'en-us',
'direction' => 'ltr',
'flag' => 'us',
],
]]);

$siteFinder = $this->prophesize(SiteFinder::class);
$siteFinder->getSiteByPageId(1)->willReturn($site);

$container = new Container();
$urlUtility = GeneralUtility::makeInstance(UrlUtility::class, null, $this->prophesize(Resolver::class)->reveal(), $siteFinder->reveal());
$container->set(UrlUtility::class, $urlUtility);
$errorController = $this->prophesize(ErrorController::class);
$errorController->pageNotFoundAction(Argument::any(), Argument::any(), Argument::any())->willReturn(new JsonResponse(['ErrorController' => true]));

$container->set(ErrorController::class, $errorController->reveal());
GeneralUtility::setContainer($container);

$uri = new Uri('https://www.typo3.org/');

$request = new ServerRequest();
$request = $request->withUri($uri);
$request = $request->withAttribute('site', $site);

$handler = $this->prophesize(RequestHandler::class);
$handler->handle($request)->willReturn(new JsonResponse(['nextMiddleware' => true]));

$response = $resolver->process($request, $handler->reveal());

self::assertSame(['ErrorController' => true], json_decode($response->getBody()->getContents(), true));
}
}

0 comments on commit 188931c

Please sign in to comment.