Skip to content

Commit 85246fe

Browse files
committed
WIP: FEATURE: Neos 9.0 Prototype
1 parent 9042e5a commit 85246fe

3 files changed

Lines changed: 190 additions & 14 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php
2+
namespace Flowpack\NodeTemplates;
3+
4+
use Neos\ContentRepository\Core\ContentRepository;
5+
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
6+
use Neos\ContentRepository\Core\Feature\NodeCreation\Dto\NodeAggregateIdsByNodePaths;
7+
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
8+
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
9+
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
10+
use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath;
11+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
12+
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
13+
14+
/**
15+
* TODO To use it replace the line https://github.com/neos/neos-ui/blob/a7ab78f006320ec6b58be106a5451cd24be075ac/Classes/Domain/Model/Changes/AbstractCreate.php#L118 with
16+
* `(new NeosEscrTemplateWriteAdapter())->writeTemplate($command, $contentRepository);`
17+
*/
18+
class NeosEscrTemplateWriteAdapter
19+
{
20+
public function writeTemplate(CreateNodeAggregateWithNode $createNodeAggregateWithNode, ContentRepository $contentRepository): void
21+
{
22+
$template = [
23+
'childNodes' => [
24+
'main' => [
25+
'name' => 'main',
26+
'childNodes' => [
27+
'content1' => [
28+
'type' => "Neos.Demo:Content.Text",
29+
'properties' => [
30+
'text' => "huhu",
31+
]
32+
]
33+
]
34+
],
35+
'foo' => [
36+
'type' => 'Neos.Demo:Document.Page',
37+
'childNodes' => [
38+
'main' => [
39+
'name' => 'main',
40+
'childNodes' => [
41+
'content1' => [
42+
'type' => "Neos.Demo:Content.Text",
43+
'properties' => [
44+
'text' => "textiii"
45+
]
46+
],
47+
'content2' => [
48+
'type' => "Neos.Demo:Content.Text",
49+
'properties' => [
50+
'text' => "huijkuihjnihujbn"
51+
]
52+
]
53+
]
54+
],
55+
]
56+
]
57+
]
58+
];
59+
60+
$createNodeCommand = $this->augmentWithTetheredDescendantNodeAggregateIds($createNodeAggregateWithNode, $contentRepository);
61+
62+
if (isset($template['properties'])) {
63+
// documents generate uripath blabla
64+
$createNodeCommand = $createNodeCommand->initialPropertyValues->merge(
65+
PropertyValuesToWrite::fromArray($this->requireValidProperties($template['properties']))
66+
);
67+
}
68+
69+
$commands = $this->createCommandsRecursivelyFromTemplateChildNodes($createNodeCommand, $template, $contentRepository);
70+
71+
$contentRepository->handle($createNodeCommand)->block();
72+
73+
foreach ($commands as $command) {
74+
$contentRepository->handle($command)->block();
75+
}
76+
}
77+
78+
/**
79+
* In the old CR, it was common practice to set internal or meta properties via this syntax: `_hidden` so it was also allowed in templates but not anymore.
80+
* @throws \InvalidArgumentException
81+
*/
82+
private function requireValidProperties(array $properties): array
83+
{
84+
$legacyInternalProperties = [
85+
'_accessRoles',
86+
'_contentObject',
87+
'_hidden',
88+
'_hiddenAfterDateTime',
89+
'_hiddenBeforeDateTime',
90+
'_hiddenInIndex',
91+
'_index',
92+
'_name',
93+
'_nodeType',
94+
'_removed',
95+
'_workspace'
96+
];
97+
foreach ($properties as $propertyName => $propertyValue) {
98+
if (str_starts_with($propertyName, '_')) {
99+
$lowerPropertyName = strtolower($propertyName);
100+
foreach ($legacyInternalProperties as $legacyInternalProperty) {
101+
if ($lowerPropertyName === strtolower($legacyInternalProperty)) {
102+
throw new \InvalidArgumentException('Internal legacy properties are not implement.' . $propertyName);
103+
}
104+
}
105+
}
106+
}
107+
return $properties;
108+
}
109+
110+
private function createCommandsRecursivelyFromTemplateChildNodes(CreateNodeAggregateWithNode $createParentNodeCommand, array $template, ContentRepository $contentRepository): array
111+
{
112+
$makeCreateNodeCommand = function (NodeAggregateId $parentNodeAggregateId, array $subTemplate) use ($createParentNodeCommand, $contentRepository) {
113+
return $this->augmentWithTetheredDescendantNodeAggregateIds(new CreateNodeAggregateWithNode(
114+
contentStreamId: $createParentNodeCommand->contentStreamId,
115+
nodeAggregateId: NodeAggregateId::create(),
116+
nodeTypeName: NodeTypeName::fromString($subTemplate['type']),
117+
originDimensionSpacePoint: $createParentNodeCommand->originDimensionSpacePoint,
118+
parentNodeAggregateId: $parentNodeAggregateId,
119+
nodeName: NodeName::fromString(uniqid('node-', false)),
120+
initialPropertyValues: isset($subTemplate['properties'])
121+
? PropertyValuesToWrite::fromArray($this->requireValidProperties($subTemplate['properties']))
122+
: null
123+
), $contentRepository);
124+
};
125+
126+
$commands = [];
127+
foreach ($template['childNodes'] ?? [] as $childNode) {
128+
if (isset($childNode['name']) && $autoCreatedNodeId = $createParentNodeCommand->tetheredDescendantNodeAggregateIds->getNodeAggregateId(NodePath::fromString($childNode['name']))) {
129+
if (isset($childNode['type'])) {
130+
throw new \Exception('For auto-created nodes the type cannot be qualified.');
131+
}
132+
if (isset($childNode['properties'])) {
133+
$commands[] = new SetNodeProperties(
134+
$createParentNodeCommand->contentStreamId,
135+
$autoCreatedNodeId,
136+
$createParentNodeCommand->originDimensionSpacePoint,
137+
PropertyValuesToWrite::fromArray($this->requireValidProperties($childNode['properties']))
138+
);
139+
}
140+
foreach ($childNode['childNodes'] ?? [] as $innerChildNode) {
141+
$commands[] = $newParent = $makeCreateNodeCommand($autoCreatedNodeId, $innerChildNode);
142+
$commands = [...$commands, ...$this->createCommandsRecursivelyFromTemplateChildNodes($newParent, $innerChildNode, $contentRepository)];
143+
}
144+
} else {
145+
// if is document setUriPath based on title
146+
$commands[] = $newParent = $makeCreateNodeCommand($createParentNodeCommand->nodeAggregateId, $childNode);
147+
$commands = [...$commands, ...$this->createCommandsRecursivelyFromTemplateChildNodes($newParent, $childNode, $contentRepository)];
148+
}
149+
}
150+
return $commands;
151+
}
152+
153+
/**
154+
* Precalculate the nodeIds for the auto-created childNodes, so that we can determine the id beforehand and use it for succeeding operations.
155+
*
156+
* ```
157+
* $mainContentCollectionNodeId = $createNodeAggregateWithNode->tetheredDescendantNodeAggregateIds->getNodeAggregateId(NodePath::fromString('main'))
158+
* ```
159+
*/
160+
private function augmentWithTetheredDescendantNodeAggregateIds(CreateNodeAggregateWithNode $createNodeAggregateWithNode, ContentRepository $contentRepository): CreateNodeAggregateWithNode
161+
{
162+
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($createNodeAggregateWithNode->nodeTypeName);
163+
if (!isset($nodeType->getFullConfiguration()['childNodes'])) {
164+
return $createNodeAggregateWithNode;
165+
}
166+
$nodeAggregateIdsByNodePaths = NodeAggregateIdsByNodePaths::createEmpty();
167+
foreach (array_keys($nodeType->getFullConfiguration()['childNodes']) as $autoCreatedNodeName) {
168+
$nodeAggregateIdsByNodePaths = $nodeAggregateIdsByNodePaths->add(
169+
NodePath::fromString($autoCreatedNodeName),
170+
NodeAggregateId::create()
171+
);
172+
}
173+
return $createNodeAggregateWithNode->withTetheredDescendantNodeAggregateIds(
174+
$nodeAggregateIdsByNodePaths->merge($createNodeAggregateWithNode->tetheredDescendantNodeAggregateIds)
175+
);
176+
}
177+
}

