Skip to content

Commit

Permalink
FEATURE: Allow NodeCreationHandlerInterface to append additional co…
Browse files Browse the repository at this point in the history
…mmands
  • Loading branch information
mhsdesign committed Jun 11, 2023
1 parent b2222ab commit bb21d12
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 43 deletions.
23 changes: 16 additions & 7 deletions Classes/Domain/Model/Changes/AbstractCreate.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\Neos\Ui\Exception\InvalidNodeCreationHandlerException;
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationCommands;
use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface;

abstract class AbstractCreate extends AbstractStructuralChange
Expand Down Expand Up @@ -113,9 +114,17 @@ protected function createNode(
);
$contentRepository = $this->contentRepositoryRegistry->get($parentNode->subgraphIdentity->contentRepositoryId);

$command = $this->applyNodeCreationHandlers($command, $nodeTypeName, $contentRepository);
$commands = $this->applyNodeCreationHandlers(
new NodeCreationCommands($command),
$nodeTypeName,
$contentRepository
);

foreach ($commands as $command) {
$contentRepository->handle($command)
->block();
}

$contentRepository->handle($command)->block();
/** @var Node $newlyCreatedNode */
$newlyCreatedNode = $this->contentRepositoryRegistry->subgraphForNode($parentNode)
->findChildNodeConnectedThroughEdgeName($parentNode->nodeAggregateId, $nodeName);
Expand All @@ -131,15 +140,15 @@ protected function createNode(
* @throws InvalidNodeCreationHandlerException
*/
protected function applyNodeCreationHandlers(
CreateNodeAggregateWithNode $command,
NodeCreationCommands $commands,
NodeTypeName $nodeTypeName,
ContentRepository $contentRepository
): CreateNodeAggregateWithNode {
): NodeCreationCommands {
$data = $this->getData() ?: [];
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($nodeTypeName);
if (!isset($nodeType->getOptions()['nodeCreationHandlers'])
|| !is_array($nodeType->getOptions()['nodeCreationHandlers'])) {
return $command;
return $commands;
}
foreach ($nodeType->getOptions()['nodeCreationHandlers'] as $nodeCreationHandlerConfiguration) {
$nodeCreationHandler = new $nodeCreationHandlerConfiguration['nodeCreationHandler']();
Expand All @@ -150,8 +159,8 @@ protected function applyNodeCreationHandlers(
get_class($nodeCreationHandler)
), 1364759956);
}
$command = $nodeCreationHandler->handle($command, $data, $contentRepository);
$commands = $nodeCreationHandler->handle($commands, $data, $contentRepository);
}
return $command;
return $commands;
}
}
20 changes: 5 additions & 15 deletions Classes/NodeCreationHandler/ContentTitleNodeCreationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,7 @@
*/

use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFoundException;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\Flow\Annotations as Flow;
use Neos\Neos\Service\TransliterationService;

/**
* Node creation handler that sets the "title" property for new content elements according
Expand All @@ -27,32 +23,26 @@
*/
class ContentTitleNodeCreationHandler implements NodeCreationHandlerInterface
{
/**
* @Flow\Inject
* @var TransliterationService
*/
protected $transliterationService;

/**
* Set the node title for the newly created Content node
*
* @param array<string|int,mixed> $data incoming data from the creationDialog
* @throws NodeTypeNotFoundException
*/
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
{
if (
!$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName)
!$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName)
->isOfType('Neos.Neos:Content')
) {
return $command;
return $commands;
}

$propertyValues = $command->initialPropertyValues;
$propertyValues = $commands->first->initialPropertyValues;
if (isset($data['title'])) {
$propertyValues = $propertyValues->withValue('title', $data['title']);
}

return $command->withInitialPropertyValues($propertyValues);
return $commands->withInitialPropertyValues($propertyValues);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
*/

use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Property\PropertyMapper;
use Neos\Flow\Property\TypeConverter\PersistentObjectConverter;
Expand All @@ -35,14 +33,14 @@ class CreationDialogPropertiesCreationHandler implements NodeCreationHandlerInte
/**
* @param array<string|int,mixed> $data
*/
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
{
$propertyMappingConfiguration = $this->propertyMapper->buildPropertyMappingConfiguration();
$propertyMappingConfiguration->forProperty('*')->allowAllProperties();
$propertyMappingConfiguration->setTypeConverterOption(PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_OVERRIDE_TARGET_TYPE_ALLOWED, true);

$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName);
$propertyValues = $command->initialPropertyValues;
$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName);
$propertyValues = $commands->first->initialPropertyValues;
foreach ($nodeType->getConfiguration('properties') as $propertyName => $propertyConfiguration) {
if (
!isset($propertyConfiguration['ui']['showInCreationDialog'])
Expand All @@ -62,6 +60,6 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
$propertyValues = $propertyValues->withValue($propertyName, $propertyValue);
}

return $command->withInitialPropertyValues($propertyValues);
return $commands->withInitialPropertyValues($propertyValues);
}
}
18 changes: 8 additions & 10 deletions Classes/NodeCreationHandler/DocumentTitleNodeCreationHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
use Behat\Transliterator\Transliterator;
use Neos\ContentRepository\Core\Dimension\ContentDimensionId;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
use Neos\Flow\I18n\Exception\InvalidLocaleIdentifierException;
use Neos\Flow\I18n\Locale;
use Neos\Neos\Service\TransliterationService;
Expand All @@ -42,15 +40,15 @@ class DocumentTitleNodeCreationHandler implements NodeCreationHandlerInterface
/**
* @param array<string|int,mixed> $data
*/
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands
{
if (
!$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName)
!$contentRepository->getNodeTypeManager()->getNodeType($commands->first->nodeTypeName)
->isOfType('Neos.Neos:Document')
) {
return $command;
return $commands;
}
$propertyValues = $command->initialPropertyValues;
$propertyValues = $commands->first->initialPropertyValues;
if (isset($data['title'])) {
$propertyValues = $propertyValues->withValue('title', $data['title']);
}
Expand All @@ -59,14 +57,14 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
$uriPathSegment = $data['title'];

