Skip to content

Commit

Permalink
Merge pull request ezsystems#1059 from ezsystems/fix-EZP-23523-embed-…
Browse files Browse the repository at this point in the history
…content-deleted

Fix EZP-23523: Remove an embed image will cause fatal error when displaying content
  • Loading branch information
pspanja committed Oct 30, 2014
2 parents 29faac1 + 06effba commit 5149151
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 34 deletions.
Expand Up @@ -116,6 +116,7 @@ services:
- @ezpublish.fieldtype.ezxmltext.converter.view_manager
- @ezpublish.api.repository
- %ezpublish.fieldType.ezxmlText.converter.embedToHtml5.excludedAttributes%
- @?logger
tags:
- { name: ezpublish.ezxml.converter }

Expand Down
145 changes: 145 additions & 0 deletions eZ/Publish/Core/FieldType/Tests/XmlText/Converter/EmbedToHtml5Test.php
Expand Up @@ -12,6 +12,7 @@
use eZ\Publish\Core\FieldType\XmlText\Converter\EmbedToHtml5;
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
use PHPUnit_Framework_TestCase;
use DOMDocument;

/**
* Tests the EmbedToHtml5 Preconverter
Expand Down Expand Up @@ -208,6 +209,14 @@ protected function getMockLocationService()
->getMock();
}

/**
* @return \PHPUnit_Framework_MockObject_MockObject
*/
protected function getLoggerMock()
{
return $this->getMock( "Psr\\Log\\LoggerInterface" );
}

/**
* @param $contentService
* @param $locationService
Expand Down Expand Up @@ -517,4 +526,140 @@ public function testEmbedLocationThrowsUnauthorizedException()

$converter->convert( $dom );
}

public function dataProviderForTestEmbedContentNotFound()
{
return array(
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph xmlns:tmp="http://ez.no/namespaces/ezpublish3/temporary/"><embed object_id="42"/></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"/>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph>hello <embed object_id="42"/> goodbye</paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph>hello goodbye</paragraph></section>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link>hello <embed size="medium" object_id="42"/> goodbye</link></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link>hello goodbye</link></paragraph></section>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link><embed object_id="42"/></link></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"/>',
),
);
}

/**
* @param string $input
* @param string $output
*
* @dataProvider dataProviderForTestEmbedContentNotFound
*/
public function testEmbedContentNotFound( $input, $output )
{
$viewManager = $this->getMockViewManager();
$contentService = $this->getMockContentService();
$repository = $this->getMockRepository( $contentService, null );
$logger = $this->getLoggerMock();

$contentService->expects( $this->once() )
->method( "loadContent" )
->with( $this->equalTo( 42 ) )
->will(
$this->throwException(
$this->getMock( "eZ\\Publish\\API\\Repository\\Exceptions\\NotFoundException" )
)
);

$logger->expects( $this->once() )
->method( "error" )
->with(
"While generating embed for xmltext, could not locate Content object with ID 42"
);

$converter = new EmbedToHtml5(
$viewManager,
$repository,
array( "view", "class", "node_id", "object_id" ),
$logger
);

$document = new DOMDocument();
$document->loadXML( $input );

$converter->convert( $document );

$outputDocument = new DOMDocument();
$outputDocument->loadXML( $output );

$this->assertEquals( $outputDocument, $document );
}

public function dataProviderForTestEmbedLocationNotFound()
{
return array(
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph xmlns:tmp="http://ez.no/namespaces/ezpublish3/temporary/"><embed node_id="42"/></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"/>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph>hello <embed node_id="42"/> goodbye</paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph>hello goodbye</paragraph></section>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link>hello <embed node_id="42"/> goodbye</link></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link>hello goodbye</link></paragraph></section>',
),
array(
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"><paragraph><link><embed node_id="42"/></link></paragraph></section>',
'<?xml version="1.0" encoding="utf-8"?><section xmlns:custom="http://ez.no/namespaces/ezpublish3/custom/" xmlns:image="http://ez.no/namespaces/ezpublish3/image/" xmlns:xhtml="http://ez.no/namespaces/ezpublish3/xhtml/"/>',
),
);
}

/**
* @param string $input
* @param string $output
*
* @dataProvider dataProviderForTestEmbedLocationNotFound
*/
public function testEmbedLocationNotFound( $input, $output )
{
$viewManager = $this->getMockViewManager();
$locationService = $this->getMockLocationService();
$repository = $this->getMockRepository( null, $locationService );
$logger = $this->getLoggerMock();

$locationService->expects( $this->once() )
->method( "loadLocation" )
->with( $this->equalTo( 42 ) )
->will(
$this->throwException(
$this->getMock( "eZ\\Publish\\API\\Repository\\Exceptions\\NotFoundException" )
)
);

$logger->expects( $this->once() )
->method( "error" )
->with(
"While generating embed for xmltext, could not locate Location with ID 42"
);

$converter = new EmbedToHtml5(
$viewManager,
$repository,
array( "view", "class", "node_id", "object_id" ),
$logger
);

$document = new DOMDocument();
$document->loadXML( $input );

$converter->convert( $document );

$outputDocument = new DOMDocument();
$outputDocument->loadXML( $output );

$this->assertEquals( $outputDocument, $document );
}
}
139 changes: 105 additions & 34 deletions eZ/Publish/Core/FieldType/XmlText/Converter/EmbedToHtml5.php
Expand Up @@ -15,6 +15,8 @@
use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo;
use eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface;
use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException;
use Psr\Log\LoggerInterface;

