contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class); } /** * @param ContentDataProcessor $contentDataProcessor */ public function setContentDataProcessor($contentDataProcessor) { $this->contentDataProcessor = $contentDataProcessor; } /** * Rendering the cObject, FLUIDTEMPLATE * * Configuration properties: * - file string+stdWrap The FLUID template file * - layoutRootPaths array of filepath+stdWrap Root paths to layouts (fallback) * - partialRootPaths array of filepath+stdWrap Root paths to partials (fallback) * - variable array of cObjects, the keys are the variable names in fluid * - dataProcessing array of data processors which are classes to manipulate $data * - extbase.pluginName * - extbase.controllerExtensionName * - extbase.controllerName * - extbase.controllerActionName * * Example: * 10 = FLUIDTEMPLATE * 10.templateName = MyTemplate * 10.templateRootPaths.10 = EXT:site_configuration/Resources/Private/Templates/ * 10.partialRootPaths.10 = EXT:site_configuration/Resources/Private/Patials/ * 10.layoutRootPaths.10 = EXT:site_configuration/Resources/Private/Layouts/ * 10.variables { * mylabel = TEXT * mylabel.value = Label from TypoScript coming * } * * @param array $conf Array of TypoScript properties * @return string The HTML output */ public function render($conf = []) { $parentView = $this->view; $this->initializeStandaloneViewInstance(); if (!is_array($conf)) { $conf = []; } $this->setFormat($conf); $this->setTemplate($conf); $this->setLayoutRootPath($conf); $this->setPartialRootPath($conf); $this->setExtbaseVariables($conf); $this->assignSettings($conf); $variables = $this->getContentObjectVariables($conf); $variables = $this->contentDataProcessor->process($this->cObj, $conf, $variables); $this->view->assignMultiple($variables); $this->renderFluidTemplateAssetsIntoPageRenderer(); $content = $this->renderFluidView(); $content = $this->applyStandardWrapToRenderedContent($content, $conf); $this->view = $parentView; return $content; } /** * Attempts to render HeaderAssets and FooterAssets sections from the * Fluid template, then adds each (if not empty) to either header or * footer, as appropriate, using PageRenderer. */ protected function renderFluidTemplateAssetsIntoPageRenderer() { $pageRenderer = $this->getPageRenderer(); $headerAssets = $this->view->renderSection('HeaderAssets', ['contentObject' => $this], true); $footerAssets = $this->view->renderSection('FooterAssets', ['contentObject' => $this], true); if (!empty(trim($headerAssets))) { $pageRenderer->addHeaderData($headerAssets); } if (!empty(trim($footerAssets))) { $pageRenderer->addFooterData($footerAssets); } } /** * Creating standalone view instance must not be done in construct() as * it can lead to a nasty cache issue since content object instances * are not always re-created by the content object rendered for every * usage, but can be re-used. Thus, we need a fresh instance of * StandaloneView every time render() is called. */ protected function initializeStandaloneViewInstance() { $this->view = GeneralUtility::makeInstance(StandaloneView::class); } /** * Set template * * @param array $conf With possibly set file resource * @throws \InvalidArgumentException */ protected function setTemplate(array $conf) { // Fetch the Fluid template by templateName if ( (!empty($conf['templateName']) || !empty($conf['templateName.'])) && !empty($conf['templateRootPaths.']) && is_array($conf['templateRootPaths.']) ) { $templateRootPaths = $this->applyStandardWrapToFluidPaths($conf['templateRootPaths.']); $this->view->setTemplateRootPaths($templateRootPaths); $templateName = isset($conf['templateName.']) ? $this->cObj->stdWrap($conf['templateName'] ?? '', $conf['templateName.']) : $conf['templateName']; $this->view->setTemplate($templateName); } elseif (!empty($conf['template']) && !empty($conf['template.'])) { // Fetch the Fluid template by template cObject $templateSource = $this->cObj->cObjGetSingle($conf['template'], $conf['template.'], 'template'); if ($templateSource === '') { throw new ContentRenderingException( 'Could not find template source for ' . $conf['template'], 1437420865 ); } $this->view->setTemplateSource($templateSource); } else { // Fetch the Fluid template by file stdWrap $file = isset($conf['file.']) ? $this->cObj->stdWrap($conf['file'] ?? '', $conf['file.']) : ($conf['file'] ?? ''); // Get the absolute file name $templatePathAndFilename = GeneralUtility::getFileAbsFileName($file); $this->view->setTemplatePathAndFilename($templatePathAndFilename); } } /** * Set layout root path if given in configuration * * @param array $conf Configuration array */ protected function setLayoutRootPath(array $conf) { // Override the default layout path via typoscript $layoutPaths = []; if (isset($conf['layoutRootPath']) || isset($conf['layoutRootPath.'])) { $layoutRootPath = isset($conf['layoutRootPath.']) ? $this->cObj->stdWrap($conf['layoutRootPath'], $conf['layoutRootPath.']) : $conf['layoutRootPath']; $layoutPaths[] = GeneralUtility::getFileAbsFileName($layoutRootPath); } if (isset($conf['layoutRootPaths.'])) { $layoutPaths = array_replace($layoutPaths, $this->applyStandardWrapToFluidPaths($conf['layoutRootPaths.'])); } if (!empty($layoutPaths)) { $this->view->setLayoutRootPaths($layoutPaths); } } /** * Set partial root path if given in configuration * * @param array $conf Configuration array */ protected function setPartialRootPath(array $conf) { $partialPaths = []; if (isset($conf['partialRootPath']) || isset($conf['partialRootPath.'])) { $partialRootPath = isset($conf['partialRootPath.']) ? $this->cObj->stdWrap($conf['partialRootPath'], $conf['partialRootPath.']) : $conf['partialRootPath']; $partialPaths[] = GeneralUtility::getFileAbsFileName($partialRootPath); } if (isset($conf['partialRootPaths.'])) { $partialPaths = array_replace($partialPaths, $this->applyStandardWrapToFluidPaths($conf['partialRootPaths.'])); } if (!empty($partialPaths)) { $this->view->setPartialRootPaths($partialPaths); } } /** * Set different format if given in configuration * * @param array $conf Configuration array */ protected function setFormat(array $conf) { $format = isset($conf['format.']) ? $this->cObj->stdWrap($conf['format'] ?? '', $conf['format.']) : ($conf['format'] ?? ''); if ($format) { $this->view->setFormat($format); } } /** * Set some extbase variables if given * * @param array $conf Configuration array */ protected function setExtbaseVariables(array $conf) { /** @var \TYPO3\CMS\Extbase\Mvc\Request $request */ $requestPluginName = isset($conf['extbase.']['pluginName.']) ? $this->cObj->stdWrap($conf['extbase.']['pluginName'] ?? '', $conf['extbase.']['pluginName.']) : ($conf['extbase.']['pluginName'] ?? ''); if ($requestPluginName) { $this->view->getRequest()->setPluginName($requestPluginName); } $requestControllerExtensionName = isset($conf['extbase.']['controllerExtensionName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerExtensionName'] ?? '', $conf['extbase.']['controllerExtensionName.']) : ($conf['extbase.']['controllerExtensionName'] ?? ''); if ($requestControllerExtensionName) { $this->view->getRequest()->setControllerExtensionName($requestControllerExtensionName); } $requestControllerName = isset($conf['extbase.']['controllerName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerName'] ?? '', $conf['extbase.']['controllerName.']) : ($conf['extbase.']['controllerName'] ?? ''); if ($requestControllerName) { $this->view->getRequest()->setControllerName($requestControllerName); } $requestControllerActionName = isset($conf['extbase.']['controllerActionName.']) ? $this->cObj->stdWrap($conf['extbase.']['controllerActionName'] ?? '', $conf['extbase.']['controllerActionName.']) : ($conf['extbase.']['controllerActionName'] ?? ''); if ($requestControllerActionName) { $this->view->getRequest()->setControllerActionName($requestControllerActionName); } if ( $requestPluginName && $requestControllerExtensionName && $requestControllerName && $requestControllerActionName ) { $configurationManager = GeneralUtility::makeInstance(ObjectManager::class)->get(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(ObjectManager::class)->get(RequestBuilder::class); $this->view->getRenderingContext()->getControllerContext()->setRequest($requestBuilder->build()); } } /** * Compile rendered content objects in variables array ready to assign to the view * * @param array $conf Configuration array * @return array the variables to be assigned * @throws \InvalidArgumentException */ protected function getContentObjectVariables(array $conf) { $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)) { $variables[$variableName] = $this->cObj->cObjGetSingle($cObjType, $variablesToProcess[$variableName . '.'], '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] ?? null; return $variables; } /** * Set any TypoScript settings to the view. This is similar to a * default MVC action controller in extbase. * * @param array $conf Configuration */ protected function assignSettings(array $conf) { if (isset($conf['settings.'])) { /** @var TypoScriptService $typoScriptService */ $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); $settings = $typoScriptService->convertTypoScriptArrayToPlainArray($conf['settings.']); $this->view->assign('settings', $settings); } } /** * Render fluid standalone view * * @return string */ protected function renderFluidView() { return $this->view->render(); } /** * Apply standard wrap to content * * @param string $content Rendered HTML content * @param array $conf Configuration array * @return string Standard wrapped content */ protected function applyStandardWrapToRenderedContent($content, array $conf) { if (isset($conf['stdWrap.'])) { $content = $this->cObj->stdWrap($content, $conf['stdWrap.']); } return $content; } /** * Applies stdWrap on Fluid path definitions * * @param array $paths * * @return array */ protected function applyStandardWrapToFluidPaths(array $paths) { $finalPaths = []; foreach ($paths as $key => $path) { if (StringUtility::endsWith($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; } }