From 37f58d84314c7d8d17dec6e3d74e4fbd4d124ed4 Mon Sep 17 00:00:00 2001 From: Luca Rath-Heel Date: Wed, 7 Jul 2021 14:59:58 +0200 Subject: [PATCH] Correctly resolve internal and external links (#98) * Correctly resolve internal and external links * Fix functional test cases * Follow multiple levels of internal links * Implement suggestions from code review --- .../ResourceLocatorResolver.php | 38 ++ Content/StructureResolver.php | 223 ++++++++--- Resources/config/content-type-resolvers.xml | 8 + .../headless_website__test_index.json | 1 + .../responses/snippet-area__default.json | 1 + ...ippet-area__default_include-extension.json | 1 + .../ResourceLocatorResolverTest.php | 118 ++++++ Tests/Unit/Content/StructureResolverTest.php | 368 ++++++++++++++++++ 8 files changed, 708 insertions(+), 50 deletions(-) create mode 100644 Content/ContentTypeResolver/ResourceLocatorResolver.php create mode 100644 Tests/Unit/Content/ContentTypeResolver/ResourceLocatorResolverTest.php diff --git a/Content/ContentTypeResolver/ResourceLocatorResolver.php b/Content/ContentTypeResolver/ResourceLocatorResolver.php new file mode 100644 index 0000000..b371b34 --- /dev/null +++ b/Content/ContentTypeResolver/ResourceLocatorResolver.php @@ -0,0 +1,38 @@ +getStructure(); + + // The getResourceLocator method returns the target url for pages of type internal and external link + if (method_exists($structure, 'getResourceLocator')) { + $resourceLocator = $structure->getResourceLocator(); + } + + return new ContentView($resourceLocator); + } +} diff --git a/Content/StructureResolver.php b/Content/StructureResolver.php index ba2f22e..fd15f0d 100644 --- a/Content/StructureResolver.php +++ b/Content/StructureResolver.php @@ -15,13 +15,18 @@ use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector; use Sulu\Bundle\PageBundle\Document\BasePageDocument; +use Sulu\Bundle\PageBundle\Preview\PageRouteDefaultsProvider; use Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStoreNotExistsException; use Sulu\Bundle\WebsiteBundle\ReferenceStore\ReferenceStorePoolInterface; +use Sulu\Component\Content\Compat\PropertyInterface; use Sulu\Component\Content\Compat\Structure\StructureBridge; use Sulu\Component\Content\Compat\StructureInterface; use Sulu\Component\Content\Compat\StructureManagerInterface; +use Sulu\Component\Content\Document\Behavior\RedirectTypeBehavior; use Sulu\Component\Content\Document\Behavior\StructureBehavior; use Sulu\Component\Content\Document\Extension\ExtensionContainer; +use Sulu\Component\Content\Document\RedirectType; +use Sulu\Component\Content\Metadata\StructureMetadata; class StructureResolver implements StructureResolverInterface { @@ -65,22 +70,25 @@ public function resolve( string $locale, bool $includeExtension = true ): array { - $data = $this->getStructureData($structure); + $requestedStructure = $structure; + $targetStructure = $this->getTargetStructure($requestedStructure); + + $data = $this->getStructureData($targetStructure, $requestedStructure); if ($includeExtension) { $data['extension'] = $this->resolveExtensionData( - $this->getExtensionData($structure), + $this->getExtensionData($targetStructure), $locale, - ['webspaceKey' => $structure->getWebspaceKey()] + ['webspaceKey' => $targetStructure->getWebspaceKey()] ); } - foreach ($structure->getProperties(true) as $property) { + foreach ($this->getProperties($targetStructure, $requestedStructure) as $property) { $contentView = $this->contentResolver->resolve( $property->getValue(), $property, $locale, - ['webspaceKey' => $structure->getWebspaceKey()] + ['webspaceKey' => $property->getStructure()->getWebspaceKey()] ); $data['content'][$property->getName()] = $contentView->getContent(); @@ -99,10 +107,14 @@ public function resolveProperties( string $locale, bool $includeExtension = false ): array { - $data = $this->getStructureData($structure); - $unresolvedExtensionData = $this->getExtensionData($structure); + $requestedStructure = $structure; + $targetStructure = $this->getTargetStructure($requestedStructure); + + $data = $this->getStructureData($targetStructure, $requestedStructure); + + $unresolvedExtensionData = $this->getExtensionData($targetStructure); - $attributes = ['webspaceKey' => $structure->getWebspaceKey()]; + $attributes = ['webspaceKey' => $targetStructure->getWebspaceKey()]; $excerptStructure = $this->structureManager->getStructure('excerpt'); if ($includeExtension) { @@ -133,17 +145,17 @@ public function resolveProperties( ); } } else { - if (!$structure->hasProperty($sourceProperty)) { + $property = $this->getProperty($sourceProperty, $targetStructure, $requestedStructure); + + if (null === $property) { continue; } - $property = $structure->getProperty($sourceProperty); - $contentView = $this->resolveProperty( - $structure, + $property->getStructure(), $sourceProperty, $locale, - $attributes, + ['webspaceKey' => $property->getStructure()->getWebspaceKey()], $property->getValue() ); } @@ -156,76 +168,117 @@ public function resolveProperties( } /** - * @param StructureBridge $structure + * @param StructureBridge $targetStructure + * @param StructureBridge $requestedStructure + */ + private function getProperty( + string $name, + StructureInterface $targetStructure, + StructureInterface $requestedStructure + ): ?PropertyInterface { + if ('title' === $name && $requestedStructure->hasProperty('title')) { + return $requestedStructure->getProperty('title'); + } + + if ($targetStructure->hasProperty($name)) { + return $targetStructure->getProperty($name); + } + + return null; + } + + /** + * @param StructureBridge $targetStructure + * @param StructureBridge $requestedStructure + * + * @return array + */ + private function getProperties(StructureInterface $targetStructure, StructureInterface $requestedStructure): array + { + $properties = []; + + foreach ($targetStructure->getProperties(true) as $property) { + $property = $this->getProperty( + $property->getName(), + $targetStructure, + $requestedStructure + ); + + if (null !== $property) { + $properties[$property->getName()] = $property; + } + } + + return $properties; + } + + /** + * @param StructureBridge $targetStructure + * @param StructureBridge $requestedStructure * * @return mixed[] */ - private function getStructureData(StructureInterface $structure): array + private function getStructureData(StructureInterface $targetStructure, StructureInterface $requestedStructure): array { - /** @var BasePageDocument $document */ - $document = $structure->getDocument(); + $targetDocument = $targetStructure->getDocument(); + $requestedDocument = $requestedStructure->getDocument(); + + /** @var string|null $templateKey */ + $templateKey = null; + if (method_exists($targetDocument, 'getStructureType')) { + $templateKey = $targetDocument->getStructureType(); + } /** @var int|null $author */ $author = null; - if (method_exists($document, 'getAuthor')) { - $author = $document->getAuthor(); + if (method_exists($targetDocument, 'getAuthor')) { + $author = $targetDocument->getAuthor(); } /** @var \DateTimeInterface|null $authored */ $authored = null; - if (method_exists($document, 'getAuthored')) { + if (method_exists($targetDocument, 'getAuthored')) { /** @var \DateTimeInterface|null $authored typehint in sulu is wrong */ - $authored = $document->getAuthored(); + $authored = $targetDocument->getAuthored(); } /** @var int|null $changer */ $changer = null; - if (method_exists($document, 'getChanger')) { - $changer = $document->getChanger(); + if (method_exists($requestedDocument, 'getChanger')) { + $changer = $requestedDocument->getChanger(); } /** @var \DateTimeInterface|null $changed */ $changed = null; - if (method_exists($document, 'getChanged')) { - $changed = $document->getChanged(); + if (method_exists($requestedDocument, 'getChanged')) { + $changed = $requestedDocument->getChanged(); } /** @var int|null $creator */ $creator = null; - if (method_exists($document, 'getCreator')) { - $creator = $document->getCreator(); + if (method_exists($requestedDocument, 'getCreator')) { + $creator = $requestedDocument->getCreator(); } /** @var \DateTimeInterface|null $created */ $created = null; - if (method_exists($document, 'getCreated')) { - $created = $document->getCreated(); + if (method_exists($requestedDocument, 'getCreated')) { + $created = $requestedDocument->getCreated(); } - $structureContent = null; - - if (method_exists($structure, 'getContent')) { - $structureContent = $structure->getContent(); - } - - $type = 'unknown'; - if (\is_object($structureContent) && method_exists($structureContent, 'getTemplateType')) { - // determine type for structure that is implemented based on the SuluContentBundle - $type = $structureContent->getTemplateType(); - } elseif ($document instanceof StructureBehavior) { - // determine type for structure that is implemented in the SuluPageBundle or the SuluArticleBundle - $type = $this->documentInspector->getMetadata($document)->getAlias(); - if ('home' === $type) { - $type = 'page'; - } - } + $templateType = $this->getTemplateType($targetStructure, $targetDocument); - $this->addToReferenceStore($structure->getUuid(), $type); + $this->addToReferenceStore($targetStructure->getUuid(), $templateType); + $this->addToReferenceStore( + $requestedStructure->getUuid(), + $this->getTemplateType($requestedStructure, $requestedDocument) + ); return [ - 'id' => $structure->getUuid(), - 'type' => $type, - 'template' => $document->getStructureType(), + 'id' => $requestedStructure->getUuid(), + 'nodeType' => $requestedStructure->getNodeType(), + 'type' => $templateType, + 'template' => $templateKey, 'content' => [], 'view' => [], 'author' => $author, @@ -327,4 +380,74 @@ private function addToReferenceStore(string $uuid, string $alias): void $referenceStore->add($uuid); } + + private function getTemplateType(StructureInterface $structure, object $document): string + { + $structureContent = null; + + if (method_exists($structure, 'getContent')) { + $structureContent = $structure->getContent(); + } + + if (\is_object($structureContent) && method_exists($structureContent, 'getTemplateType')) { + // determine type for structure that is implemented based on the SuluContentBundle + return $structureContent->getTemplateType(); + } + + if ($document instanceof StructureBehavior) { + // determine type for structure that is implemented in the SuluPageBundle or the SuluArticleBundle + $templateType = $this->documentInspector->getMetadata($document)->getAlias(); + + if ('home' === $templateType) { + return 'page'; + } + + return $templateType; + } + + return 'unknown'; + } + + /** + * @param StructureBridge $structure + * + * @return StructureBridge + */ + private function getTargetStructure(StructureInterface $structure): StructureInterface + { + $document = $structure->getDocument(); + + while ($document instanceof RedirectTypeBehavior + && RedirectType::INTERNAL === $document->getRedirectType()) { + $redirectTargetDocument = $document->getRedirectTarget(); + + if ($redirectTargetDocument instanceof StructureBehavior) { + $document = $redirectTargetDocument; + } + } + + if ($document !== $structure->getDocument() && $document instanceof StructureBehavior) { + return $this->documentToStructure($document); + } + + return $structure; + } + + /** + * @see PageRouteDefaultsProvider::documentToStructure() + * + * @return StructureBridge + */ + private function documentToStructure(StructureBehavior $document): StructureInterface + { + /** @var StructureMetadata $structure */ + $structure = $this->documentInspector->getStructureMetadata($document); + $documentAlias = $this->documentInspector->getMetadata($document)->getAlias(); + + /** @var StructureBridge $structureBridge */ + $structureBridge = $this->structureManager->wrapStructure($documentAlias, $structure); + $structureBridge->setDocument($document); + + return $structureBridge; + } } diff --git a/Resources/config/content-type-resolvers.xml b/Resources/config/content-type-resolvers.xml index efec467..79dd76b 100644 --- a/Resources/config/content-type-resolvers.xml +++ b/Resources/config/content-type-resolvers.xml @@ -200,5 +200,13 @@ + + + + diff --git a/Tests/Functional/Controller/responses/headless_website__test_index.json b/Tests/Functional/Controller/responses/headless_website__test_index.json index c20cfab..9fe0209 100644 --- a/Tests/Functional/Controller/responses/headless_website__test_index.json +++ b/Tests/Functional/Controller/responses/headless_website__test_index.json @@ -1,5 +1,6 @@ { "id": "@uuid@", + "nodeType": 1, "type": "page", "template": "default", "content": { diff --git a/Tests/Functional/Controller/responses/snippet-area__default.json b/Tests/Functional/Controller/responses/snippet-area__default.json index a00c7da..f20ba25 100644 --- a/Tests/Functional/Controller/responses/snippet-area__default.json +++ b/Tests/Functional/Controller/responses/snippet-area__default.json @@ -10,6 +10,7 @@ "created": "@string@.isDateTime()", "creator": null, "id": "@uuid@", + "nodeType": 1, "template": "default", "type": "snippet", "view": { diff --git a/Tests/Functional/Controller/responses/snippet-area__default_include-extension.json b/Tests/Functional/Controller/responses/snippet-area__default_include-extension.json index d914170..73bafb8 100644 --- a/Tests/Functional/Controller/responses/snippet-area__default_include-extension.json +++ b/Tests/Functional/Controller/responses/snippet-area__default_include-extension.json @@ -35,6 +35,7 @@ } }, "id": "@uuid@", + "nodeType": 1, "template": "default", "type": "snippet", "view": { diff --git a/Tests/Unit/Content/ContentTypeResolver/ResourceLocatorResolverTest.php b/Tests/Unit/Content/ContentTypeResolver/ResourceLocatorResolverTest.php new file mode 100644 index 0000000..78b1563 --- /dev/null +++ b/Tests/Unit/Content/ContentTypeResolver/ResourceLocatorResolverTest.php @@ -0,0 +1,118 @@ +resourceLocatorResolver = new ResourceLocatorResolver(); + } + + public function testGetContentType(): void + { + self::assertSame('resource_locator', $this->resourceLocatorResolver::getContentType()); + } + + public function testResolve(): void + { + /** @var ObjectProphecy|StructureInterface $structure */ + $structure = $this->prophesize(StructureInterface::class); + /** @var ObjectProphecy|PropertyInterface $property */ + $property = $this->prophesize(PropertyInterface::class); + $property->getStructure()->willReturn($structure->reveal()); + + $result = $this->resourceLocatorResolver->resolve('/page', $property->reveal(), 'en'); + + $this->assertInstanceOf(ContentView::class, $result); + $this->assertSame('/page', $result->getContent()); + $this->assertSame([], $result->getView()); + } + + public function testResolveWithStructureBridge(): void + { + /** @var ObjectProphecy|StructureBridge $structure */ + $structure = $this->prophesize(StructureBridge::class); + $structure->getResourceLocator()->willReturn('/other-page'); + /** @var ObjectProphecy|PropertyInterface $property */ + $property = $this->prophesize(PropertyInterface::class); + $property->getStructure()->willReturn($structure->reveal()); + + $result = $this->resourceLocatorResolver->resolve('/page', $property->reveal(), 'en'); + + $this->assertInstanceOf(ContentView::class, $result); + $this->assertSame('/other-page', $result->getContent()); + $this->assertSame([], $result->getView()); + } + + public function testResolveWithStructureBridgeResourceLocatorIsNull(): void + { + /** @var ObjectProphecy|StructureBridge $structure */ + $structure = $this->prophesize(StructureBridge::class); + $structure->getResourceLocator()->willReturn(null); + /** @var ObjectProphecy|PropertyInterface $property */ + $property = $this->prophesize(PropertyInterface::class); + $property->getStructure()->willReturn($structure->reveal()); + + $result = $this->resourceLocatorResolver->resolve('/page', $property->reveal(), 'en'); + + $this->assertInstanceOf(ContentView::class, $result); + $this->assertNull($result->getContent()); + $this->assertSame([], $result->getView()); + } + + public function testResolveDataIsNull(): void + { + /** @var ObjectProphecy|StructureInterface $structure */ + $structure = $this->prophesize(StructureInterface::class); + /** @var ObjectProphecy|PropertyInterface $property */ + $property = $this->prophesize(PropertyInterface::class); + $property->getStructure()->willReturn($structure->reveal()); + + $result = $this->resourceLocatorResolver->resolve(null, $property->reveal(), 'en'); + + $this->assertInstanceOf(ContentView::class, $result); + $this->assertNull($result->getContent()); + $this->assertSame([], $result->getView()); + } + + public function testResolveWithStructureBridgeDataIsNull(): void + { + /** @var ObjectProphecy|StructureBridge $structure */ + $structure = $this->prophesize(StructureBridge::class); + $structure->getResourceLocator()->willReturn('/other-page'); + /** @var ObjectProphecy|PropertyInterface $property */ + $property = $this->prophesize(PropertyInterface::class); + $property->getStructure()->willReturn($structure->reveal()); + + $result = $this->resourceLocatorResolver->resolve(null, $property->reveal(), 'en'); + + $this->assertInstanceOf(ContentView::class, $result); + $this->assertSame('/other-page', $result->getContent()); + $this->assertSame([], $result->getView()); + } +} diff --git a/Tests/Unit/Content/StructureResolverTest.php b/Tests/Unit/Content/StructureResolverTest.php index d969fac..6fc8cf9 100644 --- a/Tests/Unit/Content/StructureResolverTest.php +++ b/Tests/Unit/Content/StructureResolverTest.php @@ -28,6 +28,7 @@ use Sulu\Component\Content\Compat\PropertyInterface; use Sulu\Component\Content\Compat\Structure\StructureBridge; use Sulu\Component\Content\Compat\StructureManagerInterface; +use Sulu\Component\Content\Metadata\StructureMetadata; use Sulu\Component\DocumentManager\Metadata; class StructureResolverTest extends TestCase @@ -110,6 +111,9 @@ public function testResolvePage(): void $pageDocument = $this->prophesize(PageDocument::class); $pageMetadata = $this->prophesize(Metadata::class); + $structure->getNodeType()->willReturn(1); + $pageDocument->getRedirectType()->willReturn(1); + // expected object calls $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); @@ -150,10 +154,17 @@ public function testResolvePage(): void $titleProperty = $this->prophesize(PropertyInterface::class); $titleProperty->getName()->willReturn('title'); $titleProperty->getValue()->willReturn('test-123'); + $titleProperty->getStructure()->willReturn($structure->reveal()); + $mediaProperty = $this->prophesize(PropertyInterface::class); $mediaProperty->getName()->willReturn('media'); $mediaProperty->getValue()->willReturn(['ids' => [1, 2, 3]]); + $mediaProperty->getStructure()->willReturn($structure->reveal()); + $structure->hasProperty('title')->willReturn(true); + $structure->getProperty('title')->willReturn($titleProperty->reveal()); + $structure->hasProperty('media')->willReturn(true); + $structure->getProperty('media')->willReturn($mediaProperty->reveal()); $structure->getProperties(true)->willReturn( [ $titleProperty->reveal(), @@ -200,6 +211,184 @@ public function testResolvePage(): void $this->assertSame( [ 'id' => '123-123-123', + 'nodeType' => 1, + 'type' => 'page', + 'template' => 'default', + 'content' => [ + 'title' => 'test-123', + 'media' => ['media1', 'media2', 'media3'], + ], + 'view' => [ + 'title' => [], + 'media' => ['ids' => [1, 2, 3]], + ], + 'author' => 1, + 'authored' => $now->format(\DateTimeImmutable::ISO8601), + 'changer' => 3, + 'changed' => $now->format(\DateTimeImmutable::ISO8601), + 'creator' => 2, + 'created' => $now->format(\DateTimeImmutable::ISO8601), + 'extension' => [ + 'seo' => [ + 'title' => 'seo-title', + 'noIndex' => false, + ], + 'excerpt' => [ + 'title' => 'excerpt-title', + ], + ], + ], + $result + ); + } + + public function testResolveInternalLinkPage(): void + { + $structure = $this->prophesize(StructureBridge::class); + $targetStructure = $this->prophesize(StructureBridge::class); + $pageDocument = $this->prophesize(PageDocument::class); + $targetPageDocument = $this->prophesize(PageDocument::class); + $pageMetadata = $this->prophesize(Metadata::class); + $targetPageStructureMetadata = $this->prophesize(StructureMetadata::class); + + $structure->getNodeType()->willReturn(2); + $pageDocument->getRedirectType()->willReturn(2); + $pageDocument->getRedirectTarget()->willReturn($targetPageDocument->reveal()); + $targetPageDocument->getRedirectType()->willReturn(1); + + $targetStructure->setDocument($targetPageDocument->reveal())->shouldBeCalled(); + + // expected object calls + $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); + $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); + + $targetStructure->getUuid()->willReturn('456-456-456')->shouldBeCalled(); + $targetStructure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); + + $now = new \DateTimeImmutable(); + + $targetPageDocument->getStructureType()->willReturn('default')->shouldBeCalled(); + $targetPageDocument->getAuthored()->willReturn($now)->shouldBeCalled(); + $targetPageDocument->getAuthor()->willReturn(1)->shouldBeCalled(); + $pageDocument->getCreated()->willReturn($now)->shouldBeCalled(); + $pageDocument->getCreator()->willReturn(2)->shouldBeCalled(); + $pageDocument->getChanged()->willReturn($now)->shouldBeCalled(); + $pageDocument->getChanger()->willReturn(3)->shouldBeCalled(); + $targetPageDocument->getExtensionsData() + ->willReturn([ + 'seo' => [ + 'title' => 'seo-title', + 'noIndex' => false, + ], + 'excerpt' => [ + 'title' => 'excerpt-title', + 'categories' => [1, 2, 3], + 'tags' => [1, 2, 3], + 'icon' => [1, 2, 3], + 'images' => [1, 2, 3], + ], + ]) + ->shouldBeCalled(); + + $pageMetadata->getAlias() + ->willReturn('page') + ->shouldBeCalled(); + + $structure->getDocument() + ->willReturn($pageDocument->reveal()) + ->shouldBeCalled(); + + $targetStructure->getDocument() + ->willReturn($targetPageDocument->reveal()) + ->shouldBeCalled(); + + $titleProperty = $this->prophesize(PropertyInterface::class); + $titleProperty->getName()->willReturn('title'); + $titleProperty->getValue()->willReturn('test-123'); + $titleProperty->getStructure()->willReturn($structure->reveal()); + + $targetTitleProperty = $this->prophesize(PropertyInterface::class); + $targetTitleProperty->getName()->willReturn('title'); + $targetTitleProperty->getValue()->willReturn('test-456'); + $targetTitleProperty->getStructure()->willReturn($targetStructure->reveal()); + + $mediaProperty = $this->prophesize(PropertyInterface::class); + $mediaProperty->getName()->willReturn('media'); + $mediaProperty->getValue()->willReturn(['ids' => [1, 2, 3]]); + $mediaProperty->getStructure()->willReturn($targetStructure->reveal()); + + $structure->hasProperty('title')->willReturn(true); + $structure->getProperty('title')->willReturn($titleProperty->reveal()); + $targetStructure->hasProperty('media')->willReturn(true); + $targetStructure->getProperty('media')->willReturn($mediaProperty->reveal()); + $targetStructure->getProperties(true)->willReturn( + [ + $targetTitleProperty->reveal(), + $mediaProperty->reveal(), + ] + ); + + $titleContentView = $this->prophesize(ContentView::class); + $titleContentView->getContent()->willReturn('test-123'); + $titleContentView->getView()->willReturn([]); + + $targetTitleContentView = $this->prophesize(ContentView::class); + $targetTitleContentView->getContent()->willReturn('test-456'); + $targetTitleContentView->getView()->willReturn([]); + + $mediaContentView = $this->prophesize(ContentView::class); + $mediaContentView->getContent()->willReturn(['media1', 'media2', 'media3']); + $mediaContentView->getView()->willReturn(['ids' => [1, 2, 3]]); + + // expected service calls + $this->documentInspector->getStructureMetadata($targetPageDocument->reveal()) + ->willReturn($targetPageStructureMetadata->reveal()) + ->shouldBeCalled(); + + $this->structureManager->wrapStructure('page', $targetPageStructureMetadata->reveal()) + ->willReturn($targetStructure->reveal()) + ->shouldBeCalled(); + + $this->documentInspector->getMetadata($pageDocument->reveal()) + ->willReturn($pageMetadata->reveal()) + ->shouldBeCalled(); + + $this->documentInspector->getMetadata($targetPageDocument->reveal()) + ->willReturn($pageMetadata->reveal()) + ->shouldBeCalled(); + + $this->contentResolver->resolve('test-123', $titleProperty->reveal(), 'en', ['webspaceKey' => 'sulu_io']) + ->willReturn($titleContentView->reveal()) + ->shouldBeCalled(); + + $this->contentResolver->resolve('test-456', $targetTitleProperty->reveal(), 'en', ['webspaceKey' => 'sulu_io']) + ->shouldNotBeCalled(); + + $this->contentResolver->resolve( + ['ids' => [1, 2, 3]], + $mediaProperty->reveal(), + 'en', + ['webspaceKey' => 'sulu_io'] + )->willReturn($mediaContentView->reveal()) + ->shouldBeCalled(); + + $referenceStore = $this->prophesize(ReferenceStoreInterface::class); + $referenceStore->add('123-123-123') + ->shouldBeCalled(); + $referenceStore->add('456-456-456') + ->shouldBeCalled(); + + $this->referenceStorePool->getStore('content') + ->willReturn($referenceStore->reveal()) + ->shouldBeCalled(); + + // call test function + $result = $this->structureResolver->resolve($structure->reveal(), 'en'); + + $this->assertSame( + [ + 'id' => '123-123-123', + 'nodeType' => 2, 'type' => 'page', 'template' => 'default', 'content' => [ @@ -236,6 +425,9 @@ public function testResolveHomepage(): void $homepageDocument = $this->prophesize(HomeDocument::class); $homepageMetadata = $this->prophesize(Metadata::class); + $structure->getNodeType()->willReturn(1); + $homepageDocument->getRedirectType()->willReturn(1); + // expected object calls $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); @@ -258,11 +450,17 @@ public function testResolveHomepage(): void $titleProperty = $this->prophesize(PropertyInterface::class); $titleProperty->getName()->willReturn('title')->shouldBeCalled(); $titleProperty->getValue()->willReturn('test-123')->shouldBeCalled(); + $titleProperty->getStructure()->willReturn($structure->reveal()); $mediaProperty = $this->prophesize(PropertyInterface::class); $mediaProperty->getName()->willReturn('media')->shouldBeCalled(); $mediaProperty->getValue()->willReturn(['ids' => [1, 2, 3]])->shouldBeCalled(); + $mediaProperty->getStructure()->willReturn($structure->reveal()); + $structure->hasProperty('title')->willReturn(true); + $structure->getProperty('title')->willReturn($titleProperty->reveal()); + $structure->hasProperty('media')->willReturn(true); + $structure->getProperty('media')->willReturn($mediaProperty->reveal()); $structure->getProperties(true)->willReturn( [ $titleProperty->reveal(), @@ -309,6 +507,7 @@ public function testResolveHomepage(): void $this->assertSame( [ 'id' => '123-123-123', + 'nodeType' => 1, 'type' => 'page', 'template' => 'default', 'content' => [ @@ -341,6 +540,8 @@ public function testResolveSnippet(): void $snippetDocument = $this->prophesize(SnippetDocument::class); $snippetMetadata = $this->prophesize(Metadata::class); + $structure->getNodeType()->willReturn(1); + // expected object calls $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); @@ -361,11 +562,17 @@ public function testResolveSnippet(): void $titleProperty = $this->prophesize(PropertyInterface::class); $titleProperty->getName()->willReturn('title')->shouldBeCalled(); $titleProperty->getValue()->willReturn('test-123')->shouldBeCalled(); + $titleProperty->getStructure()->willReturn($structure->reveal()); $mediaProperty = $this->prophesize(PropertyInterface::class); $mediaProperty->getName()->willReturn('media')->shouldBeCalled(); $mediaProperty->getValue()->willReturn(['ids' => [1, 2, 3]])->shouldBeCalled(); + $mediaProperty->getStructure()->willReturn($structure->reveal()); + $structure->hasProperty('title')->willReturn(true); + $structure->getProperty('title')->willReturn($titleProperty->reveal()); + $structure->hasProperty('media')->willReturn(true); + $structure->getProperty('media')->willReturn($mediaProperty->reveal()); $structure->getProperties(true)->willReturn( [ $titleProperty->reveal(), @@ -412,6 +619,7 @@ public function testResolveSnippet(): void $this->assertSame( [ 'id' => '123-123-123', + 'nodeType' => 1, 'type' => 'snippet', 'template' => 'default', 'content' => [ @@ -444,6 +652,9 @@ public function testResolveProperties(): void $pageDocument = $this->prophesize(PageDocument::class); $pageMetadata = $this->prophesize(Metadata::class); + $structure->getNodeType()->willReturn(1); + $pageDocument->getRedirectType()->willReturn(1); + // expected object calls $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); @@ -486,6 +697,7 @@ public function testResolveProperties(): void $titleProperty->getName()->willReturn('title'); $titleProperty->getValue()->willReturn('test-123'); $titleProperty->setValue('test-123')->shouldBeCalled(); + $titleProperty->getStructure()->willReturn($structure->reveal()); $structure->hasProperty('title')->willReturn(true); $structure->getProperty('title')->willReturn($titleProperty->reveal()); @@ -527,6 +739,157 @@ public function testResolveProperties(): void $this->assertSame( [ 'id' => '123-123-123', + 'nodeType' => 1, + 'type' => 'page', + 'template' => 'default', + 'content' => [ + 'myTitle' => 'test-123', + 'seoDescription' => 'seo-description', + 'excerptTitle' => 'excerpt-title', + ], + 'view' => [ + 'myTitle' => [], + 'seoDescription' => [], + 'excerptTitle' => [], + ], + 'author' => 1, + 'authored' => $now->format(\DateTimeImmutable::ISO8601), + 'changer' => 3, + 'changed' => $now->format(\DateTimeImmutable::ISO8601), + 'creator' => 2, + 'created' => $now->format(\DateTimeImmutable::ISO8601), + ], + $result + ); + } + + public function testResolvePropertiesInternalLink(): void + { + $structure = $this->prophesize(StructureBridge::class); + $targetStructure = $this->prophesize(StructureBridge::class); + $pageDocument = $this->prophesize(PageDocument::class); + $targetPageDocument = $this->prophesize(PageDocument::class); + $pageMetadata = $this->prophesize(Metadata::class); + $targetPageStructureMetadata = $this->prophesize(StructureMetadata::class); + + $structure->getNodeType()->willReturn(2); + $pageDocument->getRedirectType()->willReturn(2); + $pageDocument->getRedirectTarget()->willReturn($targetPageDocument->reveal()); + $targetPageDocument->getRedirectType()->willReturn(1); + + $targetStructure->setDocument($targetPageDocument->reveal())->shouldBeCalled(); + + // expected object calls + $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); + $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); + + $targetStructure->getUuid()->willReturn('456-456-456')->shouldBeCalled(); + $targetStructure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); + + $now = new \DateTimeImmutable(); + + $targetPageDocument->getStructureType()->willReturn('default')->shouldBeCalled(); + $targetPageDocument->getAuthored()->willReturn($now)->shouldBeCalled(); + $targetPageDocument->getAuthor()->willReturn(1)->shouldBeCalled(); + $pageDocument->getCreated()->willReturn($now)->shouldBeCalled(); + $pageDocument->getCreator()->willReturn(2)->shouldBeCalled(); + $pageDocument->getChanged()->willReturn($now)->shouldBeCalled(); + $pageDocument->getChanger()->willReturn(3)->shouldBeCalled(); + $targetPageDocument->getExtensionsData() + ->willReturn([ + 'seo' => [ + 'title' => 'seo-title', + 'description' => 'seo-description', + 'noIndex' => false, + ], + 'excerpt' => [ + 'title' => 'excerpt-title', + 'categories' => [1, 2, 3], + 'tags' => [1, 2, 3], + 'icon' => [1, 2, 3], + 'images' => [1, 2, 3], + ], + ]) + ->shouldBeCalled(); + + $pageMetadata->getAlias() + ->willReturn('page') + ->shouldBeCalled(); + + $structure->getDocument() + ->willReturn($pageDocument->reveal()) + ->shouldBeCalled(); + + $targetStructure->getDocument() + ->willReturn($targetPageDocument->reveal()) + ->shouldBeCalled(); + + $titleProperty = $this->prophesize(PropertyInterface::class); + $titleProperty->getName()->willReturn('title'); + $titleProperty->getValue()->willReturn('test-123'); + $titleProperty->setValue('test-123')->shouldBeCalled(); + $titleProperty->getStructure()->willReturn($structure->reveal()); + + $structure->hasProperty('title')->willReturn(true); + $structure->getProperty('title')->willReturn($titleProperty->reveal()); + $structure->hasProperty('notExist')->shouldNotBeCalled(); + + $targetStructure->hasProperty('title')->shouldNotBeCalled(); + $targetStructure->getProperty('title')->shouldNotBeCalled(); + $targetStructure->hasProperty('notExist')->willReturn(false); + + $titleContentView = $this->prophesize(ContentView::class); + $titleContentView->getContent()->willReturn('test-123'); + $titleContentView->getView()->willReturn([]); + + // expected service calls + $this->documentInspector->getStructureMetadata($targetPageDocument->reveal()) + ->willReturn($targetPageStructureMetadata->reveal()) + ->shouldBeCalled(); + + $this->structureManager->wrapStructure('page', $targetPageStructureMetadata->reveal()) + ->willReturn($targetStructure->reveal()) + ->shouldBeCalled(); + + $this->documentInspector->getMetadata($pageDocument->reveal()) + ->willReturn($pageMetadata->reveal()) + ->shouldBeCalled(); + + $this->documentInspector->getMetadata($targetPageDocument->reveal()) + ->willReturn($pageMetadata->reveal()) + ->shouldBeCalled(); + + $this->contentResolver->resolve('test-123', $titleProperty->reveal(), 'en', ['webspaceKey' => 'sulu_io']) + ->willReturn($titleContentView->reveal()) + ->shouldBeCalled(); + + $referenceStore = $this->prophesize(ReferenceStoreInterface::class); + $referenceStore->add('123-123-123') + ->shouldBeCalled(); + $referenceStore->add('456-456-456') + ->shouldBeCalled(); + + $this->referenceStorePool->getStore('content') + ->willReturn($referenceStore->reveal()) + ->shouldBeCalled(); + + // call test function + $result = $this->structureResolver->resolveProperties( + $structure->reveal(), + [ + 'myTitle' => 'title', + 'seoDescription' => 'seo.description', + 'excerptTitle' => 'excerpt.title', + 'notExist' => 'notExist', + 'excerptNotExist' => 'excerpt.notExist', + ], + 'en' + ); + + $this->assertSame( + [ + 'id' => '123-123-123', + 'nodeType' => 2, 'type' => 'page', 'template' => 'default', 'content' => [ @@ -556,6 +919,9 @@ public function testResolvePropertiesIncludeExtension(): void $pageDocument = $this->prophesize(PageDocument::class); $pageMetadata = $this->prophesize(Metadata::class); + $structure->getNodeType()->willReturn(1); + $pageDocument->getRedirectType()->willReturn(1); + // expected object calls $structure->getUuid()->willReturn('123-123-123')->shouldBeCalled(); $structure->getWebspaceKey()->willReturn('sulu_io')->shouldBeCalled(); @@ -598,6 +964,7 @@ public function testResolvePropertiesIncludeExtension(): void $titleProperty->getName()->willReturn('title'); $titleProperty->getValue()->willReturn('test-123'); $titleProperty->setValue('test-123')->shouldBeCalled(); + $titleProperty->getStructure()->willReturn($structure->reveal()); $structure->hasProperty('title')->willReturn(true); $structure->getProperty('title')->willReturn($titleProperty->reveal()); @@ -633,6 +1000,7 @@ public function testResolvePropertiesIncludeExtension(): void $this->assertSame( [ 'id' => '123-123-123', + 'nodeType' => 1, 'type' => 'page', 'template' => 'default', 'content' => [