/**
  * Shows the specified node and takes visibility and access restrictions into
  * account.
  *
  * @param NodeInterface $node
  * @return string View output for the specified node
  * @Flow\SkipCsrfProtection We need to skip CSRF protection here because this action could be called with unsafe requests from widgets or plugins that are rendered on the node - For those the CSRF token is validated on the sub-request, so it is safe to be skipped here
  * @Flow\IgnoreValidation("node")
  * @throws NodeNotFoundException
  */
 public function showAction(NodeInterface $node = NULL)
 {
     if ($node === NULL) {
         throw new NodeNotFoundException('The requested node does not exist or isn\'t accessible to the current user', 1430218623);
     }
     if (!$node->getContext()->isLive() && !$this->privilegeManager->isPrivilegeTargetGranted('TYPO3.Neos:Backend.GeneralAccess')) {
         $this->redirect('index', 'Login', NULL, array('unauthorized' => TRUE));
     }
     $inBackend = $node->getContext()->isInBackend();
     if ($node->getNodeType()->isOfType('TYPO3.Neos:Shortcut') && !$inBackend) {
         $this->handleShortcutNode($node);
     }
     $this->view->assign('value', $node);
     if ($inBackend) {
         $this->overrideViewVariablesFromInternalArguments();
         /** @var UserInterfaceMode $renderingMode */
         $renderingMode = $node->getContext()->getCurrentRenderingMode();
         $this->response->setHeader('Cache-Control', 'no-cache');
         if ($renderingMode !== NULL) {
             // Deprecated TypoScript context variable from version 2.0.
             $this->view->assign('editPreviewMode', $renderingMode->getTypoScriptPath());
         }
         if (!$this->view->canRenderWithNodeAndPath()) {
             $this->view->setTypoScriptPath('rawContent');
         }
     }
     if ($this->session->isStarted() && $inBackend) {
         $this->session->putData('lastVisitedNode', $node->getContextPath());
     }
 }
 /**
  * Resolves a shortcut node to the target. The return value can be
  *
  * * a NodeInterface instance if the target is a node or a node:// URI
  * * a string (in case the target is a plain text URI or an asset:// URI)
  * * NULL in case the shortcut cannot be resolved
  *
  * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node
  * @return NodeInterface|string|NULL
  */
 public function resolveShortcutTarget(NodeInterface $node)
 {
     $infiniteLoopPrevention = 0;
     while ($node->getNodeType()->isOfType('TYPO3.Neos:Shortcut') && $infiniteLoopPrevention < 50) {
         $infiniteLoopPrevention++;
         switch ($node->getProperty('targetMode')) {
             case 'selectedTarget':
                 $target = $node->getProperty('target');
                 if ($this->linkingService->hasSupportedScheme($target)) {
                     $targetObject = $this->linkingService->convertUriToObject($target, $node);
                     if ($targetObject instanceof NodeInterface) {
                         $node = $targetObject;
                     } elseif ($targetObject instanceof AssetInterface) {
                         return $this->linkingService->resolveAssetUri($target);
                     }
                 } else {
                     return $target;
                 }
                 break;
             case 'parentNode':
                 $node = $node->getParent();
                 break;
             case 'firstChildNode':
             default:
                 $childNodes = $node->getChildNodes('TYPO3.Neos:Document');
                 if ($childNodes !== array()) {
                     $node = reset($childNodes);
                 } else {
                     return null;
                 }
         }
     }
     return $node;
 }
 /**
  * Set the node title for the newly created Document node
  *
  * @param NodeInterface $node The newly created node
  * @param array $data incoming data from the creationDialog
  * @return void
  */
 public function handle(NodeInterface $node, array $data)
 {
     if (isset($data['title']) && $node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         $node->setProperty('title', $data['title']);
         $node->setProperty('uriPathSegment', NodeUtility::renderValidNodeName($data['title']));
     }
 }
 /**
  * Hooks into `afterNodeCreate` event dispatched from CR,
  * when new node has been created.
  *
  * @param NodeInterface $node
  */
 public function afterNodeCreate(NodeInterface $node)
 {
     if (!($nodeType = $node->getNodeType())) {
         return;
     }
     // array with [beforeNodes, childNodes, afterNodes] keys
     $config = $this->getAssistanceConfigForNodeType($nodeType->getName());
     switch ($nodeType->getName()) {
         case 'TYPO3.Neos.NodeTypes:Image':
             $this->configureImage($node, $nodeType, $config);
             break;
         case 'M12.Foundation:GridRow1Col':
         case 'M12.Foundation:GridRow2Col':
         case 'M12.Foundation:GridRow3Col':
         case 'M12.Foundation:GridRow4Col':
             $this->configureGridRow($node, $nodeType);
             break;
         case 'M12.Foundation:GridColumn':
             break;
             // assistance text nodes are created from $this->configureGridRow()
         // assistance text nodes are created from $this->configureGridRow()
         case 'M12.Foundation:RevealModal':
             $this->configureLinkingButton($node, $nodeType, $config, 'htmlDataRevealId');
             break;
         case 'M12.Foundation:Dropdown':
         case 'M12.Foundation:DropdownContent':
             $this->configureLinkingButton($node, $nodeType, $config, 'htmlDataDropdownId');
             break;
         default:
             $this->configureCreateAssistanceChildNodes($node, $nodeType, $config);
             break;
     }
 }
 /**
  * Helper method to retrieve the closest document for a node
  *
  * @param NodeInterface $node
  * @return NodeInterface
  */
 public function getClosestDocument(NodeInterface $node)
 {
     if ($node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         return $node;
     }
     $flowQuery = new FlowQuery(array($node));
     return $flowQuery->closest('[instanceof TYPO3.Neos:Document]')->get(0);
 }
 /**
  * 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);
         }
     }
 }
Exemple #7
0
 /**
  * Returns TRUE if the given node is of the node type this filter expects.
  *
  * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node
  * @return boolean
  */
 public function matches(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node)
 {
     if ($this->withSubTypes === TRUE) {
         return $this->nodeTypeManager->getNodeType($node->getNodeType())->isOfType($this->nodeTypeName);
     } else {
         $nodeData = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($node, 'nodeData', TRUE);
         $nodeType = \TYPO3\Flow\Reflection\ObjectAccess::getProperty($nodeData, 'nodeType', TRUE);
         return $nodeType === $this->nodeTypeName;
     }
 }
 /**
  * Schedules flushing of the routing cache entry for the given $nodeData
  * Note: This is not done recursively because the nodePathChanged signal is triggered for any affected node data instance
  *
  * @param NodeInterface $node The affected node data instance
  * @return void
  */
 public function registerNodePathChange(NodeInterface $node)
 {
     if (in_array($node->getIdentifier(), $this->tagsToFlush)) {
         return;
     }
     if (!$node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         return;
     }
     $this->tagsToFlush[] = $node->getIdentifier();
 }
 /**
  * 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());
     }
 }
 /**
  * Sets the best possible uriPathSegment for the given Node.
  * Will use an already set uriPathSegment or alternatively the node name as base,
  * then checks if the uriPathSegment already exists on the same level and appends a counter until a unique path segment was found.
  *
  * @param NodeInterface $node
  * @return void
  */
 public static function setUniqueUriPathSegment(NodeInterface $node)
 {
     if ($node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         $q = new FlowQuery(array($node));
         $q = $q->context(array('invisibleContentShown' => true, 'removedContentShown' => true, 'inaccessibleContentShown' => true));
         $possibleUriPathSegment = $initialUriPathSegment = !$node->hasProperty('uriPathSegment') ? $node->getName() : $node->getProperty('uriPathSegment');
         $i = 1;
         while ($q->siblings('[instanceof TYPO3.Neos:Document][uriPathSegment="' . $possibleUriPathSegment . '"]')->count() > 0) {
             $possibleUriPathSegment = $initialUriPathSegment . '-' . $i++;
         }
         $node->setProperty('uriPathSegment', $possibleUriPathSegment);
     }
 }
 /**
  * @param NodeInterface $node
  * @return array
  */
 protected function breadcrumbNodesForNode(NodeInterface $node)
 {
     $documentNodes = [];
     $flowQuery = new FlowQuery(array($node));
     $nodes = array_reverse($flowQuery->parents('[instanceof TYPO3.Neos:Document]')->get());
     /** @var NodeInterface $node */
     foreach ($nodes as $documentNode) {
         $documentNodes[] = $documentNode;
     }
     if ($node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         $documentNodes[] = $node;
     }
     return $documentNodes;
 }
 /**
  * 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();
         }
     }
 }
 /**
  * @param string|array $nodeTypes
  * @return boolean
  */
 public function nodeIsOfType($nodeTypes)
 {
     if ($this->node === NULL) {
         return TRUE;
     }
     if (!is_array($nodeTypes)) {
         $nodeTypes = array($nodeTypes);
     }
     foreach ($nodeTypes as $nodeType) {
         if ($this->node->getNodeType()->isOfType($nodeType)) {
             return TRUE;
         }
     }
     return FALSE;
 }
 /**
  * Matches if the selected node is of the given NodeType(s). If multiple types are specified, only one entry has to match
  *
  * Example: nodeIsOfType(['TYPO3.TYPO3CR:NodeType1', 'TYPO3.TYPO3CR:NodeType2']) matches if the selected node is of (sub) type *TYPO3.TYPO3CR:NodeType1* or *TYPO3.TYPO3CR:NodeType1*
  *
  * @param string|array $nodeTypes A single or an array of fully qualified NodeType name(s), e.g. "TYPO3.Neos:Document"
  * @return boolean TRUE if the selected node matches the $nodeTypes, otherwise FALSE
  */
 public function nodeIsOfType($nodeTypes)
 {
     if ($this->node === null) {
         return true;
     }
     if (!is_array($nodeTypes)) {
         $nodeTypes = array($nodeTypes);
     }
     foreach ($nodeTypes as $nodeType) {
         if ($this->node->getNodeType()->isOfType($nodeType)) {
             return true;
         }
     }
     return false;
 }
 /**
  * Increase read counter of the node by one
  *
  * @param NodeInterface $node Node to increase the read counter for
  *
  * @throws BadRequestException
  * @return mixed[]
  */
 public function trackAction(NodeInterface $node)
 {
     // we can only count pages that include the mixin
     if ($node->getNodeType()->isOfType('Futjikato.ReadCounter:CounterMixin')) {
         $node->setProperty('readcounter', $node->getProperty('readcounter') + 1);
         /**
          * Action changes data but is accessible via GET. this issues a error if we do not manually
          * persists the object in the persistence manager
          */
         $this->persistenceManager->persistAll();
         // by default the flow JSON view uses the 'value' variable
         $this->view->assign('value', array('readcounter' => $node->getProperty('readcounter')));
     } else {
         throw new BadRequestException('Node does not contain Futjikato.ReadCounter:CounterMixin.');
     }
 }
 /**
  * @param NodeInterface $node A node
  * @return array of document nodes
  */
 public function render(NodeInterface $node)
 {
     $documentNodes = [];
     $flowQuery = new FlowQuery(array($node));
     $nodes = array_reverse($flowQuery->parents('[instanceof TYPO3.Neos:Document]')->get());
     /** @var NodeInterface $node */
     foreach ($nodes as $documentNode) {
         $documentNodes[] = $documentNode;
     }
     if ($node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         $documentNodes[] = $node;
     }
     $this->templateVariableContainer->add('documentNodes', $documentNodes);
     $content = $this->renderChildren();
     $this->templateVariableContainer->remove('documentNodes');
     return $content;
 }
 /**
  * Publishes the given node to the specified target workspace. If no workspace is specified, the base workspace
  * is assumed.
  *
  * If the given node is a Document or has ContentCollection child nodes, these nodes are published as well.
  *
  * @param NodeInterface $node
  * @param Workspace $targetWorkspace If not set the base workspace is assumed to be the publishing target
  * @return void
  * @api
  */
 public function publishNode(NodeInterface $node, Workspace $targetWorkspace = null)
 {
     if ($targetWorkspace === null) {
         $targetWorkspace = $node->getWorkspace()->getBaseWorkspace();
     }
     if (!$targetWorkspace instanceof Workspace) {
         return;
     }
     $nodes = array($node);
     $nodeType = $node->getNodeType();
     if ($nodeType->isOfType('TYPO3.Neos:Document') || $nodeType->hasConfiguration('childNodes')) {
         foreach ($node->getChildNodes('TYPO3.Neos:ContentCollection') as $contentCollectionNode) {
             array_push($nodes, $contentCollectionNode);
         }
     }
     $sourceWorkspace = $node->getWorkspace();
     $sourceWorkspace->publishNodes($nodes, $targetWorkspace);
     $this->emitNodePublished($node, $targetWorkspace);
 }
 /**
  * Returns the plugin namespace that will be prefixed to plugin parameters in URIs.
  * By default this is <plugin_class_name>
  *
  * @return string
  */
 protected function getPluginNamespace()
 {
     if ($this->getArgumentNamespace() !== NULL) {
         return $this->getArgumentNamespace();
     }
     if ($this->node instanceof NodeInterface) {
         $nodeArgumentNamespace = $this->node->getProperty('argumentNamespace');
         if ($nodeArgumentNamespace !== NULL) {
             return $nodeArgumentNamespace;
         }
         $nodeArgumentNamespace = $this->node->getNodeType()->getName();
         $nodeArgumentNamespace = str_replace(':', '-', $nodeArgumentNamespace);
         $nodeArgumentNamespace = str_replace('.', '_', $nodeArgumentNamespace);
         $nodeArgumentNamespace = strtolower($nodeArgumentNamespace);
         return $nodeArgumentNamespace;
     }
     $argumentNamespace = str_replace(array(':', '.', '\\'), array('_', '_', '_'), $this->getPackage() . '_' . $this->getSubpackage() . '-' . $this->getController());
     $argumentNamespace = strtolower($argumentNamespace);
     return $argumentNamespace;
 }
 protected function getACLPropertiesForNode(NodeInterface $node)
 {
     $properties = ['nodeIdentifier' => $node->getIdentifier(), 'nodePath' => $node->getPath(), 'nodeLabel' => $node->getLabel(), 'nodeType' => $node->getNodeType()->getName(), 'nodeLevel' => $node->getDepth()];
     return $properties;
 }
 private function buildNodeProperties(NodeInterface $node)
 {
     $encodedProperties = [];
     foreach ($node->getNodeType()->getProperties() as $propertyName => $propertyConfiguration) {
         if (substr($propertyName, 0, 2) === '__') {
             // skip fully-private properties
             continue;
         }
         /** @var $contentContext ContentContext */
         $contentContext = $node->getContext();
         if ($propertyName === '_name' && $node === $contentContext->getCurrentSiteNode()) {
             // skip the node name of the site node
             continue;
         }
         // Serialize objects to JSON strings
         $dataType = isset($propertyConfiguration['type']) ? $propertyConfiguration['type'] : 'string';
         $encodedProperties[$propertyName] = $this->buildNodeProperty($node, $propertyName, $dataType);
     }
     return $encodedProperties;
 }
 /**
  * Schedule node removal into the current bulk request.
  *
  * @param NodeInterface $node
  * @return string
  */
 public function removeNode(NodeInterface $node)
 {
     // TODO: handle deletion from the fulltext index as well
     $identifier = sha1($node->getContextPath());
     $this->currentBulkRequest[] = array(array('delete' => array('_type' => NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($node->getNodeType()), '_id' => $identifier)));
     $this->logger->log(sprintf('NodeIndexer: Removed node %s from index (node actually removed). Persistence ID: %s', $node->getContextPath(), $identifier), LOG_DEBUG, NULL, 'ElasticSearch (CR)');
 }
 /**
  * @param NodeInterface $node
  * @return boolean
  */
 protected function hasInlineEditableProperties(NodeInterface $node)
 {
     foreach (array_values($node->getNodeType()->getProperties()) as $propertyConfiguration) {
         if (isset($propertyConfiguration['ui']['inlineEditable']) && $propertyConfiguration['ui']['inlineEditable'] === true) {
             return true;
         }
     }
     return false;
 }
 /**
  * Returns the closest aggregate node of the given node
  *
  * @param NodeInterface $node
  * @return NodeInterface
  */
 public static function getClosestAggregateNode(NodeInterface $node)
 {
     while ($node !== null && !$node->getNodeType()->isAggregate()) {
         $node = $node->getParent();
     }
     return $node;
 }
 /**
  * Tries to determine a label for the specified property
  *
  * @param string $propertyName
  * @param NodeInterface $changedNode
  * @return string
  */
 protected function getPropertyLabel($propertyName, NodeInterface $changedNode)
 {
     $properties = $changedNode->getNodeType()->getProperties();
     if (!isset($properties[$propertyName]) || !isset($properties[$propertyName]['ui']['label'])) {
         return $propertyName;
     }
     return $properties[$propertyName]['ui']['label'];
 }
 /**
  * @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()]);
 }
 /**
  * Fetch the configured views for the given master plugin
  *
  * @param NodeInterface $node
  * @return string
  *
  * @Flow\IgnoreValidation("node")
  */
 public function pluginViewsAction(NodeInterface $node = NULL)
 {
     $this->response->setHeader('Content-Type', 'application/json');
     $views = array();
     if ($node !== NULL) {
         /** @var $pluginViewDefinition \TYPO3\Neos\Domain\Model\PluginViewDefinition */
         $pluginViewDefinitions = $this->pluginService->getPluginViewDefinitionsByPluginNodeType($node->getNodeType());
         foreach ($pluginViewDefinitions as $pluginViewDefinition) {
             $label = $pluginViewDefinition->getLabel();
             $views[$pluginViewDefinition->getName()] = array('label' => $label);
             $pluginViewNode = $this->pluginService->getPluginViewNodeByMasterPlugin($node, $pluginViewDefinition->getName());
             if ($pluginViewNode === NULL) {
                 continue;
             }
             $q = new FlowQuery(array($pluginViewNode));
             $page = $q->closest('[instanceof TYPO3.Neos:Document]')->get(0);
             $uri = $this->uriBuilder->reset()->uriFor('show', array('node' => $page), 'Frontend\\Node', 'TYPO3.Neos');
             $pageTitle = $page->getLabel();
             $views[$pluginViewDefinition->getName()] = array('label' => sprintf('"%s"', $label, $pageTitle), 'pageNode' => array('title' => $pageTitle, 'path' => $page->getPath(), 'uri' => $uri));
         }
     }
     return json_encode((object) $views);
 }
 /**
  * Checks if the given Node has any properties configured as 'inlineEditable'
  *
  * @param NodeInterface $node
  * @return boolean
  */
 protected function hasInlineEditableProperties(NodeInterface $node)
 {
     return array_reduce(array_values($node->getNodeType()->getProperties()), function ($hasInlineEditableProperties, $propertyConfiguration) {
         return $hasInlineEditableProperties || isset($propertyConfiguration['ui']['inlineEditable']) && $propertyConfiguration['ui']['inlineEditable'] === true;
     }, false);
 }
