/** * 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()); } }
/** * 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'])); } }
/** * 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; }
public function isActive(NodeInterface $siteNode) { if ($siteModel = $this->siteRepository->findOneByNodeName($siteNode->getName())) { return $siteModel->isOnline(); } throw new \RuntimeException('Could not find a site for the given site node', 1473366137); }
/** * Creates a new comment * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $postNode The post node which will contain the new comment * @param \TYPO3\TYPO3CR\Domain\Model\NodeTemplate<RobertLemke.Plugin.Blog:Comment> $nodeTemplate * @return void */ public function createAction(NodeInterface $postNode, NodeTemplate $newComment) { # Workaround until we can validate node templates properly: if (strlen($newComment->getProperty('author')) < 2) { $this->addFlashMessage('Your comment was NOT created - please specify your name.'); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', array('node' => $postNode)); } if (strlen($newComment->getProperty('text')) < 5) { $this->addFlashMessage('Your comment was NOT created - it was too short.'); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', array('node' => $postNode)); } if (filter_var($newComment->getProperty('emailAddress'), FILTER_VALIDATE_EMAIL) === FALSE) { $this->addFlashMessage('Your comment was NOT created - you must specify a valid email address.'); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', array('node' => $postNode)); } $commentNode = $postNode->getNode('comments')->createNodeFromTemplate($newComment, uniqid('comment-')); $commentNode->setProperty('spam', FALSE); $commentNode->setProperty('datePublished', new \DateTime()); if ($this->akismetService->isCommentSpam('', $commentNode->getProperty('text'), 'comment', $commentNode->getProperty('author'), $commentNode->getProperty('emailAddress'))) { $commentNode->setProperty('spam', TRUE); } $this->addFlashMessage('Your new comment was created.'); $this->emitCommentCreated($commentNode, $postNode); $this->redirect('show', 'Frontend\\Node', 'TYPO3.Neos', array('node' => $postNode)); }
/** * @param NodeInterface $parentNode * @param NodeType $nodeType * @return NodeInterface|void */ public function create(NodeInterface $parentNode, NodeType $nodeType) { $title = Company::name(); $name = Utility::renderValidNodeName($title); $childrenNode = $parentNode->createNode($name, $nodeType); $childrenNode->setProperty('title', $title); return $childrenNode; }
/** * 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); }
/** * Returns TRUE if the given node has the property and the value is not empty. * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @return boolean */ public function matches(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node) { if ($node->hasProperty($this->propertyName)) { $propertyValue = $node->getProperty($this->propertyName); return !empty($propertyValue); } return FALSE; }
/** * @param NodeInterface $contextNode The node for which the preceding node should be found * @return NodeInterface The following node of $contextNode or NULL */ protected function getNextForNode($contextNode) { $nodesInContext = $contextNode->getParent()->getChildNodes(); for ($i = 1; $i < count($nodesInContext); $i++) { if ($nodesInContext[$i - 1] === $contextNode) { return $nodesInContext[$i]; } } return null; }
/** * @test */ public function overridingFromTypoScriptInFilesystemFollowingNodePathsWorks() { $typoScriptService = $this->objectManager->get('TYPO3\\TYPO3\\Domain\\Service\\TypoScriptService'); ObjectAccess::setProperty($typoScriptService, 'typoScriptsPathPattern', __DIR__ . '/Fixtures/ResourcesFixture/TypoScripts', TRUE); $objectTree = $typoScriptService->getMergedTypoScriptObjectTree($this->homeNode, $this->homeNode->getNode('about-us/history')); $this->assertEquals('Root', $objectTree['text1']['value']); $this->assertEquals('AboutUs', $objectTree['text2']['value']); $this->assertEquals('History', $objectTree['text3']['value']); $this->assertEquals('Additional', $objectTree['text4']['value']); }
/** * 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(); }
/** * @param NodeInterface $contextNode The node for which the preceding node should be found * @return NodeInterface The preceding node of $contextNode or NULL */ protected function getPrevForNode($contextNode) { $nodesInContext = $contextNode->getParent()->getChildNodes(); for ($i = 0; $i < count($nodesInContext) - 1; $i++) { if ($nodesInContext[$i + 1] === $contextNode) { return $nodesInContext[$i]; } } return NULL; }
/** * 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; } }
/** * @param NodeInterface $node A document node * @return string */ public function render(NodeInterface $node) { $output = '/' . $node->getLabel(); $flowQuery = new FlowQuery(array($node)); $nodes = $flowQuery->parents('[instanceof TYPO3.Neos:Document]')->get(); /** @var NodeInterface $node */ foreach ($nodes as $node) { $output = '/' . $node->getLabel() . $output; } return $output; }
/** * Filter a node by the current context. * Will either return the node or NULL if it is not permitted in current context. * * @param NodeInterface $node * @param Context $context * @return \TYPO3\TYPO3CR\Domain\Model\Node|NULL */ protected function filterNodeByContext(NodeInterface $node, Context $context) { if (!$context->isRemovedContentShown() && $node->isRemoved()) { return NULL; } if (!$context->isInvisibleContentShown() && !$node->isVisible()) { return NULL; } if (!$context->isInaccessibleContentShown() && !$node->isAccessible()) { return NULL; } return $node; }
public function setUp() { $this->siteNode = $this->getMock('TYPO3\\TYPO3CR\\Domain\\Model\\NodeInterface'); $this->firstLevelNode = $this->getMock('TYPO3\\TYPO3CR\\Domain\\Model\\NodeInterface'); $this->secondLevelNode = $this->getMock('TYPO3\\TYPO3CR\\Domain\\Model\\NodeInterface'); $this->siteNode->expects($this->any())->method('getPath')->will($this->returnValue('/site')); $this->siteNode->expects($this->any())->method('getChildNodes')->will($this->returnValue(array($this->firstLevelNode))); $this->mockContext = $this->getMockBuilder('TYPO3\\TYPO3CR\\Domain\\Service\\Context')->disableOriginalConstructor()->getMock(); $this->firstLevelNode->expects($this->any())->method('getParent')->will($this->returnValue($this->siteNode)); $this->firstLevelNode->expects($this->any())->method('getPath')->will($this->returnValue('/site/first')); $this->secondLevelNode->expects($this->any())->method('getParent')->will($this->returnValue($this->siteNode)); $this->secondLevelNode->expects($this->any())->method('getPath')->will($this->returnValue('/site/first/second')); }
/** * @param NodeInterface $contextNode The node for which the preceding node should be found * @return NodeInterface The preceding nodes of $contextNode or NULL */ protected function getNextForNode(NodeInterface $contextNode) { $nodesInContext = $contextNode->getParent()->getChildNodes(); $count = count($nodesInContext); for ($i = 0; $i < $count; $i++) { if ($nodesInContext[$i] === $contextNode) { unset($nodesInContext[$i]); return array_values($nodesInContext); } else { unset($nodesInContext[$i]); } } return NULL; }
public function setUp() { $this->convertEmailLinks = $this->getAccessibleMock('Networkteam\\Neos\\MailObfuscator\\Typoscript\\ConvertEmailLinksImplementation', array('getValue'), array(), '', FALSE); $this->mockContext = $this->getMockBuilder('TYPO3\\TYPO3CR\\Domain\\Service\\Context')->disableOriginalConstructor()->getMock(); $this->mockContext->expects($this->any())->method('getWorkspaceName')->will($this->returnValue('live')); $this->mockNode = $this->getMockBuilder('TYPO3\\TYPO3CR\\Domain\\Model\\NodeInterface')->getMock(); $this->mockNode->expects($this->any())->method('getContext')->will($this->returnValue($this->mockContext)); $this->mockTsRuntime = $this->getMockBuilder('TYPO3\\TypoScript\\Core\\Runtime')->disableOriginalConstructor()->getMock(); $this->mockTsRuntime->expects($this->any())->method('getCurrentContext')->will($this->returnValue(array('node' => $this->mockNode))); $this->convertEmailLinks->_set('tsRuntime', $this->mockTsRuntime); $this->convertEmailLinks->_set('linkNameConverter', new \Networkteam\Neos\MailObfuscator\String\Converter\RewriteAtCharConverter()); $this->convertEmailLinks->_set('mailToHrefConverter', new \Networkteam\Neos\MailObfuscator\String\Converter\Mailto2HrefObfuscatingConverter()); srand(10); }
/** * @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; }
/** * @param NodeInterface $contextNode The node for which the preceding node should be found * @return NodeInterface The preceding nodes of $contextNode or NULL */ protected function getPrevForNode(NodeInterface $contextNode) { $nodesInContext = $contextNode->getParent()->getChildNodes(); $count = count($nodesInContext) - 1; for ($i = $count; $i > 0; $i--) { if ($nodesInContext[$i] === $contextNode) { unset($nodesInContext[$i]); return array_values($nodesInContext); } else { unset($nodesInContext[$i]); } } return null; }
/** * Publishes the given node to the specified targetWorkspace * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $node * @param string $targetWorkspaceName * @return void * @ExtDirect */ public function publishNodeAction(\TYPO3\TYPO3CR\Domain\Model\NodeInterface $node, $targetWorkspaceName) { /** * TODO: The publishing pushes the same node twice, which causes the node to be published * already when it's processed the second time. This obviously leads to a problem for the * Workspace object which will (in the second time) try to publish a node in the live workspace * to the baseWorkspace of the live workspace (which does not exist). */ if ($targetWorkspaceName === $node->getWorkspace()->getName()) { $this->view->assign('value', array('success' => TRUE)); return; } $this->workspacesService->publishNode($node, $targetWorkspaceName); $this->view->assign('value', array('success' => TRUE)); }
/** * 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); }
public function setUp() { $this->siteNode = $this->createMock(NodeInterface::class); $this->firstNodeInLevel = $this->createMock(NodeInterface::class); $this->secondNodeInLevel = $this->createMock(NodeInterface::class); $this->thirdNodeInLevel = $this->createMock(NodeInterface::class); $this->siteNode->expects($this->any())->method('getPath')->will($this->returnValue('/site')); $this->siteNode->expects($this->any())->method('getChildNodes')->will($this->returnValue(array($this->firstNodeInLevel, $this->secondNodeInLevel, $this->thirdNodeInLevel))); $this->mockContext = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $this->firstNodeInLevel->expects($this->any())->method('getParent')->will($this->returnValue($this->siteNode)); $this->firstNodeInLevel->expects($this->any())->method('getPath')->will($this->returnValue('/site/first')); $this->secondNodeInLevel->expects($this->any())->method('getParent')->will($this->returnValue($this->siteNode)); $this->secondNodeInLevel->expects($this->any())->method('getPath')->will($this->returnValue('/site/second')); $this->thirdNodeInLevel->expects($this->any())->method('getParent')->will($this->returnValue($this->siteNode)); $this->thirdNodeInLevel->expects($this->any())->method('getPath')->will($this->returnValue('/site/third')); }
/** * 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'); }
/** * Search all properties for given $term * * TODO: Implement a better search when Flow offer the possibility * * @param string $term * @param array $searchNodeTypes * @param Context $context * @param NodeInterface $startingPoint * @return array <\TYPO3\TYPO3CR\Domain\Model\NodeInterface> */ public function findByProperties($term, array $searchNodeTypes, Context $context, NodeInterface $startingPoint = null) { if (strlen($term) === 0) { throw new \InvalidArgumentException('"term" cannot be empty: provide a term to search for.', 1421329285); } $searchResult = array(); $nodeTypeFilter = implode(',', $searchNodeTypes); $nodeDataRecords = $this->nodeDataRepository->findByProperties($term, $nodeTypeFilter, $context->getWorkspace(), $context->getDimensions(), $startingPoint ? $startingPoint->getPath() : null); foreach ($nodeDataRecords as $nodeData) { $node = $this->nodeFactory->createFromNodeData($nodeData, $context); if ($node !== null) { $searchResult[$node->getPath()] = $node; } } return $searchResult; }
/** * 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.'); } }
/** * Generate CSS styles from spacing* properties * * @param NodeInterface $node * @return string */ public function generateCss(NodeInterface $node) { $css = ''; foreach (self::PROPERTY_MAPPING as $key => $cssProperty) { // Get value, also check for `0` values using strlen() if (($value = $node->getProperty($key)) || strlen($value)) { // if numeric value is provided, add default unit (e.g. px) // but don't add it to zero values if ($value && is_numeric($value)) { $value .= self::DEFAULT_UNIT; } $css .= "{$cssProperty}:{$value};"; } } return $css; }
/** * Fetch thumb url from Vimeo API * * @param NodeInterface $node * @return void */ public function fetchVimeoThumb(NodeInterface $node) { $fullVideo = $node->getProperty('fullVideo'); $fullVideoThumb = $node->getProperty('fullVideoThumb'); if ($fullVideo && !$fullVideoThumb) { $url = 'https://vimeo.com/api/oembed.json?url=http%3A//vimeo.com/' . $fullVideo; $content = file_get_contents($url); $json = json_decode($content, true); $node->setProperty('fullVideoThumb', $json['thumbnail_url']); } else { if (!$fullVideo && $fullVideoThumb) { // Clear thumb, if Vimeo video is gone. Useful for reloading thumb. $node->setProperty('fullVideoThumb', ''); } } }
/** * @param array $hits * @return array Array of Node objects */ protected function convertHitsToNodes(array $hits) { $nodes = []; $elasticSearchHitPerNode = []; /** * TODO: This code below is not fully correct yet: * * We always fetch $limit * (numerOfWorkspaces) records; so that we find a node: * - *once* if it is only in live workspace and matches the query * - *once* if it is only in user workspace and matches the query * - *twice* if it is in both workspaces and matches the query *both times*. In this case we filter the duplicate record. * - *once* if it is in the live workspace and has been DELETED in the user workspace (STILL WRONG) * - *once* if it is in the live workspace and has been MODIFIED to NOT MATCH THE QUERY ANYMORE in user workspace (STILL WRONG) * * If we want to fix this cleanly, we'd need to do an *additional query* in order to filter all nodes from a non-user workspace * which *do exist in the user workspace but do NOT match the current query*. This has to be done somehow "recursively"; and later * we might be able to use https://github.com/elasticsearch/elasticsearch/issues/3300 as soon as it is merged. */ foreach ($hits['hits'] as $hit) { $nodePath = current($hit['fields']['__path']); $node = $this->contextNode->getNode($nodePath); if ($node instanceof NodeInterface && !isset($nodes[$node->getIdentifier()])) { $nodes[$node->getIdentifier()] = $node; $elasticSearchHitPerNode[$node->getIdentifier()] = $hit; if ($this->limit > 0 && count($nodes) >= $this->limit) { break; } } } if ($this->logThisQuery === true) { $this->logger->log('Returned nodes (' . $this->logMessage . '): ' . count($nodes), LOG_DEBUG); } $this->elasticSearchHitsIndexedByNodeFromLastRequest = $elasticSearchHitPerNode; return array_values($nodes); }
/** * Send a new notification that a comment has been created * * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $commentNode The comment node * @param \TYPO3\TYPO3CR\Domain\Model\NodeInterface $postNode The post node * @return void */ public function sendNewCommentNotification(NodeInterface $commentNode, NodeInterface $postNode) { if ($this->settings['notifications']['to']['email'] === '') { return; } if (!class_exists('TYPO3\\SwiftMailer\\Message')) { $this->systemLogger->logException(new \TYPO3\Flow\Exception('The package "TYPO3.SwiftMailer" is required to send notifications!', 1359473932)); return; } try { $mail = new Message(); $mail->setFrom(array($commentNode->getProperty('emailAddress') => $commentNode->getProperty('author')))->setTo(array($this->settings['notifications']['to']['email'] => $this->settings['notifications']['to']['name']))->setSubject('New comment on blog post "' . $postNode->getProperty('title') . '"' . ($commentNode->getProperty('spam') ? ' (SPAM)' : ''))->setBody($commentNode->getProperty('text'))->send(); } catch (\Exception $e) { $this->systemLogger->logException($e); } }