/**
* Converts embedded elements from internal XmlText representation to HTML5
Expand All @@ -37,11 +39,22 @@ class EmbedToHtml5 implements Converter
*/
protected $repository;

public function __construct( ViewManagerInterface $viewManager, Repository $repository, array $excludedAttributes )
/**
* @var \Psr\Log\LoggerInterface
*/
protected $logger;

public function __construct(
ViewManagerInterface $viewManager,
Repository $repository,
array $excludedAttributes,
LoggerInterface $logger = null
)
{
$this->viewManager = $viewManager;
$this->repository = $repository;
$this->excludedAttributes = array_fill_keys( $excludedAttributes, true );
$this->logger = $logger;
}

/**
Expand Down Expand Up @@ -76,56 +89,114 @@ protected function processTag( DOMDocument $xmlDoc, $tagName )

if ( $contentId = $embed->getAttribute( "object_id" ) )
{
/** @var \eZ\Publish\API\Repository\Values\Content\Content $content */
$content = $this->repository->sudo(
function ( Repository $repository ) use ( $contentId )
try
{
/** @var \eZ\Publish\API\Repository\Values\Content\Content $content */
$content = $this->repository->sudo(
function ( Repository $repository ) use ( $contentId )
{
return $repository->getContentService()->loadContent( $contentId );
}
);

if (
!$this->repository->canUser( 'content', 'read', $content )
&& !$this->repository->canUser( 'content', 'view_embed', $content )
)
{
return $repository->getContentService()->loadContent( $contentId );
throw new UnauthorizedException( 'content', 'read', array( 'contentId' => $contentId ) );
}
);

if (
!$this->repository->canUser( 'content', 'read', $content )
&& !$this->repository->canUser( 'content', 'view_embed', $content )
)
{
throw new UnauthorizedException( 'content', 'read', array( 'contentId' => $contentId ) );
}
// Check published status of the Content
if (
$content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
&& !$this->repository->canUser( 'content', 'versionread', $content )
)
{
throw new UnauthorizedException( 'content', 'versionread', array( 'contentId' => $contentId ) );
}

// Check published status of the Content
if (
$content->getVersionInfo()->status !== APIVersionInfo::STATUS_PUBLISHED
&& !$this->repository->canUser( 'content', 'versionread', $content )
)
$embedContent = $this->viewManager->renderContent( $content, $view, $parameters );
}
catch ( APINotFoundException $e )
{
throw new UnauthorizedException( 'content', 'versionread', array( 'contentId' => $contentId ) );
if ( $this->logger )
{
$this->logger->error(
"While generating embed for xmltext, could not locate " .
"Content object with ID " . $contentId
);
}
}

$embedContent = $this->viewManager->renderContent( $content, $view, $parameters );
}
else if ( $locationId = $embed->getAttribute( "node_id" ) )
{
/** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
$location = $this->repository->sudo(
function ( Repository $repository ) use ( $locationId )
try
{
/** @var \eZ\Publish\API\Repository\Values\Content\Location $location */
$location = $this->repository->sudo(
function ( Repository $repository ) use ( $locationId )
{
return $repository->getLocationService()->loadLocation( $locationId );
}
);

if (
!$this->repository->canUser( 'content', 'read', $location->getContentInfo(), $location )
&& !$this->repository->canUser( 'content', 'view_embed', $location->getContentInfo(), $location )
)
{
return $repository->getLocationService()->loadLocation( $locationId );
throw new UnauthorizedException( 'content', 'read', array( 'locationId' => $location->id ) );
}
);

if (
!$this->repository->canUser( 'content', 'read', $location->getContentInfo(), $location )
&& !$this->repository->canUser( 'content', 'view_embed', $location->getContentInfo(), $location )
)
$embedContent = $this->viewManager->renderLocation( $location, $view, $parameters );
}
catch ( APINotFoundException $e )
{
throw new UnauthorizedException( 'content', 'read', array( 'locationId' => $location->id ) );
if ( $this->logger )
{
$this->logger->error(
"While generating embed for xmltext, could not locate " .
"Location with ID " . $locationId
);
}
}

$embedContent = $this->viewManager->renderLocation( $location, $view, $parameters );
}

if ( $embedContent !== null )
if ( $embedContent === null )
{
// Remove tmp paragraph
if ( $embed->parentNode->lookupNamespaceUri( 'tmp' ) !== null )
{
$embed->parentNode->parentNode->removeChild( $embed->parentNode );
}
// Remove empty link
else if ( $embed->parentNode->localName === "link" && $embed->parentNode->childNodes->length === 1 )
{
// Remove paragraph with empty link
if (
$embed->parentNode->parentNode->localName === "paragraph" &&
$embed->parentNode->parentNode->childNodes->length === 1
)
{
$embed->parentNode->parentNode->parentNode->removeChild( $embed->parentNode->parentNode );
}
// Remove empty link
else
{
$embed->parentNode->parentNode->removeChild( $embed->parentNode );
}
}
// Remove empty embed
else
{
$embed->parentNode->removeChild( $embed );
}
}
else
{
$embed->appendChild( $xmlDoc->createCDATASection( $embedContent ) );
}
}
}

Expand Down

0 comments on commit 5149151

Please sign in to comment.