/** * Repair inconsistent nodes * * This command analyzes and repairs the node tree structure and individual nodes * based on the current node type configuration. * * The following checks will be performed: * * {pluginDescriptions} * <b>Examples:</b> * * ./flow node:repair * * ./flow node:repair --node-type TYPO3.Neos.NodeTypes:Page * * @param string $nodeType Node type name, if empty update all declared node types * @param string $workspace Workspace name, default is 'live' * @param boolean $dryRun Don't do anything, but report actions * @param boolean $cleanup If FALSE, cleanup tasks are skipped * @return void */ public function repairCommand($nodeType = null, $workspace = 'live', $dryRun = false, $cleanup = true) { $this->pluginConfigurations = self::detectPlugins($this->objectManager); if ($this->workspaceRepository->countByName($workspace) === 0) { $this->outputLine('Workspace "%s" does not exist', array($workspace)); exit(1); } if ($nodeType !== null) { if ($this->nodeTypeManager->hasNodeType($nodeType)) { $nodeType = $this->nodeTypeManager->getNodeType($nodeType); } else { $this->outputLine('Node type "%s" does not exist', array($nodeType)); exit(1); } } if ($dryRun) { $this->outputLine('Dry run, not committing any changes.'); } foreach ($this->pluginConfigurations as $pluginConfiguration) { /** @var NodeCommandControllerPluginInterface $plugin */ $plugin = $pluginConfiguration['object']; $this->outputLine('<b>' . $plugin->getSubCommandShortDescription('repair') . '</b>'); $this->outputLine(); $plugin->invokeSubCommand('repair', $this->output, $nodeType, $workspace, $dryRun, $cleanup); $this->outputLine(); } $this->outputLine('Node repair finished.'); }
/** * Render the label for the given $nodeTypeName * * @param string $nodeTypeName * @throws \TYPO3\TYPO3CR\Exception\NodeTypeNotFoundException * @return string */ public function labelForNodeType($nodeTypeName) { if (!$this->nodeTypeManager->hasNodeType($nodeTypeName)) { $explodedNodeTypeName = explode(':', $nodeTypeName); return end($explodedNodeTypeName); } $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName); return $nodeType->getLabel(); }
/** * Shows a list of nodes * * @param string $searchTerm An optional search term used for filtering the list of nodes * @param string $workspaceName Name of the workspace to search in, "live" by default * @param array $dimensions Optional list of dimensions and their values which should be used for querying * @param array $nodeTypes A list of node types the list should be filtered by * @param NodeInterface $contextNode a node to use as context for the search * @return string */ public function indexAction($searchTerm = '', $workspaceName = 'live', array $dimensions = array(), array $nodeTypes = array('TYPO3.Neos:Document'), NodeInterface $contextNode = null) { $searchableNodeTypeNames = array(); foreach ($nodeTypes as $nodeTypeName) { if (!$this->nodeTypeManager->hasNodeType($nodeTypeName)) { $this->throwStatus(400, sprintf('Unknown node type "%s"', $nodeTypeName)); } $searchableNodeTypeNames[$nodeTypeName] = $nodeTypeName; /** @var NodeType $subNodeType */ foreach ($this->nodeTypeManager->getSubNodeTypes($nodeTypeName, false) as $subNodeTypeName => $subNodeType) { $searchableNodeTypeNames[$subNodeTypeName] = $subNodeTypeName; } } $contentContext = $this->createContentContext($workspaceName, $dimensions); $nodes = $this->nodeSearchService->findByProperties($searchTerm, $searchableNodeTypeNames, $contentContext, $contextNode); $this->view->assign('nodes', $nodes); }
/** * Detects and retrieves the NodeType of the given $nodeXml * * @param \SimpleXMLElement $nodeXml * @return NodeType * @throws \TYPO3\Neos\Domain\Exception */ protected function parseNodeType(\SimpleXMLElement $nodeXml) { $nodeTypeName = (string) $nodeXml['type']; if ($this->nodeTypeManager->hasNodeType($nodeTypeName)) { $nodeType = $this->nodeTypeManager->getNodeType($nodeTypeName); if ($nodeType->isAbstract()) { throw new DomainException(sprintf('The node type "%s" is marked as abstract and cannot be assigned to nodes.', $nodeTypeName), 1386590052); } return $nodeType; } return $this->nodeTypeManager->createNodeType($nodeTypeName); }
/** * Reorder child nodes for the given node type * * @param NodeType $nodeType * @param string $workspaceName * @param boolean $dryRun * @return void */ protected function reorderChildNodesByNodeType(NodeType $nodeType, $workspaceName, $dryRun) { $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; } } } }
/** * If the given node has the property this transformation should work on, this * returns TRUE if the given NodeType is registered with the NodeTypeManager and is not abstract. * * @param NodeData $node * @return boolean */ public function isTransformable(NodeData $node) { return $this->nodeTypeManager->hasNodeType($this->newType) && !$this->nodeTypeManager->getNodeType($this->newType)->isAbstract(); }
/** * @test */ public function hasNodeTypeReturnsTrueForAbstractNodeTypes() { $this->assertTrue($this->nodeTypeManager->hasNodeType('TYPO3.TYPO3CR.Testing:ContentObject')); }
/** * Exports a single Node into the XML structure * * @param array $nodeData The node data as an array * @param array $nodesStack The stack keeping track of open tags, as passed by exportNodeDataList() * @return void The result is written directly into $this->xmlWriter */ protected function exportNodeData(array &$nodeData, array &$nodesStack) { if ($nodeData['path'] !== '/' && !isset($this->exportedNodePaths[$nodeData['parentPath']])) { $this->xmlWriter->writeComment(sprintf('Skipped node with identifier "%s" and path "%s" because of a missing parent path. This is caused by a broken rootline and needs to be fixed with the "node:repair" command.', $nodeData['identifier'], $nodeData['path'])); return; } $this->exportedNodePaths[$nodeData['path']] = true; if ($nodeData['parentPath'] === '/') { $nodeName = substr($nodeData['path'], 1); } else { $nodeName = substr($nodeData['path'], strlen($nodeData['parentPath']) + 1); } // is this a variant of currently open node? // then close all open nodes until parent is currently open and start new node element // else reuse the currently open node element and add a new variant element // @todo what about nodes with a different path in some dimension $parentNode = end($nodesStack); if (!$parentNode || $parentNode['path'] !== $nodeData['path'] || $parentNode['identifier'] !== $nodeData['identifier']) { while ($parentNode && $nodeData['parentPath'] !== $parentNode['path']) { $this->xmlWriter->endElement(); array_pop($nodesStack); $parentNode = end($nodesStack); } $nodesStack[] = $nodeData; $this->xmlWriter->startElement('node'); $this->xmlWriter->writeAttribute('identifier', $nodeData['identifier']); $this->xmlWriter->writeAttribute('nodeName', $nodeName); } $this->xmlWriter->startElement('variant'); if ($nodeData['sortingIndex'] !== null) { // the "/" node has no sorting index by default; so we should only write it if it has been set. $this->xmlWriter->writeAttribute('sortingIndex', $nodeData['sortingIndex']); } foreach (array('workspace', 'nodeType', 'version', 'removed', 'hidden', 'hiddenInIndex') as $propertyName) { $this->xmlWriter->writeAttribute($propertyName, $nodeData[$propertyName]); } $this->xmlWriter->startElement('dimensions'); foreach ($nodeData['dimensionValues'] as $dimensionKey => $dimensionValues) { foreach ($dimensionValues as $dimensionValue) { $this->xmlWriter->writeElement($dimensionKey, $dimensionValue); } } $this->xmlWriter->endElement(); foreach (array('accessRoles', 'hiddenBeforeDateTime', 'hiddenAfterDateTime', 'creationDateTime', 'lastModificationDateTime', 'lastPublicationDateTime', 'contentObjectProxy') as $propertyName) { $this->writeConvertedElement($nodeData, $propertyName); } $this->xmlWriter->startElement('properties'); if ($this->nodeTypeManager->hasNodeType($nodeData['nodeType'])) { $nodeType = $this->nodeTypeManager->getNodeType($nodeData['nodeType']); foreach ($nodeData['properties'] as $propertyName => $propertyValue) { if ($nodeType->hasConfiguration('properties.' . $propertyName)) { $declaredPropertyType = $nodeType->getPropertyType($propertyName); $this->writeConvertedElement($nodeData['properties'], $propertyName, null, $declaredPropertyType); } } } else { foreach ($nodeData['properties'] as $propertyName => $propertyValue) { $this->writeConvertedElement($nodeData['properties'], $propertyName); } } $this->xmlWriter->endElement(); // "properties" $this->xmlWriter->endElement(); // "variant" }
/** * If the given node has the property this transformation should work on, this * returns TRUE if the given NodeType is registered with the NodeTypeManager and is not abstract. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeData $node * @return boolean */ public function isTransformable(\TYPO3\TYPO3CR\Domain\Model\NodeData $node) { return $this->nodeTypeManager->hasNodeType($this->newType) && !$this->nodeTypeManager->getNodeType($this->newType)->isAbstract(); }