Skip to content

Commit

Permalink
Merged branch '4.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
alongosz committed Nov 28, 2024
2 parents 6ca16e9 + 312f7ea commit 8bdad66
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 13 deletions.
2 changes: 2 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ parameters:
paths:
- src/*
- tests/*
-
message: "#^Cannot call method warning\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#"
paths:
- src
- tests
Expand Down
2 changes: 2 additions & 0 deletions src/bundle/Core/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ services:
$authorizationChecker: "@security.authorization_checker"
$locationProvider: '@Ibexa\Core\Helper\PreviewLocationProvider'
$controllerChecker: '@Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker'
$debugMode: '%kernel.debug%'
$logger: '@logger'
tags:
- { name: controller.service_arguments }

Expand Down
73 changes: 61 additions & 12 deletions src/lib/MVC/Symfony/Controller/Content/PreviewController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@

use Exception;
use Ibexa\Contracts\Core\Repository\ContentService;
use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException;
use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException;
use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException;
use Ibexa\Contracts\Core\Repository\LocationService;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Core\Base\Exceptions\BadStateException;
use Ibexa\Core\Helper\ContentPreviewHelper;
use Ibexa\Core\Helper\PreviewLocationProvider;
use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator;
Expand All @@ -22,6 +24,9 @@
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker;
use Ibexa\Core\MVC\Symfony\View\ViewManagerInterface;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand All @@ -30,6 +35,8 @@

class PreviewController
{
use LoggerAwareTrait;

public const PREVIEW_PARAMETER_NAME = 'isPreview';
public const CONTENT_VIEW_ROUTE = 'ibexa.content.view';

Expand All @@ -54,14 +61,18 @@ class PreviewController
/** @var \Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker */
private $controllerChecker;

private bool $debugMode;

public function __construct(
ContentService $contentService,
LocationService $locationService,
HttpKernelInterface $kernel,
ContentPreviewHelper $previewHelper,
AuthorizationCheckerInterface $authorizationChecker,
PreviewLocationProvider $locationProvider,
CustomLocationControllerChecker $controllerChecker
CustomLocationControllerChecker $controllerChecker,
bool $debugMode = false,
?LoggerInterface $logger = null
) {
$this->contentService = $contentService;
$this->locationService = $locationService;
Expand All @@ -70,6 +81,8 @@ public function __construct(
$this->authorizationChecker = $authorizationChecker;
$this->locationProvider = $locationProvider;
$this->controllerChecker = $controllerChecker;
$this->debugMode = $debugMode;
$this->logger = $logger ?? new NullLogger();
}

/**
Expand Down Expand Up @@ -120,18 +133,19 @@ public function previewContentAction(
HttpKernelInterface::SUB_REQUEST,
false
);
} catch (\Exception $e) {
if ($location->isDraft() && $this->controllerChecker->usesCustomController($content, $location)) {
// @todo This should probably be an exception that embeds the original one
$message = <<<EOF
<p>The view that rendered this location draft uses a custom controller, and resulted in a fatal error.</p>
<p>Location View is deprecated, as it causes issues with preview, such as an empty location id when previewing the first version of a content.</p>
EOF;

throw new Exception($message, 0, $e);
} else {
throw $e;
} catch (APINotFoundException $e) {
$message = sprintf('Location (%s) not found or not available in requested language (%s)', $location->id, $language);
$this->logger->warning(
sprintf('%s %s', $message, 'when loading the preview page'),
['exception' => $e]
);
if ($this->debugMode) {
throw new BadStateException('Preview page', $message, $e);
}

return new Response($message);
} catch (Exception $e) {
return $this->buildResponseForGenericPreviewError($location, $content, $e);
}
$response->headers->addCacheControlDirective('no-cache', true);
$response->setPrivate();
Expand Down Expand Up @@ -188,4 +202,39 @@ protected function getForwardRequest(
$forwardRequestParameters
);
}

/**
* @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException
*/
private function buildResponseForGenericPreviewError(Location $location, Content $content, Exception $e): Response
{
$message = '';
try {
if ($location->isDraft() && $this->controllerChecker->usesCustomController($content, $location)) {
$message = <<<EOF
<p>The view that rendered this location draft uses a custom controller, and resulted in a fatal error.</p>
<p>Location View is deprecated, as it causes issues with preview, such as an empty location id when previewing the first version of a content.</p>
EOF;
}
} catch (Exception $innerException) {
$message = 'An exception occurred when handling page preview exception';
$this->logger->warning(
'Unable to check if location uses a custom controller when loading the preview page',
['exception' => $innerException]
);
}

$this->logger->warning('Unable to load the preview page', ['exception' => $e]);

$message .= <<<EOF
<p>Unable to load the preview page</p>
<p>See logs for more information</p>
EOF;

if ($this->debugMode) {
throw new BadStateException('Preview page', $message, $e);
}

return new Response($message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
use Ibexa\Contracts\Core\Repository\LocationService;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Location;
use Ibexa\Core\Base\Exceptions\NotFoundException;
use Ibexa\Core\Base\Exceptions\UnauthorizedException;
use Ibexa\Core\Helper\ContentPreviewHelper;
use Ibexa\Core\Helper\PreviewLocationProvider;
use Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController;
use Ibexa\Core\MVC\Symfony\SiteAccess;
use Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
Expand Down Expand Up @@ -51,6 +53,9 @@ final class PreviewControllerTest extends TestCase
/** @var \Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker&\PHPUnit\Framework\MockObject\MockObject */
protected CustomLocationControllerChecker $controllerChecker;

/** @var \Psr\Log\LoggerInterface&\PHPUnit\Framework\MockObject\MockObject */
private LoggerInterface $logger;

protected function setUp(): void
{
parent::setUp();
Expand All @@ -62,6 +67,7 @@ protected function setUp(): void
$this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
$this->locationProvider = $this->createMock(PreviewLocationProvider::class);
$this->controllerChecker = $this->createMock(CustomLocationControllerChecker::class);
$this->logger = $this->createMock(LoggerInterface::class);
}

protected function getPreviewController(): PreviewController
Expand All @@ -73,7 +79,9 @@ protected function getPreviewController(): PreviewController
$this->previewHelper,
$this->authorizationChecker,
$this->locationProvider,
$this->controllerChecker
$this->controllerChecker,
false,
$this->logger
);
}

Expand Down Expand Up @@ -128,6 +136,42 @@ public function testPreviewCanUserFail(): void
$controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test');
}

public function testPreviewWithLogMessage(): void
{
$controller = $this->getPreviewController();
$contentId = 123;
$lang = 'eng-GB';
$versionNo = 3;
$content = $this->createMock(Content::class);

$location = $this->createMock(Location::class);
$location->method('__get')->with('id')->willReturn('42');

$siteAccess = $this->createMock(SiteAccess::class);
$this->locationProvider
->method('loadMainLocationByContent')
->with($content)
->willReturn($location)
;
$this->contentService
->method('loadContent')
->with($contentId, [$lang], $versionNo)
->willReturn($content)
;

$this->authorizationChecker->method('isGranted')->willReturn(true);
$siteAccess->name = 'test';
$this->previewHelper->method('getOriginalSiteAccess')->willReturn($siteAccess);
$this->httpKernel->method('handle')->willThrowException(new NotFoundException('Foo Property', 'foobar'));

$this->logger
->expects(self::once())
->method('warning')
->with('Location (42) not found or not available in requested language (eng-GB) when loading the preview page');

$controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test');
}

/**
* @return iterable<string, array{SiteAccess|null, int, string, int, int|null, string|null}>
*/
Expand Down

0 comments on commit 8bdad66

Please sign in to comment.