Exemple #28
0
 /**
  * Apply nodeCreationHandlers
  *
  * @param NodeInterface $node
  * @throws InvalidNodeCreationHandlerException
  * @return void
  */
 protected function applyNodeCreationHandlers(NodeInterface $node)
 {
     $data = $this->getData() ?: [];
     $nodeType = $node->getNodeType();
     if (isset($nodeType->getOptions()['nodeCreationHandlers'])) {
         $nodeCreationHandlers = $nodeType->getOptions()['nodeCreationHandlers'];
         if (is_array($nodeCreationHandlers)) {
             foreach ($nodeCreationHandlers as $nodeCreationHandlerConfiguration) {
                 $nodeCreationHandler = new $nodeCreationHandlerConfiguration['nodeCreationHandler']();
                 if (!$nodeCreationHandler instanceof NodeCreationHandlerInterface) {
                     throw new InvalidNodeCreationHandlerException(sprintf('Expected NodeCreationHandlerInterface but got "%s"', get_class($nodeCreationHandler)), 1364759956);
                 }
                 $nodeCreationHandler->handle($node, $data);
             }
         }
     }
 }
 /**
  * Schedule node removal into the current bulk request.
  *
  * @param NodeInterface $node
  * @return string
  */
 public function removeNode(NodeInterface $node)
 {
     if ($this->settings['indexAllWorkspaces'] === false) {
         if ($node->getContext()->getWorkspaceName() !== 'live') {
             return;
         }
     }
     // TODO: handle deletion from the fulltext index as well
     $identifier = sha1($node->getContextPath());
     $this->currentBulkRequest[] = [['delete' => ['_type' => NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($node->getNodeType()), '_id' => $identifier]]];
     $this->logger->log(sprintf('NodeIndexer: Removed node %s from index (node actually removed). Persistence ID: %s', $node->getContextPath(), $identifier), LOG_DEBUG, null, 'ElasticSearch (CR)');
 }
 /**
  * @param NodeInterface $node
  * @return NodeInterface
  */
 protected function getClosestDocumentNode(NodeInterface $node)
 {
     while ($node !== null && !$node->getNodeType()->isOfType('TYPO3.Neos:Document')) {
         $node = $node->getParent();
     }
     return $node;
 }