Configuration/NodeTypes.yaml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
'Neos.Neos:Node':
2-
options:
3-
nodeCreationHandlers:
4-
templateNodeCreationHandler:
5-
nodeCreationHandler: 'Flowpack\NodeTemplates\NodeCreationHandler\TemplateNodeCreationHandler'
6-
7-
'Neos.Neos:Document':
8-
options:
9-
nodeCreationHandlers:
10-
documentTitle:
11-
nodeCreationHandler: 'Flowpack\NodeTemplates\NodeCreationHandler\TemplatingDocumentTitleNodeCreationHandler'
1+
# 'Neos.Neos:Node':
2+
# options:
3+
# nodeCreationHandlers:
4+
# templateNodeCreationHandler:
5+
# nodeCreationHandler: 'Flowpack\NodeTemplates\NodeCreationHandler\TemplateNodeCreationHandler'
6+
#
7+
# 'Neos.Neos:Document':
8+
# options:
9+
# nodeCreationHandlers:
10+
# documentTitle:
11+
# nodeCreationHandler: 'Flowpack\NodeTemplates\NodeCreationHandler\TemplatingDocumentTitleNodeCreationHandler'

composer.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
"name": "flowpack/nodetemplates",
55
"license": "GPL-3.0+",
66
"require": {
7-
"php": ">=7.4",
8-
"neos/neos": "^7.3 || ^8.0",
9-
"neos/neos-ui": "^7.3 || ^8.0"
7+
"neos/neos": "^9.0",
8+
"neos/neos-ui": "^9.0"
109
},
1110
"autoload": {
1211
"psr-4": {

0 commit comments

Comments
 (0)