Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Neos 9 Support #133

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Project files
/.idea/workspace.xml
/.idea/tasks.xml
/.idea/markdown-navigator*
/.idea/shelf
/dev/.vagrant/
/dev/php_errors.log
/dev/dev-control/
/dev/db_backup_*
/dev/local.itermocil.yaml


# General swap/backup files
*.so
*.log
*.out
*~
*.swp
*.DS_Store
*.sass-cache*

!*.gitkeep
Comment on lines +2 to +22
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please ignore these in your local working copy (vi .git/info/exclude)

13 changes: 6 additions & 7 deletions Classes/Fusion/Helper/NodeHelper.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?php
namespace Neos\Form\Builder\Fusion\Helper;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\PropertyCollection;
use Neos\Eel\ProtectedContextAwareInterface;

/**
Expand All @@ -13,16 +14,14 @@ class NodeHelper implements ProtectedContextAwareInterface
/**
* Merge properties of the specified $node to the given $properties (with precedence to node properties)
*
* Note: This is required since NodeInterface::getProperties() does no longer return an array but an instance of PropertyCollectionInterface
*
* @param array $properties
* @param NodeInterface $node
* @param Node $node
* @return array
*/
public function mergeProperties(array $properties, NodeInterface $node): array
public function mergeProperties(array $properties, Node $node): array
{
$nodeProperties = $node->getProperties();
if ($nodeProperties instanceof \Traversable) {
$nodeProperties = $node->properties;
if ($nodeProperties instanceof PropertyCollection) {
$nodeProperties = iterator_to_array($nodeProperties);
}
return array_merge($properties, $nodeProperties);
Expand Down
17 changes: 11 additions & 6 deletions Classes/Fusion/SelectOptionCollectionImplementation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,29 @@
class SelectOptionCollectionImplementation extends AbstractArrayFusionObject
{

protected $ignoreProperties = ['prependOptionLabel', 'prependOptionValue', 'labelPropertyPath', 'valuePropertyPath'];
protected $ignoreProperties = [
'prependOptionLabel',
'prependOptionValue',
'labelPropertyPath',
'valuePropertyPath'
];

public function evaluate()
{
$collection = $this->getCollection();
$items = $this->getItems();
$options = [];
if (!empty($prependLabel = $this->getPrependOptionLabel())) {
$options[$this->getPrependOptionValue()] = $prependLabel;
}
if ($collection === null) {
if ($items === null) {
foreach ($this->properties as $propertyName => $propertyValue) {
if (in_array($propertyName, $this->ignoreProperties)) {
continue;
}
$options[$propertyName] = $propertyValue;
}
} else {
foreach ($collection as $item) {
foreach ($items as $item) {
$value = ObjectAccess::getPropertyPath($item, $this->getValuePropertyPath());
$label = ObjectAccess::getPropertyPath($item, $this->getLabelPropertyPath());
if (strlen($label) === 0) {
Expand All @@ -39,9 +44,9 @@ public function evaluate()
/**
* @return array|\Traversable
*/
private function getCollection()
private function getItems()
{
return $this->fusionValue('collection');
return $this->fusionValue('items');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just wondering: Why did you rename that? Doesn't that lead to a needless breaking change?

}

private function getValuePropertyPath(): string
Expand Down
6 changes: 3 additions & 3 deletions Classes/NodeType/FormNodeTypePostprocessor.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php
namespace Neos\Form\Builder\NodeType;

use Neos\ContentRepository\Domain\Model\NodeType;
use Neos\ContentRepository\NodeTypePostprocessor\NodeTypePostprocessorInterface;
use Neos\ContentRepository\Core\NodeType\NodeTypePostprocessorInterface;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\Flow\Annotations as Flow;

class FormNodeTypePostprocessor implements NodeTypePostprocessorInterface
Expand Down Expand Up @@ -31,4 +31,4 @@ public function process(NodeType $nodeType, array &$configuration, array $option
// The following line is a preparation for the "new Neos UI"
// $configuration['ui']['creationDialog']['elements']['preset']['ui']['editorOptions']['values'] = $presetOptions;
}
}
}
4 changes: 2 additions & 2 deletions Classes/NodeType/ResourceCollectionsPostprocessor.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php
namespace Neos\Form\Builder\NodeType;

use Neos\ContentRepository\Domain\Model\NodeType;
use Neos\ContentRepository\NodeTypePostprocessor\NodeTypePostprocessorInterface;
use Neos\ContentRepository\Core\NodeType\NodeTypePostprocessorInterface;
use Neos\ContentRepository\Core\NodeType\NodeType;
use Neos\Flow\Annotations as Flow;

/**
Expand Down
24 changes: 15 additions & 9 deletions Classes/NodeType/SelectOptionsCreationHandler.php
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
<?php
namespace Neos\Form\Builder\NodeType;

use Neos\ContentRepository\Domain\Model\NodeInterface;

use Neos\Neos\Ui\NodeCreationHandler\NodeCreationHandlerInterface;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode;

class SelectOptionsCreationHandler implements NodeCreationHandlerInterface
{

/**
* @param NodeInterface $node The newly created node
* @param array $data incoming data from the creationDialog
* @return void
* @param CreateNodeAggregateWithNode $command The original node creation command
* @param array<string|int,mixed> $data incoming data from the creationDialog
* @return CreateNodeAggregateWithNode the original command or a new creation command with altered properties
*/
public function handle(NodeInterface $node, array $data)
public function handle(CreateNodeAggregateWithNode $command, array $data, ContentRepository $contentRepository): CreateNodeAggregateWithNode
{
if (!$node->getNodeType()->isOfType('Neos.Form.Builder:SelectOption')) {
return;
if (!$contentRepository->getNodeTypeManager()->getNodeType($command->nodeTypeName)->isOfType('Neos.Form.Builder:SelectOption')) {
return $command;
}

if (isset($data['value'])) {
$node->setProperty('value', $data['value']);
$propertyValues = $propertyValues->withValue('value', $data['value']);
}
if (isset($data['label'])) {
$node->setProperty('label', $data['label']);
$propertyValues = $propertyValues->withValue('label', $data['label']);
}

return $command->withInitialPropertyValues($propertyValues);
}
}
56 changes: 3 additions & 53 deletions Classes/Package.php
Original file line number Diff line number Diff line change
@@ -1,74 +1,24 @@
<?php
namespace Neos\Form\Builder;

use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\Eel\FlowQuery\FlowQuery;
use Neos\Flow\Core\Bootstrap;
use Neos\ContentRepository\Domain\Model\Node;
use Neos\Flow\Package\Package as BasePackage;

/**
* The Package class, wiring signal/slot during boot.
*/
class Package extends BasePackage
{
private const NODE_TYPE_IDENTIFIER_MIXIN = 'Neos.Form.Builder:IdentifierMixin';

/**
* @param Bootstrap $bootstrap The current bootstrap
* @return void
*/
public function boot(Bootstrap $bootstrap)
{
$dispatcher = $bootstrap->getSignalSlotDispatcher();

$dispatcher->connect(Node::class, 'nodePropertyChanged', function (NodeInterface $node, $propertyName, $_, $newValue) {
if ($propertyName !== 'identifier' || empty($newValue) || !$node->getNodeType()->isOfType(self::NODE_TYPE_IDENTIFIER_MIXIN)) {
return;
}

$this->setUniqueFormElementIdentifier($node, $newValue);
});

$dispatcher->connect(Node::class, 'nodeAdded', function (NodeInterface $node) {
try {
$identifier = $node->getProperty('identifier');

if (empty($identifier) || !$node->getNodeType()->isOfType(self::NODE_TYPE_IDENTIFIER_MIXIN)) {
return;
}
} catch (\Neos\ContentRepository\Exception\NodeException $e) {
return;
}

$this->setUniqueFormElementIdentifier($node, $identifier);
});
}

/**
* @param NodeInterface $node
* @param string $identifier
* @throws \Neos\Eel\Exception
*/
private function setUniqueFormElementIdentifier(NodeInterface $node, string $identifier): void
{
/** @noinspection PhpUndefinedMethodInspection */
$flowQuery = (new FlowQuery([$node]))->context([
'invisibleContentShown' => true,
'removedContentShown' => true,
'inaccessibleContentShown' => true
]);
$possibleIdentifier = $identifier;
$i = 1;
/** @noinspection PhpUndefinedMethodInspection */
while ($flowQuery
->closest('[instanceof Neos.Form.Builder:NodeBasedForm]')
// [identifier=".."] matches the Form Element identifier, [_identiier!="..."] excludes the current node
->find(sprintf('[instanceof %s][identifier="%s"][_identifier!="%s"]',
self::NODE_TYPE_IDENTIFIER_MIXIN ,$possibleIdentifier, $node->getIdentifier()))
->count() > 0) {
$possibleIdentifier = $identifier . '-' . $i++;
}
$node->setProperty('identifier', $possibleIdentifier);
# BREAKING in Neos 9: No node signals anymore
# Missing here: Setting of identifier for Neos.Form.Builder:NodeBasedForm on nodePropertyChanged and nodeAdded
}
}
2 changes: 1 addition & 1 deletion Configuration/NodeTypes.FormElement.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
constraints:
nodeTypes:
'*': false
label: "${q(node).property('identifier') || q(node).property('label') || ((node.nodeType.label || node.nodeType.name) + ' (' + node.name + ')')}"
label: "${q(node).property('identifier') || q(node).property('label') || ((node.nodeType.label || node.nodeTypeName.value) + ' (' + node.name + ')')}"
ui:
inlineEditable: true
label: 'Form Element'
Expand Down
2 changes: 1 addition & 1 deletion Configuration/NodeTypes.FormPage.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'Neos.Form.Builder:FormPage':
label: "${String.cropAtWord(String.trim(String.stripTags(String.pregReplace(q(node).property('title') || q(node).property('label') || ((I18n.translate(node.nodeType.label) || node.nodeType.name) + (node.autoCreated ? ' (' + node.name + ')' : '')), '/<br\\W*?\\/?>|\\x{00a0}|[^[:print:]]|\\s+/u', ' '))), 100, '...')}"
label: "${String.cropAtWord(String.trim(String.stripTags(String.pregReplace(q(node).property('title') || q(node).property('label') || ((I18n.translate(node.nodeType.label) || node.nodeTypeName.value) + (node.autoCreated ? ' (' + node.name + ')' : '')), '/<br\\W*?\\/?>|\\x{00a0}|[^[:print:]]|\\s+/u', ' '))), 100, '...')}"
superTypes:
'Neos.Neos:Content': true
constraints:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ selectable:
// ...
properties {
options = Neos.Form.Builder:SelectOptionCollection {
collection = ${q(site).children('[instanceof Some.Package:NewsletterCategory]')}
items = ${q(site).children('[instanceof Some.Package:NewsletterCategory]')}
# we use the node identifier as value, we could use "name" or "label" instead for example
valuePropertyPath = 'identifier'
}
Expand Down
2 changes: 1 addition & 1 deletion Resources/Private/Fusion/Form.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ prototype(Neos.Form.Builder:Form) {
renderingOptions = Neos.Fusion:DataStructure
renderCallbacks = Neos.Fusion:DataStructure
firstPage = Neos.Form.Builder:FormPage.Definition {
elements = Neos.Form.Builder:ElementCollection
elements = Neos.Form.Builder:ElementCollection
}
furtherPages = Neos.Form.Builder:PageCollection
finishers = Neos.Form.Builder:FinisherCollection
Expand Down
4 changes: 2 additions & 2 deletions Resources/Private/Fusion/NodeBased/NodeBasedFinisher.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ prototype(Neos.Form.Builder:NodeBasedFinisher) < prototype(Neos.Fusion:Renderer)
}
}

prototype(Neos.Form.Builder:NodeBasedFinisherCollection) < prototype(Neos.Fusion:Collection) {
prototype(Neos.Form.Builder:NodeBasedFinisherCollection) < prototype(Neos.Fusion:Loop) {
itemName = 'finisherNode'
itemRenderer = Neos.Fusion:Case {
formElementTypeFromNodeType {
condition = ${!finisherNode.nodeType.options.form.formElementType}
renderer = Neos.Form.Builder:NodeBasedFinisher {
type = ${finisherNode.nodeType.name + '.Definition'}
type = ${finisherNode.nodeTypeName.value + '.Definition'}
}
}

Expand Down
8 changes: 4 additions & 4 deletions Resources/Private/Fusion/NodeBased/NodeBasedForm.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ prototype(Neos.Form.Builder:NodeBasedForm) < prototype(Neos.Form.Builder:Form) {
renderingOptions._node = ${formNode}
renderingOptions._fusionPath = ${formFusionPath}
elements = Neos.Form.Builder:NodeBasedElementCollection {
collection = ${q(formNode).children('elements').children()}
items = ${q(formNode).children('elements').children()}
}
}
furtherPages = Neos.Form.Builder:NodeBasedPageCollection {
collection = ${q(formNode).children('furtherPages').children()}
items = ${q(formNode).children('furtherPages').children()}
}
finishers = Neos.Form.Builder:NodeBasedFinisherCollection {
collection = ${q(formNode).children('finishers').children()}
items = ${q(formNode).children('finishers').children()}
}
@process.contentElementWrapping = Neos.Neos:ContentElementWrapping {
additionalAttributes {
'data-_neos-form-builder-type' = ${formNode.nodeType.name}
'data-_neos-form-builder-type' = ${formNode.nodeTypeName.value}
}
}

Expand Down
10 changes: 5 additions & 5 deletions Resources/Private/Fusion/NodeBased/NodeBasedFormElement.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,32 @@ prototype(Neos.Form.Builder:NodeBasedFormElement) < prototype(Neos.Fusion:Render
defaultValue = ${elementNode.properties.defaultValue}
properties.@process.addNodeProperties = ${Neos.Form.Builder.Node.mergeProperties(value, elementNode)}
validators = Neos.Form.Builder:NodeBasedValidatorCollection {
collection = ${q(elementNode).children('validators').children()}
items = ${q(elementNode).children('validators').children()}
}
renderingOptions._node = ${elementNode}
renderingOptions._fusionPath = ${element.path}

properties.options.@process.overrideFromNode = Neos.Form.Builder:SelectOptionCollection {
collection = ${q(elementNode).children('options').children()}
items = ${q(elementNode).children('options').children()}
valuePropertyPath = 'properties.value'
labelPropertyPath = 'properties.label'
@if.isSelectFormElement = ${q(elementNode).is('[instanceof Neos.Form.Builder:SelectionMixin]')}
}

elements.@process.overrideFromNode = Neos.Form.Builder:NodeBasedElementCollection {
collection = ${q(elementNode).children('elements').children()}
items = ${q(elementNode).children('elements').children()}
@if.isSectionFormElement = ${q(elementNode).is('[instanceof Neos.Form.Builder:SectionMixin]')}
}
}
}

prototype(Neos.Form.Builder:NodeBasedElementCollection) < prototype(Neos.Fusion:Collection) {
prototype(Neos.Form.Builder:NodeBasedElementCollection) < prototype(Neos.Fusion:Loop) {
itemName = 'elementNode'
itemRenderer = Neos.Fusion:Case {
formElementTypeFromNodeType {
condition = ${!elementNode.nodeType.options.form.formElementType}
renderer = Neos.Form.Builder:NodeBasedFormElement {
type = ${elementNode.nodeType.name + '.Definition'}
type = ${elementNode.nodeTypeName.value + '.Definition'}
}
}
default {
Expand Down
8 changes: 4 additions & 4 deletions Resources/Private/Fusion/NodeBased/NodeBasedFormPage.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ prototype(Neos.Form.Builder:NodeBasedFormPage) < prototype(Neos.Fusion:Renderer)
renderingOptions._fusionPath = ${page.path}

elements = Neos.Form.Builder:NodeBasedElementCollection {
collection = ${q(pageNode).children('elements').children()}
items = ${q(pageNode).children('elements').children()}
}
}
}

prototype(Neos.Form.Builder:NodeBasedPageCollection) < prototype(Neos.Fusion:Collection) {
prototype(Neos.Form.Builder:NodeBasedPageCollection) < prototype(Neos.Fusion:Loop) {
itemName = 'pageNode'
itemRenderer = Neos.Fusion:Case {
formElementTypeFromNodeType {
condition = ${!pageNode.nodeType.options.form.formElementType}
renderer = Neos.Form.Builder:NodeBasedFormPage {
type = ${pageNode.nodeType.name + '.Definition'}
type = ${pageNode.nodeTypeName.value + '.Definition'}
}
}

Expand All @@ -30,4 +30,4 @@ prototype(Neos.Form.Builder:NodeBasedPageCollection) < prototype(Neos.Fusion:Col
}
}
}
}
}