buildExtbaseRequestIfNeeded($this->request, $conf); $templateFilename = ''; $templateSource = null; if ((!empty($conf['templateName']) || !empty($conf['templateName.'])) && !empty($conf['templateRootPaths.']) && is_array($conf['templateRootPaths.']) ) { // This is the most preferred way to render fluid: set up paths, then call render('My/Template') $viewFactoryData = new ViewFactoryData( templateRootPaths: $this->applyStandardWrapToFluidPaths($conf['templateRootPaths.']), partialRootPaths: $this->getPartialRootPaths($conf), layoutRootPaths: $this->getLayoutRootPaths($conf), request: $request, format: $this->cObj->stdWrapValue('format', $conf, null), ); $templateFilename = $this->cObj->stdWrapValue('templateName', $conf); } elseif (!empty($conf['template']) && !empty($conf['template.'])) { // Fetch the Fluid template by template cObject "template = TEXT, template.value = cObj->cObjGetSingle($conf['template'], $conf['template.'], 'template'); if ($templateSource === '') { throw new ContentRenderingException( 'Could not find template source for ' . $conf['template'], 1437420865 ); } $viewFactoryData = new ViewFactoryData( partialRootPaths: $this->getPartialRootPaths($conf), layoutRootPaths: $this->getLayoutRootPaths($conf), request: $request, format: $this->cObj->stdWrapValue('format', $conf, null), ); } else { // Fetch the Fluid template by file stdWrap "file = EXT:myExt/.../Foo.html" $file = (string)$this->cObj->stdWrapValue('file', $conf); // Get the absolute file name $templatePathAndFilename = GeneralUtility::getFileAbsFileName($file); $viewFactoryData = new ViewFactoryData( partialRootPaths: $this->getPartialRootPaths($conf), layoutRootPaths: $this->getLayoutRootPaths($conf), templatePathAndFilename: $templatePathAndFilename, request: $request, format: $this->cObj->stdWrapValue('format', $conf, null), ); } $view = $this->viewFactory->create($viewFactoryData); if (!$view instanceof FluidViewAdapter) { throw new ContentRenderingException( 'The FLUIDTEMPLATE content object only works with FluidViewAdapter view. Use a different' . ' content object to render some other view', 1724680477 ); } if ($templateSource) { $view->getRenderingContext()->getTemplatePaths()->setTemplateSource($templateSource); } if (isset($conf['settings.'])) { $settings = $this->typoScriptService->convertTypoScriptArrayToPlainArray($conf['settings.']); $view->assign('settings', $settings); } $variables = $this->getContentObjectVariables($conf); $variables = $this->contentDataProcessor->process($this->cObj, $conf, $variables); $view->assignMultiple($variables); try { // View needs to be rendered before the following asset rendering because it // sets the template (paths) internally. $content = $view->render($templateFilename); } catch (InvalidTemplateResourceException $e) { // Only add a FLUIDTEMPLATE specific message in case the exception has been thrown for the given template if ($e instanceof InvalidPartialException || $e instanceof InvalidLayoutException || $templateFilename === '' || $e->templateName !== 'Default/' . $templateFilename) { throw $e; } throw new InvalidTemplateResourceException( sprintf( 'FLUIDTEMPLATE TypoScript object: Failed to resolve a template file for templateName "%s". See also: %s. The following paths were checked: "%s"', $templateFilename, Typo3Information::getDocsLink('t3tsref:cobj-template'), implode('", "', $e->evaluatedTemplatePaths), ), 1772572794, $e, $e->templateName, $e->evaluatedTemplatePaths, ); } if (isset($conf['stdWrap.'])) { return $this->cObj->stdWrap($content, $conf['stdWrap.']); } return $content; } protected function getLayoutRootPaths(array $conf): ?array { $layoutPaths = []; $layoutRootPath = (string)$this->cObj->stdWrapValue('layoutRootPath', $conf); if ($layoutRootPath !== '') { $layoutPaths[] = GeneralUtility::getFileAbsFileName($layoutRootPath); } if (isset($conf['layoutRootPaths.'])) { $layoutPaths = array_replace($layoutPaths, $this->applyStandardWrapToFluidPaths($conf['layoutRootPaths.'])); } return !empty($layoutPaths) ? $layoutPaths : null; } protected function getPartialRootPaths(array $conf): ?array { $partialPaths = []; $partialRootPath = (string)$this->cObj->stdWrapValue('partialRootPath', $conf); if ($partialRootPath !== '') { $partialPaths[] = GeneralUtility::getFileAbsFileName($partialRootPath); } if (isset($conf['partialRootPaths.'])) { $partialPaths = array_replace($partialPaths, $this->applyStandardWrapToFluidPaths($conf['partialRootPaths.'])); } return !empty($partialPaths) ? $partialPaths : null; } /** * @todo: This magic has to fall one way or the other. It has been introduced for ext:form to * mimic extbase, see https://forge.typo3.org/issues/78842. This is actively used when * rendering forms using the formvh:render strategy, see the documentation. */ protected function buildExtbaseRequestIfNeeded(ServerRequestInterface $request, array $conf): ServerRequestInterface { $requestPluginName = (string)$this->cObj->stdWrapValue('pluginName', $conf['extbase.'] ?? []); $requestControllerExtensionName = (string)$this->cObj->stdWrapValue('controllerExtensionName', $conf['extbase.'] ?? []); $requestControllerName = (string)$this->cObj->stdWrapValue('controllerName', $conf['extbase.'] ?? []); $requestControllerActionName = (string)$this->cObj->stdWrapValue('controllerActionName', $conf['extbase.'] ?? []); if ($requestPluginName && $requestControllerExtensionName && $requestControllerName && $requestControllerActionName) { $configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class); $configurationManager->setConfiguration([ 'extensionName' => $requestControllerExtensionName, 'pluginName' => $requestPluginName, ]); if (!isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$requestControllerExtensionName]['plugins'][$requestPluginName]['controllers'])) { $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$requestControllerExtensionName]['plugins'][$requestPluginName]['controllers'] = [ $requestControllerName => [ 'actions' => [ $requestControllerActionName, ], ], ]; } $requestBuilder = GeneralUtility::makeInstance(RequestBuilder::class); $request = $requestBuilder->build($request); } return $request; } /** * Compile rendered content objects in variables array ready to assign to the view. */ protected function getContentObjectVariables(array $conf): array { $variables = []; $reservedVariables = ['data', 'current']; // Accumulate the variables to be process and loop them through cObjGetSingle $variablesToProcess = (array)($conf['variables.'] ?? []); foreach ($variablesToProcess as $variableName => $cObjType) { if (is_array($cObjType)) { continue; } if (!in_array($variableName, $reservedVariables)) { $cObjConf = $variablesToProcess[$variableName . '.'] ?? []; $variables[$variableName] = $this->cObj->cObjGetSingle($cObjType, $cObjConf, 'variables.' . $variableName); } else { throw new \InvalidArgumentException( 'Cannot use reserved name "' . $variableName . '" as variable name in FLUIDTEMPLATE.', 1288095720 ); } } $variables['data'] = $this->cObj->data; $variables['current'] = $this->cObj->data[$this->cObj->currentValKey] ?? null; return $variables; } protected function applyStandardWrapToFluidPaths(array $paths): array { $finalPaths = []; foreach ($paths as $key => $path) { if (str_ends_with((string)$key, '.')) { if (isset($paths[substr($key, 0, -1)])) { continue; } $path = $this->cObj->stdWrap('', $path); } elseif (isset($paths[$key . '.'])) { $path = $this->cObj->stdWrap($path, $paths[$key . '.']); } $finalPaths[$key] = GeneralUtility::getFileAbsFileName($path); } return $finalPaths; } }