/** * Method which does the actual work of discarding, includes a protection against endless recursions and * multiple discarding of the same node. * * @param NodeInterface $node The node to discard * @param array &$alreadyDiscardedNodeIdentifiers List of node identifiers which already have been discarded during one discardNode() run * @return void * @throws \Neos\ContentRepository\Exception\WorkspaceException */ protected function doDiscardNode(NodeInterface $node, array &$alreadyDiscardedNodeIdentifiers = []) { if ($node->getWorkspace()->getBaseWorkspace() === null) { throw new WorkspaceException('Nodes in a in a workspace without a base workspace cannot be discarded.', 1395841899); } if ($node->getPath() === '/') { return; } if (array_search($node->getIdentifier(), $alreadyDiscardedNodeIdentifiers) !== false) { return; } $alreadyDiscardedNodeIdentifiers[] = $node->getIdentifier(); $possibleShadowNodeData = $this->nodeDataRepository->findOneByMovedTo($node->getNodeData()); if ($possibleShadowNodeData instanceof NodeData) { if ($possibleShadowNodeData->getMovedTo() !== null) { $parentBasePath = $node->getPath(); $affectedChildNodeDataInSameWorkspace = $this->nodeDataRepository->findByParentAndNodeType($parentBasePath, null, $node->getWorkspace(), null, false, true); foreach ($affectedChildNodeDataInSameWorkspace as $affectedChildNodeData) { /** @var NodeData $affectedChildNodeData */ $affectedChildNode = $this->nodeFactory->createFromNodeData($affectedChildNodeData, $node->getContext()); $this->doDiscardNode($affectedChildNode, $alreadyDiscardedNodeIdentifiers); } } $this->nodeDataRepository->remove($possibleShadowNodeData); } $this->nodeDataRepository->remove($node); $this->emitNodeDiscarded($node); }
/** * Returns an array of usage reference objects. * * @param AssetInterface $asset * @return array<\Neos\Neos\Domain\Model\Dto\AssetUsageInNodeProperties> * @throws \Neos\ContentRepository\Exception\NodeConfigurationException */ public function getUsageReferences(AssetInterface $asset) { $assetIdentifier = $this->persistenceManager->getIdentifierByObject($asset); if (isset($this->firstlevelCache[$assetIdentifier])) { return $this->firstlevelCache[$assetIdentifier]; } $userWorkspace = $this->userService->getPersonalWorkspace(); $relatedNodes = []; foreach ($this->getRelatedNodes($asset) as $relatedNodeData) { $accessible = $this->domainUserService->currentUserCanReadWorkspace($relatedNodeData->getWorkspace()); if ($accessible) { $context = $this->createContextMatchingNodeData($relatedNodeData); } else { $context = $this->createContentContext($userWorkspace->getName()); } $site = $context->getCurrentSite(); $node = $this->nodeFactory->createFromNodeData($relatedNodeData, $context); $flowQuery = new FlowQuery([$node]); /** @var \Neos\ContentRepository\Domain\Model\NodeInterface $documentNode */ $documentNode = $flowQuery->closest('[instanceof Neos.Neos:Document]')->get(0); $relatedNodes[] = new AssetUsageInNodeProperties($asset, $site, $documentNode, $node, $accessible); } $this->firstlevelCache[$assetIdentifier] = $relatedNodes; return $this->firstlevelCache[$assetIdentifier]; }
/** * Search all properties for given $term * * TODO: Implement a better search when Flow offer the possibility * * @param string|array $term search term * @param array $searchNodeTypes * @param Context $context * @param NodeInterface $startingPoint * @return array <\Neos\ContentRepository\Domain\Model\NodeInterface> */ public function findByProperties($term, array $searchNodeTypes, Context $context, NodeInterface $startingPoint = null) { if (empty($term)) { throw new \InvalidArgumentException('"term" cannot be empty: provide a term to search for.', 1421329285); } $searchResult = array(); $nodeTypeFilter = implode(',', $searchNodeTypes); $nodeDataRecords = $this->nodeDataRepository->findByProperties($term, $nodeTypeFilter, $context->getWorkspace(), $context->getDimensions(), $startingPoint ? $startingPoint->getPath() : null); foreach ($nodeDataRecords as $nodeData) { $node = $this->nodeFactory->createFromNodeData($nodeData, $context); if ($node !== null) { $searchResult[$node->getPath()] = $node; } } return $searchResult; }
/** * Given a context a new node is returned that is like this node, but * lives in the new context. * * @param Context $context * @return NodeInterface */ public function createVariantForContext($context) { $autoCreatedChildNodes = []; $nodeType = $this->getNodeType(); foreach ($nodeType->getAutoCreatedChildNodes() as $childNodeName => $childNodeConfiguration) { $childNode = $this->getNode($childNodeName); if ($childNode !== null) { $autoCreatedChildNodes[$childNodeName] = $childNode; } } $nodeData = new NodeData($this->nodeData->getPath(), $context->getWorkspace(), $this->nodeData->getIdentifier(), $context->getTargetDimensionValues()); $nodeData->similarize($this->nodeData); if ($this->context !== $context) { $node = $this->nodeFactory->createFromNodeData($nodeData, $context); } else { $this->setNodeData($nodeData); $node = $this; } $this->context->getFirstLevelNodeCache()->flush(); $this->emitNodeAdded($node); /** * @var $autoCreatedChildNode NodeInterface */ foreach ($autoCreatedChildNodes as $autoCreatedChildNode) { $autoCreatedChildNode->createVariantForContext($context); } return $node; }
/** * Finds a single node by its parent and (optionally) by its node type * * @param string $parentPath Absolute path of the parent node * @param string $nodeTypeFilter Filter the node type of the nodes, allows complex expressions (e.g. "Neos.Neos:Page", "!Neos.Neos:Page,Neos.Neos:Text" or NULL) * @param Context $context The containing context * @return NodeData The node found or NULL */ public function findFirstByParentAndNodeTypeInContext($parentPath, $nodeTypeFilter, Context $context) { $firstNode = $this->findFirstByParentAndNodeType($parentPath, $nodeTypeFilter, $context->getWorkspace(), $context->getDimensions(), $context->isRemovedContentShown() ? null : false); if ($firstNode !== null) { $firstNode = $this->nodeFactory->createFromNodeData($firstNode, $context); } return $firstNode; }
/** * Find all nodes of a specific node type * * @param array $nodeTypes * @param ContentContext $context current content context, see class doc comment for details * @return array<NodeInterface> all nodes of type $nodeType in the current $context */ protected function getNodes(array $nodeTypes, ContentContext $context) { $nodes = []; $siteNode = $context->getCurrentSiteNode(); foreach ($this->nodeDataRepository->findByParentAndNodeTypeRecursively($siteNode->getPath(), implode(',', $nodeTypes), $context->getWorkspace()) as $nodeData) { $nodes[] = $this->nodeFactory->createFromNodeData($nodeData, $context); } return $nodes; }
/** * Finds all nodes lying on the path specified by (and including) the given * starting point and end point. * * @param mixed $startingPoint Either an absolute path or an actual node specifying the starting point, for example /sites/mysitecom * @param mixed $endPoint Either an absolute path or an actual node specifying the end point, for example /sites/mysitecom/homepage/subpage * @return array<\Neos\ContentRepository\Domain\Model\NodeInterface> The nodes found between and including the given paths or an empty array of none were found * @api */ public function getNodesOnPath($startingPoint, $endPoint) { $startingPointPath = $startingPoint instanceof NodeInterface ? $startingPoint->getPath() : $startingPoint; $endPointPath = $endPoint instanceof NodeInterface ? $endPoint->getPath() : $endPoint; $nodeDataElements = $this->nodeDataRepository->findOnPath($startingPointPath, $endPointPath, $this->getWorkspace(), $this->getDimensions(), $this->isRemovedContentShown()); $nodes = array(); foreach ($nodeDataElements as $nodeData) { $node = $this->nodeFactory->createFromNodeData($nodeData, $this); if ($node !== null) { $nodes[] = $node; $this->firstLevelNodeCache->setByPath($node->getPath(), $node); } } return $nodes; }
/** * @test */ public function createContextMatchingNodeDataCreatesMatchingContext() { $dimensionValues = array('language' => array('is')); $workspaceName = 'some-workspace'; $mockContext = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $mockWorkspace = $this->getMockBuilder(Workspace::class)->setMockClassName('MockWorkspace')->disableOriginalConstructor()->getMock(); $mockWorkspace->expects(self::any())->method('getName')->will(self::returnValue($workspaceName)); $mockContextFactory = $this->createMock(ContextFactoryInterface::class); $mockContextFactory->expects(self::once())->method('create')->with(array('workspaceName' => $workspaceName, 'invisibleContentShown' => true, 'inaccessibleContentShown' => true, 'removedContentShown' => true, 'dimensions' => $dimensionValues))->willReturn($mockContext); $this->inject($this->nodeFactory, 'contextFactory', $mockContextFactory); $mockNodeData = $this->getMockBuilder(NodeData::class)->disableOriginalConstructor()->getMock(); $mockNodeData->expects(self::any())->method('getWorkspace')->will($this->returnValue($mockWorkspace)); $mockNodeData->expects(self::any())->method('getDimensionValues')->willReturn($dimensionValues); $context = $this->nodeFactory->createContextMatchingNodeData($mockNodeData); self::assertEquals($mockContext, $context); }
/** * Reorder child nodes for the given node type * * @param string $workspaceName * @param boolean $dryRun * @param NodeType $nodeType * @return void */ protected function reorderChildNodesByNodeType($workspaceName, $dryRun, NodeType $nodeType) { $nodeTypes = $this->nodeTypeManager->getSubNodeTypes($nodeType->getName(), false); $nodeTypes[$nodeType->getName()] = $nodeType; if ($this->nodeTypeManager->hasNodeType((string) $nodeType)) { $nodeType = $this->nodeTypeManager->getNodeType((string) $nodeType); $nodeTypeNames[$nodeType->getName()] = $nodeType; } else { $this->output->outputLine('Node type "%s" does not exist', array((string) $nodeType)); exit(1); } /** @var $nodeType NodeType */ foreach ($nodeTypes as $nodeTypeName => $nodeType) { $childNodes = $nodeType->getAutoCreatedChildNodes(); if ($childNodes === array()) { continue; } foreach ($this->getNodeDataByNodeTypeAndWorkspace($nodeTypeName, $workspaceName) as $nodeData) { /** @var NodeInterface $childNodeBefore */ $childNodeBefore = null; $context = $this->nodeFactory->createContextMatchingNodeData($nodeData); $node = $this->nodeFactory->createFromNodeData($nodeData, $context); if (!$node instanceof NodeInterface) { continue; } foreach ($childNodes as $childNodeName => $childNodeType) { $childNode = $node->getNode($childNodeName); if ($childNode) { if ($childNodeBefore) { if ($dryRun === false) { if ($childNodeBefore->getIndex() >= $childNode->getIndex()) { $childNode->moveAfter($childNodeBefore); $this->output->outputLine('Moved node named "%s" after node named "%s" in "%s"', array($childNodeName, $childNodeBefore->getName(), $node->getPath())); } } else { $this->output->outputLine('Should move node named "%s" after node named "%s" in "%s"', array($childNodeName, $childNodeBefore->getName(), $node->getPath())); } } } else { $this->output->outputLine('Missing child node named "%s" in "%s".', array($childNodeName, $node->getPath())); } $childNodeBefore = $childNode; } } } }
/** * Return child nodes of specified node for usage in a TreeLoader based on filter * * @param Node $node The node to find child nodes for * @param string $term * @param string $nodeType * @return void */ public function filterChildNodesForTreeAction(Node $node, $term, $nodeType) { $nodeTypes = strlen($nodeType) > 0 ? array($nodeType) : array_keys($this->nodeTypeManager->getSubNodeTypes('Neos.Neos:Document', false)); $context = $node->getContext(); if ($term !== '') { $nodes = $this->nodeSearchService->findByProperties($term, $nodeTypes, $context, $node); } else { $nodes = array(); $nodeDataRecords = $this->nodeDataRepository->findByParentAndNodeTypeRecursively($node->getPath(), implode(',', $nodeTypes), $context->getWorkspace(), $context->getDimensions()); foreach ($nodeDataRecords as $nodeData) { $matchedNode = $this->nodeFactory->createFromNodeData($nodeData, $context); if ($matchedNode !== null) { $nodes[$matchedNode->getPath()] = $matchedNode; } } } $this->view->assignFilteredChildNodes($node, $nodes); }
/** * @test */ public function getUnpublishedNodesDoesNotReturnInvalidNodes() { $mockContext = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $expectedContextProperties = array('workspaceName' => $this->mockWorkspace->getName(), 'inaccessibleContentShown' => true, 'invisibleContentShown' => true, 'removedContentShown' => true, 'dimensions' => array()); $this->mockContextFactory->expects($this->any())->method('create')->with($expectedContextProperties)->will($this->returnValue($mockContext)); $mockNodeData1 = $this->getMockBuilder(NodeData::class)->disableOriginalConstructor()->getMock(); $mockNodeData2 = $this->getMockBuilder(NodeData::class)->disableOriginalConstructor()->getMock(); $mockNodeData1->expects($this->any())->method('getDimensionValues')->will($this->returnValue(array())); $mockNodeData2->expects($this->any())->method('getDimensionValues')->will($this->returnValue(array())); $mockNode1 = $this->getMockBuilder(NodeInterface::class)->getMock(); $mockNode1->expects($this->any())->method('getNodeData')->will($this->returnValue($mockNodeData1)); $mockNode1->expects($this->any())->method('getPath')->will($this->returnValue('/node1')); $this->mockNodeFactory->expects($this->at(0))->method('createFromNodeData')->with($mockNodeData1, $mockContext)->will($this->returnValue($mockNode1)); $this->mockNodeFactory->expects($this->at(1))->method('createFromNodeData')->with($mockNodeData2, $mockContext)->will($this->returnValue(null)); $this->mockNodeDataRepository->expects($this->atLeastOnce())->method('findByWorkspace')->with($this->mockWorkspace)->will($this->returnValue(array($mockNodeData1, $mockNodeData2))); $actualResult = $this->publishingService->getUnpublishedNodes($this->mockWorkspace); $this->assertSame($actualResult, array($mockNode1)); }