Skip to content

Commit

Permalink
Merge branch '4.x' of https://github.com/neos/neos-seo into 4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernhard Schmitt committed Aug 27, 2024
2 parents da48fdc + fb0d1f6 commit c220557
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 31 deletions.
64 changes: 64 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Tests

on:
push:
branches: [ main, '[0-9]+.x' ]
pull_request:
branches: [ main, '[0-9]+.x' ]
workflow_dispatch: # Allow manual triggering on any branch via `gh workflow run tests.yml -r branch-name`

jobs:
build:
env:
FLOW_PATH_ROOT: ../neos-base-distribution

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
include:
- php-version: "8.2"
neos-version: "9.0"
- php-version: "8.3"
neos-version: "9.0"

steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: mbstring, xml, json, zlib, iconv, intl, pdo_sqlite, mysql

- id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
shell: bash

- uses: actions/cache@v2
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-

- name: Prepare Neos distribution
run: |
git clone --depth 1 --branch ${{ matrix.neos-version }} https://github.com/neos/neos-development-distribution.git ${FLOW_PATH_ROOT}
cd ${FLOW_PATH_ROOT}
composer config --no-plugins allow-plugins.neos/composer-plugin true
composer config repositories.package '{ "type": "path", "url": "../neos-seo", "options": { "symlink": false } }'
composer require --no-update --no-interaction neos/seo:@dev
composer require --dev --no-update --no-interaction phpstan/phpstan:^1.10
- name: Install dependencies
run: |
cd ${FLOW_PATH_ROOT}
rm -rf composer.lock
composer install --no-interaction --no-progress --prefer-dist
- name: Linting
run: |
cd ${FLOW_PATH_ROOT}/Packages/Application/Neos.Seo
composer run lint:phpstan
71 changes: 41 additions & 30 deletions Classes/Fusion/XmlSitemapUrlsImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/

use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\NodeType\NodeTypeNames;
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindSubtreeFilter;
Expand All @@ -24,32 +25,37 @@
use Neos\Flow\Persistence\Doctrine\PersistenceManager;
use Neos\Fusion\FusionObjects\AbstractFusionObject;
use Neos\Media\Domain\Model\ImageInterface;
use Neos\Neos\Utility\NodeTypeWithFallbackProvider;
use Neos\Utility\Exception\PropertyNotAccessibleException;

/**
* @phpstan-type SitemapUrlItem array{
* node: Node,
* lastModificationDateTime: \DateTimeImmutable,
* priority: string,
* images: array<string, ImageInterface>,
* changeFrequency?: true
* }
*/
class XmlSitemapUrlsImplementation extends AbstractFusionObject
{
use NodeTypeWithFallbackProvider;

#[Flow\Inject]
protected ContentRepositoryRegistry $contentRepositoryRegistry;

#[Flow\Inject(lazy: true)]
protected PersistenceManager $persistenceManager;

/**
* @var array<string, array<int, string>>
* @var array<string, list<string>>
*/
protected array $assetPropertiesByNodeType = [];

protected ?bool $renderHiddenInIndex = null;
protected ?bool $renderHiddenInMenu = null;

protected ?bool $includeImageUrls = null;

protected ?Node $startingPoint = null;

/**
* @var array|null
* @var list<SitemapUrlItem>|null
*/
protected ?array $items = null;

Expand All @@ -62,13 +68,13 @@ public function getIncludeImageUrls(): bool
return $this->includeImageUrls;
}

public function getRenderHiddenInIndex(): bool
public function getRenderHiddenInMenu(): bool
{
if ($this->renderHiddenInIndex === null) {
$this->renderHiddenInIndex = (boolean)$this->fusionValue('renderHiddenInIndex');
if ($this->renderHiddenInMenu === null) {
$this->renderHiddenInMenu = (boolean)$this->fusionValue('renderHiddenInMenu');
}

return $this->renderHiddenInIndex;
return $this->renderHiddenInMenu;
}

/**
Expand All @@ -86,8 +92,7 @@ public function getStartingPoint(): Node
/**
* Evaluate this Fusion object and return the result
*
* @return array
* @throws PropertyNotAccessibleException
* @return list<SitemapUrlItem>
*/
public function evaluate(): array
{
Expand All @@ -107,14 +112,19 @@ public function evaluate(): array
$startingPoint->aggregateId,
FindSubtreeFilter::create(nodeTypes: NodeTypeCriteria::create($nodeTypeNames, NodeTypeNames::createEmpty()))
);

if (!$subtree) {
throw new \RuntimeException(sprintf('The "startingPoint" Node with identifier "%s" doesnt exist anymore.', $startingPoint->aggregateId->value), 1718735861);
}
$this->collectItems($items, $subtree);
$this->items = $items;
}

return $this->items;
}

