Exemple #1
0
 /**
  * Get the root node
  *
  * @return NodeInterface
  */
 public function getRoot()
 {
     if (!$this->root) {
         $this->root = $this->active->getContext()->getCurrentSiteNode();
     }
     return $this->root;
 }
 /**
  * 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());
     }
 }
 /**
  * @param NodeInterface $contextNode
  * @return QueryBuilder
  */
 public function query(NodeInterface $contextNode)
 {
     $this->where[] = "(__parentPath LIKE '%#" . $contextNode->getPath() . "#%' OR __path LIKE '" . $contextNode->getPath() . "')";
     $this->where[] = "(__workspace LIKE '%#" . $contextNode->getContext()->getWorkspace()->getName() . "#%')";
     $this->where[] = "(__dimensionshash LIKE '%#" . md5(json_encode($contextNode->getContext()->getDimensions())) . "#%')";
     $this->contextNode = $contextNode;
     return $this;
 }
 /**
  * Generates a URI path segment for a given node taking it's language dimension into account
  *
  * @param NodeInterface $node Optional node to determine language dimension
  * @param string $text Optional text
  * @return string
  */
 public function generateUriPathSegment(NodeInterface $node = null, $text = null)
 {
     if ($node) {
         $text = $text ?: $node->getLabel() ?: $node->getName();
         $dimensions = $node->getContext()->getDimensions();
         if (array_key_exists('language', $dimensions) && $dimensions['language'] !== array()) {
             $locale = new Locale($dimensions['language'][0]);
             $language = $locale->getLanguage();
         }
     } elseif (strlen($text) === 0) {
         throw new Exception('Given text was empty.', 1457591815);
     }
     $text = $this->transliterationService->transliterate($text, isset($language) ? $language : null);
     return Transliterator::urlize($text);
 }
 /**
  * Wrap the $content identified by $node with the needed markup for the backend.
  *
  * @param NodeInterface $node
  * @param string $property
  * @param string $content
  * @return string
  */
 public function wrapContentProperty(NodeInterface $node, $property, $content)
 {
     /** @var $contentContext ContentContext */
     $contentContext = $node->getContext();
     if ($contentContext->getWorkspaceName() === 'live' || !$this->privilegeManager->isPrivilegeTargetGranted('TYPO3.Neos:Backend.GeneralAccess')) {
         return $content;
     }
     if (!$this->nodeAuthorizationService->isGrantedToEditNode($node)) {
         return $content;
     }
     $attributes = array();
     $attributes['class'] = 'neos-inline-editable';
     $attributes['property'] = 'typo3:' . $property;
     $attributes['data-neos-node-type'] = $node->getNodeType()->getName();
     return $this->htmlAugmenter->addAttributes($content, $attributes, 'span');
 }
 /**
  * {@inheritdoc}
  *
  * @param FlowQuery $flowQuery the FlowQuery object
  * @param array $arguments the arguments for this operation
  * @return mixed|null if the operation is final, the return value
  */
 public function evaluate(FlowQuery $flowQuery, array $arguments)
 {
     $imagePropertyName = $arguments[0];
     if ($this->contextNode->hasProperty($imagePropertyName)) {
         $image = $this->contextNode->getProperty($imagePropertyName);
         if ($image instanceof ImageVariant) {
             $image = $image->getOriginalAsset();
         }
         if ($image instanceof Image) {
             $identifier = $image->getIdentifier();
             $nodeData = $this->metaDataRepository->findOneByAssetIdentifier($identifier, $this->contextNode->getContext()->getWorkspace());
             if ($nodeData instanceof NodeData) {
                 return $this->nodeFactory->createFromNodeData($nodeData, $this->contextNode->getContext());
             }
         }
     }
     return null;
 }
 /**
  * Matches if the currently-selected preset in the passed $dimensionName is one of $presets.
  *
  * Example: isInDimensionPreset('language', 'de') checks whether the currently-selected language
  * preset (in the Neos backend) is "de".
  *
  * Implementation Note: We deliberately work on the Dimension Preset Name, and not on the
  * dimension values itself; as the preset is user-visible and the actual dimension-values
  * for a preset are just implementation details.
  *
  * @param string $dimensionName
  * @param string|array $presets
  * @return boolean
  */
 public function isInDimensionPreset($dimensionName, $presets)
 {
     if ($this->node === NULL) {
         return TRUE;
     }
     $dimensionValues = $this->node->getContext()->getDimensions();
     if (!isset($dimensionValues[$dimensionName])) {
         return FALSE;
     }
     $preset = $this->contentDimensionPresetSource->findPresetByDimensionValues($dimensionName, $dimensionValues[$dimensionName]);
     if ($preset === NULL) {
         return FALSE;
     }
     $presetIdentifier = $preset['identifier'];
     if (!is_array($presets)) {
         $presets = array($presets);
     }
     return in_array($presetIdentifier, $presets);
 }
 /**
  * Creates missing child nodes for the given node.
  *
  * @param NodeInterface $node
  * @return void
  */
 public function createChildNodes(NodeInterface $node)
 {
     $nodeType = $node->getNodeType();
     foreach ($nodeType->getAutoCreatedChildNodes() as $childNodeName => $childNodeType) {
         try {
             $node->createNode($childNodeName, $childNodeType);
         } catch (NodeExistsException $exception) {
             // If you have a node that has been marked as removed, but is needed again
             // the old node is recovered
             $childNodePath = NodePaths::addNodePathSegment($node->getPath(), $childNodeName);
             $contextProperties = $node->getContext()->getProperties();
             $contextProperties['removedContentShown'] = true;
             $context = $this->contextFactory->create($contextProperties);
             $childNode = $context->getNode($childNodePath);
             if ($childNode->isRemoved()) {
                 $childNode->setRemoved(false);
             }
         }
     }
 }
 /**
  * Returns the rendered content of this plugin
  *
  * @return string The rendered content as a string
  * @throws StopActionException
  */
 public function evaluate()
 {
     $currentContext = $this->tsRuntime->getCurrentContext();
     $this->pluginViewNode = $currentContext['node'];
     /** @var $parentResponse Response */
     $parentResponse = $this->tsRuntime->getControllerContext()->getResponse();
     $pluginResponse = new Response($parentResponse);
     $pluginRequest = $this->buildPluginRequest();
     if ($pluginRequest->getControllerObjectName() === '') {
         $message = 'Master View not selected';
         if ($this->pluginViewNode->getProperty('plugin')) {
             $message = 'Plugin View not selected';
         }
         if ($this->pluginViewNode->getProperty('view')) {
             $message = 'Master View or Plugin View not found';
         }
         return $this->pluginViewNode->getContext()->getWorkspaceName() !== 'live' || $this->objectManager->getContext()->isDevelopment() ? '<p>' . $message . '</p>' : '<!-- ' . $message . '-->';
     }
     $this->dispatcher->dispatch($pluginRequest, $pluginResponse);
     return $pluginResponse->getContent();
 }
 /**
  * Shows the specified node and takes visibility and access restrictions into
  * account.
  *
  * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node
  * @return string View output for the specified node
  */
 public function showAction(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node)
 {
     if ($node->getContext()->getWorkspace()->getName() !== 'live') {
         // TODO: Introduce check if workspace is visible or accessible to the user
         try {
             $this->accessDecisionManager->decideOnResource('TYPO3_TYPO3_Backend_BackendController');
             $this->nodeRepository->getContext()->setInvisibleContentShown(TRUE);
             $this->nodeRepository->getContext()->setRemovedContentShown(TRUE);
         } catch (\TYPO3\FLOW3\Security\Exception\AccessDeniedException $exception) {
             $this->throwStatus(403);
         }
     }
     if ($this->isWireframeModeEnabled($node)) {
         $this->forward('showWireframe', NULL, NULL, array('node' => $node->getPath()));
     }
     if (!$node->isAccessible()) {
         try {
             $this->authenticationManager->authenticate();
         } catch (\Exception $exception) {
         }
     }
     if (!$node->isAccessible() && !$this->nodeRepository->getContext()->isInaccessibleContentShown()) {
         $this->throwStatus(403);
     }
     if (!$node->isVisible() && !$this->nodeRepository->getContext()->isInvisibleContentShown()) {
         $this->throwStatus(404);
     }
     if ($node->getContentType()->isOfType('TYPO3.Phoenix.ContentTypes:Shortcut')) {
         while ($node->getContentType()->isOfType('TYPO3.Phoenix.ContentTypes:Shortcut')) {
             $childNodes = $node->getChildNodes('TYPO3.Phoenix.ContentTypes:Page,TYPO3.Phoenix.ContentTypes:Shortcut');
             $node = current($childNodes);
         }
         $this->redirect('show', NULL, NULL, array('node' => $node));
     }
     $this->nodeRepository->getContext()->setCurrentNode($node);
     $this->view->assign('value', $node);
     $this->response->setHeader('Cache-Control', 'public, s-maxage=600', FALSE);
 }
 /**
  * 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()));
     }
 }
 /**
  * Returns a merged TypoScript object tree in the context of the given nodes
  *
  * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $startNode Node marking the starting point
  * @return array The merged object tree as of the given node
  * @throws \TYPO3\Neos\Domain\Exception
  */
 public function getMergedTypoScriptObjectTree(NodeInterface $startNode)
 {
     $contentContext = $startNode->getContext();
     $siteResourcesPackageKey = $contentContext->getCurrentSite()->getSiteResourcesPackageKey();
     $siteRootTypoScriptPathAndFilename = sprintf($this->siteRootTypoScriptPattern, $siteResourcesPackageKey);
     $siteRootTypoScriptCode = $this->readExternalTypoScriptFile($siteRootTypoScriptPathAndFilename);
     if ($siteRootTypoScriptCode === '') {
         $siteRootTypoScriptPathAndFilename = sprintf($this->legacySiteRootTypoScriptPattern, $siteResourcesPackageKey);
         $siteRootTypoScriptCode = $this->readExternalTypoScriptFile($siteRootTypoScriptPathAndFilename);
     }
     $mergedTypoScriptCode = '';
     $mergedTypoScriptCode .= $this->generateNodeTypeDefinitions();
     $mergedTypoScriptCode .= $this->getTypoScriptIncludes($this->prepareAutoIncludeTypoScript());
     $mergedTypoScriptCode .= $this->getTypoScriptIncludes($this->prependTypoScriptIncludes);
     $mergedTypoScriptCode .= $siteRootTypoScriptCode;
     $mergedTypoScriptCode .= $this->getTypoScriptIncludes($this->appendTypoScriptIncludes);
     return $this->typoScriptParser->parse($mergedTypoScriptCode, $siteRootTypoScriptPathAndFilename);
 }
 /**
  * Create a recursive copy of this node below $referenceNode with $nodeName.
  *
  * $detachedCopy only has an influence if we are copying from one dimension to the other, possibly creating a new
  * node variant:
  *
  * - If $detachedCopy is TRUE, the whole (recursive) copy is done without connecting original and copied node,
  *   so NOT CREATING a new node variant.
  * - If $detachedCopy is FALSE, and the node does not yet have a variant in the target dimension, we are CREATING
  *   a new node variant.
  *
  * As a caller of this method, $detachedCopy should be TRUE if $this->getNodeType()->isAggregate() is TRUE, and FALSE
  * otherwise.
  *
  * @param NodeInterface $referenceNode
  * @param boolean $detachedCopy
  * @param string $nodeName
  * @return NodeInterface
  */
 protected function createRecursiveCopy(NodeInterface $referenceNode, $nodeName, $detachedCopy)
 {
     $identifier = null;
     $referenceNodeDimensions = $referenceNode->getDimensions();
     $referenceNodeDimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($referenceNodeDimensions);
     $thisDimensions = $this->getDimensions();
     $thisNodeDimensionsHash = Utility::sortDimensionValueArrayAndReturnDimensionsHash($thisDimensions);
     if ($detachedCopy === false && $referenceNodeDimensionsHash !== $thisNodeDimensionsHash && $referenceNode->getContext()->getNodeByIdentifier($this->getIdentifier()) === null) {
         // If the target dimensions are different than this one, and there is no node shadowing this one in the target dimension yet, we use the same
         // node identifier, effectively creating a new node variant.
         $identifier = $this->getIdentifier();
     }
     $copiedNode = $referenceNode->createSingleNode($nodeName, null, $identifier);
     $copiedNode->similarize($this, true);
     /** @var $childNode Node */
     foreach ($this->getChildNodes() as $childNode) {
         // Prevent recursive copy when copying into itself
         if ($childNode->getIdentifier() !== $copiedNode->getIdentifier()) {
             $childNode->copyIntoInternal($copiedNode, $childNode->getName(), $detachedCopy);
         }
     }
     return $copiedNode;
 }
 /**
  * Sets the starting point for this query. Search result should only contain nodes that
  * match the context of the given node and have it as parent node in their rootline.
  *
  * @param NodeInterface $contextNode
  * @return QueryBuilderInterface
  * @api
  */
 public function query(NodeInterface $contextNode)
 {
     // on indexing, the __parentPath is tokenized to contain ALL parent path parts,
     // e.g. /foo, /foo/bar/, /foo/bar/baz; to speed up matching.. That's why we use a simple "term" filter here.
     // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-term-filter.html
     $this->queryFilter('term', ['__parentPath' => $contextNode->getPath()]);
     //
     // http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-terms-filter.html
     $this->queryFilter('terms', ['__workspace' => array_unique(['live', $contextNode->getContext()->getWorkspace()->getName()])]);
     // match exact dimension values for each dimension, this works because the indexing flattens the node variants for all dimension preset combinations
     $dimensionCombinations = $contextNode->getContext()->getDimensions();
     if (is_array($dimensionCombinations)) {
         $this->queryFilter('term', ['__dimensionCombinationHash' => md5(json_encode($dimensionCombinations))]);
     }
     $this->contextNode = $contextNode;
     return $this;
 }
 /**
  * @param NodeInterface $node
  * @param boolean $renderCurrentDocumentMetadata
  * @return boolean
  */
 protected function needsMetadata(NodeInterface $node, $renderCurrentDocumentMetadata)
 {
     /** @var $contentContext ContentContext */
     $contentContext = $node->getContext();
     return $contentContext->isInBackend() === true && ($renderCurrentDocumentMetadata === true || $this->nodeAuthorizationService->isGrantedToEditNode($node) === 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)');
 }
 /**
  * Retrieves the given node's corresponding node in the base workspace (that is, which would be overwritten if the
  * given node would be published)
  *
  * @param NodeInterface $modifiedNode
  * @return NodeInterface
  */
 protected function getOriginalNode(NodeInterface $modifiedNode)
 {
     $baseWorkspaceName = $modifiedNode->getWorkspace()->getBaseWorkspace()->getName();
     $contextProperties = $modifiedNode->getContext()->getProperties();
     $contextProperties['workspaceName'] = $baseWorkspaceName;
     $contentContext = $this->contextFactory->create($contextProperties);
     return $contentContext->getNodeByIdentifier($modifiedNode->getIdentifier());
 }
 /**
  * Fetch all master plugins that are available in the current
  * workspace.
  *
  * @param NodeInterface $node
  * @return string JSON encoded array of node path => label
  */
 public function masterPluginsAction(NodeInterface $node)
 {
     $this->response->setHeader('Content-Type', 'application/json');
     $pluginNodes = $this->pluginService->getPluginNodesWithViewDefinitions($node->getContext());
     $masterPlugins = array();
     if (is_array($pluginNodes)) {
         /** @var $pluginNode NodeInterface */
         foreach ($pluginNodes as $pluginNode) {
             if ($pluginNode->isRemoved()) {
                 continue;
             }
             $q = new FlowQuery(array($pluginNode));
             $page = $q->closest('[instanceof TYPO3.Neos:Document]')->get(0);
             if ($page === NULL) {
                 continue;
             }
             $masterPlugins[$pluginNode->getPath()] = sprintf('"%s" on page "%s"', $pluginNode->getNodeType()->getLabel(), $page->getLabel());
         }
     }
     return json_encode((object) $masterPlugins);
 }
 /**
  * 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;
 }
 /**
  * Creates a mock sub node of the given parent node
  *
  * @param NodeInterface $mockParentNode
  * @param string $nodeName
  * @return NodeInterface
  */
 protected function buildSubNode($mockParentNode, $nodeName, $nodeTypeName = 'TYPO3.Neos:Document')
 {
     $mockNode = $this->buildNode($mockParentNode->getContext(), $nodeName, $nodeTypeName);
     $mockNode->mockParentNode = $mockParentNode;
     $mockParentNode->mockChildNodes[$nodeName] = $mockNode;
     $mockNode->expects($this->any())->method('getChildNodes')->will($this->returnCallback(function ($nodeTypeFilter) use($mockNode) {
         return $mockNode->mockChildNodes;
     }));
     return $mockNode;
 }
 /**
  * Renders the URI to a given node instance or -path.
  *
  * @param ControllerContext $controllerContext
  * @param mixed $node A node object or a string node path, if a relative path is provided the baseNode argument is required
  * @param NodeInterface $baseNode
  * @param string $format Format to use for the URL, for example "html" or "json"
  * @param boolean $absolute If set, an absolute URI is rendered
  * @param array $arguments Additional arguments to be passed to the UriBuilder (for example pagination parameters)
  * @param string $section
  * @param boolean $addQueryString If set, the current query parameters will be kept in the URI
  * @param array $argumentsToBeExcludedFromQueryString arguments to be removed from the URI. Only active if $addQueryString = TRUE
  * @param boolean $resolveShortcuts INTERNAL Parameter - if FALSE, shortcuts are not redirected to their target. Only needed on rare backend occasions when we want to link to the shortcut itself.
  * @return string The rendered URI
  * @throws \InvalidArgumentException if the given node/baseNode is not valid
  * @throws NeosException if no URI could be resolved for the given node
  */
 public function createNodeUri(ControllerContext $controllerContext, $node = null, NodeInterface $baseNode = null, $format = null, $absolute = false, array $arguments = array(), $section = '', $addQueryString = false, array $argumentsToBeExcludedFromQueryString = array(), $resolveShortcuts = true)
 {
     $this->lastLinkedNode = null;
     if (!($node instanceof NodeInterface || is_string($node) || $baseNode instanceof NodeInterface)) {
         throw new \InvalidArgumentException('Expected an instance of NodeInterface or a string for the node argument, or alternatively a baseNode argument.', 1373101025);
     }
     if (is_string($node)) {
         $nodeString = $node;
         if ($nodeString === '') {
             throw new NeosException(sprintf('Empty strings can not be resolved to nodes.', $nodeString), 1415709942);
         }
         preg_match(NodeInterface::MATCH_PATTERN_CONTEXTPATH, $nodeString, $matches);
         if (isset($matches['WorkspaceName']) && $matches['WorkspaceName'] !== '') {
             $node = $this->propertyMapper->convert($nodeString, 'TYPO3\\TYPO3CR\\Domain\\Model\\NodeInterface');
         } else {
             if ($baseNode === null) {
                 throw new NeosException('The baseNode argument is required for linking to nodes with a relative path.', 1407879905);
             }
             /** @var ContentContext $contentContext */
             $contentContext = $baseNode->getContext();
             $normalizedPath = $this->nodeService->normalizePath($nodeString, $baseNode->getPath(), $contentContext->getCurrentSiteNode()->getPath());
             $node = $contentContext->getNode($normalizedPath);
         }
         if (!$node instanceof NodeInterface) {
             throw new NeosException(sprintf('The string "%s" could not be resolved to an existing node.', $nodeString), 1415709674);
         }
     } elseif (!$node instanceof NodeInterface) {
         $node = $baseNode;
     }
     if (!$node instanceof NodeInterface) {
         throw new NeosException(sprintf('Node must be an instance of NodeInterface or string, given "%s".', gettype($node)), 1414772029);
     }
     $this->lastLinkedNode = $node;
     if ($resolveShortcuts === true) {
         $resolvedNode = $this->nodeShortcutResolver->resolveShortcutTarget($node);
     } else {
         // this case is only relevant in extremely rare occasions in the Neos Backend, when we want to generate
         // a link towards the *shortcut itself*, and not to its target.
         $resolvedNode = $node;
     }
     if (is_string($resolvedNode)) {
         return $resolvedNode;
     }
     if (!$resolvedNode instanceof NodeInterface) {
         throw new NeosException(sprintf('Could not resolve shortcut target for node "%s"', $node->getPath()), 1414771137);
     }
     /** @var ActionRequest $request */
     $request = $controllerContext->getRequest()->getMainRequest();
     $uriBuilder = clone $controllerContext->getUriBuilder();
     $uriBuilder->setRequest($request);
     $uri = $uriBuilder->reset()->setSection($section)->setCreateAbsoluteUri($absolute)->setArguments($arguments)->setAddQueryString($addQueryString)->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString)->setFormat($format ?: $request->getFormat())->uriFor('show', array('node' => $resolvedNode), 'Frontend\\Node', 'TYPO3.Neos');
     return $uri;
 }
 /**
  * returns a specific view node of an master plugin
  * or NULL if it does not exist
  *
  * @param NodeInterface $node
  * @param string $viewName
  * @return NodeInterface
  */
 public function getPluginViewNodeByMasterPlugin(NodeInterface $node, $viewName)
 {
     /** @var $context ContentContext */
     $context = $node->getContext();
     foreach ($this->getNodes('TYPO3.Neos:PluginView', $context) as $pluginViewNode) {
         /** @var \TYPO3\TYPO3CR\Domain\Model\NodeInterface $pluginViewNode */
         if ($pluginViewNode->isRemoved()) {
             continue;
         }
         if ($pluginViewNode->getProperty('plugin') === $node->getPath() && $pluginViewNode->getProperty('view') === $viewName) {
             return $pluginViewNode;
         }
     }
     return null;
 }
 /**
  * Adds node properties to the given $attributes collection and returns the extended array
  *
  * @param NodeInterface $node
  * @param array $attributes
  * @return array the merged attributes
  */
 public function addNodePropertyAttributes(NodeInterface $node, array $attributes)
 {
     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';
         $dasherizedPropertyName = $this->dasherize($propertyName);
         $attributes['data-node-' . $dasherizedPropertyName] = $this->getNodeProperty($node, $propertyName, $dataType);
         if ($dataType !== 'string') {
             $prefixedDataType = $dataType === 'jsonEncoded' ? 'typo3:jsonEncoded' : 'xsd:' . $dataType;
             $attributes['data-nodedatatype-' . $dasherizedPropertyName] = $prefixedDataType;
         }
     }
     return $attributes;
 }
 /**
  * Rebase the current users personal workspace onto the given $targetWorkspace and then
  * redirects to the $targetNode in the content module.
  *
  * @param NodeInterface $targetNode
  * @param Workspace $targetWorkspace
  * @return void
  */
 public function rebaseAndRedirectAction(NodeInterface $targetNode, Workspace $targetWorkspace)
 {
     $currentAccount = $this->securityContext->getAccount();
     $personalWorkspace = $this->workspaceRepository->findOneByName('user-' . $currentAccount->getAccountIdentifier());
     /** @var Workspace $personalWorkspace */
     if ($this->publishingService->getUnpublishedNodesCount($personalWorkspace) > 0) {
         $message = $this->translator->translateById('workspaces.cantEditBecauseWorkspaceContainsChanges', [], null, null, 'Modules', 'TYPO3.Neos');
         $this->addFlashMessage($message, '', Message::SEVERITY_WARNING, [], 1437833387);
         $this->redirect('show', null, null, ['workspace' => $targetWorkspace]);
     }
     $personalWorkspace->setBaseWorkspace($targetWorkspace);
     $this->workspaceRepository->update($personalWorkspace);
     $contextProperties = $targetNode->getContext()->getProperties();
     $contextProperties['workspaceName'] = $personalWorkspace->getName();
     $context = $this->contextFactory->create($contextProperties);
     $mainRequest = $this->controllerContext->getRequest()->getMainRequest();
     /** @var ActionRequest $mainRequest */
     $this->uriBuilder->setRequest($mainRequest);
     $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', ['node' => $context->getNode($targetNode->getPath())]);
 }
    /**
     * index this node, and add it to the current bulk request.
     *
     * @param NodeInterface $node
     * @param string $targetWorkspaceName In case this is triggered during publishing, a workspace name will be passed in
     * @return void
     * @throws \TYPO3\TYPO3CR\Search\Exception\IndexingException
     */
    public function indexNode(NodeInterface $node, $targetWorkspaceName = NULL)
    {
        $contextPath = $node->getContextPath();
        if ($targetWorkspaceName !== NULL) {
            $contextPath = str_replace($node->getContext()->getWorkspace()->getName(), $targetWorkspaceName, $contextPath);
        }
        $contextPathHash = sha1($contextPath);
        $nodeType = $node->getNodeType();
        $mappingType = $this->getIndex()->findType(NodeTypeMappingBuilder::convertNodeTypeNameToMappingName($nodeType));
        // Remove document with the same contextPathHash but different NodeType, required after NodeType change
        $this->getIndex()->request('DELETE', '/_query', array(), json_encode(['query' => ['bool' => ['must' => ['ids' => ['values' => [$contextPathHash]]], 'must_not' => ['term' => ['_type' => str_replace('.', '/', $node->getNodeType()->getName())]]]]]));
        if ($node->isRemoved()) {
            // TODO: handle deletion from the fulltext index as well
            $mappingType->deleteDocumentById($contextPathHash);
            $this->logger->log(sprintf('NodeIndexer: Removed node %s from index (node flagged as removed). ID: %s', $contextPath, $contextPathHash), LOG_DEBUG, NULL, 'ElasticSearch (CR)');
            return;
        }
        $logger = $this->logger;
        $fulltextIndexOfNode = array();
        $nodePropertiesToBeStoredInIndex = $this->extractPropertiesAndFulltext($node, $fulltextIndexOfNode, function ($propertyName) use($logger, $contextPathHash) {
            $logger->log(sprintf('NodeIndexer (%s) - Property "%s" not indexed because no configuration found.', $contextPathHash, $propertyName), LOG_DEBUG, NULL, 'ElasticSearch (CR)');
        });
        $document = new ElasticSearchDocument($mappingType, $nodePropertiesToBeStoredInIndex, $contextPathHash);
        $documentData = $document->getData();
        if ($targetWorkspaceName !== NULL) {
            $documentData['__workspace'] = $targetWorkspaceName;
        }
        $dimensionCombinations = $node->getContext()->getDimensions();
        if (is_array($dimensionCombinations)) {
            $documentData['__dimensionCombinations'] = $dimensionCombinations;
        }
        if ($this->isFulltextEnabled($node)) {
            if ($this->isFulltextRoot($node)) {
                // for fulltext root documents, we need to preserve the "__fulltext" field. That's why we use the
                // "update" API instead of the "index" API, with a custom script internally; as we
                // shall not delete the "__fulltext" part of the document if it has any.
                $this->currentBulkRequest[] = array(array('update' => array('_type' => $document->getType()->getName(), '_id' => $document->getId())), array('script' => '
							fulltext = (ctx._source.containsKey("__fulltext") ? ctx._source.__fulltext : new LinkedHashMap());
							fulltextParts = (ctx._source.containsKey("__fulltextParts") ? ctx._source.__fulltextParts : new LinkedHashMap());
							ctx._source = newData;
							ctx._source.__fulltext = fulltext;
							ctx._source.__fulltextParts = fulltextParts
						', 'params' => array('newData' => $documentData), 'upsert' => $documentData, 'lang' => 'groovy'));
            } else {
                // non-fulltext-root documents can be indexed as-they-are
                $this->currentBulkRequest[] = array(array('index' => array('_type' => $document->getType()->getName(), '_id' => $document->getId())), $documentData);
            }
            $this->updateFulltext($node, $fulltextIndexOfNode, $targetWorkspaceName);
        }
        $this->logger->log(sprintf('NodeIndexer: Added / updated node %s. ID: %s', $contextPath, $contextPathHash), LOG_DEBUG, NULL, 'ElasticSearch (CR)');
    }
 /**
  * Fetch all master plugins that are available in the current
  * workspace.
  *
  * @param NodeInterface $node
  * @return string JSON encoded array of node path => label
  */
 public function masterPluginsAction(NodeInterface $node)
 {
     $this->response->setHeader('Content-Type', 'application/json');
     $pluginNodes = $this->pluginService->getPluginNodesWithViewDefinitions($node->getContext());
     $masterPlugins = array();
     if (is_array($pluginNodes)) {
         /** @var $pluginNode NodeInterface */
         foreach ($pluginNodes as $pluginNode) {
             if ($pluginNode->isRemoved()) {
                 continue;
             }
             $q = new FlowQuery(array($pluginNode));
             $page = $q->closest('[instanceof TYPO3.Neos:Document]')->get(0);
             if ($page === null) {
                 continue;
             }
             $translationHelper = new TranslationHelper();
             $masterPlugins[$pluginNode->getPath()] = $translationHelper->translate('masterPlugins.nodeTypeOnPageLabel', null, ['nodeTypeName' => $translationHelper->translate($pluginNode->getNodeType()->getLabel()), 'pageLabel' => $page->getLabel()], 'Main', 'TYPO3.Neos');
         }
     }
     return json_encode((object) $masterPlugins);
 }
 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;
 }
 /**
  * Adopts a node from a (possibly) different context to this context
  *
  * Checks if a node variant matching the exact dimensions already exists for this context and
  * return it if found. Otherwise a new node variant for this context is created.
  *
  * In case the node already exists in the context but does not match the target dimensions a
  * new, more specific node is created and returned.
  *
  * @param NodeInterface $node The node with a different context. If the context of the given node is the same as this context the operation will have no effect.
  * @param boolean $recursive If TRUE also adopt all descendant nodes which are non-aggregate
  * @return NodeInterface A new or existing node that matches this context
  */
 public function adoptNode(NodeInterface $node, $recursive = false)
 {
     if ($node->getContext() === $this && $node->dimensionsAreMatchingTargetDimensionValues()) {
         return $node;
     }
     $this->emitBeforeAdoptNode($node, $this, $recursive);
     $existingNode = $this->getNodeByIdentifier($node->getIdentifier());
     if ($existingNode !== null) {
         if ($existingNode->dimensionsAreMatchingTargetDimensionValues()) {
             $adoptedNode = $existingNode;
         } else {
             $adoptedNode = $existingNode->createVariantForContext($this);
         }
     } else {
         $adoptedNode = $node->createVariantForContext($this);
     }
     $this->firstLevelNodeCache->setByIdentifier($adoptedNode->getIdentifier(), $adoptedNode);
     if ($recursive) {
         $childNodes = $node->getChildNodes();
         /** @var NodeInterface $childNode */
         foreach ($childNodes as $childNode) {
             if (!$childNode->getNodeType()->isAggregate()) {
                 $this->adoptNode($childNode, true);
             }
         }
     }
     $this->emitAfterAdoptNode($node, $this, $recursive);
     return $adoptedNode;
 }
 /**
  * Node Level relative to site root node.
  * 0 = Site root node
  *
  * @param NodeInterface $node
  * @return integer
  */
 protected function getNodeLevelInSite(NodeInterface $node)
 {
     $siteNode = $this->currentNode->getContext()->getCurrentSiteNode();
     return $node->getDepth() - $siteNode->getDepth();
 }
 /**
  * Method which does the actual work of discarding, includes a protection against endless recursions and
  * multiple discarding of the same node.
  *
  * @param NodeInterface $node The node to discard
  * @param array &$alreadyDiscardedNodeIdentifiers List of node identifiers which already have been discarded during one discardNode() run
  * @return void
  * @throws \TYPO3\TYPO3CR\Exception\WorkspaceException
  */
 protected function doDiscardNode(NodeInterface $node, array &$alreadyDiscardedNodeIdentifiers = [])
 {
     if ($node->getWorkspace()->getBaseWorkspace() === null) {
         throw new WorkspaceException('Nodes in a in a workspace without a base workspace cannot be discarded.', 1395841899);
     }
     if ($node->getPath() === '/') {
         return;
     }
     if (array_search($node->getIdentifier(), $alreadyDiscardedNodeIdentifiers) !== false) {
         return;
     }
     $alreadyDiscardedNodeIdentifiers[] = $node->getIdentifier();
     $possibleShadowNodeData = $this->nodeDataRepository->findOneByMovedTo($node->getNodeData());
     if ($possibleShadowNodeData instanceof NodeData) {
         if ($possibleShadowNodeData->getMovedTo() !== null) {
             $parentBasePath = $node->getPath();
             $affectedChildNodeDataInSameWorkspace = $this->nodeDataRepository->findByParentAndNodeType($parentBasePath, null, $node->getWorkspace(), null, false, true);
             foreach ($affectedChildNodeDataInSameWorkspace as $affectedChildNodeData) {
                 /** @var NodeData $affectedChildNodeData */
                 $affectedChildNode = $this->nodeFactory->createFromNodeData($affectedChildNodeData, $node->getContext());
                 $this->doDiscardNode($affectedChildNode, $alreadyDiscardedNodeIdentifiers);
             }
         }
         $this->nodeDataRepository->remove($possibleShadowNodeData);
     }
     $this->nodeDataRepository->remove($node);
     $this->emitNodeDiscarded($node);
 }