Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
14a54ff
TASK: make Neos UI roughly work with Event Sourced CR (TODO we need t…
skurfuerst Oct 16, 2018
96fda15
BUGFIX (for ES CR): Do not manipulate ContextPath on client side
skurfuerst Dec 14, 2018
1ca2917
BUGFIX: allow to reset feedback collection (needed for E2E tests)
skurfuerst Dec 21, 2018
dbe6f43
Merge branch 'master' into event-sourced-patch
skurfuerst Dec 21, 2018
6fdf14d
Merge remote-tracking branch 'origin/master' into event-sourced-patch
skurfuerst Feb 14, 2019
55c71d1
BUGFIX: make parent state explicit
skurfuerst Feb 14, 2019
4be1aa0
HOTFIX for event-sourced-patch branch and neos-ui compiled
skurfuerst Feb 23, 2019
eb0d140
revert non-needed changes
skurfuerst Feb 27, 2019
a8e3cd8
TASK: starting to get rid of parentNodeContextPath method
skurfuerst Feb 27, 2019
52942df
Adjust to new namespace of NodeTypeConstraintFactory
Apr 6, 2019
900165d
Merge pull request #2445 from neos/cr-namespacing
skurfuerst Apr 10, 2019
7a05e21
Merge remote-tracking branch 'origin/master' into event-sourced-patch
skurfuerst May 4, 2019
fed0112
TASK: make code more robust in content tree
skurfuerst May 4, 2019
f0f8318
BUGFIX: fix move node for ES CR
skurfuerst May 7, 2019
89426a5
Merge remote-tracking branch 'origin/master' into event-sourced-patch
skurfuerst Oct 17, 2019
544f240
WIP: for event sourced patch, you need the standalone UI anyways
skurfuerst Oct 17, 2019
38f2ad0
Merge remote-tracking branch 'origin/master' into event-sourced-patch
skurfuerst Nov 18, 2019
473291e
TASK: get rid of in-frontend calculation of parent paths
skurfuerst Nov 18, 2019
e59b0ca
TASK: get fully rid of parentNodeContextPath in UI code
skurfuerst Feb 14, 2020
5cb50c3
BUGFIX: fix missing imports
skurfuerst Feb 14, 2020
3c0fa62
BUGFIX: fix context path
skurfuerst Feb 14, 2020
0096b3e
BUGFIX: fix undefined method reference
skurfuerst Feb 14, 2020
ef38b5c
Merge remote-tracking branch 'origin/master' into event-sourced-patch
skurfuerst Feb 24, 2020
14ef051
BUGFIX: fix moving of nodes
skurfuerst Feb 24, 2020
341357c
fix move node testcases
skurfuerst Feb 24, 2020
5313ffb
BUGFIX: fix discard all recursively
skurfuerst Feb 25, 2020
39bb719
BUGFIX: fix pasting of nodes
skurfuerst Feb 25, 2020
913afb4
fix edge cases found by E2E tests. Great to have them :)
skurfuerst Feb 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Classes/Domain/Model/FeedbackCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public function jsonSerialize()
'feedbacks' => $feedbacks
];
}

public function reset()
{
$this->feedbacks = [];
}
Copy link
Member Author

Choose a reason for hiding this comment

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

this does not really have anything to do with the core of the change; but we were missing a way to reset feedbacks beforehand.

}
47 changes: 33 additions & 14 deletions Classes/FlowQueryOperations/NeosUiDefaultNodesOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
* source code.
*/

use Neos\ContentRepository\Domain\NodeType\NodeTypeConstraintFactory;
use Neos\ContentRepository\Domain\Projection\Content\TraversableNodeInterface;
Copy link
Member Author

Choose a reason for hiding this comment

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

In this file, we implement everything against the TraversableNodeInterface instead of NodeInterface; so that we use the forward-compatible API already.

use Neos\ContentRepository\Exception\NodeException;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Property\PropertyMapper;
use Neos\ContentRepository\Domain\Model\NodeInterface;
Expand Down Expand Up @@ -42,6 +45,12 @@ class NeosUiDefaultNodesOperation extends AbstractOperation
*/
protected $propertyMapper;

/**
* @Flow\Inject
* @var NodeTypeConstraintFactory
*/
protected $nodeTypeConstraintFactory;

