diff --git a/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php index d55b49e688..eb5dadb47e 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php +++ b/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php @@ -6,9 +6,12 @@ */ namespace eZ\Publish\API\Repository\Tests\FieldType; +use Doctrine\DBAL\ParameterType; +use DOMDocument; use eZ\Publish\API\Repository\Values\Content\Content; use eZ\Publish\API\Repository\Values\Content\Field; use eZ\Publish\Core\FieldType\Image\Value as ImageValue; +use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; /** * Integration test for use field type. @@ -760,6 +763,108 @@ public function testRemovingDraftRemovesOldImage(): void ); } + public function testDeleteImageWithCorruptedName(): void + { + $content = $this->publishNewImage( + __METHOD__, + new ImageValue( + [ + 'inputUri' => __DIR__ . '/_fixtures/image.jpg', + 'fileName' => 'image.jpg', + 'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'), + 'alternativeText' => 'Alternative', + ] + ), + [2] + ); + + $imageFieldDefinition = $content->getContentType()->getFieldDefinition('image'); + + $record = $this->fetchXML( + $content->id, + $content->getVersionInfo()->versionNo, + $imageFieldDefinition->id + ); + + $document = $this->corruptImageFieldXML($record); + + $this->updateXML( + $content->id, + $content->getVersionInfo()->versionNo, + $imageFieldDefinition->id, + $document + ); + + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + + $contentService->deleteContent($content->getVersionInfo()->getContentInfo()); + + // Expect no League\Flysystem\CorruptedPathDetected thrown + } + + /** + * @return array + */ + private function fetchXML(int $contentId, int $versionNo, int $fieldDefinitionId): array + { + $connection = $this->getRawDatabaseConnection(); + + $query = $connection->createQueryBuilder(); + $query + ->select('data_text') + ->from(Gateway::CONTENT_FIELD_TABLE) + ->andWhere('contentclassattribute_id = :contentclassattribute_id') + ->andWhere('version = :version') + ->andWhere('contentobject_id = :contentobject_id') + ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('version', $versionNo, ParameterType::INTEGER) + ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); + $result = $query->execute(); + + return $result->fetchAssociative(); + } + + /** + * @param array $row + */ + private function corruptImageFieldXML(array $row): DOMDocument + { + $corruptedChar = '­'; + + $document = new DOMDocument('1.0', 'utf-8'); + $document->loadXML($row['data_text']); + $elements = $document->getElementsByTagName('ezimage'); + $element = $elements->item(0); + $element->setAttribute('filename', $element->getAttribute('filename') . $corruptedChar); + $element->setAttribute('url', $element->getAttribute('url') . $corruptedChar); + + return $document; + } + + private function updateXML( + int $contentId, + int $versionNo, + int $fieldDefinitionId, + DOMDocument $document + ): void { + $connection = $this->getRawDatabaseConnection(); + + $query = $connection->createQueryBuilder(); + $query + ->update(Gateway::CONTENT_FIELD_TABLE) + ->set('data_text', ':data_text') + ->setParameter('data_text', $document->saveXML(), ParameterType::STRING) + ->andWhere('contentclassattribute_id = :contentclassattribute_id') + ->andWhere('version = :version') + ->andWhere('contentobject_id = :contentobject_id') + ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('version', $versionNo, ParameterType::INTEGER) + ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); + + $query->execute(); + } + /** * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException diff --git a/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php b/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php index 433802adf4..e0575b0b85 100644 --- a/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php +++ b/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php @@ -11,6 +11,7 @@ use eZ\Publish\Core\IO\UrlDecorator; use eZ\Publish\SPI\IO\BinaryFileCreateStruct; use League\Flysystem\AdapterInterface; +use League\Flysystem\CorruptedPathDetected; use League\Flysystem\FileExistsException; use League\Flysystem\FileNotFoundException as FlysystemNotFoundException; use League\Flysystem\FilesystemInterface; @@ -56,7 +57,7 @@ public function delete($spiBinaryFileId) { try { $this->filesystem->delete($spiBinaryFileId); - } catch (FlysystemNotFoundException $e) { + } catch (FlysystemNotFoundException|CorruptedPathDetected $e) { throw new BinaryFileNotFoundException($spiBinaryFileId, $e); } } diff --git a/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php b/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php index cc8036d3cb..80c9af1790 100644 --- a/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php +++ b/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php @@ -11,6 +11,7 @@ use eZ\Publish\Core\IO\IOMetadataHandler; use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use League\Flysystem\CorruptedPathDetected; use League\Flysystem\FileNotFoundException; use League\Flysystem\FilesystemInterface; @@ -47,7 +48,7 @@ public function load($spiBinaryFileId) { try { $info = $this->filesystem->getMetadata($spiBinaryFileId); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException|CorruptedPathDetected $e) { throw new BinaryFileNotFoundException($spiBinaryFileId); } @@ -64,7 +65,11 @@ public function load($spiBinaryFileId) public function exists($spiBinaryFileId) { - return $this->filesystem->has($spiBinaryFileId); + try { + return $this->filesystem->has($spiBinaryFileId); + } catch (CorruptedPathDetected $e) { + return false; + } } public function getMimeType($spiBinaryFileId)