/** * Recursive function for fetching all node type names * * @param NodeType $nodeType * @param array $nodeTypeNames * @return void */ protected function extractNodeTypeNamesAndSupertypesInternal(NodeType $nodeType, array &$nodeTypeNames) { $nodeTypeNames[$nodeType->getName()] = $nodeType->getName(); foreach ($nodeType->getDeclaredSuperTypes() as $superType) { $this->extractNodeTypeNamesAndSupertypesInternal($superType, $nodeTypeNames); } }
/** * Convert raw property values to the correct type according to a node type configuration * * @param NodeType $nodeType * @param string $propertyName * @param string $rawValue * @param Context $context * @return mixed */ public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context) { $propertyType = $nodeType->getPropertyType($propertyName); switch ($propertyType) { case 'string': return $rawValue; case 'reference': return $this->convertReference($rawValue, $context); case 'references': return $this->convertReferences($rawValue, $context); case 'DateTime': return $this->convertDateTime($rawValue); case 'integer': return $this->convertInteger($rawValue); case 'boolean': return $this->convertBoolean($rawValue); case 'array': return $this->convertArray($rawValue); default: $innerType = $propertyType; if ($propertyType !== null) { try { $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { } } if (is_string($rawValue) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') { return $this->propertyMapper->convert(json_decode($rawValue, true), $propertyType, $configuration); } } }
/** * @param NodeType $nodeType The (uninitialized) node type to process * @param array $configuration The configuration of the node type * @param array $options The processor options * @return void */ public function process(NodeType $nodeType, array &$configuration, array $options) { if ($nodeType->isOfType('TYPO3.TYPO3CR.Testing:NodeTypeWithProcessor')) { $someOption = isset($options['someOption']) ? $options['someOption'] : ''; $someOtherOption = isset($options['someOtherOption']) ? $options['someOtherOption'] : ''; $configuration['properties']['test1']['defaultValue'] = sprintf('The value of "someOption" is "%s", the value of "someOtherOption" is "%s"', $someOption, $someOtherOption); } }
/** * Generate a TypoScript prototype definition for a given node type * * A node will be rendered by TYPO3.Neos:Content by default with a template in * resource://PACKAGE_KEY/Private/Templates/NodeTypes/NAME.html and forwards all public * node properties to the template TypoScript object. * * @param NodeType $nodeType * @return string */ public function generate(NodeType $nodeType) { if (strpos($nodeType->getName(), ':') === false) { return ''; } if ($nodeType->isOfType('TYPO3.Neos:Content')) { $basePrototypeName = 'TYPO3.Neos:Content'; } elseif ($nodeType->isOfType('TYPO3.Neos:Document')) { $basePrototypeName = 'TYPO3.Neos:Document'; } else { $basePrototypeName = 'TYPO3.TypoScript:Template'; } $output = 'prototype(' . $nodeType->getName() . ') < prototype(' . $basePrototypeName . ') {' . chr(10); list($packageKey, $relativeName) = explode(':', $nodeType->getName(), 2); $templatePath = 'resource://' . $packageKey . '/Private/Templates/NodeTypes/' . $relativeName . '.html'; $output .= "\t" . 'templatePath = \'' . $templatePath . '\'' . chr(10); foreach ($nodeType->getProperties() as $propertyName => $propertyConfiguration) { if (isset($propertyName[0]) && $propertyName[0] !== '_') { $output .= "\t" . $propertyName . ' = ${q(node).property("' . $propertyName . '")}' . chr(10); if (isset($propertyConfiguration['type']) && isset($propertyConfiguration['ui']['inlineEditable']) && $propertyConfiguration['type'] === 'string' && $propertyConfiguration['ui']['inlineEditable'] === true) { $output .= "\t" . $propertyName . '.@process.convertUris = TYPO3.Neos:ConvertUris' . chr(10); } } } $output .= '}' . chr(10); return $output; }
/** * Convert raw property values to the correct type according to a node type configuration * * @param NodeType $nodeType * @param string $propertyName * @param string $rawValue * @param Context $context * @return mixed */ public function convert(NodeType $nodeType, $propertyName, $rawValue, Context $context) { $propertyType = $nodeType->getPropertyType($propertyName); switch ($propertyType) { case 'string': return $rawValue; case 'reference': return $this->convertReference($rawValue, $context); case 'references': return $this->convertReferences($rawValue, $context); case 'DateTime': return $this->convertDateTime($rawValue); case 'integer': return $this->convertInteger($rawValue); case 'boolean': return $this->convertBoolean($rawValue); case 'array': return $this->convertArray($rawValue); default: $innerType = $propertyType; if ($propertyType !== null) { try { $parsedType = \TYPO3\Flow\Utility\TypeHandling::parseType($propertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (\TYPO3\Flow\Utility\Exception\InvalidTypeException $exception) { } } if ((is_string($rawValue) || is_array($rawValue)) && $this->objectManager->isRegistered($innerType) && $rawValue !== '') { $propertyMappingConfiguration = new MvcPropertyMappingConfiguration(); $propertyMappingConfiguration->allowOverrideTargetType(); $propertyMappingConfiguration->allowAllProperties(); $propertyMappingConfiguration->skipUnknownProperties(); $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_MODIFICATION_ALLOWED, true); $propertyMappingConfiguration->setTypeConverterOption('TYPO3\\Flow\\Property\\TypeConverter\\PersistentObjectConverter', \TYPO3\Flow\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED, true); return $this->propertyMapper->convert($rawValue, $propertyType, $propertyMappingConfiguration); } else { return $rawValue; } } }
/** * @param NodeType $nodetype * @return boolean */ private function isNodeTypeInAllowedSite(NodeType $nodetype) { // dont restrict abstract nodes if ($nodetype->isAbstract()) { return true; } $deniedWilcard = false; $allowedSites = $nodetype->getConfiguration('allowedSites'); if ($allowedSites) { $currentDomain = $this->domainRepository->findOneByActiveRequest(); if ($currentDomain !== null) { $currentSite = $currentDomain->getSite(); } else { $currentSite = $this->siteRepository->findFirstOnline(); } if (!$currentSite) { return true; } foreach ($allowedSites as $siteName => $allowed) { if ($allowed == TRUE && $siteName == '*' | $currentSite->getSiteResourcesPackageKey() == $siteName) { return true; } if ($allowed == FALSE && $currentSite->getSiteResourcesPackageKey() == $siteName) { return false; } if ($allowed == TRUE && $siteName == '*') { $deniedWilcard = false; } if ($allowed == FALSE && $siteName == '*') { $deniedWilcard = true; } } if ($deniedWilcard == true) { return false; } } return true; }
/** * This method traverses the given node type to find the first super type that matches the constraint node type. * In case the hierarchy has more than one way of finding a path to the node type it's not taken into account, * since the first matched is returned. This is accepted on purpose for performance reasons and due to the fact * that such hierarchies should be avoided. * * @param NodeType $currentNodeType * @param string $constraintNodeTypeName * @param integer $distance * @return integer or NULL if no NodeType matched */ protected function traverseSuperTypes(NodeType $currentNodeType, $constraintNodeTypeName, $distance) { if ($currentNodeType->getName() === $constraintNodeTypeName) { return $distance; } $distance++; foreach ($currentNodeType->getDeclaredSuperTypes() as $superType) { $result = $this->traverseSuperTypes($superType, $constraintNodeTypeName, $distance); if ($result !== null) { return $result; } } return null; }
/** * @param NodeInterface $node * @param NodeType $nodeType * @return boolean */ public function isNodeOfType(NodeInterface $node, NodeType $nodeType) { if ($node->getNodeType()->getName() === $nodeType->getName()) { return true; } $subNodeTypes = $this->nodeTypeManager->getSubNodeTypes($nodeType->getName()); return isset($subNodeTypes[$node->getNodeType()->getName()]); }
/** * @param string $nodeTypeName * @param NodeType $nodeType * @return void */ protected function readNodeTypeConfiguration($nodeTypeName, NodeType $nodeType) { $nodeTypeConfiguration = $nodeType->getFullConfiguration(); $this->superTypeConfiguration['typo3:' . $nodeTypeName] = array(); /** @var NodeType $superType */ foreach ($nodeType->getDeclaredSuperTypes() as $superType) { $this->superTypeConfiguration['typo3:' . $nodeTypeName][] = 'typo3:' . $superType->getName(); } $nodeTypeProperties = array(); if (isset($nodeTypeConfiguration['properties'])) { foreach ($nodeTypeConfiguration['properties'] as $property => $propertyConfiguration) { // TODO Make sure we can configure the range for all multi column elements to define what types a column may contain $this->addProperty('typo3:' . $nodeTypeName, 'typo3:' . $property, $propertyConfiguration); $nodeTypeProperties[] = 'typo3:' . $property; } } $metadata = array(); $metaDataPropertyIndexes = array('ui'); foreach ($metaDataPropertyIndexes as $propertyName) { if (isset($nodeTypeConfiguration[$propertyName])) { $metadata[$propertyName] = $nodeTypeConfiguration[$propertyName]; } } if ($nodeType->isAbstract()) { $metadata['abstract'] = true; } $this->types['typo3:' . $nodeTypeName] = (object) array('label' => $nodeType->getLabel() ?: $nodeTypeName, 'id' => 'typo3:' . $nodeTypeName, 'properties' => array(), 'specific_properties' => $nodeTypeProperties, 'subtypes' => array(), 'metadata' => (object) $metadata, 'supertypes' => $this->superTypeConfiguration['typo3:' . $nodeTypeName], 'url' => 'http://www.typo3.org/ns/2012/Flow/Packages/Neos/Content/', 'ancestors' => array(), 'comment' => '', 'comment_plain' => ''); }
/** * @test */ public function magicHasReturnsFalseIfPropertyDoesNotExist() { $baseType = new NodeType('TYPO3.TYPO3CR:Base', array(), array()); $this->assertFalse($baseType->hasFooKey()); }
/** * Sets the node type of this node. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeType $nodeType * @return void */ public function setNodeType(NodeType $nodeType) { if ($this->nodeType !== $nodeType->getName()) { $this->nodeType = $nodeType->getName(); $this->update(); } }
/** * Collects all nodes with missing shadow nodes * * @param Workspace $workspace * @param boolean $dryRun * @param NodeType $nodeType * @return array */ protected function fixShadowNodesInWorkspace(Workspace $workspace, $dryRun, NodeType $nodeType = null) { $workspaces = array_merge([$workspace], $workspace->getBaseWorkspaces()); $fixedShadowNodes = 0; foreach ($workspaces as $workspace) { /** @var Workspace $workspace */ if ($workspace->getBaseWorkspace() === null) { continue; } /** @var QueryBuilder $queryBuilder */ $queryBuilder = $this->entityManager->createQueryBuilder(); $queryBuilder->select('n')->from(NodeData::class, 'n')->where('n.workspace = :workspace'); $queryBuilder->setParameter('workspace', $workspace->getName()); if ($nodeType !== null) { $queryBuilder->andWhere('n.nodeType = :nodeType'); $queryBuilder->setParameter('nodeType', $nodeType->getName()); } /** @var NodeData $nodeData */ foreach ($queryBuilder->getQuery()->getResult() as $nodeData) { $nodeDataSeenFromParentWorkspace = $this->nodeDataRepository->findOneByIdentifier($nodeData->getIdentifier(), $workspace->getBaseWorkspace(), $nodeData->getDimensionValues()); // This is the good case, either the node does not exist or was shadowed if ($nodeDataSeenFromParentWorkspace === null) { continue; } // Also good, the node was not moved at all. if ($nodeDataSeenFromParentWorkspace->getPath() === $nodeData->getPath()) { continue; } $nodeDataOnSamePath = $this->nodeDataRepository->findOneByPath($nodeData->getPath(), $workspace->getBaseWorkspace(), $nodeData->getDimensionValues(), null); // We cannot just put a shadow node in the path, something exists, but that should be fine. if ($nodeDataOnSamePath !== null) { continue; } if (!$dryRun) { $nodeData->createShadow($nodeDataSeenFromParentWorkspace->getPath()); } $fixedShadowNodes++; } } return $fixedShadowNodes; }
/** * Iterates through the given $properties setting them on the specified $node using the appropriate TypeConverters. * * @param object $nodeLike * @param NodeType $nodeType * @param array $properties * @param \TYPO3\TYPO3CR\Domain\Service\Context $context * @throws \TYPO3\Flow\Property\Exception\TypeConverterException * @return void */ protected function setNodeProperties($nodeLike, NodeType $nodeType, array $properties, TYPO3CRContext $context) { $nodeTypeProperties = $nodeType->getProperties(); foreach ($properties as $nodePropertyName => $nodePropertyValue) { if (substr($nodePropertyName, 0, 2) === '__') { continue; } $nodePropertyType = isset($nodeTypeProperties[$nodePropertyName]['type']) ? $nodeTypeProperties[$nodePropertyName]['type'] : NULL; switch ($nodePropertyType) { case 'reference': $nodePropertyValue = $context->getNodeByIdentifier($nodePropertyValue); break; case 'references': $nodeIdentifiers = json_decode($nodePropertyValue); $nodePropertyValue = array(); if (is_array($nodeIdentifiers)) { foreach ($nodeIdentifiers as $nodeIdentifier) { $referencedNode = $context->getNodeByIdentifier($nodeIdentifier); if ($referencedNode !== NULL) { $nodePropertyValue[] = $referencedNode; } } } else { throw new TypeConverterException(sprintf('node type "%s" expects an array of identifiers for its property "%s"', $nodeType->getName(), $nodePropertyName), 1383587419); } break; case 'date': if ($nodePropertyValue !== '') { $nodePropertyValue = \DateTime::createFromFormat('!Y-m-d', $nodePropertyValue); } else { $nodePropertyValue = NULL; } break; } if (substr($nodePropertyName, 0, 1) === '_') { $nodePropertyName = substr($nodePropertyName, 1); ObjectAccess::setProperty($nodeLike, $nodePropertyName, $nodePropertyValue); continue; } if (!isset($nodeTypeProperties[$nodePropertyName])) { throw new TypeConverterException(sprintf('node type "%s" does not have a property "%s" according to the schema', $nodeType->getName(), $nodePropertyName), 1359552744); } if ($this->objectManager->isRegistered($nodePropertyType) && $nodePropertyValue !== '') { $nodePropertyValue = $this->propertyMapper->convert(json_decode($nodePropertyValue, TRUE), $nodePropertyType); } $nodeLike->setProperty($nodePropertyName, $nodePropertyValue); } }
/** * Configure grid row element (M12.Foundation:GridRowXCol): * - add child columns * - set default column width (based on num of columns and grid size) * - create text node with sample content in each column * * @param NodeInterface $node * @param NodeType $nodeType : one of M12.Foundation:GridRowXCol * @throws \TYPO3\TYPO3CR\Exception\NodeTypeNotFoundException */ protected function configureGridRow(NodeInterface $node, NodeType $nodeType) { if (false === $this->enableAutoCreateNodes) { return; } $gs = $this->gridSize; // Get number of columns to create from M12.Foundation:GridRowXCol node type name $cols = (int) substr($nodeType->getName(), -4, 1); for ($k = 1; $k <= $cols; $k++) { $newNodeType = $this->nodeTypeManager->getNodeType('M12.Foundation:GridColumn'); $newNode = $node->createNode(uniqid('node-'), $newNodeType); // configure sensible default columns width if (1 === $cols) { $newNode->setProperty('classGridSize', ['small-' . $gs]); } elseif (4 === $cols) { $newNode->setProperty('classGridSize', ['small-' . $gs, 'medium-' . $gs / 2, 'large-' . $gs / 4]); } else { $newNode->setProperty('classGridSize', ['small-' . $gs, 'medium-' . (int) $gs / $cols]); } // Create sample content in each column $config = $this->getAssistanceConfigForNodeType($newNodeType->getName()); $this->configureCreateAssistanceChildNodes($newNode, $newNodeType, $config, [$k]); } }
/** * Iterates through the given $properties setting them on the specified $node using the appropriate TypeConverters. * * @param object $nodeLike * @param NodeType $nodeType * @param array $properties * @param TYPO3CRContext $context * @param PropertyMappingConfigurationInterface $configuration * @return void * @throws TypeConverterException */ protected function setNodeProperties($nodeLike, NodeType $nodeType, array $properties, TYPO3CRContext $context, PropertyMappingConfigurationInterface $configuration = null) { $nodeTypeProperties = $nodeType->getProperties(); unset($properties['_lastPublicationDateTime']); foreach ($properties as $nodePropertyName => $nodePropertyValue) { if (substr($nodePropertyName, 0, 2) === '__') { continue; } $nodePropertyType = isset($nodeTypeProperties[$nodePropertyName]['type']) ? $nodeTypeProperties[$nodePropertyName]['type'] : null; switch ($nodePropertyType) { case 'reference': $nodePropertyValue = $context->getNodeByIdentifier($nodePropertyValue); break; case 'references': $nodeIdentifiers = json_decode($nodePropertyValue); $nodePropertyValue = array(); if (is_array($nodeIdentifiers)) { foreach ($nodeIdentifiers as $nodeIdentifier) { $referencedNode = $context->getNodeByIdentifier($nodeIdentifier); if ($referencedNode !== null) { $nodePropertyValue[] = $referencedNode; } } } elseif ($nodeIdentifiers !== null) { throw new TypeConverterException(sprintf('node type "%s" expects an array of identifiers for its property "%s"', $nodeType->getName(), $nodePropertyName), 1383587419); } break; case 'DateTime': if ($nodePropertyValue !== '' && ($nodePropertyValue = \DateTime::createFromFormat(\DateTime::W3C, $nodePropertyValue)) !== false) { $nodePropertyValue->setTimezone(new \DateTimeZone(date_default_timezone_get())); } else { $nodePropertyValue = null; } break; case 'integer': $nodePropertyValue = intval($nodePropertyValue); break; case 'boolean': if (is_string($nodePropertyValue)) { $nodePropertyValue = $nodePropertyValue === 'true' ? true : false; } break; case 'array': $nodePropertyValue = json_decode($nodePropertyValue, true); break; } if (substr($nodePropertyName, 0, 1) === '_') { $nodePropertyName = substr($nodePropertyName, 1); ObjectAccess::setProperty($nodeLike, $nodePropertyName, $nodePropertyValue); continue; } if (!isset($nodeTypeProperties[$nodePropertyName])) { if ($configuration !== null && $configuration->shouldSkipUnknownProperties()) { continue; } else { throw new TypeConverterException(sprintf('Node type "%s" does not have a property "%s" according to the schema', $nodeType->getName(), $nodePropertyName), 1359552744); } } $innerType = $nodePropertyType; if ($nodePropertyType !== null) { try { $parsedType = TypeHandling::parseType($nodePropertyType); $innerType = $parsedType['elementType'] ?: $parsedType['type']; } catch (InvalidTypeException $exception) { } } if (is_string($nodePropertyValue) && $this->objectManager->isRegistered($innerType) && $nodePropertyValue !== '') { $nodePropertyValue = $this->propertyMapper->convert(json_decode($nodePropertyValue, true), $nodePropertyType, $configuration); } $nodeLike->setProperty($nodePropertyName, $nodePropertyValue); } }
/** * @param NodeType $pluginNodeType * @return array */ protected function getPluginViewConfigurationsByPluginNodeType(NodeType $pluginNodeType) { $pluginNodeTypeOptions = $pluginNodeType->getOptions(); return isset($pluginNodeTypeOptions['pluginViews']) ? $pluginNodeTypeOptions['pluginViews'] : array(); }
/** * @param AbstractNodeData $nodeData * @param NodeType $nodeType * @param MetaDataCollection $metaDataCollection * @throws \TYPO3\Eel\Exception */ protected function mapMetaDataToNodeData(AbstractNodeData $nodeData, NodeType $nodeType, MetaDataCollection $metaDataCollection) { if ($this->defaultContextVariables === NULL) { $this->defaultContextVariables = EelUtility::getDefaultContextVariables($this->settings['defaultEelContext']); } foreach ($nodeType->getProperties() as $propertyName => $propertyConfiguration) { $contextVariables = array_merge($this->defaultContextVariables, $metaDataCollection->toArray()); if (isset($propertyConfiguration['mapping'])) { $nodeData->setProperty($propertyName, EelUtility::evaluateEelExpression($propertyConfiguration['mapping'], $this->eelEvaluator, $contextVariables)); } } }
/** * @test */ public function defaultValuesForPropertiesHandlesDateTypes() { $nodeType = new NodeType('TYPO3.TYPO3CR:Base', array(), array('properties' => array('date' => array('type' => 'DateTime', 'defaultValue' => '2014-09-23')))); $this->assertEquals($nodeType->getDefaultValuesForProperties(), array('date' => new \DateTime('2014-09-23'))); }
/** * @param NodeType $nodeType * @return array<NodeType> */ protected function getAllImplementedNodeTypes(NodeType $nodeType) { $types = array($nodeType); foreach ($nodeType->getDeclaredSuperTypes() as $superType) { $types = array_merge($types, $this->getAllImplementedNodeTypes($superType)); } return $types; }
/** * Generate an `@cache` entry tag for a node type * A cache entry with this tag will be flushed whenever a node * (for any variant) that is of the given node type (including inheritance) * is updated. * * @param NodeType $nodeType * @return string */ public function nodeTypeTag(NodeType $nodeType) { return 'NodeType_' . $nodeType->getName(); }
/** * 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; } } } }
/** * Tries to find a default value for the given property trying: * 1) The specific property configuration for the given NodeType * 2) The generic configuration for the property type in setings. * * @param NodeType $nodeType * @param string $propertyName * @return mixed */ protected function getDefaultValueForProperty(NodeType $nodeType, $propertyName) { $defaultValues = $nodeType->getDefaultValuesForProperties(); if (!isset($defaultValues[$propertyName])) { return null; } return $defaultValues[$propertyName]; }