TypoScript Rendering Process
============================
During rendering, all TypoScript objects form a tree.
When a TypoScript object at a certain $typoScriptPath is invoked, it has
access to all variables stored in the $context (which is an array).
The TypoScript object can then add or replace variables to this context using pushContext()
or pushContextArray(), before rendering sub-TypoScript objects. After rendering
these, it must call popContext() to reset the context to the last state.
/** * Return the TypoScript value relative to this TypoScript object (with processors etc applied). * * Note that subsequent calls of tsValue() with the same TypoScript path will return the same values since the * first evaluated value will be cached in memory. * * @param string $path * @return mixed */ protected function tsValue($path) { $fullPath = $this->path . '/' . $path; if (!isset($this->tsValueCache[$fullPath])) { $this->tsValueCache[$fullPath] = $this->tsRuntime->evaluate($fullPath, $this); } return $this->tsValueCache[$fullPath]; }
/** * Handle an Exception thrown while rendering TypoScript * * @param string $typoScriptPath * @param \Exception $exception * @return string * @throws StopActionException|SecurityException */ public function handleRenderingException($typoScriptPath, \Exception $exception) { if ($exception instanceof StopActionException || $exception instanceof SecurityException) { throw $exception; } if ($exception instanceof Exceptions\RuntimeException) { $typoScriptPath = $exception->getTypoScriptPath(); $exception = $exception->getPrevious(); } if ($this->exceptionDisablesCache($typoScriptPath, $exception)) { $this->runtime->setEnableContentCache(false); } $referenceCode = $exception instanceof \Neos\Flow\Exception ? $exception->getReferenceCode() : null; return $this->handle($typoScriptPath, $exception, $referenceCode); }
/** * @test * @dataProvider attributeExamples */ public function evaluateTests($properties, $expectedOutput) { $path = 'attributes/test'; $this->mockTsRuntime->expects($this->any())->method('evaluate')->will($this->returnCallback(function ($evaluatePath, $that) use($path, $properties) { $relativePath = str_replace($path . '/', '', $evaluatePath); return ObjectAccess::getPropertyPath($properties, str_replace('/', '.', $relativePath)); })); $typoScriptObjectName = 'Neos.Fusion:Attributes'; $renderer = new AttributesImplementation($this->mockTsRuntime, $path, $typoScriptObjectName); if ($properties !== null) { foreach ($properties as $name => $value) { ObjectAccess::setProperty($renderer, $name, $value); } } $result = $renderer->evaluate(); $this->assertEquals($expectedOutput, $result); }
/** * @test */ public function viewHelperRendersUriViaStringPointingToSubNodes() { $this->tsRuntime->pushContext('documentNode', $this->contentContext->getCurrentSiteNode()->getNode('home/about-us/mission')); $this->assertOutputLinkValid('en/home/about-us/history.html', $this->viewHelper->render('../history')); $this->tsRuntime->popContext(); $this->assertOutputLinkValid('en/home/about-us/our-mission.html', $this->viewHelper->render('about-us/mission')); $this->assertOutputLinkValid('en/home/about-us/our-mission.html', $this->viewHelper->render('./about-us/mission')); }
/** * @test */ public function viewHelperRendersUriViaStringPointingToSubNodes() { $this->tsRuntime->pushContext('documentNode', $this->contentContext->getCurrentSiteNode()->getNode('home/about-us/mission')); $this->assertSame('<a href="/en/home/about-us/history.html">History</a>', $this->viewHelper->render('../history')); $this->tsRuntime->popContext(); $this->assertSame('<a href="/en/home/about-us/our-mission.html">Our mission</a>', $this->viewHelper->render('about-us/mission')); $this->assertSame('<a href="/en/home/about-us/our-mission.html">Our mission</a>', $this->viewHelper->render('./about-us/mission')); }
public function setUp() { $this->pluginImplementation = $this->getAccessibleMock(PluginImplementation::class, ['buildPluginRequest'], [], '', false); $this->mockHttpUri = $this->getMockBuilder(Uri::class)->disableOriginalConstructor()->getMock(); $this->mockHttpUri->expects($this->any())->method('getHost')->will($this->returnValue('localhost')); $this->mockHttpRequest = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock(); $this->mockHttpRequest->expects($this->any())->method('getUri')->will($this->returnValue($this->mockHttpUri)); $this->mockActionRequest = $this->getMockBuilder(ActionRequest::class)->disableOriginalConstructor()->getMock(); $this->mockActionRequest->expects($this->any())->method('getHttpRequest')->will($this->returnValue($this->mockHttpRequest)); $this->mockControllerContext = $this->getMockBuilder(ControllerContext::class)->disableOriginalConstructor()->getMock(); $this->mockControllerContext->expects($this->any())->method('getRequest')->will($this->returnValue($this->mockActionRequest)); $this->mockTsRuntime = $this->getMockBuilder(Runtime::class)->disableOriginalConstructor()->getMock(); $this->mockTsRuntime->expects($this->any())->method('getControllerContext')->will($this->returnValue($this->mockControllerContext)); $this->pluginImplementation->_set('tsRuntime', $this->mockTsRuntime); $this->mockDispatcher = $this->getMockBuilder(Dispatcher::class)->disableOriginalConstructor()->getMock(); $this->pluginImplementation->_set('dispatcher', $this->mockDispatcher); }
public function setUp() { parent::setUp(); $this->contentElementEditableService = new ContentElementEditableService(); $this->mockPrivilegeManager = $this->getMockBuilder(PrivilegeManagerInterface::class)->getMock(); $this->inject($this->contentElementEditableService, 'privilegeManager', $this->mockPrivilegeManager); $this->mockNodeAuthorizationService = $this->getMockBuilder(AuthorizationService::class)->getMock(); $this->inject($this->contentElementEditableService, 'nodeAuthorizationService', $this->mockNodeAuthorizationService); $this->mockHtmlAugmenter = $this->getMockBuilder(HtmlAugmenter::class)->getMock(); $this->inject($this->contentElementEditableService, 'htmlAugmenter', $this->mockHtmlAugmenter); $this->mockTsRuntime = $this->getMockBuilder(\Neos\Fusion\Core\Runtime::class)->disableOriginalConstructor()->getMock(); $this->mockContentContext = $this->getMockBuilder(\Neos\Neos\Domain\Service\ContentContext::class)->disableOriginalConstructor()->getMock(); $this->mockNode = $this->getMockBuilder(\Neos\ContentRepository\Domain\Model\NodeInterface::class)->getMock(); $this->mockNode->expects($this->any())->method('getContext')->will($this->returnValue($this->mockContentContext)); $this->mockNode->expects($this->any())->method('getNodeType')->will($this->returnValue(new NodeType('Acme.Test:Headline', [], []))); $this->mockTsContext = array('node' => $this->mockNode); $this->mockTsRuntime->expects($this->any())->method('getCurrentContext')->will($this->returnValue($this->mockTsContext)); }
/** * @test * @dataProvider tagExamples */ public function evaluateTests($properties, $attributes, $content, $expectedOutput) { $path = 'tag/test'; $this->mockTsRuntime->expects($this->any())->method('evaluate')->will($this->returnCallback(function ($evaluatePath, $that) use($properties, $path, $attributes, $content) { $relativePath = str_replace($path . '/', '', $evaluatePath); switch ($relativePath) { case 'attributes': return $attributes; case 'content': return $content; } return isset($properties[$relativePath]) ? $properties[$relativePath] : null; })); $typoScriptObjectName = 'Neos.Fusion:Tag'; $renderer = new TagImplementation($this->mockTsRuntime, $path, $typoScriptObjectName); $result = $renderer->evaluate(); $this->assertEquals($expectedOutput, $result); }
/** * @param NodeInterface $currentSiteNode * @return \Neos\Fusion\Core\Runtime */ protected function getTypoScriptRuntime(NodeInterface $currentSiteNode) { if ($this->typoScriptRuntime === null) { $this->typoScriptRuntime = $this->typoScriptService->createRuntime($currentSiteNode, $this->controllerContext); if (isset($this->options['enableContentCache']) && $this->options['enableContentCache'] !== null) { $this->typoScriptRuntime->setEnableContentCache($this->options['enableContentCache']); } } return $this->typoScriptRuntime; }
/** * @test * @dataProvider responseHeadExamples */ public function evaluateTests($httpVersion, $statusCode, $headers, $expectedOutput) { $path = 'responseHead/test'; $this->mockTsRuntime->expects($this->any())->method('evaluate')->will($this->returnCallback(function ($evaluatePath) use($path, $httpVersion, $statusCode, $headers) { $relativePath = str_replace($path . '/', '', $evaluatePath); switch ($relativePath) { case 'httpVersion': return $httpVersion; case 'statusCode': return $statusCode; case 'headers': return $headers; } return isset($properties[$relativePath]) ? $properties[$relativePath] : null; })); $typoScriptObjectName = 'Neos.Fusion:Http.ResponseHead'; $renderer = new ResponseHeadImplementation($this->mockTsRuntime, $path, $typoScriptObjectName); $result = $renderer->evaluate(); $this->assertEquals($expectedOutput, $result); }
/** * Render the given TypoScript and return the rendered page * * @return string */ protected function renderTypoScript() { $this->typoScriptRuntime->pushContextArray($this->variables); try { $output = $this->typoScriptRuntime->render($this->getTypoScriptPathForCurrentRequest()); } catch (RuntimeException $exception) { throw $exception->getPrevious(); } $this->typoScriptRuntime->popContext(); return $output; }
/** * Iterates through all subelements. * * @return \ArrayIterator */ public function getIterator() { $evaluatedArray = array(); foreach ($this->partialTypoScriptTree as $key => $value) { if (!is_array($value)) { $evaluatedArray[$key] = $value; } elseif (isset($value['__objectType'])) { $evaluatedArray[$key] = $this->fusionRuntime->evaluate($this->path . '/' . $key); } elseif (isset($value['__eelExpression'])) { $evaluatedArray[$key] = $this->fusionRuntime->evaluate($this->path . '/' . $key, $this->templateImplementation); } else { $evaluatedArray[$key] = new FusionPathProxy($this->templateImplementation, $this->path . '/' . $key, $this->partialTypoScriptTree[$key]); } } return new \ArrayIterator($evaluatedArray); }
/** * @test */ public function evaluateLocalizesFilenameIfLocalize() { $this->mockTsRuntime->expects($this->any())->method('evaluate')->will($this->returnCallback(function ($evaluatePath, $that) { $relativePath = str_replace('resourceUri/test/', '', $evaluatePath); switch ($relativePath) { case 'localize': return true; case 'path': return 'resource://Some.Package/Public/SomeResource'; case 'package': return 'Specified.Package'; } return null; })); $this->mockI18nService->expects($this->atLeastOnce())->method('getLocalizedFilename')->will($this->returnValue(array('resource://Some.Package/Public/LocalizedFilename'))); $this->mockResourceManager->expects($this->atLeastOnce())->method('getPublicPackageResourceUri')->will($this->returnValue('Static/Resources/Packages/Some.Package/LocalizedFilename')); $this->assertSame('Static/Resources/Packages/Some.Package/LocalizedFilename', $this->resourceUriImplementation->evaluate()); }
/** * Builds an array of string which must be used as tags for the cache entry identifier of a specific cached content segment. * * @param array $configuration * @param string $typoScriptPath * @param object $tsObject The actual TypoScript object * @return array */ protected function buildCacheTags(array $configuration, $typoScriptPath, $tsObject) { $cacheTags = []; if (isset($configuration['entryTags'])) { foreach ($configuration['entryTags'] as $tagKey => $tagValue) { $tagValue = $this->runtime->evaluate($typoScriptPath . '/__meta/cache/entryTags/' . $tagKey, $tsObject); if (is_array($tagValue)) { $cacheTags = array_merge($cacheTags, $tagValue); } elseif ((string) $tagValue !== '') { $cacheTags[] = $tagValue; } } foreach ($this->flushTags() as $tagKey => $tagValue) { $cacheTags[] = $tagValue; } } else { $cacheTags = [ContentCache::TAG_EVERYTHING]; } return array_unique($cacheTags); }
public function setUp() { $this->convertUrisImplementation = $this->getAccessibleMock(ConvertUrisImplementation::class, array('tsValue'), array(), '', false); $this->mockWorkspace = $this->getMockBuilder(Workspace::class)->disableOriginalConstructor()->getMock(); $this->mockContext = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); $this->mockContext->expects($this->any())->method('getWorkspace')->will($this->returnValue($this->mockWorkspace)); $this->mockNode = $this->getMockBuilder(NodeInterface::class)->getMock(); $this->mockNode->expects($this->any())->method('getContext')->will($this->returnValue($this->mockContext)); $this->mockHttpUri = $this->getMockBuilder(Uri::class)->disableOriginalConstructor()->getMock(); $this->mockHttpUri->expects($this->any())->method('getHost')->will($this->returnValue('localhost')); $this->mockHttpRequest = $this->getMockBuilder(Request::class)->disableOriginalConstructor()->getMock(); $this->mockHttpRequest->expects($this->any())->method('getUri')->will($this->returnValue($this->mockHttpUri)); $this->mockActionRequest = $this->getMockBuilder(ActionRequest::class)->disableOriginalConstructor()->getMock(); $this->mockActionRequest->expects($this->any())->method('getHttpRequest')->will($this->returnValue($this->mockHttpRequest)); $this->mockControllerContext = $this->getMockBuilder(ControllerContext::class)->disableOriginalConstructor()->getMock(); $this->mockControllerContext->expects($this->any())->method('getRequest')->will($this->returnValue($this->mockActionRequest)); $this->mockLinkingService = $this->createMock(LinkingService::class); $this->convertUrisImplementation->_set('linkingService', $this->mockLinkingService); $this->mockTsRuntime = $this->getMockBuilder(Runtime::class)->disableOriginalConstructor()->getMock(); $this->mockTsRuntime->expects($this->any())->method('getControllerContext')->will($this->returnValue($this->mockControllerContext)); $this->convertUrisImplementation->_set('tsRuntime', $this->mockTsRuntime); }
public function setUp() { parent::setUp(); $this->editableViewHelper = $this->getAccessibleMock(EditableViewHelper::class, array('renderChildren')); $this->mockPrivilegeManager = $this->getMockBuilder(PrivilegeManagerInterface::class)->getMock(); $this->inject($this->editableViewHelper, 'privilegeManager', $this->mockPrivilegeManager); $this->mockNodeAuthorizationService = $this->getMockBuilder(AuthorizationService::class)->getMock(); $this->inject($this->editableViewHelper, 'nodeAuthorizationService', $this->mockNodeAuthorizationService); $this->mockContentElementEditableService = $this->getMockBuilder(ContentElementEditableService::class)->getMock(); $this->inject($this->editableViewHelper, 'contentElementEditableService', $this->mockContentElementEditableService); $this->mockTemplateImplementation = $this->getMockBuilder(TemplateImplementation::class)->disableOriginalConstructor()->getMock(); $this->mockTsRuntime = $this->getMockBuilder(Runtime::class)->disableOriginalConstructor()->getMock(); $this->mockContentContext = $this->getMockBuilder(ContentContext::class)->disableOriginalConstructor()->getMock(); $this->mockNode = $this->getMockBuilder(NodeInterface::class)->getMock(); $this->mockNode->expects($this->any())->method('getContext')->will($this->returnValue($this->mockContentContext)); $this->mockNode->expects($this->any())->method('getNodeType')->will($this->returnValue(new NodeType('Acme.Test:Headline', [], []))); $this->mockTsContext = array('node' => $this->mockNode); $this->mockTsRuntime->expects($this->any())->method('getCurrentContext')->will($this->returnValue($this->mockTsContext)); $this->mockTemplateImplementation->expects($this->any())->method('getTsRuntime')->will($this->returnValue($this->mockTsRuntime)); $this->mockView = $this->getAccessibleMock(FluidView::class, array(), array(), '', false); $this->mockView->expects($this->any())->method('getTypoScriptObject')->will($this->returnValue($this->mockTemplateImplementation)); $this->editableViewHelper->initializeArguments(); }
/** * @test * @expectedException \Neos\Fusion\Exception * @expectedExceptionCode 1395922119 */ public function evaluateWithCacheModeUncachedAndUnspecifiedContextThrowsException() { $mockControllerContext = $this->getMockBuilder(ControllerContext::class)->disableOriginalConstructor()->getMock(); $runtime = new Runtime(array('foo' => array('bar' => array('__meta' => array('cache' => array('mode' => 'uncached'))))), $mockControllerContext); $runtime->evaluate('foo/bar'); }
/** * @test */ public function renderPutsSiteNodeInTypoScriptContext() { $this->setUpMockView(); $this->mockRuntime->expects($this->once())->method('pushContextArray')->with($this->arrayHasKey('site')); $this->mockView->render(); }