3333use Neos \Flow \Annotations as Flow ;
3434use Neos \Neos \Domain \NodeLabel \NodeLabelGeneratorInterface ;
3535use Neos \Neos \Domain \SubtreeTagging \NeosSubtreeTag ;
36+ use Neos \Neos \PendingChangesProjection \Changes ;
3637use Neos \Neos \Ui \Application \Shared \NodeTypeWasNotFound ;
3738use Neos \Neos \Ui \Application \Shared \NodeWasNotFound ;
3839use Neos \Neos \Ui \Application \Shared \TreeNodeBuilder ;
40+ use Neos \Workspace \Ui \ViewModel \PendingChanges ;
3941
4042/**
4143 * @internal
@@ -46,7 +48,8 @@ final class NodeService
4648 public function __construct (
4749 private readonly ContentRepository $ contentRepository ,
4850 public readonly ContentSubgraphInterface $ subgraph ,
49- private readonly NodeLabelGeneratorInterface $ nodeLabelGenerator
51+ private readonly NodeLabelGeneratorInterface $ nodeLabelGenerator ,
52+ public readonly NodeChangeStateCollection $ pendingChanges ,
5053 ) {
5154 }
5255
@@ -88,26 +91,30 @@ public function getLabelForNode(Node $node): string
8891
8992 public function findParentNode (Node $ node ): ?Node
9093 {
91- return $ this ->subgraph ->findParentNode ($ node ->aggregateId );
94+ $ parent = $ this ->subgraph ->findParentNode ($ node ->aggregateId );
95+ return $ parent && $ this ->filterRemovedNodes ($ parent ) ? $ parent : null ;
9296 }
9397
9498 public function findPrecedingSiblingNodes (Node $ node ): Nodes
9599 {
96100 $ filter = FindPrecedingSiblingNodesFilter::create ();
97101
98- return $ this ->subgraph ->findPrecedingSiblingNodes ($ node ->aggregateId , $ filter );
102+ return $ this ->subgraph ->findPrecedingSiblingNodes ($ node ->aggregateId , $ filter )
103+ ->filter (fn (Node $ node ) => $ this ->filterRemovedNodes ($ node ));
99104 }
100105
101106 public function findSucceedingSiblingNodes (Node $ node ): Nodes
102107 {
103108 $ filter = FindSucceedingSiblingNodesFilter::create ();
104109
105- return $ this ->subgraph ->findSucceedingSiblingNodes ($ node ->aggregateId , $ filter );
110+ return $ this ->subgraph ->findSucceedingSiblingNodes ($ node ->aggregateId , $ filter )
111+ ->filter (fn (Node $ node ) => $ this ->filterRemovedNodes ($ node ));
106112 }
107113
108114 public function findNodeByAbsoluteNodePath (AbsoluteNodePath $ path ): ?Node
109115 {
110- return $ this ->subgraph ->findNodeByAbsolutePath ($ path );
116+ $ node = $ this ->subgraph ->findNodeByAbsolutePath ($ path );
117+ return $ node && $ this ->filterRemovedNodes ($ node ) ? $ node : null ;
111118 }
112119
113120 public function search (Node $ rootNode , string $ searchTerm , NodeTypeFilter $ nodeTypeFilter ): Nodes
@@ -117,7 +124,8 @@ public function search(Node $rootNode, string $searchTerm, NodeTypeFilter $nodeT
117124 searchTerm: $ searchTerm
118125 );
119126
120- return $ this ->subgraph ->findDescendantNodes ($ rootNode ->aggregateId , $ filter );
127+ return $ this ->subgraph ->findDescendantNodes ($ rootNode ->aggregateId , $ filter )
128+ ->filter (fn (Node $ node ) => $ this ->filterRemovedNodes ($ node ));
121129 }
122130
123131 public function createTreeBuilderForRootNode (
@@ -135,6 +143,7 @@ public function createTreeBuilderForRootNode(
135143 public function createTreeNodeBuilderForNode (Node $ node ): TreeNodeBuilder
136144 {
137145 $ nodeType = $ this ->requireNodeTypeByName ($ node ->nodeTypeName );
146+ $ pendingChange = $ this ->pendingChanges ->findByNodeAggreqateId ($ node ->aggregateId );
138147
139148 return new TreeNodeBuilder (
140149 nodeAddress: NodeAddress::fromNode ($ node ),
@@ -147,7 +156,10 @@ public function createTreeNodeBuilderForNode(Node $node): TreeNodeBuilder
147156 hasScheduledDisabledState:
148157 $ node ->getProperty ('enableAfterDateTime ' ) instanceof \DateTimeInterface
149158 || $ node ->getProperty ('disableAfterDateTime ' ) instanceof \DateTimeInterface,
150- hasUnloadedChildren: false
159+ hasUnloadedChildren: false ,
160+ isCreated: $ pendingChange ? $ pendingChange ->isCreated : false ,
161+ isModified: $ pendingChange ? $ pendingChange ->isChanged : false ,
162+ isDeleted: $ pendingChange ? $ pendingChange ->isDeleted : false ,
151163 );
152164 }
153165
@@ -157,7 +169,8 @@ public function findChildNodes(Node $parentNode, NodeTypeCriteria $nodeTypeCrite
157169 nodeTypes: $ nodeTypeCriteria ,
158170 );
159171
160- return $ this ->subgraph ->findChildNodes ($ parentNode ->aggregateId , $ filter );
172+ return $ this ->subgraph ->findChildNodes ($ parentNode ->aggregateId , $ filter )
173+ ->filter (fn (Node $ node ) => $ this ->filterRemovedNodes ($ node ));
161174 }
162175
163176 public function getNumberOfChildNodes (Node $ parentNode , NodeTypeCriteria $ nodeTypeCriteria ): int
@@ -173,6 +186,24 @@ public function findAncestorNodes(Node $node): Nodes
173186 {
174187 $ filter = FindAncestorNodesFilter::create ();
175188
176- return $ this ->subgraph ->findAncestorNodes ($ node ->aggregateId , $ filter );
189+ return $ this ->subgraph ->findAncestorNodes ($ node ->aggregateId , $ filter ); }
190+
191+ /**
192+ * Filter function to remove all nodes that are tagged as removed
193+ * AND do not have the tag assigned directly (deleted as children)
194+ * AND are not in the current changes (other workspaces)
195+ */
196+ public function filterRemovedNodes (Node $ node ): bool
197+ {
198+ if (!$ node ->tags ->contain (NeosSubtreeTag::removed ())) {
199+ return true ;
200+ }
201+ if ($ node ->tags ->withoutInherited ()->contain (NeosSubtreeTag::removed ())) {
202+ $ changeState = $ this ->pendingChanges ->findByNodeAggreqateId ($ node ->aggregateId );
203+ if ($ changeState instanceof NodeChangeState && $ changeState ->isDeleted ) {
204+ return true ;
205+ }
206+ }
207+ return false ;
177208 }
178209}
0 commit comments