Skip to content

Commit

Permalink
Correctly resolve internal and external links (#98)
Browse files Browse the repository at this point in the history
* Correctly resolve internal and external links

* Fix functional test cases

* Follow multiple levels of internal links

* Implement suggestions from code review
  • Loading branch information
luca-rath authored Jul 7, 2021
1 parent edf1c60 commit 37f58d8
Show file tree
Hide file tree
Showing 8 changed files with 708 additions and 50 deletions.
38 changes: 38 additions & 0 deletions Content/ContentTypeResolver/ResourceLocatorResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/*
* This file is part of Sulu.
*
* (c) Sulu GmbH
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace Sulu\Bundle\HeadlessBundle\Content\ContentTypeResolver;

use Sulu\Bundle\HeadlessBundle\Content\ContentView;
use Sulu\Component\Content\Compat\PropertyInterface;

class ResourceLocatorResolver implements ContentTypeResolverInterface
{
public static function getContentType(): string
{
return 'resource_locator';
}

public function resolve($data, PropertyInterface $property, string $locale, array $attributes = []): ContentView
{
$resourceLocator = $data;
$structure = $property->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);
}
}
223 changes: 173 additions & 50 deletions Content/StructureResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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();
Expand All @@ -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) {
Expand Down Expand Up @@ -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()
);
}
Expand All @@ -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<string, PropertyInterface>
*/
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,
Expand Down Expand Up @@ -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;
}
}
8 changes: 8 additions & 0 deletions Resources/config/content-type-resolvers.xml
Original file line number Diff line number Diff line change
Expand Up @@ -200,5 +200,13 @@

<tag name="sulu_headless.content_type_resolver"/>
</service>

<service
id="sulu_headless.content_resolver.resource_locator"
class="Sulu\Bundle\HeadlessBundle\Content\ContentTypeResolver\ResourceLocatorResolver"
lazy="true"
>
<tag name="sulu_headless.content_type_resolver"/>
</service>
</services>
</container>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"id": "@uuid@",
"nodeType": 1,
"type": "page",
"template": "default",
"content": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"created": "@[email protected]()",
"creator": null,
"id": "@uuid@",
"nodeType": 1,
"template": "default",
"type": "snippet",
"view": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
}
},
"id": "@uuid@",
"nodeType": 1,
"template": "default",
"type": "snippet",
"view": {
Expand Down
Loading

0 comments on commit 37f58d8

Please sign in to comment.