/**
* @return list<string>
*/
private function getAssetPropertiesForNodeType(NodeType $nodeType): array
{
if (!array_key_exists($nodeType->name->value, $this->assetPropertiesByNodeType)) {
Expand All @@ -138,13 +148,14 @@ private function getAssetPropertiesForNodeType(NodeType $nodeType): array
}

/**
* @throws PropertyNotAccessibleException
* @param list<SitemapUrlItem> $items
*/
protected function collectItems(array &$items, Subtree $subtree): void
{
$node = $subtree->node;
$nodeTypeManager = $this->contentRepositoryRegistry->get($node->contentRepositoryId)->getNodeTypeManager();

if ($this->isDocumentNodeToBeIndexed($node)) {
if ($this->isDocumentNodeToBeIndexed($node, $nodeTypeManager)) {
$item = [
'node' => $node,
'lastModificationDateTime' => $node->timestamps->lastModified ?: $node->timestamps->created,
Expand All @@ -156,7 +167,6 @@ protected function collectItems(array &$items, Subtree $subtree): void
}

if ($this->getIncludeImageUrls()) {
$nodeTypeManager = $this->contentRepositoryRegistry->get($node->contentRepositoryId)->getNodeTypeManager();
$collectionNodeTypeNames = array_map(
fn(NodeType $nodeType): NodeTypeName => $nodeType->name,
$nodeTypeManager->getSubNodeTypes('Neos.Neos:ContentCollection', false)
Expand All @@ -173,8 +183,10 @@ protected function collectItems(array &$items, Subtree $subtree): void
$node->aggregateId,
FindSubtreeFilter::create(nodeTypes: NodeTypeCriteria::create($nodeTypeNames, NodeTypeNames::createEmpty()))
);

$this->resolveImages($contentSubtree, $item);
if (!$contentSubtree) {
throw new \RuntimeException(sprintf('The Node with identifier "%s" doesnt exist anymore.', $node->aggregateId->value), 1718735861);
}
$this->resolveImages($contentSubtree, $item, $nodeTypeManager);
}

$items[] = $item;
Expand All @@ -186,15 +198,13 @@ protected function collectItems(array &$items, Subtree $subtree): void
}

/**
* @param Subtree $subtree
* @param array & $item
* @return void
* @throws PropertyNotAccessibleException
* @param SitemapUrlItem $item
*/
protected function resolveImages(Subtree $subtree, array &$item): void
protected function resolveImages(Subtree $subtree, array &$item, NodeTypeManager $nodeTypeManager): void
{
$node = $subtree->node;
$assetPropertiesForNodeType = $this->getAssetPropertiesForNodeType($this->getNodeType($node));
$nodeType = $nodeTypeManager->getNodeType($node->nodeTypeName);
$assetPropertiesForNodeType = $nodeType ? $this->getAssetPropertiesForNodeType($nodeType) : [];

foreach ($assetPropertiesForNodeType as $propertyName) {
if (is_array($node->getProperty($propertyName)) && !empty($node->getProperty($propertyName))) {
Expand All @@ -209,18 +219,19 @@ protected function resolveImages(Subtree $subtree, array &$item): void
}

foreach ($subtree->children as $childSubtree) {
$this->resolveImages($childSubtree, $item);
$this->resolveImages($childSubtree, $item, $nodeTypeManager);
}
}

/**
* Return TRUE/FALSE if the node is currently hidden; taking the "renderHiddenInIndex" configuration
* Return TRUE/FALSE if the node is currently hidden; taking the "renderHiddenInMenu" configuration
* of the Menu Fusion object into account.
*/
protected function isDocumentNodeToBeIndexed(Node $node): bool
protected function isDocumentNodeToBeIndexed(Node $node, NodeTypeManager $nodeTypeManager): bool
{
return !$this->getNodeType($node)->isOfType('Neos.Seo:NoindexMixin')
&& ($this->getRenderHiddenInIndex() || $node->getProperty('hiddenInIndex') !== true)
$nodeType = $nodeTypeManager->getNodeType($node->nodeTypeName);
return !$nodeType?->isOfType('Neos.Seo:NoindexMixin')
&& ($this->getRenderHiddenInMenu() || $node->getProperty('hiddenInMenu') !== true)
&& $node->getProperty('metaRobotsNoindex') !== true
&& (
(string)$node->getProperty('canonicalLink') === ''
Expand Down
2 changes: 1 addition & 1 deletion Resources/Private/Fusion/Helper/SitemapUrls.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ prototype(Neos.Seo:Helper.SitemapUrls) {
@class = 'Neos\\Seo\\Fusion\\XmlSitemapUrlsImplementation'
startingPoint = null
includeImageUrls = false
renderHiddenInIndex = true
renderHiddenInMenu = true
}
3 changes: 3 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
"neos/neos": "^9.0",
"neos/fusion-afx": "^9.0"
},
"scripts": {
"lint:phpstan": "../../../bin/phpstan analyse"
},
"autoload": {
"psr-4": {
"Neos\\Seo\\": "Classes"
Expand Down
4 changes: 4 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
parameters:
level: 8
paths:
- Classes

0 comments on commit c220557

Please sign in to comment.