Project

General

Profile

Feature #91925 » PageContentErrorHandler.php

Aristeidis Karavas, 2020-08-04 12:52

 
<?php

declare(strict_types=1);

/*
* 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!
*/

namespace TYPO3\CMS\Core\Error\PageErrorHandler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RequestFactory;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Routing\InvalidRouteArgumentsException;
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;

/**
* Renders the content of a page to be displayed (also in relation to language etc)
* This is typically configured via the "Sites configuration" module in the backend.
*/
class PageContentErrorHandler implements PageErrorHandlerInterface
{

/**
* @var int
*/
protected $statusCode;

/**
* @var array
*/
protected $errorHandlerConfiguration;

/**
* PageContentErrorHandler constructor.
* @param int $statusCode
* @param array $configuration
* @throws \InvalidArgumentException
*/
public function __construct(int $statusCode, array $configuration)
{
$this->statusCode = $statusCode;
if (empty($configuration['errorContentSource'])) {
throw new \InvalidArgumentException('PageContentErrorHandler needs to have a proper link set.', 1522826413);
}
$this->errorHandlerConfiguration = $configuration;
}

/**
* returns the errorHandler's configuration
*/
public function getConfiguration()
{
return $this->errorHandlerConfiguration;
}

/**
* @param string $attribute
* @return string
*/
public function getConfigurationAttribute(string $attribute): ?string
{
$value = null;
if (array_key_exists($attribute, $this->errorHandlerConfiguration))
{
$value = $this->errorHandlerConfiguration[$attribute];
}
return $value;
}

/**
* @param ServerRequestInterface $request
* @param string $message
* @param array $reasons
* @return ResponseInterface
* @throws \RuntimeException
*/
public function handlePageError(ServerRequestInterface $request, string $message, array $reasons = []): ResponseInterface
{
try {
$resolvedUrl = $this->resolveUrl($request, $this->errorHandlerConfiguration['errorContentSource']);
$content = null;
if ($resolvedUrl !== (string)$request->getUri()) {
try {
$subResponse = GeneralUtility::makeInstance(RequestFactory::class)->request($resolvedUrl, 'GET');
} catch (\Exception $e) {
throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", reason: ' . $e->getMessage(), 1544172838);
}
if ($subResponse->getStatusCode() >= 300) {
throw new \RuntimeException('Error handler could not fetch error page "' . $resolvedUrl . '", status code: ' . $subResponse->getStatusCode(), 1544172839);
}
// create new response object and re-use only the body and the content-type of the sub-request
return new Response($subResponse->getBody(), $this->statusCode, [
'Content-Type' => $subResponse->getHeader('Content-Type')
]);
}
$content = 'The error page could not be resolved, as the error page itself is not accessible';
} catch (InvalidRouteArgumentsException | SiteNotFoundException $e) {
$content = 'Invalid error handler configuration: ' . $this->errorHandlerConfiguration['errorContentSource'];
}
return new HtmlResponse($content, $this->statusCode);
}

/**
* Resolve the URL (currently only page and external URL are supported)
*
* @param ServerRequestInterface $request
* @param string $typoLinkUrl
* @return string
* @throws SiteNotFoundException
* @throws InvalidRouteArgumentsException
*/
protected function resolveUrl(ServerRequestInterface $request, string $typoLinkUrl): string
{
$linkService = GeneralUtility::makeInstance(LinkService::class);
$urlParams = $linkService->resolve($typoLinkUrl);
if ($urlParams['type'] !== 'page' && $urlParams['type'] !== 'url') {
throw new \InvalidArgumentException('PageContentErrorHandler can only handle TYPO3 urls of types "page" or "url"', 1522826609);
}
if ($urlParams['type'] === 'url') {
return $urlParams['url'];
}

// Get the site related to the configured error page
$site = GeneralUtility::makeInstance(SiteFinder::class)->getSiteByPageId((int)$urlParams['pageuid']);
// Fall back to current request for the site
if (!$site instanceof Site) {
$site = $request->getAttribute('site', null);
}
/** @var SiteLanguage $requestLanguage */
$requestLanguage = $request->getAttribute('language', null);
// Try to get the current request language from the site that was found above
if ($requestLanguage instanceof SiteLanguage && $requestLanguage->isEnabled()) {
try {
$language = $site->getLanguageById($requestLanguage->getLanguageId());
} catch (\InvalidArgumentException $e) {
$language = $site->getDefaultLanguage();
}
} else {
$language = $site->getDefaultLanguage();
}

// Build Url
$uri = $site->getRouter()->generateUri(
(int)$urlParams['pageuid'],
['_language' => $language]
);

// Fallback to the current URL if the site is not having a proper scheme and host
$currentUri = $request->getUri();
if (empty($uri->getScheme())) {
$uri = $uri->withScheme($currentUri->getScheme());
}
if (empty($uri->getUserInfo())) {
$uri = $uri->withUserInfo($currentUri->getUserInfo());
}
if (empty($uri->getHost())) {
$uri = $uri->withHost($currentUri->getHost());
}
if ($uri->getPort() === null) {
$uri = $uri->withPort($currentUri->getPort());
}

return (string)$uri;
}
}
(2-2/2)