176 lines
8.0 KiB
PHP
176 lines
8.0 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/*
|
|
* This file is part of the TYPO3 CMS project.
|
|
*
|
|
* It is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License, either version 2
|
|
* of the License, or any later version.
|
|
*
|
|
* For the full copyright and license information, please read the
|
|
* LICENSE.txt file that was distributed with this source code.
|
|
*
|
|
* The TYPO3 project - inspiring people to share!
|
|
*/
|
|
|
|
namespace TYPO3\CMS\Frontend\ContentObject;
|
|
|
|
use TYPO3\CMS\Core\Information\Typo3Information;
|
|
use TYPO3\CMS\Core\Page\PageLayoutResolver;
|
|
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
|
use TYPO3\CMS\Core\Utility\PathUtility;
|
|
use TYPO3\CMS\Core\View\ViewFactoryData;
|
|
use TYPO3\CMS\Core\View\ViewFactoryInterface;
|
|
use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
|
|
use TYPO3Fluid\Fluid\View\Exception\InvalidLayoutException;
|
|
use TYPO3Fluid\Fluid\View\Exception\InvalidPartialException;
|
|
use TYPO3Fluid\Fluid\View\Exception\InvalidTemplateResourceException;
|
|
|
|
/**
|
|
* PAGEVIEW Content Object.
|
|
*
|
|
* Built to render a full page with Fluid, and does the following
|
|
* - uses the template from the given Page Layout / Backend Layout of the current page in a folder "Pages/Mylayout.html"
|
|
* - paths are resolved from "paths." configuration
|
|
* - automatically adds templateRootPaths to the layoutRootPaths and partialRootPaths
|
|
* - injects pageInformation, site and siteLanguage (= language) as variables by default
|
|
* - adds all page settings (= TypoScript constants) into the settings variable of the View
|
|
*
|
|
* In contrast to FLUIDTEMPLATE, by design this cObject
|
|
* - does not handle custom layoutRootPaths and partialRootPaths
|
|
* - does not handle Extbase specialities
|
|
* - does not handle "templateName.", "template." and "file." resolving from cObject
|
|
*/
|
|
final class PageViewContentObject extends AbstractContentObject
|
|
{
|
|
private const array reservedVariables = ['site', 'language', 'page'];
|
|
|
|
public function __construct(
|
|
private readonly ContentDataProcessor $contentDataProcessor,
|
|
private readonly TypoScriptService $typoScriptService,
|
|
private readonly PageLayoutResolver $pageLayoutResolver,
|
|
private readonly ViewFactoryInterface $viewFactory,
|
|
) {}
|
|
|
|
/**
|
|
* Rendering the cObject, PAGEVIEW
|
|
*
|
|
* Configuration properties:
|
|
* - paths array to template files
|
|
* - variables array of cObjects, the keys are the variable names in fluid
|
|
* - dataProcessing array of data processors which are classes to manipulate $data
|
|
*
|
|
* Example:
|
|
* page.10 = PAGEVIEW
|
|
* page.10.paths.10 = EXT:site_configuration/Resources/Private/Templates/
|
|
* page.10.variables {
|
|
* mylabel = TEXT
|
|
* mylabel.value = Label from TypoScript
|
|
* }
|
|
*
|
|
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
|
* @return string The HTML output
|
|
* @throws ContentRenderingException
|
|
*/
|
|
public function render($conf = []): string
|
|
{
|
|
if (!is_array($conf)) {
|
|
$conf = [];
|
|
}
|
|
if (!is_array($conf['paths.'] ?? false) || $conf['paths.'] === []) {
|
|
throw new ContentRenderingException(
|
|
'PAGEVIEW content object needs a "paths." TypoScript array',
|
|
1724601907
|
|
);
|
|
}
|
|
$paths = array_map(PathUtility::sanitizeTrailingSeparator(...), $conf['paths.']);
|
|
$viewFactoryData = new ViewFactoryData(
|
|
// @todo: Do discuss: Rename 'paths.' to 'templateRootPaths.' again?
|
|
templateRootPaths: array_map(static fn(string $path): string => $path . 'Pages/', $paths),
|
|
// @todo: We should *still* allow setting both partialRootPaths and layoutRootPaths, and only fall back to
|
|
// [templateRootPaths]/Partials and [templateRootPaths]/Layouts if not set. And the fallback should be
|
|
// advertised as best practice.
|
|
partialRootPaths: array_map(static fn(string $path): string => $path . 'Partials/', $paths),
|
|
layoutRootPaths: array_map(static fn(string $path): string => $path . 'Layouts/', $paths),
|
|
request: $this->request,
|
|
);
|
|
$view = $this->viewFactory->create($viewFactoryData);
|
|
|
|
$pageSettings = $this->request->getAttribute('frontend.typoscript')->getSettingsTree()->toArray();
|
|
$view->assign('settings', $this->typoScriptService->convertTypoScriptArrayToPlainArray($pageSettings));
|
|
$variables = $this->getContentObjectVariables($conf);
|
|
$variables = $this->contentDataProcessor->process($this->cObj, $conf, $variables);
|
|
$view->assignMultiple($variables);
|
|
|
|
// Fetch the Fluid template by the name of the Page Layout and underneath "Pages"
|
|
$pageInformationObject = $this->request->getAttribute('frontend.page.information');
|
|
$pageLayoutName = $this->pageLayoutResolver->getLayoutIdentifierForPageWithoutPrefix(
|
|
$pageInformationObject->getPageRecord(),
|
|
$pageInformationObject->getRootLine()
|
|
);
|
|
try {
|
|
return $view->render($pageLayoutName);
|
|
} catch (InvalidTemplateResourceException $e) {
|
|
// Only add a PAGEVIEW specific message in case the exception has been thrown for the given template.
|
|
if ($e instanceof InvalidPartialException || $e instanceof InvalidLayoutException || $e->templateName !== 'Default/' . $pageLayoutName) {
|
|
throw $e;
|
|
}
|
|
throw new InvalidTemplateResourceException(
|
|
sprintf(
|
|
'PAGEVIEW TypoScript object: Failed to resolve a template file for page layout "%s". See also: %s. The following paths were checked: "%s"',
|
|
$pageLayoutName,
|
|
Typo3Information::getDocsLink('t3tsref:cobj-pageview'),
|
|
implode('", "', $e->evaluatedTemplatePaths),
|
|
),
|
|
1742058289,
|
|
$e,
|
|
$e->templateName,
|
|
$e->evaluatedTemplatePaths,
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
private function getContentObjectVariables(array $conf): array
|
|
{
|
|
$pageInformation = $this->request->getAttribute('frontend.page.information');
|
|
$variables = [
|
|
'site' => $this->request->getAttribute('site'),
|
|
'language' => $this->request->getAttribute('language'),
|
|
'page' => $pageInformation,
|
|
];
|
|
// Accumulate the variables to be process and loop them through cObjGetSingle
|
|
if (is_array($conf['variables.'] ?? false) && $conf['variables.'] !== []) {
|
|
foreach ($conf['variables.'] as $variableName => $cObjType) {
|
|
if (!is_string($cObjType)) {
|
|
continue;
|
|
}
|
|
if (in_array($variableName, self::reservedVariables, true)) {
|
|
throw new \InvalidArgumentException(
|
|
'Cannot use reserved name "' . $variableName . '" as variable name in PAGEVIEW.',
|
|
1711748615
|
|
);
|
|
}
|
|
$cObjConf = $conf['variables.'][$variableName . '.'] ?? [];
|
|
$variables[$variableName] = $this->cObj->cObjGetSingle($cObjType, $cObjConf, 'variables.' . $variableName);
|
|
}
|
|
}
|
|
if (!($conf['contentAs'] ?? false) && isset($variables['content'])) {
|
|
throw new \InvalidArgumentException(
|
|
'No variable name ("contentAs" option) for the content areas has been defined in PAGEVIEW, and the fallback name "content" is not available because it has been manually set.',
|
|
1726475574
|
|
);
|
|
}
|
|
|
|
$variables[$conf['contentAs'] ?? 'content'] = $pageInformation->getPageLayout()?->getContentAreas()->withRequest($this->request);
|
|
return $variables;
|
|
}
|
|
}
|