/**
* {@inheritdoc}
*
Expand All @@ -50,7 +59,7 @@ class NeosUiDefaultNodesOperation extends AbstractOperation
*/
public function canEvaluate($context)
{
return isset($context[0]) && ($context[0] instanceof NodeInterface);
return isset($context[0]) && ($context[0] instanceof TraversableNodeInterface);
}

/**
Expand All @@ -62,38 +71,48 @@ public function canEvaluate($context)
*/
public function evaluate(FlowQuery $flowQuery, array $arguments)
{
/** @var TraversableNodeInterface $siteNode */
/** @var TraversableNodeInterface $documentNode */
list($siteNode, $documentNode) = $flowQuery->getContext();
/** @var TraversableNodeInterface $toggledNodes */
list($baseNodeType, $loadingDepth, $toggledNodes, $clipboardNodesContextPaths) = $arguments;

// Collect all parents of documentNode up to siteNode
$parents = [];
$currentNode = $documentNode->getParent();
$currentNode = null;
try {
$currentNode = $documentNode->findParentNode();
} catch (NodeException $ignored) {
// parent does not exist
}
if ($currentNode) {
$parentNodeIsUnderneathSiteNode = strpos($currentNode->getPath(), $siteNode->getPath()) === 0;
while ($currentNode !== $siteNode && $parentNodeIsUnderneathSiteNode) {
$parents[] = $currentNode->getContextPath();
$currentNode = $currentNode->getParent();
$parentNodeIsUnderneathSiteNode = strpos((string)$currentNode->findNodePath(), (string)$siteNode->findNodePath()) === 0;
while ((string)$currentNode->getNodeAggregateIdentifier() !== (string)$siteNode->getNodeAggregateIdentifier() && $parentNodeIsUnderneathSiteNode) {
$parents[] = (string)$currentNode->getNodeAggregateIdentifier();
$currentNode = $currentNode->findParentNode();
}
}

$nodes = [$siteNode];
$gatherNodesRecursively = function (&$nodes, $baseNode, $level = 0) use (&$gatherNodesRecursively, $baseNodeType, $loadingDepth, $toggledNodes, $parents) {
$nodes = [
((string)$siteNode->getNodeAggregateIdentifier()) => $siteNode
];
$gatherNodesRecursively = function (&$nodes, TraversableNodeInterface $baseNode, $level = 0) use (&$gatherNodesRecursively, $baseNodeType, $loadingDepth, $toggledNodes, $parents) {
if (
$level < $loadingDepth || // load all nodes within loadingDepth
$loadingDepth === 0 || // unlimited loadingDepth
in_array($baseNode->getContextPath(), $toggledNodes) || // load toggled nodes
in_array($baseNode->getContextPath(), $parents) // load children of all parents of documentNode
in_array((string)$baseNode->getNodeAggregateIdentifier(), $toggledNodes) || // load toggled nodes
in_array((string)$baseNode->getNodeAggregateIdentifier(), $parents) // load children of all parents of documentNode
) {
foreach ($baseNode->getChildNodes($baseNodeType) as $childNode) {
$nodes[] = $childNode;
foreach ($baseNode->findChildNodes($this->nodeTypeConstraintFactory->parseFilterString($baseNodeType)) as $childNode) {
$nodes[(string)$childNode->getNodeAggregateIdentifier()] = $childNode;
$gatherNodesRecursively($nodes, $childNode, $level + 1);
}
}
};
$gatherNodesRecursively($nodes, $siteNode);

if (!in_array($documentNode, $nodes)) {
$nodes[] = $documentNode;
if (!isset($nodes[(string)$documentNode->getNodeAggregateIdentifier()])) {
$nodes[(string)$documentNode->getNodeAggregateIdentifier()] = $documentNode;
}

foreach ($clipboardNodesContextPaths as $clipboardNodeContextPath) {
Expand Down
2 changes: 2 additions & 0 deletions Classes/Fusion/Helper/NodeInfoHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ protected function getBasicNodeInformation(NodeInterface $node): array
'isAutoCreated' => $node->isAutoCreated(),
'depth' => $node->getDepth(),
'children' => [],
// In some rare cases the parent node cannot be resolved properly
'parent' => ($node->getParent() ? $node->getParent()->getContextPath() : null),
'matchesCurrentDimensions' => ($node instanceof Node && $node->dimensionsAreMatchingTargetDimensionValues())
];
}
Expand Down
1 change: 1 addition & 0 deletions packages/neos-ts-interfaces/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export interface Node {
};
isFullyLoaded: boolean;
uri: string;
parent: NodeContextPath;
policy?: NodePolicy;
dimensions?: DimensionPresetCombination;
otherNodeVariants?: DimensionPresetCombination[];
Expand Down
24 changes: 3 additions & 21 deletions packages/neos-ui-redux-store/src/CR/Nodes/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,6 @@ export const getAllowedNodeTypesTakingAutoCreatedIntoAccount = (baseNode: Node,
return nodeTypesRegistry.getAllowedChildNodeTypes(baseNode.nodeType);
};

//
// Helper function to get parent contextPath from current contextPath
//
export const parentNodeContextPath = (contextPath: NodeContextPath) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

basically, we remove this method and all transitive usages.

if (typeof contextPath !== 'string') {
console.error('`contextPath` must be a string!'); // tslint:disable-line
return null;
}
const [path, context] = contextPath.split('@');

if (path.length === 0) {
// We are at top level; so there is no parent anymore!
return null;
}

return `${path.substr(0, path.lastIndexOf('/'))}@${context}`;
};

//
// Helper function to check if the node is collapsed
//
Expand All @@ -60,9 +42,9 @@ export const calculateNewFocusedNodes = (selectionMode: SelectionModeTypes, cont
return [contextPath];
} else if (selectionMode === SelectionModeTypes.RANGE_SELECT) {
const lastSelectedNodeContextPath = focusedNodesContextPaths[focusedNodesContextPaths.length - 1];
const maybeParentNodeContextPath = parentNodeContextPath(lastSelectedNodeContextPath);
if (maybeParentNodeContextPath) {
const parentNode = getNodeOrThrow(nodesByContextPath, maybeParentNodeContextPath);
const lastSelectedNode = nodesByContextPath[lastSelectedNodeContextPath];
if (lastSelectedNode && lastSelectedNode.parent) {
const parentNode = getNodeOrThrow(nodesByContextPath, lastSelectedNode.parent);
const tempSelection: string[] = [];
let startSelectionFlag = false;
// if both start and end nodes are within children, then we can do range select
Expand Down
10 changes: 10 additions & 0 deletions packages/neos-ui-redux-store/src/CR/Nodes/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ test(`The "move" action should move things right.`, () => {
]
},
'abc/abc@user-admin;language=en_US': {
parent: 'abc@user-admin;language=en_US',
contextPath: 'abc/abc@user-admin;language=en_US',
children: [
{
Expand All @@ -122,10 +123,12 @@ test(`The "move" action should move things right.`, () => {
]
},
'abc/abc2@user-admin;language=en_US': {
parent: 'abc@user-admin;language=en_US',
contextPath: 'abc/abc@user-admin;language=en_US',
children: []
},
'abc/abc/abc@user-admin;language=en_US': {
parent: 'abc/abc@user-admin;language=en_US',
contextPath: 'abc/abc/abc@user-admin;language=en_US',
children: []
}
Expand All @@ -145,19 +148,26 @@ test(`The "move" action should move things right.`, () => {
contextPath: 'abc/abc2@user-admin;language=en_US'
},
{
// NOTE: the context path below is "wrong" (because its's the pre-move state), but this gets updated when the server roundtrip is completed.
// we just move the node client-side for immediate user feedback
contextPath: 'abc/abc/abc@user-admin;language=en_US'
}
]
},
'abc/abc@user-admin;language=en_US': {
parent: 'abc@user-admin;language=en_US',
contextPath: 'abc/abc@user-admin;language=en_US',
children: []
},
'abc/abc2@user-admin;language=en_US': {
parent: 'abc@user-admin;language=en_US',
contextPath: 'abc/abc@user-admin;language=en_US',
children: []
},
'abc/abc/abc@user-admin;language=en_US': {
// NOTE: the parent path below is "wrong" (because its's the pre-move state), but this gets updated when the server roundtrip is completed.
// we just move the node client-side for immediate user feedback
parent: 'abc/abc@user-admin;language=en_US',
contextPath: 'abc/abc/abc@user-admin;language=en_US',
children: []
}
Expand Down
22 changes: 8 additions & 14 deletions packages/neos-ui-redux-store/src/CR/Nodes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {action as createAction, ActionType} from 'typesafe-actions';
import {actionTypes as system, InitAction} from '@neos-project/neos-ui-redux-store/src/System';

import * as selectors from './selectors';
import {parentNodeContextPath, getNodeOrThrow, calculateNewFocusedNodes} from './helpers';
import {calculateNewFocusedNodes, getNodeOrThrow} from './helpers';

import {FusionPath, NodeContextPath, InsertPosition, NodeMap, ClipboardMode, SelectionModeTypes, NodeTypeName} from '@neos-project/neos-ts-interfaces';

Expand Down Expand Up @@ -340,30 +340,24 @@ const moveNodeInState = (
position: string,
draft: State
) => {
let baseNodeContextPath;
const sourceNode = getNodeOrThrow(draft.byContextPath, sourceNodeContextPath);
const targetNode = getNodeOrThrow(draft.byContextPath, targetNodeContextPath);
let baseNode;
if (position === 'into') {
baseNodeContextPath = targetNodeContextPath;
baseNode = targetNode;
} else {
baseNodeContextPath = parentNodeContextPath(targetNodeContextPath);
if (baseNodeContextPath === null) {
throw new Error(`Target node "{targetNodeContextPath}" doesn't have a parent, yet you are trying to move a node next to it`);
}
baseNode = getNodeOrThrow(draft.byContextPath, targetNode.parent);
}

const sourceNodeParentContextPath = parentNodeContextPath(sourceNodeContextPath);
if (sourceNodeParentContextPath === null) {
throw new Error(`The source node "{sourceNodeParentContextPath}" doesn't have a parent, you can't move it`);
}
const baseNode = getNodeOrThrow(draft.byContextPath, baseNodeContextPath);
const sourceNodeParent = getNodeOrThrow(draft.byContextPath, sourceNodeParentContextPath);
const sourceNodeParent = getNodeOrThrow(draft.byContextPath, sourceNode.parent);

const originalSourceChildren = sourceNodeParent.children;
const sourceIndex = originalSourceChildren.findIndex(child => child.contextPath === sourceNodeContextPath);
const childRepresentationOfSourceNode = originalSourceChildren[sourceIndex];

const processedChildren = baseNode.children;

if (sourceNodeParentContextPath === baseNodeContextPath) {
if (sourceNodeParent === baseNode) {
// If moving into the same parent, delete source node from it
processedChildren.splice(sourceIndex, 1);
} else {
Expand Down
38 changes: 19 additions & 19 deletions packages/neos-ui-redux-store/src/CR/Nodes/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import {$get} from 'plow-js';
import {createSelector, defaultMemoize} from 'reselect';
import {GlobalState} from '@neos-project/neos-ui-redux-store/src/System';
import {parentNodeContextPath} from './helpers';
import {NodeContextPath, NodeMap, Node, NodeTypeName, ClipboardMode, NodeTypesRegistry} from '@neos-project/neos-ts-interfaces';

export const inlineValidationErrorsSelector = (state: GlobalState) => $get(['cr', 'nodes', 'inlineValidationErrors'], state);
Expand Down Expand Up @@ -138,19 +137,17 @@ export const byContextPathSelector = defaultMemoize(
);

export const parentNodeSelector = (state: GlobalState) => (baseNode: Node) => {
const parent = parentNodeContextPath(baseNode.contextPath);
if (parent !== null) {
return byContextPathSelector(parent)(state);
if (baseNode.parent !== null) {
return byContextPathSelector(baseNode.parent)(state);
}
return null;
};

export const grandParentNodeSelector = (state: GlobalState) => (baseNode: Node) => {
const parent = parentNodeContextPath(baseNode.contextPath);
if (parent !== null) {
const grandParent = parentNodeContextPath(parent);
if (grandParent !== null) {
byContextPathSelector(grandParent)(state);
if (baseNode.parent !== null) {
const parentNode = byContextPathSelector(baseNode.parent)(state);
if (parentNode && parentNode.parent) {
return byContextPathSelector(parentNode.parent)(state);
}
}
return null;
Expand Down Expand Up @@ -244,13 +241,13 @@ export const getPathInNode = (state: GlobalState, contextPath: NodeContextPath,
return $get(propertyPath, node);
};

export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeTypesRegistry, elevator: (id: string) => string | null = id => id) => createSelector(
export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeTypesRegistry, elevator: (id: string, state: GlobalState) => string | null = id => id) => createSelector(
[
(state: GlobalState, {reference}: {reference: NodeContextPath | null, role: string}) => {
if (reference === null) {
return null;
}
const elevatedReference = elevator(reference);
const elevatedReference = elevator(reference, state);
if (elevatedReference) {
return $get(['cr', 'nodes', 'byContextPath', elevatedReference], state) || null;
}
Expand All @@ -260,9 +257,9 @@ export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeType
if (reference === null) {
return null;
}
const parentReference = parentNodeContextPath(reference);
const parentReference = getPathInNode(state, reference, 'parent');
if (parentReference !== null) {
const elevatedReferenceParent = elevator(parentReference);
const elevatedReferenceParent = elevator(parentReference, state);
if (elevatedReferenceParent !== null) {
return $get(['cr', 'nodes', 'byContextPath', elevatedReferenceParent], state) || null;
}
Expand All @@ -286,7 +283,8 @@ export const makeGetAllowedChildNodeTypesSelector = (nodeTypesRegistry: NodeType
);

export const makeGetAllowedSiblingNodeTypesSelector = (nodeTypesRegistry: NodeTypesRegistry) =>
makeGetAllowedChildNodeTypesSelector(nodeTypesRegistry, parentNodeContextPath);
makeGetAllowedChildNodeTypesSelector(nodeTypesRegistry, (nodeContextPath, state) => getPathInNode(state, nodeContextPath, 'parent'));


export const makeIsAllowedToAddChildOrSiblingNodes = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
Expand Down Expand Up @@ -327,13 +325,13 @@ export const makeCanBeMovedIntoSelector = (nodeTypesRegistry: NodeTypesRegistry)
export const makeCanBeMovedAlongsideSelector = (nodeTypesRegistry: NodeTypesRegistry) => createSelector(
[
makeCanBeCopiedAlongsideSelector(nodeTypesRegistry),
(_, {subject, reference}) => {
(state: GlobalState, {subject, reference}) => {
if (reference === null) {
return false;
}
const subjectPath = subject && subject.split('@')[0];
const referenceParent = parentNodeContextPath(reference);
if (referenceParent === null) {
const referenceParent = getPathInNode(state, reference, 'parent') as string;
if (!referenceParent) {
return false;
}
return subjectPath ? referenceParent.indexOf(subjectPath) === 0 : false;
Expand Down Expand Up @@ -406,12 +404,14 @@ export const focusedNodeParentLineSelector = createSelector(
let currentNode = focusedNode;

while (currentNode) {
const parent = parentNodeContextPath(currentNode.contextPath);
if (parent !== null) {
const parent = currentNode.parent;
if (parent) {
currentNode = nodesByContextPath[parent] || null;
if (currentNode) {
result.push(currentNode);
}
} else {
break;
}
}

Expand Down
10 changes: 7 additions & 3 deletions packages/neos-ui-sagas/src/CR/NodeOperations/addNode.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {takeLatest, take, put, race, call} from 'redux-saga/effects';
import {takeLatest, take, put, race, call, select} from 'redux-saga/effects';
import {$get} from 'plow-js';

import {actions, actionTypes} from '@neos-project/neos-ui-redux-store';
import {actions, actionTypes, selectors} from '@neos-project/neos-ui-redux-store';

import {calculateChangeTypeFromMode, calculateDomAddressesFromMode} from './helpers';

Expand Down Expand Up @@ -91,11 +91,15 @@ function * nodeCreationWorkflow(context, step = STEP_SELECT_NODETYPE, workflowDa
if (nodeTypesRegistry.hasRole(nodeType, 'document')) {
yield put(actions.UI.ContentCanvas.startLoading());
}

const referenceNodeSelector = selectors.CR.Nodes.makeGetNodeByContextPathSelector(referenceNodeContextPath);
const referenceNode = yield select(referenceNodeSelector);

return yield put(actions.Changes.persistChanges([{
type: calculateChangeTypeFromMode(mode, 'Create'),
subject: referenceNodeContextPath,
payload: {
...calculateDomAddressesFromMode(mode, referenceNodeContextPath, referenceNodeFusionPath),
...calculateDomAddressesFromMode(mode, referenceNode, referenceNodeFusionPath),
nodeType,
data
}
Expand Down
Loading