From 80f6a9bc520d41009626594ef9fe941068dcbe21 Mon Sep 17 00:00:00 2001
From: batabana <36864084+batabana@users.noreply.github.com>
Date: Thu, 26 Oct 2023 09:52:35 +0200
Subject: [PATCH 1/6] TASK: neos 9 compatibility
---
Configuration/Routes.yaml | 2 +-
Resources/Private/Fusion/Collection.fusion | 4 ++--
Resources/Private/Fusion/List.fusion | 2 +-
Resources/Private/Fusion/PaginatedCollection.fusion | 8 ++++----
Resources/Private/Fusion/Pagination.fusion | 4 ++--
composer.json | 2 +-
6 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/Configuration/Routes.yaml b/Configuration/Routes.yaml
index de915c8..fd7f7d1 100644
--- a/Configuration/Routes.yaml
+++ b/Configuration/Routes.yaml
@@ -8,5 +8,5 @@
'@action': 'show'
routeParts:
node:
- handler: Neos\Neos\Routing\FrontendNodeRoutePartHandlerInterface
+ handler: Neos\Neos\FrontendRouting\FrontendNodeRoutePartHandlerInterface
appendExceedingArguments: TRUE
diff --git a/Resources/Private/Fusion/Collection.fusion b/Resources/Private/Fusion/Collection.fusion
index 7925b81..c6d913f 100644
--- a/Resources/Private/Fusion/Collection.fusion
+++ b/Resources/Private/Fusion/Collection.fusion
@@ -1,10 +1,10 @@
-prototype(Flowpack.Listable:Collection) < prototype(Neos.Fusion:Collection) {
+prototype(Flowpack.Listable:Collection) < prototype(Neos.Fusion:Loop) {
listClass = ''
itemClass = ''
@context.itemClass = ${this.itemClass}
@process.tmpl = ${'
'}
- collection = 'must-be-set'
+ items = 'must-be-set'
itemName = 'node'
iterationName = 'iteration'
itemRenderer = Flowpack.Listable:ContentCaseShort
diff --git a/Resources/Private/Fusion/List.fusion b/Resources/Private/Fusion/List.fusion
index 03a756b..474a224 100644
--- a/Resources/Private/Fusion/List.fusion
+++ b/Resources/Private/Fusion/List.fusion
@@ -15,7 +15,7 @@ prototype(Flowpack.Listable:List) < prototype(Neos.Fusion:Component) {
@if.listNotEmpty = ${props.list != null}
attributes.class = ${props.wrapClass}
- content = Neos.Fusion:Array {
+ content = Neos.Fusion:Join {
listTitleTag = Neos.Fusion:Tag {
tagName = 'h2'
attributes.class = ${props.listTitleClass}
diff --git a/Resources/Private/Fusion/PaginatedCollection.fusion b/Resources/Private/Fusion/PaginatedCollection.fusion
index fbfb039..8505fb3 100644
--- a/Resources/Private/Fusion/PaginatedCollection.fusion
+++ b/Resources/Private/Fusion/PaginatedCollection.fusion
@@ -9,8 +9,8 @@ prototype(Flowpack.Listable:PaginatedCollection) < prototype(Neos.Fusion:Compone
showPreviousNextLinks = false
listRenderer = 'Flowpack.Listable:Collection'
- renderer = Neos.Fusion:Array {
- @context.data = Neos.Fusion:RawArray {
+ renderer = Neos.Fusion:Join {
+ @context.data = Neos.Fusion:DataStructure {
collection = Neos.Fusion:Case {
@context.limit = ${props.currentPage * props.itemsPerPage}
@context.offset = ${(props.currentPage - 1) * props.itemsPerPage}
@@ -33,7 +33,7 @@ prototype(Flowpack.Listable:PaginatedCollection) < prototype(Neos.Fusion:Compone
list = Neos.Fusion:Renderer {
type = ${props.listRenderer}
- element.collection = ${data.collection}
+ element.items = ${data.collection}
}
pagination = Flowpack.Listable:Pagination {
currentPage = ${props.currentPage}
@@ -47,7 +47,7 @@ prototype(Flowpack.Listable:PaginatedCollection) < prototype(Neos.Fusion:Compone
@cache {
mode = 'dynamic'
entryIdentifier {
- node = ${node}
+ node = ${Neos.Caching.entryIdentifierForNode(node)}
}
entryDiscriminator = ${request.arguments.currentPage}
context {
diff --git a/Resources/Private/Fusion/Pagination.fusion b/Resources/Private/Fusion/Pagination.fusion
index 5a8f41e..eab47a9 100644
--- a/Resources/Private/Fusion/Pagination.fusion
+++ b/Resources/Private/Fusion/Pagination.fusion
@@ -7,7 +7,7 @@ prototype(Flowpack.Listable:PaginationArray) {
showPreviousNextLinks = false
}
-prototype(Flowpack.Listable:PaginationParameters) < prototype(Neos.Fusion:RawArray)
+prototype(Flowpack.Listable:PaginationParameters) < prototype(Neos.Fusion:DataStructure)
prototype(Flowpack.Listable:Pagination) < prototype(Neos.Fusion:Component) {
totalCount = 'to-be-set'
@@ -20,7 +20,7 @@ prototype(Flowpack.Listable:Pagination) < prototype(Neos.Fusion:Component) {
currentItemClass = 'isCurrent'
currentPage = ${request.arguments.currentPage || 1}
- renderer = Neos.Fusion:Collection {
+ renderer = Neos.Fusion:Loop {
@if.paginationNeeded = ${(props.totalCount/props.itemsPerPage) > 1}
@process.tmpl = ${''}
collection = Flowpack.Listable:PaginationArray {
diff --git a/composer.json b/composer.json
index 4ed6277..a99bc23 100755
--- a/composer.json
+++ b/composer.json
@@ -7,7 +7,7 @@
"description": "Tiny extension for listing things",
"license": "MIT",
"require": {
- "neos/neos": "^3.3 || ^4.0 || ^5.0 || ^7.0 || ^8.0 || dev-master"
+ "neos/neos": "^9.0"
},
"autoload": {
"psr-4": {
From f78be672894296b47e5ddca1439b53a2ec4f2f86 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anke=20H=C3=A4slich?=
Date: Thu, 2 Nov 2023 15:23:29 +0100
Subject: [PATCH 2/6] TASK: Add PHP 8.2 minimum requirement
---
composer.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/composer.json b/composer.json
index a99bc23..e460b52 100755
--- a/composer.json
+++ b/composer.json
@@ -7,6 +7,7 @@
"description": "Tiny extension for listing things",
"license": "MIT",
"require": {
+ "php": ">=8.2",
"neos/neos": "^9.0"
},
"autoload": {
From 7601d8861d9b6553a1af8eef3aecdd3749a4ca47 Mon Sep 17 00:00:00 2001
From: Denny Lubitz
Date: Mon, 13 Jan 2025 16:45:36 +0100
Subject: [PATCH 3/6] FEATURE: Neos 9 compatibility
---
.../FilterByDateOperation.php | 7 +-
.../FilterByReferenceOperation.php | 9 +-
.../SortRecursiveByIndexOperation.php | 90 ++++++++++++++-----
Configuration/Routes.yaml | 2 +-
README.md | 4 +-
Resources/Private/Fusion/Pagination.fusion | 2 +-
Resources/Private/Fusion/Utility.fusion | 2 +-
7 files changed, 82 insertions(+), 34 deletions(-)
diff --git a/Classes/Fusion/Eel/FlowQueryOperations/FilterByDateOperation.php b/Classes/Fusion/Eel/FlowQueryOperations/FilterByDateOperation.php
index 770133e..c3aeb00 100644
--- a/Classes/Fusion/Eel/FlowQueryOperations/FilterByDateOperation.php
+++ b/Classes/Fusion/Eel/FlowQueryOperations/FilterByDateOperation.php
@@ -1,15 +1,16 @@
getContext() as $node) {
- /** @var NodeInterface $node */
+ /** @var Node $node */
$propertyValue = $node->getProperty($filterByPropertyPath);
if (($compareOperator === '>' && $propertyValue > $date) || ($compareOperator === '<' && $propertyValue < $date)) {
$filteredNodes[] = $node;
diff --git a/Classes/Fusion/Eel/FlowQueryOperations/FilterByReferenceOperation.php b/Classes/Fusion/Eel/FlowQueryOperations/FilterByReferenceOperation.php
index f0265ef..1719a71 100644
--- a/Classes/Fusion/Eel/FlowQueryOperations/FilterByReferenceOperation.php
+++ b/Classes/Fusion/Eel/FlowQueryOperations/FilterByReferenceOperation.php
@@ -1,15 +1,16 @@
getContext() as $node) {
- /** @var NodeInterface $node */
+ /** @var Node $node */
$propertyValue = $node->getProperty($filterByPropertyPath);
if ($nodeReference == $propertyValue || (is_array($propertyValue) && in_array($nodeReference, $propertyValue, false))) {
$filteredNodes[] = $node;
diff --git a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
index 7e66981..96e915e 100644
--- a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
+++ b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
@@ -1,17 +1,24 @@
getContext();
+ if (count($nodes) <= 1) {
+ return;
+ }
- $indexPathCache = [];
+ $pathMap = [];
+ $subgraph = $this->contentRepositoryRegistry->subgraphForNode($nodes[0]);
- /** @var NodeInterface $node */
+ // Collect all NodeAggregateId paths
+ /** @var Node $node */
foreach ($nodes as $node) {
- // Collect the list of sorting indices for all parents of the node and the node itself
- $nodeIdentifier = $node->getIdentifier();
- $indexPath = [$node->getIndex()];
- while ($node = $node->getParent()) {
- $indexPath[] = $node->getIndex();
- }
- $indexPathCache[$nodeIdentifier] = $indexPath;
+ $nodeIdentifier = $node->aggregateId->value;
+ $ancestors = $subgraph->findAncestorNodes($node->aggregateId, FindAncestorNodesFilter::create());
+ $pathMap[$nodeIdentifier] = array_merge([$node->aggregateId], array_map(static fn($ancestor) => $ancestor->aggregateId, iterator_to_array($ancestors)));
}
$flip = $sortOrder === 'DESC' ? -1 : 1;
- usort($nodes, function (NodeInterface $a, NodeInterface $b) use ($indexPathCache, $flip) {
- if ($a === $b) {
+ usort($nodes, function (Node $a, Node $b) use ($subgraph, $pathMap, $flip) {
+ // Both nodes are equal
+ if ($a->equals($b)) {
return 0;
}
- // Compare index path starting from the site root until a difference is found
- $aIndexPath = $indexPathCache[$a->getIdentifier()];
- $bIndexPath = $indexPathCache[$b->getIdentifier()];
- while (count($aIndexPath) > 0 && count($bIndexPath) > 0) {
- $diff = (array_pop($aIndexPath) - array_pop($bIndexPath));
- if ($diff !== 0) {
- return $flip * $diff < 0 ? -1 : 1;
+ $commonParentPathSegmentNodeAggregateId = null;
+ $childNodesCache = [];
+
+ // Compare path starting from the site root until a difference is found.
+ $aPath = $pathMap[$a->aggregateId->value];
+ $bPath = $pathMap[$b->aggregateId->value];
+ while (count($aPath) > 0 && count($bPath) > 0) {
+
+ /** @var NodeAggregateId $aPathSegmentNodeAggregateId */
+ $aPathSegmentNodeAggregateId = array_pop($aPath);
+ /** @var NodeAggregateId $bPathSegmentNodeAggregateId */
+ $bPathSegmentNodeAggregateId = array_pop($bPath);
+
+ $pathDiff = (!$aPathSegmentNodeAggregateId->equals($bPathSegmentNodeAggregateId));
+
+ if ($pathDiff === true) {
+ // Path is different at this segment, so we need to figure out their position under the last common parent.
+ if ($commonParentPathSegmentNodeAggregateId === null) {
+ return 0;
+ }
+
+ if (!isset($childNodesCache[$commonParentPathSegmentNodeAggregateId->value])) {
+ $childNodesCache[$commonParentPathSegmentNodeAggregateId->value] = $subgraph->findChildNodes($commonParentPathSegmentNodeAggregateId, FindChildNodesFilter::create());
+ }
+
+ $positionDiff = $this->getIndexOfNodeAggregateIdInNodes($childNodesCache[$commonParentPathSegmentNodeAggregateId->value], $aPathSegmentNodeAggregateId)
+ - $this->getIndexOfNodeAggregateIdInNodes($childNodesCache[$commonParentPathSegmentNodeAggregateId->value], $bPathSegmentNodeAggregateId);
+ return $flip * $positionDiff < 0 ? -1 : 1;
}
+ // No diff in path, we need to go deeper, or they are eventually equal
+ $commonParentPathSegmentNodeAggregateId = $aPathSegmentNodeAggregateId;
}
return 0;
@@ -88,4 +123,15 @@ public function evaluate(FlowQuery $flowQuery, array $arguments)
$flowQuery->setContext($nodes);
}
+
+ private function getIndexOfNodeAggregateIdInNodes(Nodes $childNodes, NodeAggregateId $nodeAggregateId): int
+ {
+ foreach ($childNodes as $key => $childNode) {
+ if ($childNode->aggregateId->value === $nodeAggregateId->value) {
+ return $key;
+ }
+ }
+
+ throw new \Exception("Exception on sorting nodes by there position in content tree.");
+ }
}
diff --git a/Configuration/Routes.yaml b/Configuration/Routes.yaml
index fd7f7d1..e022273 100644
--- a/Configuration/Routes.yaml
+++ b/Configuration/Routes.yaml
@@ -1,6 +1,6 @@
-
name: 'Paginate for Flowpack.Listable'
- uriPattern: '{node}{currentPage}'
+ uriPattern: '{node}{currentPage}'
defaults:
'@package': 'Neos.Neos'
'@controller': 'Frontend\Node'
diff --git a/README.md b/README.md
index b2ecd99..3acb8ae 100644
--- a/README.md
+++ b/README.md
@@ -153,8 +153,8 @@ Filter nodes by properties of type reference or references.
## sortRecursiveByIndex
-Sort nodes recursively by their sorting property.
+Sort nodes by their position in the node tree. Please use with care, as this can become a very expensive operation, if you operate on bigger subtrees.
Example:
- ${q(site).find('[instanceof Neos.Neos:Document]').sortRecursiveByIndex('DESC').get()}
+ ${q(node).children("main").sortRecursiveByIndex(['ASC'|'DESC']).get()}
diff --git a/Resources/Private/Fusion/Pagination.fusion b/Resources/Private/Fusion/Pagination.fusion
index eab47a9..e4350d2 100644
--- a/Resources/Private/Fusion/Pagination.fusion
+++ b/Resources/Private/Fusion/Pagination.fusion
@@ -23,7 +23,7 @@ prototype(Flowpack.Listable:Pagination) < prototype(Neos.Fusion:Component) {
renderer = Neos.Fusion:Loop {
@if.paginationNeeded = ${(props.totalCount/props.itemsPerPage) > 1}
@process.tmpl = ${''}
- collection = Flowpack.Listable:PaginationArray {
+ items = Flowpack.Listable:PaginationArray {
currentPage = ${props.currentPage}
maximumNumberOfLinks = ${props.maximumNumberOfLinks}
totalCount = ${props.totalCount}
diff --git a/Resources/Private/Fusion/Utility.fusion b/Resources/Private/Fusion/Utility.fusion
index 0d56ef9..af24877 100644
--- a/Resources/Private/Fusion/Utility.fusion
+++ b/Resources/Private/Fusion/Utility.fusion
@@ -3,6 +3,6 @@ prototype(Flowpack.Listable:ContentCaseShort) < prototype(Neos.Neos:ContentCase)
default {
@position = 'end'
condition = true
- type = ${q(node).property('_nodeType.name') + '.Short'}
+ type = ${node.nodeTypeName + '.Short'}
}
}
From 44b1eeece81b5767ed6caa346879c000949323ab Mon Sep 17 00:00:00 2001
From: Denny Lubitz
Date: Tue, 28 Jan 2025 12:38:57 +0100
Subject: [PATCH 4/6] BUGFIX: Fix code example
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 3acb8ae..1820e40 100644
--- a/README.md
+++ b/README.md
@@ -157,4 +157,4 @@ Sort nodes by their position in the node tree. Please use with care, as this can
Example:
- ${q(node).children("main").sortRecursiveByIndex(['ASC'|'DESC']).get()}
+ ${q(node).children("main").sortRecursiveByIndex('DESC').get()}
From 14203491584422d8373f1343389276e076e7587e Mon Sep 17 00:00:00 2001
From: Denny Lubitz
Date: Tue, 28 Jan 2025 12:40:05 +0100
Subject: [PATCH 5/6] TASK: Revert non Neos 9 related change
---
.../Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
index 96e915e..d42abfd 100644
--- a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
+++ b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
@@ -46,7 +46,7 @@ class SortRecursiveByIndexOperation extends AbstractOperation
*/
public function canEvaluate($context)
{
- return (isset($context[0]) && ($context[0] instanceof Node)) || (is_array($context) && count($context) === 0);
+ return (isset($context[0]) && ($context[0] instanceof Node));
}
/**
From 6e1e49d710ebdfda6d05477fa243abbcf2fb29f1 Mon Sep 17 00:00:00 2001
From: Denny Lubitz
Date: Wed, 5 Mar 2025 12:32:28 +0100
Subject: [PATCH 6/6] TASK: Update annotation syntax for dependency injection
---
.../Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
index d42abfd..c240954 100644
--- a/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
+++ b/Classes/Fusion/Eel/FlowQueryOperations/SortRecursiveByIndexOperation.php
@@ -36,7 +36,7 @@ class SortRecursiveByIndexOperation extends AbstractOperation
*/
protected static $priority = 100;
- #[\Neos\Flow\Annotations\Inject]
+ #[Flow\Inject]
protected ContentRepositoryRegistry $contentRepositoryRegistry;
/**