// otherwise, we fall back to the node name
if ($uriPathSegment === null && $command->nodeName !== null) {
$uriPathSegment = $command->nodeName->value;
if ($uriPathSegment === null && $commands->first->nodeName !== null) {
$uriPathSegment = $commands->first->nodeName->value;
}

// if not empty, we transliterate the uriPathSegment according to the language of the new node
if ($uriPathSegment !== null && $uriPathSegment !== '') {
$uriPathSegment = $this->transliterateText(
$command->originDimensionSpacePoint->toDimensionSpacePoint(),
$commands->first->originDimensionSpacePoint->toDimensionSpacePoint(),
$uriPathSegment
);
} else {
Expand All @@ -76,7 +74,7 @@ public function handle(CreateNodeAggregateWithNode $command, array $data, Conten
$uriPathSegment = Transliterator::urlize($uriPathSegment);
$propertyValues = $propertyValues->withValue('uriPathSegment', $uriPathSegment);

return $command->withInitialPropertyValues($propertyValues);
return $commands->withInitialPropertyValues($propertyValues);
}

private function transliterateText(DimensionSpacePoint $dimensionSpacePoint, string $text): string
Expand Down
87 changes: 87 additions & 0 deletions Classes/NodeCreationHandler/NodeCreationCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace Neos\Neos\Ui\NodeCreationHandler;

/*
* This file is part of the Neos.Neos.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetNodeReferences;

/**
* A collection of commands that can be "enriched" via a {@see NodeCreationHandlerInterface}
* The first command points to the initial node creation command.
* It is ensured, that the initial node creation command, will be mostly preserved, to not contradict the users intend.
*
* Additional commands can be also appended, to be run after the initial node creation command.
*
* All commands will be executed blocking.
*
* @api except the constructor
*/
class NodeCreationCommands implements \IteratorAggregate
{
/**
* The initial node creation command.
* It is only allowed to change its properties via {@see self::withInitialPropertyValues()}
*/
public readonly CreateNodeAggregateWithNode $first;

/**
* Add a list of commands that are executed after the initial created command was run.
* This allows to create child-nodes and append other allowed commands.
*
* @var array<int|string, CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences>
*/
public readonly array $additionalCommands;

/**
* @internal to guarantee that the initial create command is mostly preserved as intended.
* You can use {@see self::withInitialPropertyValues()} to add new properties of the to be created node.
*/
public function __construct(
CreateNodeAggregateWithNode $first,
CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands
) {
$this->first = $first;
$this->additionalCommands = $additionalCommands;
}

/**
* Augment the first {@see CreateNodeAggregateWithNode} command with altered properties.
*/
public function withInitialPropertyValues(PropertyValuesToWrite $newInitialPropertyValues): self
{
return new self(
$this->first->withInitialPropertyValues($newInitialPropertyValues),
...$this->additionalCommands
);
}

public function withAdditionalCommands(
CreateNodeAggregateWithNode|SetNodeProperties|DisableNodeAggregate|EnableNodeAggregate|SetNodeReferences ...$additionalCommands
): self {
return new self($this->first, ...$this->additionalCommands, ...$additionalCommands);
}

/**
* @return \Traversable<int, CommandInterface>
*/
public function getIterator(): \Traversable
{
yield $this->first;
yield from $this->additionalCommands;
}
}
10 changes: 5 additions & 5 deletions Classes/NodeCreationHandler/NodeCreationHandlerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
*/

use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;

/**
* Contract for Node Creation handler that allow to hook into the process just before a node is being added
Expand All @@ -21,11 +20,12 @@
interface NodeCreationHandlerInterface
{
/**
* Do something with the newly created node
* You can "enrich" the node creation, by for example adding initial properties {@see NodeCreationCommands::withInitialPropertyValues()}
* or appending additional f.x. create-child nodes commands {@see NodeCreationCommands::withAdditionalCommands()}
*
* @param CreateNodeAggregateWithNode $command The original node creation command
* @param NodeCreationCommands $commands original or previous commands, with the first command being the initial intended node creation
* @param array<string|int,mixed> $data incoming data from the creationDialog
* @return CreateNodeAggregateWithNode the original command or a new creation command with altered properties
* @return NodeCreationCommands the "enriched" commands, to be passed to the next handler or run at the end
*/
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode;
public function handle(NodeCreationCommands $commands, array $data, ContentRepository $contentRepository): NodeCreationCommands;
}

0 comments on commit bb21d12

Please sign in to comment.