Skip to content

Commit

Permalink
IBX-4877: Allow Location-based Content preview (#359)
Browse files Browse the repository at this point in the history
  • Loading branch information
barw4 committed Feb 6, 2023
1 parent 42b6cdf commit f2bc8ef
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 27 deletions.
13 changes: 7 additions & 6 deletions eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,13 @@ services:
ezpublish.controller.content.preview.core:
class: eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController
arguments:
- "@ezpublish.api.service.content"
- "@http_kernel"
- "@ezpublish.content_preview_helper"
- "@security.authorization_checker"
- "@ezpublish.content_preview.location_provider"
- "@ezpublish.view.custom_location_controller_checker"
$contentService: '@ezpublish.api.service.content'
$locationService: '@ezpublish.api.service.location'
$kernel: '@http_kernel'
$previewHelper: '@ezpublish.content_preview_helper'
$authorizationChecker: '@security.authorization_checker'
$locationProvider: '@ezpublish.content_preview.location_provider'
$controllerChecker: '@ezpublish.view.custom_location_controller_checker'
tags:
- { name: controller.service_arguments }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
{% endif %}
<title>{{ title|default( 'Home' ) }}</title>
<meta name="generator" content="Ibexa DXP"/>
{% if content is defined and content.contentInfo.mainLocationId %}
{% if content is defined and location is defined and location is not null and location.id is not null %}
<link rel="canonical" href="{{ ez_path(location) }}" />
{% elseif content is defined and content.contentInfo.mainLocationId %}
<link rel="canonical" href="{{ ez_path(content) }}" />
{% endif %}
</head>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\Exceptions\NotImplementedException;
use eZ\Publish\API\Repository\Exceptions\UnauthorizedException;
use eZ\Publish\API\Repository\LocationService;
use eZ\Publish\API\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\Location;
use eZ\Publish\Core\Helper\ContentPreviewHelper;
Expand All @@ -33,6 +34,12 @@ class PreviewController
/** @var \eZ\Publish\API\Repository\ContentService */
private $contentService;

/** @var \eZ\Publish\API\Repository\LocationService */
private $locationService;

/** @var \eZ\Publish\Core\Helper\PreviewLocationProvider */
private $locationProvider;

/** @var \Symfony\Component\HttpKernel\HttpKernelInterface */
private $kernel;

Expand All @@ -47,13 +54,15 @@ class PreviewController

public function __construct(
ContentService $contentService,
LocationService $locationService,
HttpKernelInterface $kernel,
ContentPreviewHelper $previewHelper,
AuthorizationCheckerInterface $authorizationChecker,
PreviewLocationProvider $locationProvider,
CustomLocationControllerChecker $controllerChecker
) {
$this->contentService = $contentService;
$this->locationService = $locationService;
$this->kernel = $kernel;
$this->previewHelper = $previewHelper;
$this->authorizationChecker = $authorizationChecker;
Expand All @@ -63,14 +72,24 @@ public function __construct(

/**
* @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If Content is missing location as this is not supported in current version
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
*/
public function previewContentAction(Request $request, $contentId, $versionNo, $language, $siteAccessName = null)
{
public function previewContentAction(
Request $request,
$contentId,
$versionNo,
$language,
$siteAccessName = null,
?int $locationId = null
) {
$this->previewHelper->setPreviewActive(true);

try {
$content = $this->contentService->loadContent($contentId, [$language], $versionNo);
$location = $this->locationProvider->loadMainLocationByContent($content);
$location = $locationId !== null
? $this->locationService->loadLocation($locationId)
: $this->locationProvider->loadMainLocationByContent($content);

if (!$location instanceof Location) {
throw new NotImplementedException('Preview for content without Locations');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
namespace eZ\Publish\Core\MVC\Symfony\Controller\Tests\Controller\Content;

use eZ\Publish\API\Repository\ContentService;
use eZ\Publish\API\Repository\LocationService;
use eZ\Publish\API\Repository\Values\Content\Content;
use eZ\Publish\API\Repository\Values\Content\ContentInfo;
use eZ\Publish\API\Repository\Values\Content\Location;
Expand All @@ -26,50 +27,51 @@

class PreviewControllerTest extends TestCase
{
/** @var \PHPUnit\Framework\MockObject\MockObject */
/** @var \eZ\Publish\API\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */
protected $contentService;

/** @var \PHPUnit\Framework\MockObject\MockObject */
/** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */
protected $locationService;

/** @var \eZ\Publish\Core\Helper\PreviewLocationProvider|\PHPUnit\Framework\MockObject\MockObject */
protected $httpKernel;

/** @var \PHPUnit\Framework\MockObject\MockObject */
/** @var \eZ\Publish\Core\Helper\ContentPreviewHelper|\PHPUnit\Framework\MockObject\MockObject */
protected $previewHelper;

/** @var \PHPUnit\Framework\MockObject\MockObject */
/** @var \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface|\PHPUnit\Framework\MockObject\MockObject */
protected $authorizationChecker;

/** @var \eZ\Publish\Core\Helper\PreviewLocationProvider|\PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker */
/** @var \eZ\Publish\Core\Helper\PreviewLocationProvider|\PHPUnit\Framework\MockObject\MockObject */
protected $locationProvider;

/** @var \eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker|\PHPUnit\Framework\MockObject\MockObject */
protected $controllerChecker;

protected function setUp(): void
{
parent::setUp();

$this->contentService = $this->createMock(ContentService::class);
$this->locationService = $this->createMock(LocationService::class);
$this->httpKernel = $this->createMock(HttpKernelInterface::class);
$this->previewHelper = $this->createMock(ContentPreviewHelper::class);
$this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class);
$this->locationProvider = $this->createMock(PreviewLocationProvider::class);
$this->controllerChecker = $this->createMock(CustomLocationControllerChecker::class);
}

/**
* @return \eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController
*/
protected function getPreviewController()
protected function getPreviewController(): PreviewController
{
$controller = new PreviewController(
return new PreviewController(
$this->contentService,
$this->locationService,
$this->httpKernel,
$this->previewHelper,
$this->authorizationChecker,
$this->locationProvider,
$this->controllerChecker
);

return $controller;
}

public function testPreviewUnauthorized()
Expand Down Expand Up @@ -275,6 +277,83 @@ public function testPreviewDefaultSiteaccess()
);
}

/**
* @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException
* @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException
* @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException
*/
public function testPreviewWithLocationId(): void
{
$contentId = 123;
$lang = 'eng-GB';
$versionNo = 3;
$locationId = 456;
$content = $this->createMock(Content::class);
$location = $this->getMockBuilder(Location::class)
->setConstructorArgs([['id' => $locationId]])
->getMockForAbstractClass();

$this->contentService
->expects(self::once())
->method('loadContent')
->with($contentId, [$lang], $versionNo)
->willReturn($content);

$this->locationService
->expects(self::once())
->method('loadLocation')
->with($locationId)
->willReturn($location);

$this->authorizationChecker
->expects(self::once())
->method('isGranted')
->with(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))
->willReturn(true);

$previousSiteAccessName = 'foo';
$previousSiteAccess = new SiteAccess($previousSiteAccessName);
$request = $this->getMockBuilder(Request::class)
->onlyMethods(['duplicate'])
->getMock();

$this->previewHelper
->expects(self::once())
->method('getOriginalSiteAccess')
->willReturn($previousSiteAccess);

$this->previewHelper
->expects(self::once())
->method('restoreConfigScope');

$duplicatedRequest = $this->getDuplicatedRequest($location, $content, $previousSiteAccess);
$request
->expects(self::once())
->method('duplicate')
->willReturn($duplicatedRequest);

$expectedResponse = new Response();
$this->httpKernel
->expects(self::once())
->method('handle')
->with($duplicatedRequest, HttpKernelInterface::SUB_REQUEST)
->willReturn($expectedResponse);

$controller = $this->getPreviewController();

self::assertSame(
$expectedResponse,
$controller->previewContentAction(
$request,
$contentId,
$versionNo,
$lang,
null,
$locationId
)
);
}

/**
* @param $location
* @param $content
Expand Down
5 changes: 0 additions & 5 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,6 @@ parameters:
count: 1
path: eZ/Publish/Core/FieldType/User/UserStorage.php

-
message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\PreviewController\\:\\:\\$locationProvider\\.$#"
count: 2
path: eZ/Publish/Core/MVC/Symfony/Controller/Content/PreviewController.php

-
message: "#^Method eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationRemote\\:\\:matchContentInfo\\(\\) should return bool but return statement is missing\\.$#"
count: 1
Expand Down

0 comments on commit f2bc8ef

Please sign in to comment.