Commit aacf6289 authored by Kevin Ditscheid's avatar Kevin Ditscheid

[TASK] Rebuild redirect handling as a PSR-15 middleware

parent b0241e53
......@@ -370,15 +370,17 @@ class RouteController extends ActionController {
*
* @param array $filters
* @return void
* @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Core\Exception
* @throws \TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException
* @throws \InvalidArgumentException
* @throws \UnexpectedValueException
*/
public function htaccessAction(array $filters = []) {
if (LicensingService::checkKey()) {
/** @var array $routes */
$routes = $this->routeRepository->findRoutes($this->rootPageUid, $filters, TRUE);
$data = RoutingService::getHtaccessEntries($routes);
$routingService = GeneralUtility::makeInstance(RoutingService::class);
$data = $routingService->getHtaccessEntries($routes);
$this->view->assign('data', $data);
}
......
<?php
/**
*
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
namespace SGalinski\SgRoutes\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use SGalinski\SgRoutes\Service\LicensingService;
use SGalinski\SgRoutes\Service\RoutingService;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Controller\ErrorController;
use TYPO3\CMS\Frontend\Page\PageAccessFailureReasons;
/**
* Middleware resolver class for the sg_routes
*
* @package SGalinski\SgRoutes\Middleware
* @author Kevin Ditscheid <kevin.ditscheid@sgalinski.de>
*/
class RedirectResolver implements MiddlewareInterface {
const URL_WIZARD_ID = 't3://page?uid=';
/**
* Process an incoming server request.
*
* Processes an incoming server request in order to produce a response.
* If unable to produce the response itself, it may delegate to the provided
* request handler to do so.
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
$start = \microtime(TRUE);
$uri = $request->getUri();
$requestParameters = $request->getQueryParams();
$requestPath = $uri->getPath();
$requestParameterPageId = NULL;
$filteredRequestParameters = [];
foreach ($requestParameters as $key => $splittedParameter) {
if ($key === 'id') {
$requestParameterPageId = $splittedParameter;
continue;
}
$filteredRequestParameters[$key] = $splittedParameter;
}
$requestParameters = $filteredRequestParameters;
$routingService = GeneralUtility::makeInstance(RoutingService::class);
if ($requestParameterPageId !== NULL) {
$uri = $uri->withQuery('id=' . $requestParameterPageId);
$rootUid = $routingService->getRootUid($requestParameterPageId);
} else {
$uri = $uri->withQuery('');
$rootUid = $routingService->getRootUidByDomain($uri->getHost(), $requestPath);
}
if ($rootUid) {
if ($GLOBALS['TYPO3_CONF_VARS']['FE']['tx_routes_executed'][$rootUid]) {
// already redirected the route to $rootUid, proceed with page loading
return $handler->handle($request);
}
$GLOBALS['TYPO3_CONF_VARS']['FE']['tx_routes_executed'][$rootUid] = TRUE;
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_sgroutes_domain_model_route');
$queryBuilder->getRestrictions()->removeAll()->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$resultArray = $queryBuilder->select(
'uid',
'pid',
'use_regular_expression',
'source_url',
'source_url_case_sensitive',
'destination_url',
'redirect_url_parameters',
'redirect_code',
'description')
->from('tx_sgroutes_domain_model_route')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($rootUid, \PDO::PARAM_INT))
)
->orderBy('sorting')
->execute()->fetchAll();
if ($resultArray) {
foreach ($resultArray as $redirect) {
if ($redirect['source_url'] === '' || $redirect['destination_url'] === '') {
continue;
}
if (\substr($redirect['source_url'], -1) === '/') {
$trimmedSourceUrl = \substr($redirect['source_url'], 0, -1);
} else {
$trimmedSourceUrl = $redirect['source_url'];
}
if ($redirect['use_regular_expression'] && LicensingService::checkKey()) {
$pattern = '/' . \str_replace('/', '\\/', $redirect['source_url']) . '/is';
if ($redirect['redirect_url_parameters']) {
$uri = $uri->withQuery($request->getUri()->getQuery());
} elseif (!empty($requestParameters)) {
$uri = $uri->withQuery(GeneralUtility::implodeArrayForUrl('', $requestParameters));
}
if (\preg_match($pattern, \rawurldecode($uri))) {
$redirectUri = \preg_replace(
$pattern, $redirect['destination_url'], \rawurldecode($uri)
);
$this->logRedirect(
$redirect, \rawurldecode($uri), $redirectUri, $start, \microtime(TRUE)
);
return new RedirectResponse($redirectUri, $redirect['redirect_code']);
}
} elseif (
(
(bool)$redirect['source_url_case_sensitive'] &&
\rawurldecode($uri) === $trimmedSourceUrl
) ||
(
!(bool)$redirect['source_url_case_sensitive'] &&
\mb_strtolower(\rawurldecode($uri)) === \mb_strtolower($trimmedSourceUrl)
)
) {
$destinationUrl = $redirect['destination_url'];
if (\strpos($destinationUrl, self::URL_WIZARD_ID) === 0) {
$destinationUrl = (int) \substr($destinationUrl, 14);
}
if (\is_numeric($destinationUrl)) {
$contentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class, $GLOBALS['TSFE']);
$redirectUri = $contentObjectRenderer->typoLink_URL([
'parameter' => $destinationUrl,
'additionalParams' => '&' . \implode('&', $requestParameters)
]);
if ($redirectUri === '') {
// the page either does not exist or isn't accessible, drop out
return GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction(
$request,
'The page with id #' . $destinationUrl . ' could not be found!',
['code' => PageAccessFailureReasons::PAGE_NOT_FOUND]
);
}
} else {
$redirectUri = $destinationUrl . '&' . \implode('&', $requestParameters);
$this->logRedirect(
$redirect, \rawurldecode($uri), $redirectUri, $start, \microtime(TRUE)
);
return new RedirectResponse($redirectUri, $redirect['redirect_code']);
}
}
}
}
}
return $handler->handle($request);
}
/**
* @param array $routeRow
* @param string $requestUri
* @param string $redirectUri
* @param float $start
* @param float $end
* @return void
* @throws \InvalidArgumentException
*/
private function logRedirect($routeRow, $requestUri, $redirectUri, $start, $end): void {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_sgroutes_domain_model_log');
$queryBuilder->getRestrictions()->removeAll();
$categories = $queryBuilder->select('c.title')
->from('tx_sgroutes_domain_model_category', 'c')
->innerJoin('c', 'tx_sgroutes_domain_model_route_category', 'mm', 'c.uid = mm.uid_foreign')
->where(
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($routeRow['uid'], \PDO::PARAM_INT))
)
->orderBy('mm.sorting')
->execute()->fetchAll();
$insertRow = [
'pid' => $routeRow['pid'],
'use_regular_expression' => $routeRow['use_regular_expression'],
'source_url' => $routeRow['source_url'],
'destination_url' => $routeRow['destination_url'],
'redirect_url_parameters' => $routeRow['redirect_url_parameters'],
'redirect_code' => $routeRow['redirect_code'],
'description' => $routeRow['description'],
'categories' => '',
'request_url' => $requestUri,
'redirect_url' => $redirectUri,
'execution_duration' => 1000 * ($end - $start),
'crdate' => $GLOBALS['EXEC_TIME'],
];
if ($categories) {
/** @var array $category */
foreach ($categories as $i => $category) {
if ($insertRow['categories'] !== '') {
$insertRow['categories'] .= ', ';
}
$insertRow['categories'] .= $category['category_title'];
}
}
$queryBuilder->resetQueryParts();
$queryBuilder->insert('tx_sgroutes_domain_model_log')
->values($insertRow)
->execute();
}
}
This diff is collapsed.
<?php
/**
*
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
*/
return [
'frontend' => [
'sgalinski/sg-routes/redirect-resolver' => [
'target' => '',
'before' => [
'typo3/cms-frontend/page-resolver'
]
]
]
];
......@@ -11,7 +11,16 @@
"issues": "https://gitlab.sgalinski.de/typo3/sg_routes"
},
"require": {
"typo3/cms-core": "^8.7.0 || ^9.5.1"
"typo3/cms-core": "^9.5.3",
"typo3/cms-frontend": "^9.5.3",
"typo3/cms-extbase": "^9.5.3",
"typo3/cms-fluid": "^9.5.3"
},
"require-dev": {
"roave/security-advisories": "dev-master"
},
"suggest": {
"sgalinski/sg-account": "^5.10.3"
},
"replace": {
"sg_routes": "self.version"
......
......@@ -14,12 +14,13 @@ $EM_CONF[$_EXTKEY] = [
'author_company' => 'sgalinski Internet Services (https://www.sgalinski.de)',
'constraints' => [
'depends' => [
'typo3' => '8.7.0-9.5.99',
'php' => '7.0.0-7.3.99',
'typo3' => '9.5.0-9.5.99',
'php' => '7.2.0-7.3.99',
],
'conflicts' => [
],
'suggests' => [
'sg_account' => ''
],
],
];
......
......@@ -26,9 +26,6 @@
call_user_func(
function ($extKey) {
// hook registration
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_fe.php']['determineId-PostProc'][$extKey]
= 'SGalinski\SgRoutes\Service\RoutingService->hook';
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['linkHandler']['unknown'] =
\SGalinski\SgRoutes\Hook\UnknownLinkHandlerType::class;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment