/** * Resolves the request path, also known as route path, identifying the given node. * * A path is built, based on the uri path segment properties of the parents of and the given node itself. * If content dimensions are configured, the first path segment will the identifiers of the dimension * values according to the current context. * * @param NodeInterface $siteNode The site node, used as a starting point while traversing the tree * @param NodeInterface $node The node where the generated path should lead to * @return string The relative route path, possibly prefixed with a segment for identifying the current content dimension values */ protected function resolveRoutePathForNode(NodeInterface $siteNode, NodeInterface $node) { $workspaceName = $node->getContext()->getWorkspaceName(); $nodeContextPath = $node->getContextPath(); $nodeContextPathSuffix = $workspaceName !== 'live' ? substr($nodeContextPath, strpos($nodeContextPath, '@')) : ''; $currentNodeIsSiteNode = $siteNode === $node; $dimensionsUriSegment = $this->getUriSegmentForDimensions($node->getContext()->getDimensions(), $currentNodeIsSiteNode); $requestPath = $this->getRequestPathByNode($siteNode, $node); return trim($dimensionsUriSegment . $requestPath, '/') . $nodeContextPathSuffix; }
/** * Takes care of creating a redirect to properly render the collection the given node is in. * * @param NodeInterface $node * @param string $typoScriptPath * @return string */ protected function redirectToRenderNode(NodeInterface $node, $typoScriptPath) { $q = new FlowQuery(array($node)); $closestContentCollection = $q->closest('[instanceof TYPO3.Neos:ContentCollection]')->get(0); $closestDocumentNode = $q->closest('[instanceof TYPO3.Neos:Document]')->get(0); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', ['node' => $closestDocumentNode, '__nodeContextPath' => $closestContentCollection->getContextPath(), '__affectedNodeContextPath' => $node->getContextPath(), '__typoScriptPath' => $typoScriptPath], 0, 303, 'html'); }
/** * This method generates the Uri through the joinPoint with * temporary overriding the used node * * @param ActionRequest $request * @param JoinPointInterface $joinPoint The current join point * @param NodeInterface $node * @return string $uri */ public function generateUriForNode(ActionRequest $request, JoinPointInterface $joinPoint, NodeInterface $node) { // store original node path to restore it after generating the uri $originalNodePath = $request->getMainRequest()->getArgument('node'); // generate the uri for the given node $request->getMainRequest()->setArgument('node', $node->getContextPath()); $result = $joinPoint->getAdviceChain()->proceed($joinPoint); // restore the original node path $request->getMainRequest()->setArgument('node', $originalNodePath); return $result; }
/** * 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)'); }
protected function renderNodeToList(&$nodes, NodeInterface $node, ControllerContext $controllerContext) { $nodes[$node->getContextPath()] = $this->renderNode($node, $controllerContext); }
/** * Set the "context node" this operation was working on. * * @param NodeInterface $node * @return void */ public function setNode(NodeInterface $node) { $this->nodeIdentifier = $node->getIdentifier(); $this->workspaceName = $node->getContext()->getWorkspaceName(); $this->dimension = $node->getContext()->getDimensions(); $context = $node->getContext(); if ($context instanceof ContentContext && $context->getCurrentSite() !== null) { $siteIdentifier = $this->persistenceManager->getIdentifierByObject($context->getCurrentSite()); } else { $siteIdentifier = null; } $this->data = Arrays::arrayMergeRecursiveOverrule($this->data, array('nodeContextPath' => $node->getContextPath(), 'nodeLabel' => $node->getLabel(), 'nodeType' => $node->getNodeType()->getName(), 'site' => $siteIdentifier)); $node = self::getClosestAggregateNode($node); if ($node !== null) { $this->documentNodeIdentifier = $node->getIdentifier(); $this->data = Arrays::arrayMergeRecursiveOverrule($this->data, array('documentNodeContextPath' => $node->getContextPath(), 'documentNodeLabel' => $node->getLabel(), 'documentNodeType' => $node->getNodeType()->getName())); } }
/** * Wrap the $content identified by $node with the needed markup for the backend. * * @param NodeInterface $node * @param string $typoScriptPath * @param string $content * @param boolean $renderCurrentDocumentMetadata When this flag is set we will render the global metadata for the current document * @return string */ public function wrapContentObject(NodeInterface $node, $typoScriptPath, $content, $renderCurrentDocumentMetadata = false) { /** @var $contentContext ContentContext */ $contentContext = $node->getContext(); if ($contentContext->getWorkspaceName() === 'live' || !$this->privilegeManager->isPrivilegeTargetGranted('TYPO3.Neos:Backend.GeneralAccess')) { return $content; } $nodeType = $node->getNodeType(); $attributes = array(); $attributes['typeof'] = 'typo3:' . $nodeType->getName(); $attributes['about'] = $node->getContextPath(); $classNames = array(); if ($renderCurrentDocumentMetadata === true) { $attributes['data-neos-site-name'] = $contentContext->getCurrentSite()->getName(); $attributes['data-neos-site-node-context-path'] = $contentContext->getCurrentSiteNode()->getContextPath(); // Add the workspace of the TYPO3CR context to the attributes $attributes['data-neos-context-workspace-name'] = $contentContext->getWorkspaceName(); $attributes['data-neos-context-dimensions'] = json_encode($contentContext->getDimensions()); if (!$this->nodeAuthorizationService->isGrantedToEditNode($node)) { $attributes['data-node-__read-only'] = 'true'; $attributes['data-nodedatatype-__read-only'] = 'boolean'; } } else { if (!$this->nodeAuthorizationService->isGrantedToEditNode($node)) { return $content; } if ($node->isRemoved()) { $classNames[] = 'neos-contentelement-removed'; } if ($node->isHidden()) { $classNames[] = 'neos-contentelement-hidden'; } if ($nodeType->isOfType('TYPO3.Neos:ContentCollection')) { $attributes['rel'] = 'typo3:content-collection'; // This is needed since the backend relies on this class (should not be necessary) $classNames[] = 'neos-contentcollection'; } else { $classNames[] = 'neos-contentelement'; } $uiConfiguration = $nodeType->hasConfiguration('ui') ? $nodeType->getConfiguration('ui') : array(); if (isset($uiConfiguration['inlineEditable']) && $uiConfiguration['inlineEditable'] !== true || !isset($uiConfiguration['inlineEditable']) && !$this->hasInlineEditableProperties($node)) { $classNames[] = 'neos-not-inline-editable'; } $attributes['tabindex'] = 0; } if ($node instanceof Node && !$node->dimensionsAreMatchingTargetDimensionValues()) { $classNames[] = 'neos-contentelement-shine-through'; } if (count($classNames) > 0) { $attributes['class'] = implode(' ', $classNames); } // Add the actual workspace of the node, the node identifier and the TypoScript path to the attributes $attributes['data-node-_identifier'] = $node->getIdentifier(); $attributes['data-node-__workspace-name'] = $node->getWorkspace()->getName(); $attributes['data-node-__typoscript-path'] = $typoScriptPath; // these properties are needed together with the current NodeType to evaluate Node Type Constraints // TODO: this can probably be greatly cleaned up once we do not use CreateJS or VIE anymore. if ($node->getParent()) { $attributes['data-node-__parent-node-type'] = $node->getParent()->getNodeType()->getName(); } if ($node->isAutoCreated()) { $attributes['data-node-_name'] = $node->getName(); $attributes['data-node-_is-autocreated'] = 'true'; } if ($node->getParent() && $node->getParent()->isAutoCreated()) { $attributes['data-node-_parent-is-autocreated'] = 'true'; // we shall only add these properties if the parent is actually auto-created; as the Node-Type-Switcher in the UI relies on that. $attributes['data-node-__parent-node-name'] = $node->getParent()->getName(); $attributes['data-node-__grandparent-node-type'] = $node->getParent()->getParent()->getNodeType()->getName(); } $attributes = $this->addNodePropertyAttributes($node, $attributes); return $this->htmlAugmenter->addAttributes($content, $attributes, 'div', array('typeof')); }
/** * * * @param NodeInterface $movedNode * @param NodeInterface $referenceNode * @param integer $moveOperation */ public function beforeNodeMove(NodeInterface $movedNode, NodeInterface $referenceNode, $moveOperation) { if (!$this->eventEmittingService->isEnabled()) { return; } $this->currentlyMoving += 1; /* @var $nodeEvent NodeEvent */ $nodeEvent = $this->eventEmittingService->emit(self::NODE_MOVE, array('referenceNode' => $referenceNode->getContextPath(), 'moveOperation' => $moveOperation), 'TYPO3\\Neos\\EventLog\\Domain\\Model\\NodeEvent'); $nodeEvent->setNode($movedNode); $this->eventEmittingService->pushContext(); }
/** * Collects metadata attributes used to allow editing of the node in the Neos backend. * * @param array $attributes * @param NodeInterface $node * @return array */ protected function addGenericEditingMetadata(array $attributes, NodeInterface $node) { $attributes['typeof'] = 'typo3:' . $node->getNodeType()->getName(); $attributes['about'] = $node->getContextPath(); $attributes['data-node-_identifier'] = $node->getIdentifier(); $attributes['data-node-__workspace-name'] = $node->getWorkspace()->getName(); $attributes['data-node-__label'] = $node->getLabel(); if ($node->getNodeType()->isOfType('TYPO3.Neos:ContentCollection')) { $attributes['rel'] = 'typo3:content-collection'; } // these properties are needed together with the current NodeType to evaluate Node Type Constraints // TODO: this can probably be greatly cleaned up once we do not use CreateJS or VIE anymore. if ($node->getParent()) { $attributes['data-node-__parent-node-type'] = $node->getParent()->getNodeType()->getName(); } if ($node->isAutoCreated()) { $attributes['data-node-_name'] = $node->getName(); $attributes['data-node-_is-autocreated'] = 'true'; } if ($node->getParent() && $node->getParent()->isAutoCreated()) { $attributes['data-node-_parent-is-autocreated'] = 'true'; // we shall only add these properties if the parent is actually auto-created; as the Node-Type-Switcher in the UI relies on that. $attributes['data-node-__parent-node-name'] = $node->getParent()->getName(); $attributes['data-node-__grandparent-node-type'] = $node->getParent()->getParent()->getNodeType()->getName(); } return $attributes; }
/** * Move $node before, into or after $targetNode * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $targetNode * @param string $position where the node should be added (allowed: before, into, after) * @return void * @throws \TYPO3\TYPO3CR\Exception\NodeException * @ExtDirect */ public function moveAction(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, \TYPO3\TYPO3CR\Domain\Model\NodeInterface $targetNode, $position) { if (!in_array($position, array('before', 'into', 'after'), TRUE)) { throw new \TYPO3\TYPO3CR\Exception\NodeException('The position should be one of the following: "before", "into", "after".', 1296132542); } switch ($position) { case 'before': $node->moveBefore($targetNode); break; case 'into': $node->moveInto($targetNode); break; case 'after': $node->moveAfter($targetNode); } $nextUri = $this->uriBuilder->reset()->setFormat('html')->setCreateAbsoluteUri(TRUE)->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.TYPO3', ''); $this->view->assign('value', array('data' => array('nextUri' => $nextUri, 'newNodePath' => $node->getContextPath()), 'success' => TRUE)); }
/** * 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 * @param boolean $expand * @param array $children * @param boolean $hasChildNodes * @param boolean $matched * @return array */ public function collectTreeNodeData(NodeInterface $node, $expand = TRUE, array $children = array(), $hasChildNodes = FALSE, $matched = FALSE) { $isTimedPage = FALSE; $now = new \DateTime(); $now = $now->getTimestamp(); $hiddenBeforeDateTime = $node->getHiddenBeforeDateTime(); $hiddenAfterDateTime = $node->getHiddenAfterDateTime(); if ($hiddenBeforeDateTime !== NULL && $hiddenBeforeDateTime->getTimestamp() > $now) { $isTimedPage = TRUE; } if ($hiddenAfterDateTime !== NULL) { $isTimedPage = TRUE; } $classes = array(); if ($isTimedPage === TRUE && $node->isHidden() === FALSE) { array_push($classes, 'neos-timedVisibility'); } if ($node->isHidden() === TRUE) { array_push($classes, 'neos-hidden'); } if ($node->isHiddenInIndex() === TRUE) { array_push($classes, 'neos-hiddenInIndex'); } if ($matched) { array_push($classes, 'neos-matched'); } $uriBuilder = $this->controllerContext->getUriBuilder(); $nodeType = $node->getNodeType(); $nodeTypeConfiguration = $nodeType->getFullConfiguration(); if ($node->getNodeType()->isOfType('TYPO3.Neos:Document')) { $uriForNode = $uriBuilder->reset()->setFormat('html')->setCreateAbsoluteUri(TRUE)->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.Neos'); } else { $uriForNode = '#'; } $label = $node->getLabel(); $nodeTypeLabel = $node->getNodeType()->getLabel(); $treeNode = array('key' => $node->getContextPath(), 'title' => $label, 'fullTitle' => $node->getProperty('title'), 'nodeTypeLabel' => $nodeTypeLabel, 'tooltip' => '', 'href' => $uriForNode, 'isFolder' => $hasChildNodes, 'isLazy' => $hasChildNodes && !$expand, 'nodeType' => $nodeType->getName(), 'isAutoCreated' => $node->isAutoCreated(), 'expand' => $expand, 'addClass' => implode(' ', $classes), 'name' => $node->getName(), 'iconClass' => isset($nodeTypeConfiguration['ui']) && isset($nodeTypeConfiguration['ui']['icon']) ? $nodeTypeConfiguration['ui']['icon'] : '', 'isHidden' => $node->isHidden()); if ($hasChildNodes) { $treeNode['children'] = $children; } return $treeNode; }
/** * @param NodeInterface $source The node instance * @param string $targetType not used * @param array $subProperties not used * @param PropertyMappingConfigurationInterface $configuration * @return string The node context path */ public function convertFrom($source, $targetType = null, array $subProperties = array(), PropertyMappingConfigurationInterface $configuration = null) { return $source->getContextPath(); }
/** * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param boolean $expand * @param array $children * @param string $contentTypeFilter * @return array */ public function collectTreeNodeData(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, $expand = TRUE, array $children = array()) { $contentType = $node->getContentType()->getName(); $classes = array(strtolower(str_replace(array('.', ':'), array('_', '-'), $contentType))); if ($node->isHidden() === TRUE) { array_push($classes, 'hidden'); } if ($node->isHiddenInIndex() === TRUE) { array_push($classes, 'hiddenInIndex'); } $uriBuilder = $this->controllerContext->getUriBuilder(); $hasChildNodes = $children !== array() ? TRUE : FALSE; $contentType = $node->getContentType()->getName(); $treeNode = array('key' => $node->getContextPath(), 'title' => $node->getContentType() === 'TYPO3.Phoenix.ContentTypes:Page' ? $node->getProperty('title') : $node->getLabel(), 'href' => $uriBuilder->reset()->setFormat('html')->setCreateAbsoluteUri(TRUE)->uriFor('show', array('node' => $node), 'Frontend\\Node', 'TYPO3.TYPO3', ''), 'isFolder' => $hasChildNodes, 'isLazy' => $hasChildNodes && !$expand, 'contentType' => $contentType, 'expand' => $expand, 'addClass' => implode(' ', $classes), 'name' => $node->getName()); if ($hasChildNodes) { $treeNode['children'] = $children; } return $treeNode; }
/** * Serialize node and all child nodes * * @param NodeInterface $node * @param array $list * @return array */ public function serializeNodeRecursively(NodeInterface $node, ControllerContext $controllerContext) { $result = [$node->getContextPath() => $this->nodeInfoHelper->renderNode($node, $controllerContext)]; foreach ($node->getChildNodes() as $childNode) { $result = array_merge($result, $this->serializeNodeRecursively($childNode, $controllerContext)); } return $result; }
/** * Wrap the $content identified by $node with the needed markup for * the backend. * $parameters can be used to further pass parameters to the content element. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param string $typoscriptPath * @param string $content * @param boolean $isPage * @return string */ public function wrapContentObject(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, $typoscriptPath, $content, $isPage = FALSE) { $contentType = $node->getContentType(); $tagBuilder = new \TYPO3\Fluid\Core\ViewHelper\TagBuilder('div'); $tagBuilder->forceClosingTag(TRUE); if (!$node->isRemoved()) { $tagBuilder->setContent($content); } if (!$isPage) { $cssClasses = array('t3-contentelement'); $cssClasses[] = str_replace(array(':', '.'), '-', strtolower($contentType->getName())); if ($node->isHidden()) { $cssClasses[] = 't3-contentelement-hidden'; } if ($node->isRemoved()) { $cssClasses[] = 't3-contentelement-removed'; } $tagBuilder->addAttribute('class', implode(' ', $cssClasses)); $tagBuilder->addAttribute('id', 'c' . $node->getIdentifier()); } try { $this->accessDecisionManager->decideOnResource('TYPO3_TYPO3_Backend_BackendController'); } catch (\TYPO3\FLOW3\Security\Exception\AccessDeniedException $e) { return $tagBuilder->render(); } $tagBuilder->addAttribute('typeof', 'typo3:' . $contentType->getName()); $tagBuilder->addAttribute('about', $node->getContextPath()); $this->addScriptTag($tagBuilder, '__workspacename', $node->getWorkspace()->getName()); $this->addScriptTag($tagBuilder, '_removed', $node->isRemoved() ? 'true' : 'false', 'boolean'); $this->addScriptTag($tagBuilder, '_typoscriptPath', $typoscriptPath); foreach ($contentType->getProperties() as $propertyName => $propertyConfiguration) { $dataType = isset($propertyConfiguration['type']) ? $propertyConfiguration['type'] : 'string'; if ($propertyName[0] === '_') { $propertyValue = \TYPO3\FLOW3\Reflection\ObjectAccess::getProperty($node, substr($propertyName, 1)); } else { $propertyValue = $node->getProperty($propertyName); } // Serialize boolean values to String if (isset($propertyConfiguration['type']) && $propertyConfiguration['type'] === 'boolean') { $propertyValue = $propertyValue ? 'true' : 'false'; } // Serialize date values to String if ($propertyValue !== NULL && isset($propertyConfiguration['type']) && $propertyConfiguration['type'] === 'date') { $propertyValue = $propertyValue->format('Y-m-d'); } // Serialize objects to JSON strings if (is_object($propertyValue) && $propertyValue !== NULL && isset($propertyConfiguration['type']) && $this->objectManager->isRegistered($propertyConfiguration['type'])) { $gettableProperties = \TYPO3\FLOW3\Reflection\ObjectAccess::getGettableProperties($propertyValue); $convertedProperties = array(); foreach ($gettableProperties as $key => $value) { if (is_object($value)) { $entityIdentifier = $this->persistenceManager->getIdentifierByObject($value); if ($entityIdentifier !== NULL) { $value = $entityIdentifier; } } $convertedProperties[$key] = $value; } $propertyValue = json_encode($convertedProperties); $dataType = 'jsonEncoded'; } $this->addScriptTag($tagBuilder, $propertyName, $propertyValue, $dataType); } if (!$isPage) { // add CSS classes $this->addScriptTag($tagBuilder, '__contenttype', $contentType->getName()); } else { $tagBuilder->addAttribute('id', 't3-page-metainformation'); $tagBuilder->addAttribute('data-__sitename', $this->nodeRepository->getContext()->getCurrentSite()->getName()); $tagBuilder->addAttribute('data-__siteroot', sprintf('/sites/%s@%s', $this->nodeRepository->getContext()->getCurrentSite()->getNodeName(), $this->nodeRepository->getContext()->getWorkspace()->getName())); } return $tagBuilder->render(); }