Leuchtfeuer/locate

View on GitHub
Classes/Middleware/PageUnavailableMiddleware.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php

declare(strict_types=1);

/*
 * This file is part of the "Locate" extension for TYPO3 CMS.
 *
 * For the full copyright and license information, please read the
 * LICENSE.txt file that was distributed with this source code.
 *
 * Team YD <dev@Leuchtfeuer.com>, Leuchtfeuer Digital Marketing
 */

namespace Leuchtfeuer\Locate\Middleware;

use Doctrine\DBAL\Exception;
use Leuchtfeuer\Locate\Domain\Repository\RegionRepository;
use Leuchtfeuer\Locate\Utility\LocateUtility;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Controller\ErrorPageController;
use TYPO3\CMS\Core\Error\PageErrorHandler\InvalidPageErrorHandlerException;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerInterface;
use TYPO3\CMS\Core\Error\PageErrorHandler\PageErrorHandlerNotConfiguredException;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class PageUnavailableMiddleware implements MiddlewareInterface
{
    /**
     * @throws InvalidPageErrorHandlerException|Exception
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // Not responsible if backend user is logged in or EXT:static_info_tables is not loaded.
        if (($GLOBALS['BE_USER'] !== null && $GLOBALS['BE_USER']->user !== null && $GLOBALS['BE_USER']->user['uid'] > 0) || ExtensionManagementUtility::isLoaded('static_info_tables') === false) {
            return $handler->handle($request);
        }

        /** @var PageArguments $routing */
        $routing = $request->getAttribute('routing');
        $page = BackendUtility::getRecord('pages', $routing->getPageId());

        if (($page['tx_locate_regions'] ?? 0) > 0 && !$this->isPageAvailableInCurrentRegion($page)) {
            $errorHandler = $this->getErrorHandlerFromSite($request, 451);
            $message = 'The requested page is not available in your country.';

            if ($errorHandler instanceof PageErrorHandlerInterface) {
                return $errorHandler->handlePageError($request, $message, []);
            }

            return $this->handleDefaultError($request, 451, $message);
        }

        return $handler->handle($request);
    }

    /**
     * @throws Exception
     */
    private function isPageAvailableInCurrentRegion(array $page): bool
    {
        $countryCode = GeneralUtility::makeInstance(LocateUtility::class)->getCountryIso2FromIP();
        $regionRepository = GeneralUtility::makeInstance(RegionRepository::class);
        $countries = $regionRepository->getCountriesForPage($page['uid']);

        if (($countryCode === false || $countryCode === '-') && $regionRepository->shouldApplyWhenNoIpMatches($page['uid'])) {
            $countryCode = '-';
            $countries[$countryCode] = true;
        }

        return (bool)$page['tx_locate_invert'] === false ? isset($countries[$countryCode]) : !isset($countries[$countryCode]);
    }

    /**
     * Checks if a site is configured, and an error handler is configured for this specific status code.
     *
     * @param ServerRequestInterface $request
     * @param int $statusCode
     * @return PageErrorHandlerInterface|null
     *
     * @throws InvalidPageErrorHandlerException
     */
    protected function getErrorHandlerFromSite(ServerRequestInterface $request, int $statusCode): ?PageErrorHandlerInterface
    {
        $site = $request->getAttribute('site');
        if ($site instanceof Site) {
            try {
                return $site->getErrorHandler($statusCode);
            } catch (PageErrorHandlerNotConfiguredException $e) {
                // No error handler found, so fallback back to the generic TYPO3 error handler.
            }
        }
        return null;
    }

    /**
     * Ensures that a response object is created as a "fallback" when no error handler is configured.
     *
     * @param ServerRequestInterface $request
     * @param int $statusCode
     * @param string $reason
     * @return ResponseInterface
     */
    protected function handleDefaultError(ServerRequestInterface $request, int $statusCode, string $reason = ''): ResponseInterface
    {
        if (str_contains($request->getHeaderLine('Accept'), 'application/json')) {
            return new JsonResponse(['reason' => $reason], $statusCode);
        }
        $content = GeneralUtility::makeInstance(ErrorPageController::class)->errorAction(
            'Page Not Found',
            'The page did not exist or was inaccessible.' . ($reason ? ' Reason: ' . $reason : '')
        );
        return new HtmlResponse($content, $statusCode);
    }
}