/** * @param NodeInterface $contextNode * @return QueryBuilder */ public function query(NodeInterface $contextNode) { $this->where[] = "(__parentPath LIKE '%#" . $contextNode->getPath() . "#%' OR __path LIKE '" . $contextNode->getPath() . "')"; $this->where[] = "(__workspace LIKE '%#" . $contextNode->getContext()->getWorkspace()->getName() . "#%')"; $this->where[] = "(__dimensionshash LIKE '%#" . md5(json_encode($contextNode->getContext()->getDimensions())) . "#%')"; $this->contextNode = $contextNode; return $this; }
/** * Matches if the selected node is a *descendant* of the given node specified by $nodePathOrIdentifier * * Example: isDescendantNodeOf('/sites/some/path') matches for the nodes "/sites/some/path", "/sites/some/path/subnode" but not for "/sites/some/other" * * @param string $nodePathOrIdentifier The identifier or absolute path of the node to match * @return boolean TRUE if the given node matches otherwise false */ public function isDescendantNodeOf($nodePathOrIdentifier) { $nodePath = $this->resolveNodePath($nodePathOrIdentifier); if (is_bool($nodePath)) { return $nodePath; } return substr($this->node->getPath() . '/', 0, strlen($nodePath)) === $nodePath; }
/** * @param string $nodePathOrIdentifier * @return boolean */ public function isDescendantNodeOf($nodePathOrIdentifier) { if ($this->node === NULL) { return TRUE; } if (preg_match(UuidValidator::PATTERN_MATCH_UUID, $nodePathOrIdentifier) === 1) { if ($this->node->getIdentifier() === $nodePathOrIdentifier) { return TRUE; } $node = $this->getNodeByIdentifier($nodePathOrIdentifier); if ($node === NULL) { return FALSE; } $nodePath = $node->getPath() . '/'; } else { $nodePath = rtrim($nodePathOrIdentifier, '/') . '/'; } return substr($this->node->getPath() . '/', 0, strlen($nodePath)) === $nodePath; }
/** * Register a node change for a later cache flush. This method is triggered by a signal sent via TYPO3CR's Node * model or the Neos Publishing Service. * * @param NodeInterface $node The node which has changed in some way * @return void */ public function registerNodeChange(NodeInterface $node) { $this->tagsToFlush[ContentCache::TAG_EVERYTHING] = 'which were tagged with "Everything".'; $nodeTypesToFlush = $this->getAllImplementedNodeTypes($node->getNodeType()); foreach ($nodeTypesToFlush as $nodeType) { $nodeTypeName = $nodeType->getName(); $this->tagsToFlush['NodeType_' . $nodeTypeName] = sprintf('which were tagged with "NodeType_%s" because node "%s" has changed and was of type "%s".', $nodeTypeName, $node->getPath(), $node->getNodeType()->getName()); } $this->tagsToFlush['Node_' . $node->getIdentifier()] = sprintf('which were tagged with "Node_%s" because node "%s" has changed.', $node->getIdentifier(), $node->getPath()); $originalNode = $node; while ($node->getDepth() > 1) { $node = $node->getParent(); // Workaround for issue #56566 in TYPO3.TYPO3CR if ($node === null) { break; } $tagName = 'DescendantOf_' . $node->getIdentifier(); $this->tagsToFlush[$tagName] = sprintf('which were tagged with "%s" because node "%s" has changed.', $tagName, $originalNode->getPath()); } }
/** * Get the rootline from the current node up to the site node. * * @return array */ protected function getCurrentNodeRootline() { if ($this->currentNodeRootline === null) { $nodeRootline = $this->currentNode->getContext()->getNodesOnPath($this->currentNode->getContext()->getCurrentSiteNode()->getPath(), $this->currentNode->getPath()); $this->currentNodeRootline = array(); foreach ($nodeRootline as $rootlineElement) { $this->currentNodeRootline[$this->getNodeLevelInSite($rootlineElement)] = $rootlineElement; } } return $this->currentNodeRootline; }
/** * Generates cache tags to be flushed for a node which is flushed on shutdown. * * Code duplicated from Neos' ContentCacheFlusher class * * @param NodeInterface|NodeData $node The node which has changed in some way * @return void */ protected function generateCacheTags($node) { $this->tagsToFlush[ContentCache::TAG_EVERYTHING] = 'which were tagged with "Everything".'; $nodeTypesToFlush = $this->getAllImplementedNodeTypes($node->getNodeType()); foreach ($nodeTypesToFlush as $nodeType) { /** @var NodeType $nodeType */ $nodeTypeName = $nodeType->getName(); $this->tagsToFlush['NodeType_' . $nodeTypeName] = sprintf('which were tagged with "NodeType_%s" because node "%s" has changed and was of type "%s".', $nodeTypeName, $node->getPath(), $node->getNodeType()->getName()); } $this->tagsToFlush['Node_' . $node->getIdentifier()] = sprintf('which were tagged with "Node_%s" because node "%s" has changed.', $node->getIdentifier(), $node->getPath()); while ($node->getDepth() > 1) { $node = $node->getParent(); if ($node === NULL) { break; } $this->tagsToFlush['DescendantOf_' . $node->getIdentifier()] = sprintf('which were tagged with "DescendantOf_%s" because node "%s" has changed.', $node->getIdentifier(), $node->getPath()); } if ($node instanceof NodeInterface && $node->getContext() instanceof ContentContext) { $firstActiveDomain = $node->getContext()->getCurrentSite()->getFirstActiveDomain(); if ($firstActiveDomain) { $this->domainsToFlush[] = $firstActiveDomain->getHostPattern(); } } }
/** * Search all properties for given $term * * TODO: Implement a better search when Flow offer the possibility * * @param string $term * @param array $searchNodeTypes * @param Context $context * @param NodeInterface $startingPoint * @return array <\TYPO3\TYPO3CR\Domain\Model\NodeInterface> */ public function findByProperties($term, array $searchNodeTypes, Context $context, NodeInterface $startingPoint = null) { if (strlen($term) === 0) { 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; }
/** * Check if the given node is already a collection, find collection by nodePath otherwise, throw exception * if no content collection could be found * * @param NodeInterface $node * @param string $nodePath * @return NodeInterface * @throws Exception */ public function nearestContentCollection(NodeInterface $node, $nodePath) { $contentCollectionType = 'TYPO3.Neos:ContentCollection'; if ($node->getNodeType()->isOfType($contentCollectionType)) { return $node; } else { if ((string) $nodePath === '') { throw new Exception(sprintf('No content collection of type %s could be found in the current node and no node path was provided. You might want to configure the nodePath property with a relative path to the content collection.', $contentCollectionType), 1409300545); } $subNode = $node->getNode($nodePath); if ($subNode !== null && $subNode->getNodeType()->isOfType($contentCollectionType)) { return $subNode; } else { throw new Exception(sprintf('No content collection of type %s could be found in the current node (%s) or at the path "%s". You might want to adjust your node type configuration and create the missing child node through the "flow node:repair --node-type %s" command.', $contentCollectionType, $node->getPath(), $nodePath, (string) $node->getNodeType()), 1389352984); } } }
/** * Creates missing child nodes for the given node. * * @param NodeInterface $node * @return void */ public function createChildNodes(NodeInterface $node) { $nodeType = $node->getNodeType(); foreach ($nodeType->getAutoCreatedChildNodes() as $childNodeName => $childNodeType) { try { $node->createNode($childNodeName, $childNodeType); } catch (NodeExistsException $exception) { // If you have a node that has been marked as removed, but is needed again // the old node is recovered $childNodePath = NodePaths::addNodePathSegment($node->getPath(), $childNodeName); $contextProperties = $node->getContext()->getProperties(); $contextProperties['removedContentShown'] = true; $context = $this->contextFactory->create($contextProperties); $childNode = $context->getNode($childNodePath); if ($childNode->isRemoved()) { $childNode->setRemoved(false); } } } }
/** * Returns a merged TypoScript object tree in the context of the given nodes * * The start node and end node mark the starting point and end point of the * path to take while searching for TypoScript configuration. The path of the * start node must be the base path of the end node's path. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $startNode Node marking the starting point * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $endNode Node marking the end point * @return array The merged object tree as of the given node */ public function getMergedTypoScriptObjectTree(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $startNode, \TYPO3\TYPO3CR\Domain\Model\NodeInterface $endNode) { $contentContext = $this->nodeRepository->getContext(); $parentNodes = $contentContext->getNodesOnPath($startNode->getPath(), $endNode->getPath()); if (!is_array($parentNodes)) { return NULL; } $siteResourcesPackageKey = $contentContext->getCurrentSite()->getSiteResourcesPackageKey(); $typoScriptsPath = sprintf($this->typoScriptsPathPattern, $siteResourcesPackageKey); $mergedTypoScriptCode = $this->readExternalTypoScriptFile('resource://TYPO3.TYPO3/Private/DefaultTypoScript/All.ts2'); $mergedTypoScriptCode .= $this->readExternalTypoScriptFile($typoScriptsPath . '/Library/Root.ts2'); $currentTypoScriptPath = $typoScriptsPath . '/Nodes'; foreach ($parentNodes as $node) { $nodeName = $node->getName(); $mergedTypoScriptCode .= Files::getFileContents($this->getMixedCasedPathAndFilename($currentTypoScriptPath . '/' . $nodeName . '.ts2')) . chr(10); $currentTypoScriptPath .= '/' . basename($this->getMixedCasedPathAndFilename($currentTypoScriptPath . '/' . $nodeName)); $typoScriptNodes = $node->getChildNodes('TYPO3.TYPO3:TypoScript'); foreach ($typoScriptNodes as $typoScriptNode) { $mergedTypoScriptCode .= $typoScriptNode->getProperty('sourceCode') . chr(10); } } return $this->typoScriptParser->parse($mergedTypoScriptCode, $typoScriptsPath); }
/** * Returns an array with the data needed by for example the Hallo and Aloha * link plugins to represent the passed Node instance. * * @param NodeInterface $node * @return array */ protected function processNodeForEditorPlugins(NodeInterface $node) { return array('id' => $node->getPath(), 'name' => $node->getLabel(), 'url' => $this->uriBuilder->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.Neos'), 'type' => 'neos/internal-link'); }
/** * Adds the given node to the cache for the given identifier. The node * will also be added with is's path. * * @param string $identifier * @param NodeInterface $node * @return void */ public function setByIdentifier($identifier, NodeInterface $node = null) { $this->nodesByIdentifier[$identifier] = $node; if ($node !== null) { $this->nodesByPath[$node->getPath()] = $node; } }
protected function getACLPropertiesForNode(NodeInterface $node) { $properties = ['nodeIdentifier' => $node->getIdentifier(), 'nodePath' => $node->getPath(), 'nodeLabel' => $node->getLabel(), 'nodeType' => $node->getNodeType()->getName(), 'nodeLevel' => $node->getDepth()]; return $properties; }
/** * Internal method to do the actual copying. * * For behavior of the $detachedCopy parameter, see method Node::createRecursiveCopy(). * * @param NodeInterface $referenceNode * @param $nodeName * @param boolean $detachedCopy * @return NodeInterface * @throws NodeConstraintException * @throws NodeExistsException */ protected function copyIntoInternal(NodeInterface $referenceNode, $nodeName, $detachedCopy) { if ($referenceNode->getNode($nodeName) !== null) { throw new NodeExistsException('Node with path "' . $referenceNode->getPath() . '/' . $nodeName . '" already exists.', 1292503467); } // On copy we basically re-recreate an existing node on a new location. As we skip the constraints check on // node creation we should do the same while writing the node on the new location. if (!$referenceNode->willChildNodeBeAutoCreated($nodeName) && !$referenceNode->isNodeTypeAllowedAsChildNode($this->getNodeType())) { throw new NodeConstraintException(sprintf('Cannot copy "%s" into "%s" due to node type constraints.', $this->__toString(), $referenceNode->__toString()), 1404648177); } $copiedNode = $this->createRecursiveCopy($referenceNode, $nodeName, $detachedCopy); $this->context->getFirstLevelNodeCache()->flush(); $this->emitNodeAdded($copiedNode); return $copiedNode; }
/** * * * @param NodeInterface $node * @param array $fulltextIndexOfNode * @param string $targetWorkspaceName * @return void */ protected function updateFulltext(NodeInterface $node, array $fulltextIndexOfNode, $targetWorkspaceName = NULL) { if ($targetWorkspaceName !== NULL && $targetWorkspaceName !== 'live' || $node->getWorkspace()->getName() !== 'live' || count($fulltextIndexOfNode) === 0) { return; } $closestFulltextNode = $node; while (!$this->isFulltextRoot($closestFulltextNode)) { $closestFulltextNode = $closestFulltextNode->getParent(); if ($closestFulltextNode === NULL) { // root of hierarchy, no fulltext root found anymore, abort silently... $this->logger->log('No fulltext root found for ' . $node->getPath(), LOG_WARNING); return; } } $closestFulltextNodeContextPath = str_replace($closestFulltextNode->getContext()->getWorkspace()->getName(), 'live', $closestFulltextNode->getContextPath()); $closestFulltextNodeContextPathHash = sha1($closestFulltextNodeContextPath); $this->currentBulkRequest[] = array(array('update' => array('_type' => NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($closestFulltextNode->getNodeType()->getName()), '_id' => $closestFulltextNodeContextPathHash)), array('script' => ' if (!ctx._source.containsKey("__fulltextParts")) { ctx._source.__fulltextParts = new LinkedHashMap(); } ctx._source.__fulltextParts[identifier] = fulltext; ctx._source.__fulltext = new LinkedHashMap(); Iterator<LinkedHashMap.Entry<String, LinkedHashMap>> fulltextByNode = ctx._source.__fulltextParts.entrySet().iterator(); for (fulltextByNode; fulltextByNode.hasNext();) { Iterator<LinkedHashMap.Entry<String, String>> elementIterator = fulltextByNode.next().getValue().entrySet().iterator(); for (elementIterator; elementIterator.hasNext();) { Map.Entry<String, String> element = elementIterator.next(); String value; if (ctx._source.__fulltext.containsKey(element.key)) { value = ctx._source.__fulltext[element.key] + " " + element.value.trim(); } else { value = element.value.trim(); } ctx._source.__fulltext[element.key] = value; } } ', 'params' => array('identifier' => $node->getIdentifier(), 'fulltext' => $fulltextIndexOfNode), 'upsert' => array('__fulltext' => $fulltextIndexOfNode, '__fulltextParts' => array($node->getIdentifier() => $fulltextIndexOfNode)), 'lang' => 'groovy')); }
/** * Replace the node data of a node instance with a given target node data * * The node data of the node that is published will be removed and the existing node data inside the target * workspace is updated to the changes and will be injected into the node instance. If the node was marked as * removed, both node data are removed. * * @param NodeInterface $node The node instance with node data to be published * @param NodeData $targetNodeData The existing node data in the target workspace * @return void */ protected function replaceNodeData(NodeInterface $node, NodeData $targetNodeData) { $sourceNodeData = $node->getNodeData(); $nodeWasMoved = $this->handleShadowNodeData($sourceNodeData, $targetNodeData->getWorkspace(), $targetNodeData); // Technically this shouldn't be needed but due to doctrines behavior we need it. if ($sourceNodeData->isRemoved() && $targetNodeData->getWorkspace()->getBaseWorkspace() === null) { $this->nodeDataRepository->remove($targetNodeData); $this->nodeDataRepository->remove($sourceNodeData); return; } $targetNodeData->similarize($sourceNodeData); $targetNodeData->setLastPublicationDateTime($this->now); if ($nodeWasMoved) { // TODO: This seems wrong and introduces a publish order between nodes. We should always set the path. $targetNodeData->setPath($node->getPath(), false); } $node->setNodeData($targetNodeData); $this->nodeService->cleanUpProperties($node); $targetNodeData->setRemoved($sourceNodeData->isRemoved()); $this->nodeDataRepository->remove($sourceNodeData); }
/** * Sets the starting point for this query. Search result should only contain nodes that * match the context of the given node and have it as parent node in their rootline. * * @param NodeInterface $contextNode * @return QueryBuilderInterface * @api */ public function query(NodeInterface $contextNode) { // on indexing, the __parentPath is tokenized to contain ALL parent path parts, // e.g. /foo, /foo/bar/, /foo/bar/baz; to speed up matching.. That's why we use a simple "term" filter here. // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-term-filter.html $this->queryFilter('term', ['__parentPath' => $contextNode->getPath()]); // // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-terms-filter.html $this->queryFilter('terms', ['__workspace' => array_unique(['live', $contextNode->getContext()->getWorkspace()->getName()])]); // match exact dimension values for each dimension, this works because the indexing flattens the node variants for all dimension preset combinations $dimensionCombinations = $contextNode->getContext()->getDimensions(); if (is_array($dimensionCombinations)) { $this->queryFilter('term', ['__dimensionCombinationHash' => md5(json_encode($dimensionCombinations))]); } $this->contextNode = $contextNode; return $this; }
/** * Traverses through the tree starting at the given root node and sets the uriPathSegment property derived from * the node label. * * @param NodeInterface $node The node where the traversal starts * @param boolean $dryRun * @return void */ protected function generateUriPathSegmentsForNode(NodeInterface $node, $dryRun) { if ((string) $node->getProperty('uriPathSegment') === '') { $name = $node->getLabel() ?: $node->getName(); $uriPathSegment = Utility::renderValidNodeName($name); if ($dryRun === FALSE) { $node->setProperty('uriPathSegment', $uriPathSegment); $this->output->outputLine('Added missing URI path segment for "%s" (%s) => %s', array($node->getPath(), $name, $uriPathSegment)); } else { $this->output->outputLine('Found missing URI path segment for "%s" (%s) => %s', array($node->getPath(), $name, $uriPathSegment)); } } foreach ($node->getChildNodes('TYPO3.Neos:Document') as $childNode) { $this->generateUriPathSegmentsForNode($childNode, $dryRun); } }
/** * @param array $parentNodes the parent nodes * @param NodeInterface $until * @return array */ protected function getNodesUntil($parentNodes, NodeInterface $until) { $count = count($parentNodes) - 1; for ($i = $count; $i >= 0; $i--) { if ($parentNodes[$i]->getPath() === $until->getPath()) { unset($parentNodes[$i]); return array_values($parentNodes); } else { unset($parentNodes[$i]); } } return array_values($parentNodes); }
/** * 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 \TYPO3\TYPO3CR\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); }
/** * @param string $externalIdentifier * @param NodeInterface $node * @return ProcessedNodeDefinition */ public static function createFromNode($externalIdentifier, NodeInterface $node) { return new ProcessedNodeDefinition($node->getIdentifier(), $node->getPath(), $externalIdentifier); }
/** * Returns an array with the data needed by for example the Hallo and Aloha * link plugins to represent the passed NodeInterface instance. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @return array */ protected function processNodeForEditorPlugins(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node) { return array('id' => $node->getPath(), 'name' => $node->getLabel(), 'url' => $this->uriBuilder->setLinkProtectionEnabled(FALSE)->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.TYPO3', ''), 'type' => 'phoenix/internal-link'); }
/** * Creates a new node beneath $parent * * @param NodeInterface $parent * @return NodeInterface */ protected function createNode(NodeInterface $parent) { $nodeType = $this->getNodeType(); $initialProperties = $this->getInitialProperties(); $name = $this->getName() ?: $this->nodeService->generateUniqueNodeName($parent->getPath()); // // If we're about to create a document, check for the presence of the uriPathSegment property first // and create it, if it's missing // if ($nodeType->isOfType('TYPO3.Neos:Document') && !isset($initialProperties['uriPathSegment'])) { if (!isset($initialProperties['title'])) { throw new \IllegalArgumentException('You must either provide a title or a uriPathSegment in order to create a document.', 1452103891); } $initialProperties['uriPathSegment'] = NodeUtility::renderValidNodeName($initialProperties['title']); } $node = $parent->createNode($name, $nodeType); foreach ($initialProperties as $key => $value) { $node->setProperty($key, $value); } return $node; }
/** * Rebase the current users personal workspace onto the given $targetWorkspace and then * redirects to the $targetNode in the content module. * * @param NodeInterface $targetNode * @param Workspace $targetWorkspace * @return void */ public function rebaseAndRedirectAction(NodeInterface $targetNode, Workspace $targetWorkspace) { $currentAccount = $this->securityContext->getAccount(); $personalWorkspace = $this->workspaceRepository->findOneByName('user-' . $currentAccount->getAccountIdentifier()); /** @var Workspace $personalWorkspace */ if ($this->publishingService->getUnpublishedNodesCount($personalWorkspace) > 0) { $message = $this->translator->translateById('workspaces.cantEditBecauseWorkspaceContainsChanges', [], null, null, 'Modules', 'TYPO3.Neos'); $this->addFlashMessage($message, '', Message::SEVERITY_WARNING, [], 1437833387); $this->redirect('show', null, null, ['workspace' => $targetWorkspace]); } $personalWorkspace->setBaseWorkspace($targetWorkspace); $this->workspaceRepository->update($personalWorkspace); $contextProperties = $targetNode->getContext()->getProperties(); $contextProperties['workspaceName'] = $personalWorkspace->getName(); $context = $this->contextFactory->create($contextProperties); $mainRequest = $this->controllerContext->getRequest()->getMainRequest(); /** @var ActionRequest $mainRequest */ $this->uriBuilder->setRequest($mainRequest); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', ['node' => $context->getNode($targetNode->getPath())]); }
/** * Renders a request path based on the "uriPathSegment" properties of the nodes leading to the given node. * * @param NodeInterface $siteNode Top level node, corresponds to the top level of the request path * @param NodeInterface $node The node where the generated path should lead to * @return string A relative request path * @throws Exception\MissingNodePropertyException if the given node doesn't have a "uriPathSegment" property set */ protected function getRequestPathByNode(NodeInterface $siteNode, NodeInterface $node) { if ($siteNode === $node) { return ''; } $requestPathSegments = array(); while ($siteNode !== $node && $node instanceof NodeInterface) { if (!$node->hasProperty('uriPathSegment')) { throw new Exception\MissingNodePropertyException(sprintf('Missing "uriPathSegment" property for node "%s". Nodes can be migrated with the "flow node:repair" command.', $node->getPath()), 1415020326); } $pathSegment = $node->getProperty('uriPathSegment'); $requestPathSegments[] = $pathSegment; $node = $node->getParent(); } return implode('/', array_reverse($requestPathSegments)); }
/** * returns a specific view node of an master plugin * or NULL if it does not exist * * @param NodeInterface $node * @param string $viewName * @return NodeInterface */ public function getPluginViewNodeByMasterPlugin(NodeInterface $node, $viewName) { /** @var $context ContentContext */ $context = $node->getContext(); foreach ($this->getNodes('TYPO3.Neos:PluginView', $context) as $pluginViewNode) { /** @var \TYPO3\TYPO3CR\Domain\Model\NodeInterface $pluginViewNode */ if ($pluginViewNode->isRemoved()) { continue; } if ($pluginViewNode->getProperty('plugin') === $node->getPath() && $pluginViewNode->getProperty('view') === $viewName) { return $pluginViewNode; } } return null; }
/** * Copies this node into the given node * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $referenceNode * @param string $nodeName * @return \TYPO3\TYPO3CR\Domain\Model\NodeInterface * @throws NodeExistsException * @api */ public function copyInto(NodeInterface $referenceNode, $nodeName) { if ($referenceNode->getNode($nodeName) !== NULL) { throw new NodeExistsException('Node with path "' . $referenceNode->getPath() . '/' . $nodeName . '" already exists.', 1292503467); } if (!$this->isNodeDataMatchingContext()) { $this->materializeNodeData(); } $copiedNode = $this->createRecursiveCopy($referenceNode, $nodeName); $this->context->getFirstLevelNodeCache()->flush(); $this->emitNodeAdded($copiedNode); return $copiedNode; }
/** * Creates a new node beneath $parent * * @param NodeInterface $parent * @return NodeInterface */ protected function createNode(NodeInterface $parent) { $nodeType = $this->getNodeType(); $name = $this->getName() ?: $this->nodeService->generateUniqueNodeName($parent->getPath()); $node = $parent->createNode($name, $nodeType); $this->applyNodeCreationHandlers($node); $this->persistenceManager->persistAll(); if ($nodeType->isOfType('TYPO3.Neos:Content') && ($this->getParentDomAddress() || $this->getSiblingDomAddress())) { if ($parent->getNodeType()->isOfType('TYPO3.Neos:ContentCollection')) { $renderContentOutOfBand = new RenderContentOutOfBand(); $renderContentOutOfBand->setNode($node); $renderContentOutOfBand->setParentDomAddress($this->getParentDomAddress()); $renderContentOutOfBand->setSiblingDomAddress($this->getSiblingDomAddress()); $renderContentOutOfBand->setMode($this->getMode()); $this->feedbackCollection->add($renderContentOutOfBand); } else { $flowQuery = new FlowQuery(array($node)); $closestDocument = $flowQuery->closest('[instanceof TYPO3.Neos:Document]')->get(0); $reloadDocument = new ReloadDocument(); $reloadDocument->setDocument($closestDocument); $this->feedbackCollection->add($reloadDocument); } } $updateNodeInfo = new UpdateNodeInfo(); $updateNodeInfo->setNode($node); $this->feedbackCollection->add($updateNodeInfo); return $node; }
/** * Replace the node data of a node instance with a given target node data * * The node data of the node that is published will be removed and the existing node data inside the target * workspace is updated to the changes and will be injected into the node instance. If the node was marked as * removed, both node data are removed. * * @param NodeInterface $node The node instance with node data to be published * @param NodeData $targetNodeData The existing node data in the target workspace * @return void */ protected function replaceNodeData(NodeInterface $node, NodeData $targetNodeData) { $sourceNodeData = $node->getNodeData(); $nodeWasMoved = false; $movedShadowNodeData = $this->nodeDataRepository->findOneByMovedTo($sourceNodeData); if ($movedShadowNodeData instanceof NodeData) { $nodeWasMoved = true; if ($movedShadowNodeData->isRemoved()) { $this->nodeDataRepository->remove($movedShadowNodeData); } } if ($node->isRemoved() === true) { $this->nodeDataRepository->remove($targetNodeData); } else { $targetNodeData->similarize($node->getNodeData()); if ($nodeWasMoved) { $targetNodeData->setPath($node->getPath(), false); } $targetNodeData->setLastPublicationDateTime($this->now); $node->setNodeData($targetNodeData); $this->nodeService->cleanUpProperties($node); } $this->nodeDataRepository->remove($sourceNodeData); }
/** * Discards the given node. * * @param NodeInterface $node * @return void * @throws \TYPO3\TYPO3CR\Exception\WorkspaceException * @api */ public function discardNode(NodeInterface $node) { if ($node->getWorkspace()->getBaseWorkspace() === NULL) { throw new WorkspaceException('Nodes in a in a workspace without a base workspace cannot be discarded.', 1395841899); } $possibleShadowNodeData = $this->nodeDataRepository->findOneByMovedTo($node->getNodeData()); if ($possibleShadowNodeData !== NULL) { $this->nodeDataRepository->remove($possibleShadowNodeData); } if ($node->getPath() !== '/') { $this->nodeDataRepository->remove($node); $this->emitNodeDiscarded($node); } }