/** * Normalizes the given node path to a reference path and returns an absolute path. * * @param string $path The non-normalized path * @param string $referencePath a reference path in case the given path is relative. * @param string $siteNodePath Reference path to a site node. Relative paths starting with "~" will be based on the siteNodePath. * @return string The normalized absolute path * @throws \InvalidArgumentException if the node path was invalid. */ public function normalizePath($path, $referencePath = null, $siteNodePath = null) { if (strpos($path, '~') === 0) { $path = NodePaths::addNodePathSegment($siteNodePath, substr($path, 1)); } return parent::normalizePath($path, $referencePath); }
/** * Remove given site all nodes for that site and all domains associated. * * @param Site $site * @return void */ public function pruneSite(Site $site) { $siteNodePath = NodePaths::addNodePathSegment(static::SITES_ROOT_PATH, $site->getNodeName()); $this->nodeDataRepository->removeAllInPath($siteNodePath); $siteNodes = $this->nodeDataRepository->findByPath($siteNodePath); foreach ($siteNodes as $siteNode) { $this->nodeDataRepository->remove($siteNode); } $site->setPrimaryDomain(null); $this->siteRepository->update($site); $domainsForSite = $this->domainRepository->findBySite($site); foreach ($domainsForSite as $domain) { $this->domainRepository->remove($domain); } $this->persistenceManager->persistAll(); $this->siteRepository->remove($site); $this->emitSitePruned($site); }
/** * Normalizes the given node path to a reference path and returns an absolute path. * * You should usually use \Neos\ContentRepository\Domain\Service\NodeService::normalizePath() because functionality could be overloaded, * this is here only for low level operations. * * * @see \Neos\ContentRepository\Domain\Service\NodeService::normalizePath() * @param $path * @param string $referencePath * @return string */ public static function normalizePath($path, $referencePath = null) { if ($path === '.') { return $referencePath; } if (!is_string($path)) { throw new \InvalidArgumentException(sprintf('An invalid node path was specified: is of type %s but a string is expected.', gettype($path)), 1357832901); } if (strpos($path, '//') !== false) { throw new \InvalidArgumentException('Paths must not contain two consecutive slashes.', 1291371910); } if ($path[0] === '/') { $absolutePath = $path; } else { $absolutePath = NodePaths::addNodePathSegment($referencePath, $path); } $normalizedPath = NodePaths::replaceRelativePathElements($absolutePath); return strtolower($normalizedPath); }
/** * Creates, adds and returns a child node of this node, without setting default * properties or creating subnodes. * * @param string $name Name of the new node * @param NodeType $nodeType Node type of the new node (optional) * @param string $identifier The identifier of the node, unique within the workspace, optional(!) * @param Workspace $workspace * @param array $dimensions An array of dimension name to dimension values * @throws NodeExistsException if a node with this path already exists. * @throws \InvalidArgumentException if the node name is not accepted. * @return NodeData */ public function createSingleNodeData($name, NodeType $nodeType = null, $identifier = null, Workspace $workspace = null, array $dimensions = null) { if (!is_string($name) || preg_match(NodeInterface::MATCH_PATTERN_NAME, $name) !== 1) { throw new \InvalidArgumentException('Invalid node name "' . $name . '" (a node name must only contain lowercase characters, numbers and the "-" sign).', 1292428697); } $nodeWorkspace = $workspace ?: $this->workspace; $newPath = NodePaths::addNodePathSegment($this->path, $name); if ($this->nodeDataRepository->findOneByPath($newPath, $nodeWorkspace, $dimensions, null) !== null) { throw new NodeExistsException(sprintf('Node with path "' . $newPath . '" already exists in workspace %s and given dimensions %s.', $nodeWorkspace->getName(), var_export($dimensions, true)), 1292503471); } $newNodeData = new NodeData($newPath, $nodeWorkspace, $identifier, $dimensions); if ($nodeType !== null) { $newNodeData->setNodeType($nodeType); } $this->nodeDataRepository->setNewIndex($newNodeData, NodeDataRepository::POSITION_LAST); return $newNodeData; }
/** * Returns the node of the current site. * * @return NodeInterface */ public function getCurrentSiteNode() { if ($this->currentSite !== null && $this->currentSiteNode === null) { $siteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $this->currentSite->getNodeName()); $this->currentSiteNode = $this->getNode($siteNodePath); if (!$this->currentSiteNode instanceof NodeInterface) { $this->systemLogger->log(sprintf('Warning: %s::getCurrentSiteNode() couldn\'t load the site node for path "%s" in workspace "%s". This is probably due to a missing baseworkspace for the workspace of the current user.', __CLASS__, $siteNodePath, $this->workspaceName), LOG_WARNING); } } return $this->currentSiteNode; }
/** * If the node is not found, we *first* want to figure out whether the node exists in other dimensions or is really non-existent * * @param $identifier * @param ContentContext $context * @return void */ protected function addExistingNodeVariantInformationToResponse($identifier, ContentContext $context) { $nodeVariants = $context->getNodeVariantsByIdentifier($identifier); if (count($nodeVariants) > 0) { $this->response->setHeader('X-Neos-Node-Exists-In-Other-Dimensions', true); // If the node exists in another dimension, we want to know how many nodes in the rootline are also missing for the target // dimension. This is needed in the UI to tell the user if nodes will be materialized recursively upwards in the rootline. // To find the node path for the given identifier, we just use the first result. This is a safe assumption at least for // "Document" nodes (aggregate=TRUE), because they are always moved in-sync. $node = reset($nodeVariants); /** @var NodeInterface $node */ if ($node->getNodeType()->isAggregate()) { $pathSegmentsToSites = NodePaths::getPathDepth(SiteService::SITES_ROOT_PATH); $pathSegmentsToNodeVariant = NodePaths::getPathDepth($node->getPath()); // Segments between the sites root "/sites" and the node variant (minimum 1) $pathSegments = $pathSegmentsToNodeVariant - $pathSegmentsToSites; // Nodes between (and including) the site root node and the node variant (minimum 1) $siteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $context->getCurrentSite()->getNodeName()); $nodes = $context->getNodesOnPath($siteNodePath, $node->getPath()); $missingNodesOnRootline = $pathSegments - count($nodes); if ($missingNodesOnRootline > 0) { $this->response->setHeader('X-Neos-Nodes-Missing-On-Rootline', $missingNodesOnRootline); } } } }
/** * Move $node before, into or after $targetNode * * @param NodeInterface $node * @param NodeInterface $targetNode * @param string $position where the node should be added (allowed: before, into, after) * @return NodeInterface The same node given as first argument * @throws NodeException */ public function move(NodeInterface $node, NodeInterface $targetNode, $position) { if (!in_array($position, array('before', 'into', 'after'), true)) { throw new NodeException('The position should be one of the following: "before", "into", "after".', 1296132542); } $designatedParentNode = $this->getDesignatedParentNode($targetNode, $position); // If we stay inside the same parent we basically just reorder, no rename needed or wanted. if ($designatedParentNode !== $node->getParent()) { $designatedNodePath = NodePaths::addNodePathSegment($designatedParentNode->getPath(), $node->getName()); if ($this->nodeService->nodePathAvailableForNode($designatedNodePath, $node) === false) { $nodeName = $this->nodeService->generateUniqueNodeName($designatedParentNode->getPath(), $node->getName()); if ($nodeName !== $node->getName()) { // FIXME: This can be removed if $node->move* supports additionally changing the name of the node. $node->setName($nodeName); } } } switch ($position) { case 'before': $node->moveBefore($targetNode); break; case 'into': $node->moveInto($targetNode); break; case 'after': $node->moveAfter($targetNode); } return $node; }
/** * Finds all nodes of the specified workspace lying on the path specified by * (and including) the given starting point and end point and (optionally) a node type filter. * * If some node does not exist in the specified workspace, this function will * try to find a corresponding node in one of the base workspaces (if any). * * @param string $pathStartingPoint Absolute path specifying the starting point * @param string $pathEndPoint Absolute path specifying the end point * @param Workspace $workspace The containing workspace * @param array $dimensions Array of dimensions to array of dimension values * @param boolean $includeRemovedNodes Should removed nodes be included in the result (defaults to FALSE) * @param string $nodeTypeFilter Optional filter for the node type of the nodes, supports complex expressions (e.g. "Neos.Neos:Page", "!Neos.Neos:Page,Neos.Neos:Text" or NULL) * @throws \InvalidArgumentException * @return array<\Neos\ContentRepository\Domain\Model\NodeData> The nodes found on the given path * @todo findOnPath should probably not return child nodes of removed nodes unless removed nodes are included. */ public function findOnPath($pathStartingPoint, $pathEndPoint, Workspace $workspace, array $dimensions = null, $includeRemovedNodes = false, $nodeTypeFilter = null) { $pathStartingPoint = strtolower($pathStartingPoint); $pathEndPoint = strtolower($pathEndPoint); if (NodePaths::isSubPathOf($pathStartingPoint, $pathEndPoint) === false) { throw new \InvalidArgumentException('Invalid paths: path of starting point must be first part of end point path.', 1284391181); } $workspaces = $this->collectWorkspaceAndAllBaseWorkspaces($workspace); $queryBuilder = $this->createQueryBuilder($workspaces); if ($dimensions !== null) { $this->addDimensionJoinConstraintsToQueryBuilder($queryBuilder, $dimensions); } else { $dimensions = []; } if ($nodeTypeFilter !== null) { $this->addNodeTypeFilterConstraintsToQueryBuilder($queryBuilder, $nodeTypeFilter); } $pathConstraints = []; $constraintPath = $pathStartingPoint; $pathConstraints[] = md5($constraintPath); $pathSegments = explode('/', NodePaths::getRelativePathBetween($pathStartingPoint, $pathEndPoint)); foreach ($pathSegments as $pathSegment) { $constraintPath = NodePaths::addNodePathSegment($constraintPath, $pathSegment); $pathConstraints[] = md5($constraintPath); } if (count($pathConstraints) > 0) { $queryBuilder->andWhere('n.pathHash IN (:paths)')->setParameter('paths', $pathConstraints); } $query = $queryBuilder->getQuery(); $foundNodes = $query->getResult(); $foundNodes = $this->reduceNodeVariantsByWorkspacesAndDimensions($foundNodes, $workspaces, $dimensions); $foundNodes = $this->filterNodeDataByBestMatchInContext($foundNodes, $workspaces[0], $dimensions, $includeRemovedNodes); if ($includeRemovedNodes === false) { $foundNodes = $this->filterRemovedNodes($foundNodes, false); } $nodesByDepth = []; /** @var NodeData $node */ foreach ($foundNodes as $node) { $nodesByDepth[$node->getDepth()] = $node; } ksort($nodesByDepth); return array_values($nodesByDepth); }
/** * Moves this node into the given node * * @param NodeInterface $referenceNode * @param string $newName * @throws NodeConstraintException * @throws NodeException * @throws NodeExistsException * @api */ public function moveInto(NodeInterface $referenceNode, $newName = null) { if ($referenceNode === $this || $referenceNode === $this->getParent()) { return; } if ($this->getPath() === '/') { throw new NodeException('The root node cannot be moved.', 1346769001); } if ($referenceNode !== $this->getParent() && $referenceNode->getNode($this->getName()) !== null) { throw new NodeExistsException('Node with path "' . $this->getName() . '" already exists.', 1292503470); } if (!$referenceNode->willChildNodeBeAutoCreated($this->getName()) && !$referenceNode->isNodeTypeAllowedAsChildNode($this->getNodeType())) { throw new NodeConstraintException('Cannot move ' . $this->__toString() . ' into ' . $referenceNode->__toString(), 1404648124); } $name = $newName !== null ? $newName : $this->getName(); $this->emitBeforeNodeMove($this, $referenceNode, NodeDataRepository::POSITION_LAST); $this->setPath(NodePaths::addNodePathSegment($referenceNode->getPath(), $name)); $this->nodeDataRepository->persistEntities(); $this->nodeDataRepository->setNewIndex($this->nodeData, NodeDataRepository::POSITION_LAST); $this->context->getFirstLevelNodeCache()->flush(); $this->emitAfterNodeMove($this, $referenceNode, NodeDataRepository::POSITION_LAST); $this->emitNodeUpdated($this); }
/** * Update a site * * @param Site $site A site to update * @param string $newSiteNodeName A new site node name * @return void * @Flow\Validate(argumentName="$site", type="UniqueEntity") * @Flow\Validate(argumentName="$newSiteNodeName", type="NotEmpty") * @Flow\Validate(argumentName="$newSiteNodeName", type="StringLength", options={ "minimum"=1, "maximum"=250 }) * @Flow\Validate(argumentName="$newSiteNodeName", type="Neos.Neos:NodeName") */ public function updateSiteAction(Site $site, $newSiteNodeName) { if ($site->getNodeName() !== $newSiteNodeName) { $oldSiteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $site->getNodeName()); $newSiteNodePath = NodePaths::addNodePathSegment(SiteService::SITES_ROOT_PATH, $newSiteNodeName); /** @var $workspace Workspace */ foreach ($this->workspaceRepository->findAll() as $workspace) { $siteNode = $this->nodeDataRepository->findOneByPath($oldSiteNodePath, $workspace); if ($siteNode !== null) { $siteNode->setPath($newSiteNodePath); } } $site->setNodeName($newSiteNodeName); $this->nodeDataRepository->persistEntities(); } $this->siteRepository->update($site); $this->addFlashMessage('The site "%s" has been updated.', 'Update', null, array(htmlspecialchars($site->getName())), 1412371798); $this->unsetLastVisitedNodeAndRedirect('index'); }