TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
<?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 Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
|
||||
|
||||
/**
|
||||
* Contains an abstract class for all tslib content class implementations.
|
||||
*/
|
||||
abstract class AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Always set via setRequest() by ContentObjectFactory after instantiation
|
||||
*/
|
||||
protected ServerRequestInterface $request;
|
||||
|
||||
protected ?ContentObjectRenderer $cObj = null;
|
||||
|
||||
/**
|
||||
* Renders the content object.
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string
|
||||
* @throws ContentRenderingException
|
||||
* @throws \Exception
|
||||
*/
|
||||
abstract public function render($conf = []);
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->cObj;
|
||||
}
|
||||
|
||||
public function setRequest(ServerRequestInterface $request): void
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
|
||||
{
|
||||
$this->cObj = $cObj;
|
||||
// Provide the ContentObjectRenderer to the request as well, for code
|
||||
// that only passes the request to more underlying layers, like Extbase does.
|
||||
// Also makes sure the request in a Fluid RenderingContext also has the current
|
||||
// content object available.
|
||||
$this->request = $this->request->withAttribute('currentContentObject', $cObj);
|
||||
}
|
||||
|
||||
protected function getPageRepository(): PageRepository
|
||||
{
|
||||
return GeneralUtility::makeInstance(PageRepository::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Contains CASE class object.
|
||||
*/
|
||||
class CaseContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, CASE
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$setCurrent = $this->cObj->stdWrapValue('setCurrent', $conf);
|
||||
if ($setCurrent) {
|
||||
$this->cObj->data[$this->cObj->currentValKey] = $setCurrent;
|
||||
}
|
||||
$key = $this->cObj->stdWrapValue('key', $conf, null);
|
||||
$key = isset($conf[$key]) && (string)$conf[$key] !== '' ? $key : 'default';
|
||||
// If no "default" property is available, then an empty string is returned
|
||||
if ($key === 'default' && !isset($conf['default'])) {
|
||||
$theValue = '';
|
||||
} else {
|
||||
$theValue = $this->cObj->cObjGetSingle($conf[$key], $conf[$key . '.'] ?? [], $key);
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Event\ModifyRecordsAfterFetchingContentEvent;
|
||||
|
||||
/**
|
||||
* Contains CONTENT class object.
|
||||
*/
|
||||
class ContentContentObject extends AbstractContentObject
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TimeTracker $timeTracker,
|
||||
private readonly EventDispatcherInterface $eventDispatcher,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Rendering the cObject, CONTENT
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$theValue = '';
|
||||
$conf['table'] = trim((string)$this->cObj->stdWrapValue('table', $conf));
|
||||
$conf['select.'] = !empty($conf['select.']) ? $conf['select.'] : [];
|
||||
$renderObjName = ($conf['renderObj'] ?? false) ? $conf['renderObj'] : '<' . $conf['table'];
|
||||
$renderObjKey = ($conf['renderObj'] ?? false) ? 'renderObj' : '';
|
||||
$renderObjConf = $conf['renderObj.'] ?? [];
|
||||
$slide = (int)$this->cObj->stdWrapValue('slide', $conf);
|
||||
if (!$slide) {
|
||||
$slide = 0;
|
||||
}
|
||||
$slideCollect = (int)$this->cObj->stdWrapValue('collect', $conf['slide.'] ?? []);
|
||||
if (!$slideCollect) {
|
||||
$slideCollect = 0;
|
||||
}
|
||||
$slideCollectReverse = (bool)$this->cObj->stdWrapValue('collectReverse', $conf['slide.'] ?? []);
|
||||
$slideCollectFuzzy = (bool)$this->cObj->stdWrapValue('collectFuzzy', $conf['slide.'] ?? []);
|
||||
if (!$slideCollect) {
|
||||
$slideCollectFuzzy = true;
|
||||
}
|
||||
$again = false;
|
||||
$tmpValue = '';
|
||||
|
||||
do {
|
||||
$cobjValue = '';
|
||||
$modifyRecordsEvent = $this->eventDispatcher->dispatch(
|
||||
new ModifyRecordsAfterFetchingContentEvent(
|
||||
$this->cObj->getRecords($conf['table'], $conf['select.']),
|
||||
$theValue,
|
||||
$slide,
|
||||
$slideCollect,
|
||||
$slideCollectReverse,
|
||||
$slideCollectFuzzy,
|
||||
$conf
|
||||
)
|
||||
);
|
||||
|
||||
$records = $modifyRecordsEvent->getRecords();
|
||||
$theValue = $modifyRecordsEvent->getFinalContent();
|
||||
$slide = $modifyRecordsEvent->getSlide();
|
||||
$slideCollect = $modifyRecordsEvent->getSlideCollect();
|
||||
$slideCollectReverse = $modifyRecordsEvent->getSlideCollectReverse();
|
||||
$slideCollectFuzzy = $modifyRecordsEvent->getSlideCollectFuzzy();
|
||||
$conf = $modifyRecordsEvent->getConfiguration();
|
||||
|
||||
if ($records !== []) {
|
||||
$this->timeTracker->setTSlogMessage('NUMROWS: ' . count($records));
|
||||
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$cObj->setParent($this->cObj->data, $this->cObj->currentRecord);
|
||||
|
||||
foreach ($records as $row) {
|
||||
$this->cObj->lastChanged($row['tstamp'] ?? 0);
|
||||
$cObj->setRequest($this->request);
|
||||
$cObj->start($row, $conf['table']);
|
||||
$tmpValue = $cObj->cObjGetSingle($renderObjName, $renderObjConf, $renderObjKey);
|
||||
$cobjValue .= $tmpValue;
|
||||
}
|
||||
}
|
||||
if ($slideCollectReverse) {
|
||||
$theValue = $cobjValue . $theValue;
|
||||
} else {
|
||||
$theValue .= $cobjValue;
|
||||
}
|
||||
if ($slideCollect > 0) {
|
||||
$slideCollect--;
|
||||
}
|
||||
if ($slide) {
|
||||
if ($slide > 0) {
|
||||
$slide--;
|
||||
}
|
||||
$conf['select.']['pidInList'] = $this->cObj->getSlidePids(
|
||||
$conf['select.']['pidInList'] ?? '',
|
||||
$conf['select.']['pidInList.'] ?? [],
|
||||
);
|
||||
if (isset($conf['select.']['pidInList.'])) {
|
||||
unset($conf['select.']['pidInList.']);
|
||||
}
|
||||
$again = (string)$conf['select.']['pidInList'] !== '';
|
||||
}
|
||||
} while ($again && $slide && ((string)$tmpValue === '' && $slideCollectFuzzy || $slideCollect));
|
||||
|
||||
$wrap = $this->cObj->stdWrapValue('wrap', $conf);
|
||||
if ($wrap) {
|
||||
$theValue = $this->cObj->wrap($theValue, $wrap);
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\DataProcessing\DataProcessorRegistry;
|
||||
|
||||
/**
|
||||
* A class that contains methods that can be used to use the dataProcessing functionality
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
readonly class ContentDataProcessor
|
||||
{
|
||||
public function __construct(
|
||||
private ContainerInterface $container,
|
||||
private DataProcessorRegistry $dataProcessorRegistry,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Check for the availability of processors, defined in TypoScript, and use them for data processing
|
||||
*
|
||||
* @param array $configuration Configuration array
|
||||
* @param array $variables the variables to be processed
|
||||
* @return array the processed data and variables as key/value store
|
||||
* @throws \UnexpectedValueException If a processor class does not exist
|
||||
*/
|
||||
public function process(ContentObjectRenderer $cObject, array $configuration, array $variables)
|
||||
{
|
||||
if (
|
||||
!empty($configuration['dataProcessing.'])
|
||||
&& is_array($configuration['dataProcessing.'])
|
||||
) {
|
||||
$processors = $configuration['dataProcessing.'];
|
||||
$processorKeys = ArrayUtility::filterAndSortByNumericKeys($processors);
|
||||
|
||||
foreach ($processorKeys as $key) {
|
||||
$dataProcessor = $this->dataProcessorRegistry->getDataProcessor($processors[$key])
|
||||
?? $this->getDataProcessor($processors[$key]);
|
||||
$processorConfiguration = $processors[$key . '.'] ?? [];
|
||||
$variables = $dataProcessor->process(
|
||||
$cObject,
|
||||
$configuration,
|
||||
$processorConfiguration,
|
||||
$variables
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
private function getDataProcessor(string $serviceName): DataProcessorInterface
|
||||
{
|
||||
if (!$this->container->has($serviceName)) {
|
||||
// assume serviceName is the class name if it is not available in the container
|
||||
return $this->instantiateDataProcessor($serviceName);
|
||||
}
|
||||
|
||||
$dataProcessor = $this->container->get($serviceName);
|
||||
if (!$dataProcessor instanceof DataProcessorInterface) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Processor with service name "' . $serviceName . '" '
|
||||
. 'must implement interface "' . DataProcessorInterface::class . '"',
|
||||
1635927108
|
||||
);
|
||||
}
|
||||
return $dataProcessor;
|
||||
}
|
||||
|
||||
private function instantiateDataProcessor(string $className): DataProcessorInterface
|
||||
{
|
||||
if (!class_exists($className)) {
|
||||
throw new \UnexpectedValueException('Processor class or service name "' . $className . '" does not exist!', 1427455378);
|
||||
}
|
||||
|
||||
if (!in_array(DataProcessorInterface::class, class_implements($className) ?: [], true)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'Processor with class name "' . $className . '" '
|
||||
. 'must implement interface "' . DataProcessorInterface::class . '"',
|
||||
1427455377
|
||||
);
|
||||
}
|
||||
return GeneralUtility::makeInstance($className);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Contains COA class object.
|
||||
*/
|
||||
class ContentObjectArrayContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, COBJ_ARRAY / COA
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (empty($conf)) {
|
||||
$this->getTimeTracker()->setTSlogMessage('No elements in this content object array (COBJ_ARRAY, COA).', LogLevel::WARNING);
|
||||
return '';
|
||||
}
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$content = $this->cObj->cObjGet($conf);
|
||||
$wrap = $this->cObj->stdWrapValue('wrap', $conf);
|
||||
if ($wrap) {
|
||||
$content = $this->cObj->wrap($content, $wrap);
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TimeTracker
|
||||
*/
|
||||
protected function getTimeTracker()
|
||||
{
|
||||
return GeneralUtility::makeInstance(TimeTracker::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
|
||||
/**
|
||||
* Contains COA_INT class object.
|
||||
*/
|
||||
class ContentObjectArrayInternalContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, COA_INT
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (empty($conf)) {
|
||||
$this->getTimeTracker()->setTSlogMessage('No elements in this content object array (COA_INT).', LogLevel::WARNING);
|
||||
return '';
|
||||
}
|
||||
$substKey = 'INT_SCRIPT.' . md5(StringUtility::getUniqueId());
|
||||
$pageParts = $this->request->getAttribute('frontend.page.parts');
|
||||
$pageParts->addNotCachedContentElement([
|
||||
'substKey' => $substKey,
|
||||
'conf' => $conf,
|
||||
'cObjData' => serialize($this->cObj->getState()),
|
||||
'type' => 'COA',
|
||||
]);
|
||||
return '<!--' . $substKey . '-->';
|
||||
}
|
||||
|
||||
protected function getTimeTracker(): TimeTracker
|
||||
{
|
||||
return GeneralUtility::makeInstance(TimeTracker::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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 Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException;
|
||||
|
||||
/**
|
||||
* Registry to create cObjects (e.g. TEXT)
|
||||
* @internal
|
||||
*/
|
||||
class ContentObjectFactory
|
||||
{
|
||||
public function __construct(private ContainerInterface $contentObjectLocator) {}
|
||||
|
||||
public function getContentObject(string $name, ServerRequestInterface $request, ContentObjectRenderer $contentObjectRenderer): ?AbstractContentObject
|
||||
{
|
||||
if (!$this->contentObjectLocator->has($name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$contentObject = $this->contentObjectLocator->get($name);
|
||||
if (!($contentObject instanceof AbstractContentObject)) {
|
||||
throw new ContentRenderingException(sprintf('Registered content object class name "%s" must be an instance of AbstractContentObject, but is not!', get_class($contentObject)), 1422564295);
|
||||
}
|
||||
|
||||
$contentObject->setRequest($request);
|
||||
$contentObject->setContentObjectRenderer($contentObjectRenderer);
|
||||
|
||||
return $contentObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Resource\File;
|
||||
|
||||
/**
|
||||
* Interface for hooks to fetch the public URL of files
|
||||
*/
|
||||
interface ContentObjectGetPublicUrlForFileHookInterface
|
||||
{
|
||||
/**
|
||||
* Post-processes a public URL.
|
||||
*
|
||||
* @param \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $parent The current content object (context)
|
||||
* @param array $configuration TypoScript configuration
|
||||
* @param File $file The file object to be used
|
||||
* @param string $pubicUrl Reference to the public URL
|
||||
*/
|
||||
public function postProcess(ContentObjectRenderer $parent, array $configuration, File $file, &$pubicUrl);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Interface for data processor classes processing data from
|
||||
* ContentObjectRenderer, used e.g. with the FLUIDTEMPLATE content object
|
||||
*/
|
||||
interface DataProcessorInterface
|
||||
{
|
||||
/**
|
||||
* Process content object data
|
||||
*
|
||||
* @param ContentObjectRenderer $cObj The data of the content element or page
|
||||
* @param array $contentObjectConfiguration The configuration of Content Object
|
||||
* @param array $processorConfiguration The configuration of this processor
|
||||
* @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View)
|
||||
* @return array the processed data as key/value store
|
||||
*/
|
||||
public function process(
|
||||
ContentObjectRenderer $cObj,
|
||||
array $contentObjectConfiguration,
|
||||
array $processorConfiguration,
|
||||
array $processedData
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Listeners are able to modify the initialized ContentObjectRenderer instance
|
||||
*/
|
||||
final readonly class AfterContentObjectRendererInitializedEvent
|
||||
{
|
||||
public function __construct(
|
||||
private ContentObjectRenderer $contentObjectRenderer
|
||||
) {}
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->contentObjectRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Listeners are able to modify the resolved ContentObjectRenderer->getData() result
|
||||
*/
|
||||
final class AfterGetDataResolvedEvent
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $parameterString,
|
||||
private readonly array $alternativeFieldArray,
|
||||
private mixed $result,
|
||||
private readonly ContentObjectRenderer $contentObjectRenderer
|
||||
) {}
|
||||
|
||||
public function getResult(): mixed
|
||||
{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function setResult(mixed $result): void
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
public function getParameterString(): string
|
||||
{
|
||||
return $this->parameterString;
|
||||
}
|
||||
|
||||
public function getAlternativeFieldArray(): array
|
||||
{
|
||||
return $this->alternativeFieldArray;
|
||||
}
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->contentObjectRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Core\Imaging\ImageResource;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
|
||||
/**
|
||||
* Listeners are able to modify the resolved ContentObjectRenderer->getImgResource() result
|
||||
*/
|
||||
final class AfterImageResourceResolvedEvent
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string|File|FileReference $file,
|
||||
private readonly array $fileArray,
|
||||
private ?ImageResource $imageResource
|
||||
) {}
|
||||
|
||||
public function getFile(): string|File|FileReference
|
||||
{
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
public function getFileArray(): array
|
||||
{
|
||||
return $this->fileArray;
|
||||
}
|
||||
|
||||
public function getImageResource(): ?ImageResource
|
||||
{
|
||||
return $this->imageResource;
|
||||
}
|
||||
|
||||
public function setImageResource(?ImageResource $imageResource): void
|
||||
{
|
||||
$this->imageResource = $imageResource;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Event is called after the content has been modified by the rest of the stdWrap functions
|
||||
*/
|
||||
final class AfterStdWrapFunctionsExecutedEvent extends EnhanceStdWrapEvent {}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Event is dispatched after stdWrap functions have been initialized,
|
||||
* but before any content gets modified or replaced.
|
||||
*/
|
||||
final class AfterStdWrapFunctionsInitializedEvent extends EnhanceStdWrapEvent {}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Listeners to this Event are able to modify the final stdWrap content
|
||||
* and corresponding cache tags, before being stored in cache.
|
||||
*
|
||||
* Additionally, listeners are also able to change the cache key to be used
|
||||
* as well as the lifetime. Therefore, the whole configuration is available.
|
||||
*/
|
||||
final class BeforeStdWrapContentStoredInCacheEvent
|
||||
{
|
||||
public function __construct(
|
||||
private ?string $content,
|
||||
private array $tags,
|
||||
private string $key,
|
||||
private ?int $lifetime,
|
||||
private readonly array $configuration,
|
||||
private readonly ContentObjectRenderer $contentObjectRenderer
|
||||
) {}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(string $content): void
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getTags(): array
|
||||
{
|
||||
return $this->tags;
|
||||
}
|
||||
|
||||
public function setTags(array $tags): void
|
||||
{
|
||||
$this->tags = $tags;
|
||||
}
|
||||
|
||||
public function getKey(): string
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function setKey(string $key): void
|
||||
{
|
||||
$this->key = $key;
|
||||
}
|
||||
|
||||
public function getLifetime(): ?int
|
||||
{
|
||||
return $this->lifetime;
|
||||
}
|
||||
|
||||
public function setLifetime(?int $lifetime): void
|
||||
{
|
||||
$this->lifetime = $lifetime;
|
||||
}
|
||||
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->contentObjectRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Event is called directly after the recursive stdWrap function call but still before the content gets modified
|
||||
*/
|
||||
final class BeforeStdWrapFunctionsExecutedEvent extends EnhanceStdWrapEvent {}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Event is dispatched before any stdWrap function is initialized / called
|
||||
*/
|
||||
final class BeforeStdWrapFunctionsInitializedEvent extends EnhanceStdWrapEvent {}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Listeners to this Event are able to modify the stdWrap processing, enhancing the functionality and
|
||||
* manipulating the final result / content. This is the parent Event, which allows the corresponding
|
||||
* listeners to be called on each step, see child Events:
|
||||
*
|
||||
* @see BeforeStdWrapFunctionsInitializedEvent
|
||||
* @see AfterStdWrapFunctionsInitializedEvent
|
||||
* @see BeforeStdWrapFunctionsExecutedEvent
|
||||
* @see AfterStdWrapFunctionsExecutedEvent
|
||||
*
|
||||
* Note: The class is declared abstract to prevent it from being dispatched directly.
|
||||
* Only child classes are to be dispatched to prevent duplicate executions.
|
||||
*/
|
||||
abstract class EnhanceStdWrapEvent
|
||||
{
|
||||
public function __construct(
|
||||
private ?string $content,
|
||||
private readonly array $configuration,
|
||||
private readonly ContentObjectRenderer $contentObjectRenderer
|
||||
) {}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(string $content): void
|
||||
{
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->contentObjectRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?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\Event;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Listeners are able to enrich the final source collection result
|
||||
*/
|
||||
final class ModifyImageSourceCollectionEvent
|
||||
{
|
||||
public function __construct(
|
||||
private string $sourceCollection,
|
||||
private readonly string $fullSourceCollection,
|
||||
private readonly array $sourceConfiguration,
|
||||
private readonly array $sourceRenderConfiguration,
|
||||
private readonly ContentObjectRenderer $contentObjectRenderer
|
||||
) {}
|
||||
|
||||
public function setSourceCollection(string $sourceCollection): void
|
||||
{
|
||||
$this->sourceCollection = $sourceCollection;
|
||||
}
|
||||
|
||||
public function getSourceCollection(): string
|
||||
{
|
||||
return $this->sourceCollection;
|
||||
}
|
||||
|
||||
public function getFullSourceCollection(): string
|
||||
{
|
||||
return $this->fullSourceCollection;
|
||||
}
|
||||
|
||||
public function getSourceConfiguration(): array
|
||||
{
|
||||
return $this->sourceConfiguration;
|
||||
}
|
||||
|
||||
public function getSourceRenderConfiguration(): array
|
||||
{
|
||||
return $this->sourceRenderConfiguration;
|
||||
}
|
||||
|
||||
public function getContentObjectRenderer(): ContentObjectRenderer
|
||||
{
|
||||
return $this->contentObjectRenderer;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?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\Event;
|
||||
|
||||
/**
|
||||
* Event which is fired after ContentContentObject has pulled records from database.
|
||||
*
|
||||
* Therefore, allows listeners to completely manipulate the fetched
|
||||
* records, prior to being further processed by the content object.
|
||||
*
|
||||
* Additionally, the event also allows to manipulate the configuration
|
||||
* and options, such as the "value" or "slide".
|
||||
*/
|
||||
final class ModifyRecordsAfterFetchingContentEvent
|
||||
{
|
||||
public function __construct(
|
||||
private array $records,
|
||||
private string $finalContent,
|
||||
private int $slide,
|
||||
private int $slideCollect,
|
||||
private bool $slideCollectReverse,
|
||||
private bool $slideCollectFuzzy,
|
||||
private array $configuration,
|
||||
) {}
|
||||
|
||||
public function getRecords(): array
|
||||
{
|
||||
return $this->records;
|
||||
}
|
||||
|
||||
public function setRecords(array $records): void
|
||||
{
|
||||
$this->records = $records;
|
||||
}
|
||||
|
||||
public function getFinalContent(): string
|
||||
{
|
||||
return $this->finalContent;
|
||||
}
|
||||
|
||||
public function setFinalContent(string $finalContent): void
|
||||
{
|
||||
$this->finalContent = $finalContent;
|
||||
}
|
||||
|
||||
public function getSlide(): int
|
||||
{
|
||||
return $this->slide;
|
||||
}
|
||||
|
||||
public function setSlide(int $slide): void
|
||||
{
|
||||
$this->slide = $slide;
|
||||
}
|
||||
|
||||
public function getSlideCollect(): int
|
||||
{
|
||||
return $this->slideCollect;
|
||||
}
|
||||
|
||||
public function setSlideCollect(int $slideCollect): void
|
||||
{
|
||||
$this->slideCollect = $slideCollect;
|
||||
}
|
||||
|
||||
public function getSlideCollectReverse(): bool
|
||||
{
|
||||
return $this->slideCollectReverse;
|
||||
}
|
||||
|
||||
public function setSlideCollectReverse(bool $slideCollectReverse): void
|
||||
{
|
||||
$this->slideCollectReverse = $slideCollectReverse;
|
||||
}
|
||||
|
||||
public function getSlideCollectFuzzy(): bool
|
||||
{
|
||||
return $this->slideCollectFuzzy;
|
||||
}
|
||||
|
||||
public function setSlideCollectFuzzy(bool $slideCollectFuzzy): void
|
||||
{
|
||||
$this->slideCollectFuzzy = $slideCollectFuzzy;
|
||||
}
|
||||
|
||||
public function getConfiguration(): array
|
||||
{
|
||||
return $this->configuration;
|
||||
}
|
||||
|
||||
public function setConfiguration(array $configuration): void
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Exception;
|
||||
|
||||
use TYPO3\CMS\Core\Error\Exception;
|
||||
|
||||
/**
|
||||
* @internal this is a concrete TYPO3 implementation and solely used for EXT:frontend and not part of TYPO3's Core API.
|
||||
*/
|
||||
class ContentRenderingException extends Exception {}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Exception;
|
||||
|
||||
use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject;
|
||||
|
||||
/**
|
||||
* Interface ExceptionHandlerInterface
|
||||
*/
|
||||
interface ExceptionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Handles exceptions thrown during rendering of content objects
|
||||
* The handler can decide whether to re-throw the exception or
|
||||
* return a nice error message for production context.
|
||||
*
|
||||
* @param array $contentObjectConfiguration
|
||||
* @return string
|
||||
*/
|
||||
public function handle(\Exception $exception, ?AbstractContentObject $contentObject = null, $contentObjectConfiguration = []);
|
||||
|
||||
/**
|
||||
* Used to pass the TypoScript configuration to the exception handler
|
||||
*/
|
||||
public function setConfiguration(array $configuration): void;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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\Exception;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Core\RequestId;
|
||||
use TYPO3\CMS\Core\Crypto\Random;
|
||||
use TYPO3\CMS\Core\Error\AbstractExceptionHandler;
|
||||
use TYPO3\CMS\Core\Http\ImmediateResponseException;
|
||||
use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject;
|
||||
|
||||
/**
|
||||
* Exception handler class for content object rendering
|
||||
* @internal this is a concrete TYPO3 implementation and solely used for EXT:frontend and not part of TYPO3's Core API.
|
||||
*/
|
||||
#[Autoconfigure(public: true, shared: false)]
|
||||
class ProductionExceptionHandler implements ExceptionHandlerInterface
|
||||
{
|
||||
protected array $configuration = [];
|
||||
|
||||
public function __construct(
|
||||
protected Context $context,
|
||||
protected Random $random,
|
||||
protected LoggerInterface $logger,
|
||||
protected RequestId $requestId
|
||||
) {}
|
||||
|
||||
public function setConfiguration(array $configuration): void
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles exceptions thrown during rendering of content objects
|
||||
* The handler can decide whether to re-throw the exception or
|
||||
* return a nice error message for production context.
|
||||
*
|
||||
* @param array $contentObjectConfiguration
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(\Exception $exception, ?AbstractContentObject $contentObject = null, $contentObjectConfiguration = []): string
|
||||
{
|
||||
// ImmediateResponseException (and the derived PropagateResponseException) should work similar to
|
||||
// exit / die and must therefore not be handled by this ExceptionHandler.
|
||||
if ($exception instanceof ImmediateResponseException) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
if (!empty($this->configuration['ignoreCodes.'])
|
||||
&& in_array($exception->getCode(), array_map('intval', $this->configuration['ignoreCodes.']), true)
|
||||
) {
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
$errorMessage = $this->configuration['errorMessage'] ?? 'Oops, an error occurred! Request: {requestId}';
|
||||
|
||||
// $code and it's placeholder %s for b/w compatibility
|
||||
$code = $this->context->getAspect('date')->getDateTime()->format('YmdHis') . $this->random->generateRandomHexString(8);
|
||||
$errorMessage = str_replace('%s', '{code}', $errorMessage);
|
||||
|
||||
// Log exception except HMAC validation exceptions caused by potentially forged requests
|
||||
if (!in_array($exception->getCode(), AbstractExceptionHandler::IGNORED_HMAC_EXCEPTION_CODES, true)) {
|
||||
$this->logger->alert($errorMessage, ['exception' => $exception, 'code' => $code, 'requestId' => $this->requestId]);
|
||||
}
|
||||
|
||||
// Return interpolated error message
|
||||
return str_replace(['{code}', '{requestId}'], [$code, (string)$this->requestId], $errorMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Resource\File;
|
||||
|
||||
/**
|
||||
* Interface for classes which hook into \TYPO3\CMS\Frontend\ContentObject and do additional getImgResource processing
|
||||
*/
|
||||
interface FileLinkHookInterface
|
||||
{
|
||||
/**
|
||||
* Finds alternative previewImage for given File.
|
||||
*
|
||||
* @return File
|
||||
*/
|
||||
public function getPreviewImage(File $file);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Frontend\Resource\FileCollector;
|
||||
|
||||
/**
|
||||
* Contains FILES content object
|
||||
*/
|
||||
class FilesContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject FILES
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
$register = $this->request->getAttribute('frontend.register.stack')->current();
|
||||
// Store the original "currentFile" within a variable so it can be re-applied later-on
|
||||
$originalFileInContentObject = $this->cObj->getCurrentFile();
|
||||
|
||||
$fileCollector = $this->findAndSortFiles($conf);
|
||||
$fileObjects = $fileCollector->getFiles();
|
||||
$availableFileObjectCount = count($fileObjects);
|
||||
|
||||
// optionSplit applied to conf to allow different settings per file
|
||||
$splitConf = GeneralUtility::makeInstance(TypoScriptService::class)
|
||||
->explodeConfigurationForOptionSplit($conf, $availableFileObjectCount);
|
||||
|
||||
$start = (int)$this->cObj->stdWrapValue('begin', $conf, 0);
|
||||
$start = MathUtility::forceIntegerInRange($start, 0, $availableFileObjectCount);
|
||||
|
||||
$limit = (int)$this->cObj->stdWrapValue('maxItems', $conf, $availableFileObjectCount);
|
||||
$end = MathUtility::forceIntegerInRange($start + $limit, $start, $availableFileObjectCount);
|
||||
|
||||
$register->set('FILES_COUNT', min($limit, $availableFileObjectCount));
|
||||
$fileObjectCounter = 0;
|
||||
$keys = array_keys($fileObjects);
|
||||
|
||||
$content = '';
|
||||
for ($i = $start; $i < $end; $i++) {
|
||||
$key = $keys[$i];
|
||||
$fileObject = $fileObjects[$key];
|
||||
$register->set('FILE_NUM_CURRENT', $fileObjectCounter);
|
||||
$this->cObj->setCurrentFile($fileObject);
|
||||
$content .= $this->cObj->cObjGetSingle($splitConf[$key]['renderObj'], $splitConf[$key]['renderObj.'], 'renderObj');
|
||||
$fileObjectCounter++;
|
||||
}
|
||||
|
||||
// Reset current file within cObj to the original file after rendering output of FILES
|
||||
// so e.g. stdWrap is not working on the last current file applied, thus avoiding side-effects
|
||||
$this->cObj->setCurrentFile($originalFileInContentObject);
|
||||
|
||||
return $this->cObj->stdWrap($content, $conf['stdWrap.'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to check for references, collections, folders and
|
||||
* accumulates into one etc.
|
||||
*/
|
||||
protected function findAndSortFiles(array $conf): FileCollector
|
||||
{
|
||||
$fileCollector = $this->getFileCollector();
|
||||
|
||||
// Getting the files
|
||||
if ((isset($conf['references']) && $conf['references']) || (isset($conf['references.']) && $conf['references.'])) {
|
||||
/*
|
||||
The TypoScript could look like this:
|
||||
# all items related to the page.media field:
|
||||
references {
|
||||
table = pages
|
||||
uid.data = page:uid
|
||||
fieldName = media
|
||||
}
|
||||
# or: sys_file_references with uid 27:
|
||||
references = 27
|
||||
*/
|
||||
$referencesUidList = (string)$this->cObj->stdWrapValue('references', $conf);
|
||||
$referencesUids = GeneralUtility::intExplode(',', $referencesUidList, true);
|
||||
$fileCollector->addFileReferences($referencesUids);
|
||||
|
||||
if (!empty($conf['references.'])) {
|
||||
$this->addFileReferences($conf, (array)$this->cObj->data, $fileCollector);
|
||||
}
|
||||
}
|
||||
|
||||
if ((isset($conf['files']) && $conf['files']) || (isset($conf['files.']) && $conf['files.'])) {
|
||||
/*
|
||||
The TypoScript could look like this:
|
||||
# with sys_file UIDs:
|
||||
files = 12,14,15# using stdWrap:
|
||||
files.field = some_field
|
||||
*/
|
||||
$fileUids = GeneralUtility::intExplode(',', (string)$this->cObj->stdWrapValue('files', $conf), true);
|
||||
$fileCollector->addFiles($fileUids);
|
||||
}
|
||||
|
||||
if ((isset($conf['collections']) && $conf['collections']) || (isset($conf['collections.']) && $conf['collections.'])) {
|
||||
$collectionUids = GeneralUtility::intExplode(',', (string)$this->cObj->stdWrapValue('collections', $conf), true);
|
||||
$fileCollector->addFilesFromFileCollections($collectionUids);
|
||||
}
|
||||
|
||||
if ((isset($conf['folders']) && $conf['folders']) || (isset($conf['folders.']) && $conf['folders.'])) {
|
||||
$folderIdentifiers = GeneralUtility::trimExplode(',', (string)$this->cObj->stdWrapValue('folders', $conf));
|
||||
$fileCollector->addFilesFromFolders($folderIdentifiers, !empty($conf['folders.']['recursive']));
|
||||
}
|
||||
|
||||
// Enable sorting for multiple fileObjects
|
||||
$sortingProperty = (string)$this->cObj->stdWrapValue('sorting', $conf);
|
||||
if ($sortingProperty !== '') {
|
||||
$sortingDirection = $this->cObj->stdWrapValue('direction', $conf['sorting.'] ?? []);
|
||||
$fileCollector->sort($sortingProperty, $sortingDirection);
|
||||
}
|
||||
|
||||
return $fileCollector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles and resolves file references.
|
||||
*
|
||||
* @param array $configuration TypoScript configuration
|
||||
* @param array $element The parent element referencing to files
|
||||
*/
|
||||
protected function addFileReferences(array $configuration, array $element, FileCollector $fileCollector): void
|
||||
{
|
||||
// It's important that this always stays "fieldName" and not be renamed to "field" as it would otherwise collide with the stdWrap key of that name
|
||||
$referencesFieldName = $this->cObj->stdWrapValue('fieldName', $configuration['references.'] ?? []);
|
||||
|
||||
// If no reference fieldName is set, there's nothing to do
|
||||
if (empty($referencesFieldName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$currentId = !empty($element['uid']) ? $element['uid'] : 0;
|
||||
$tableName = $this->cObj->getCurrentTable();
|
||||
|
||||
// Fetch the references of the default element
|
||||
$referencesForeignTable = (string)$this->cObj->stdWrapValue('table', $configuration['references.'], $tableName);
|
||||
$referencesForeignUid = (int)$this->cObj->stdWrapValue('uid', $configuration['references.'], $currentId);
|
||||
|
||||
$pageRepository = $this->getPageRepository();
|
||||
// Fetch element if definition has been modified via TypoScript
|
||||
if (
|
||||
($referencesForeignTable !== '' && $referencesForeignTable !== $tableName)
|
||||
|| ($referencesForeignUid !== 0 && $referencesForeignUid !== $currentId)
|
||||
) {
|
||||
$element = $pageRepository->getRawRecord($referencesForeignTable, $referencesForeignUid);
|
||||
// Do versionOL() again and unset move pointers
|
||||
$pageRepository->versionOL($referencesForeignTable, $element, true);
|
||||
if (is_array($element)) {
|
||||
$element = $pageRepository->getLanguageOverlay($referencesForeignTable, $element);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($element)) {
|
||||
$fileCollector->addFilesFromRelation($referencesForeignTable ?: $tableName, $referencesFieldName, $element);
|
||||
}
|
||||
}
|
||||
|
||||
protected function getFileCollector(): FileCollector
|
||||
{
|
||||
return GeneralUtility::makeInstance(FileCollector::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Core\Information\Typo3Information;
|
||||
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\View\ViewFactoryData;
|
||||
use TYPO3\CMS\Core\View\ViewFactoryInterface;
|
||||
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
|
||||
use TYPO3\CMS\Extbase\Mvc\Web\RequestBuilder;
|
||||
use TYPO3\CMS\Fluid\View\FluidViewAdapter;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Contains FLUIDTEMPLATE class object
|
||||
*/
|
||||
class FluidTemplateContentObject extends AbstractContentObject
|
||||
{
|
||||
public function __construct(
|
||||
private readonly ContentDataProcessor $contentDataProcessor,
|
||||
private readonly TypoScriptService $typoScriptService,
|
||||
private readonly ViewFactoryInterface $viewFactory,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* 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/Partials/
|
||||
* 10.layoutRootPaths.10 = EXT:site_configuration/Resources/Private/Layouts/
|
||||
* 10.variables {
|
||||
* mylabel = TEXT
|
||||
* mylabel.value = Label from TypoScript coming
|
||||
* }
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
if (!is_array($conf)) {
|
||||
$conf = [];
|
||||
}
|
||||
|
||||
$request = $this->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 = <f:foo ..."
|
||||
$templateSource = $this->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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Menu\MenuContentObjectFactory;
|
||||
|
||||
/**
|
||||
* Contains HMENU class object.
|
||||
*/
|
||||
class HierarchicalMenuContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, HMENU
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$theValue = '';
|
||||
$menuType = $conf[1] ?? '';
|
||||
try {
|
||||
$register = $this->request->getAttribute('frontend.register.stack')->current();
|
||||
$menuObjectFactory = GeneralUtility::makeInstance(MenuContentObjectFactory::class);
|
||||
$menu = $menuObjectFactory->getMenuObjectByType($menuType);
|
||||
$countHMENU = (int)$register->get('count_HMENU', 0);
|
||||
$countHMENU++;
|
||||
$register->set('count_HMENU', $countHMENU);
|
||||
$register->set('count_HMENU_MENUOBJ', 0);
|
||||
$register->set('count_MENUOBJ', 0);
|
||||
$menu->parent_cObj = $this->getContentObjectRenderer();
|
||||
$menu->start(null, $this->getPageRepository(), '', $conf, 1, '', $this->request);
|
||||
$menu->makeMenu();
|
||||
$theValue .= $menu->writeMenu();
|
||||
} catch (NoSuchMenuTypeException) {
|
||||
}
|
||||
$wrap = $this->cObj->stdWrapValue('wrap', $conf);
|
||||
if ($wrap) {
|
||||
$theValue = $this->cObj->wrap($theValue, $wrap);
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\Page\AssetCollector;
|
||||
use TYPO3\CMS\Core\Resource\File;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Type\DocType;
|
||||
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Event\ModifyImageSourceCollectionEvent;
|
||||
use TYPO3\CMS\Frontend\Page\FrontendUrlPrefix;
|
||||
|
||||
/**
|
||||
* Contains IMAGE class object.
|
||||
*/
|
||||
class ImageContentObject extends AbstractContentObject
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly MarkerBasedTemplateService $markerTemplateService,
|
||||
protected readonly TimeTracker $timeTracker,
|
||||
protected readonly LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Rendering the cObject, IMAGE
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$theValue = $this->cImage($conf['file'] ?? '', is_array($conf) ? $conf : []);
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <img> tag with the image file defined by $file and processed according to the properties in the TypoScript array.
|
||||
* Mostly this function is a sub-function to the IMAGE function which renders the IMAGE cObject in TypoScript.
|
||||
*
|
||||
* @param string|File|FileReference|null $file File TypoScript resource
|
||||
* @param array $conf TypoScript configuration properties
|
||||
* @return string HTML <img> tag, (possibly wrapped in links and other HTML) if any image found.
|
||||
*/
|
||||
protected function cImage($file, array $conf): string
|
||||
{
|
||||
$imageResource = $this->cObj->getImgResource($file, $conf['file.'] ?? []);
|
||||
if ($imageResource === null) {
|
||||
return '';
|
||||
}
|
||||
// $info['originalFile'] will be set, when the file is processed by FAL.
|
||||
// In that case the URL is final and we must not add a prefix
|
||||
if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) {
|
||||
$absRefPrefix = GeneralUtility::makeInstance(FrontendUrlPrefix::class)->getUrlPrefix($this->request);
|
||||
$source = $absRefPrefix . str_replace('%2F', '/', rawurlencode($imageResource->getPublicUrl()));
|
||||
} else {
|
||||
$source = $imageResource->getPublicUrl();
|
||||
}
|
||||
// A file whose physical resource is gone (sys_file.missing=1) resolves to
|
||||
// an image resource without public URL. Render nothing in this case, just
|
||||
// like for an image resource that could not be resolved at all.
|
||||
if ($source === null) {
|
||||
$identifier = $imageResource->getOriginalFile()?->getIdentifier() ?: $imageResource->getFullPath();
|
||||
$this->logger->warning('The image "{file}" has no public URL, the file is probably missing, and won\'t be included in frontend output', [
|
||||
'file' => $identifier,
|
||||
]);
|
||||
$this->timeTracker->setTSlogMessage(
|
||||
'The image "' . $identifier . '" has no public URL, the file is probably missing. It is not rendered.',
|
||||
LogLevel::WARNING
|
||||
);
|
||||
return '';
|
||||
}
|
||||
GeneralUtility::makeInstance(AssetCollector::class)->addMedia(
|
||||
$source,
|
||||
$imageResource->getLegacyImageResourceInformation()
|
||||
);
|
||||
|
||||
$layoutKey = (string)$this->cObj->stdWrapValue('layoutKey', $conf);
|
||||
$imageTagTemplate = $this->getImageTagTemplate($layoutKey, $conf);
|
||||
$sourceCollection = $this->getImageSourceCollection($layoutKey, $conf, $file);
|
||||
|
||||
$altParam = $this->getAltParam($conf);
|
||||
$params = $this->cObj->stdWrapValue('params', $conf);
|
||||
if ($params !== '' && $params[0] !== ' ') {
|
||||
$params = ' ' . $params;
|
||||
}
|
||||
|
||||
$imageTagValues = [
|
||||
'width' => $imageResource->getWidth(),
|
||||
'height' => $imageResource->getHeight(),
|
||||
'src' => htmlspecialchars($source),
|
||||
'params' => $params,
|
||||
'altParams' => $altParam,
|
||||
'sourceCollection' => $sourceCollection,
|
||||
'selfClosingTagSlash' => DocType::createFromRequest($this->request)->isXmlCompliant() ? ' /' : '',
|
||||
];
|
||||
|
||||
$theValue = $this->markerTemplateService->substituteMarkerArray($imageTagTemplate, $imageTagValues, '###|###', true, true);
|
||||
|
||||
$linkWrap = (string)$this->cObj->stdWrapValue('linkWrap', $conf);
|
||||
if ($linkWrap !== '') {
|
||||
$theValue = $this->linkWrap($theValue, $linkWrap);
|
||||
} elseif ($conf['imageLinkWrap'] ?? false) {
|
||||
$originalFile = urldecode($imageResource->getFullPath());
|
||||
$theValue = $this->cObj->imageLinkWrap($theValue, $originalFile, $conf['imageLinkWrap.']);
|
||||
}
|
||||
$wrap = $this->cObj->stdWrapValue('wrap', $conf);
|
||||
if ((string)$wrap !== '') {
|
||||
$theValue = $this->cObj->wrap($theValue, $conf['wrap']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the html-template for rendering the image-Tag if no template is defined via typoscript the
|
||||
* default <img> tag template is returned
|
||||
*
|
||||
* @param string $layoutKey rendering key
|
||||
* @param array $conf TypoScript configuration properties
|
||||
*/
|
||||
protected function getImageTagTemplate($layoutKey, $conf): string
|
||||
{
|
||||
if ($layoutKey && isset($conf['layout.']) && isset($conf['layout.'][$layoutKey . '.'])) {
|
||||
return $this->cObj->stdWrapValue('element', $conf['layout.'][$layoutKey . '.']);
|
||||
}
|
||||
return '<img src="###SRC###" width="###WIDTH###" height="###HEIGHT###" ###PARAMS### ###ALTPARAMS### ###SELFCLOSINGTAGSLASH###>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Render alternate sources for the image tag. If no source collection is given an empty string is returned.
|
||||
*
|
||||
* @param string $layoutKey rendering key
|
||||
* @param array $conf TypoScript configuration properties
|
||||
* @param string|File|FileReference|null $file
|
||||
* @return string
|
||||
*/
|
||||
protected function getImageSourceCollection(string $layoutKey, array $conf, $file)
|
||||
{
|
||||
$sourceCollection = '';
|
||||
if ($layoutKey
|
||||
&& isset($conf['sourceCollection.']) && $conf['sourceCollection.']
|
||||
&& (
|
||||
isset($conf['layout.'][$layoutKey . '.']['source']) && $conf['layout.'][$layoutKey . '.']['source']
|
||||
|| isset($conf['layout.'][$layoutKey . '.']['source.']) && $conf['layout.'][$layoutKey . '.']['source.']
|
||||
)
|
||||
) {
|
||||
// find active sourceCollection
|
||||
$activeSourceCollections = [];
|
||||
foreach ($conf['sourceCollection.'] as $sourceCollectionKey => $sourceCollectionConfiguration) {
|
||||
if (str_ends_with($sourceCollectionKey, '.')) {
|
||||
if (empty($sourceCollectionConfiguration['if.']) || $this->cObj->checkIf($sourceCollectionConfiguration['if.'])) {
|
||||
$activeSourceCollections[] = $sourceCollectionConfiguration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply option split to configurations
|
||||
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
|
||||
$srcLayoutOptionSplitted = $typoScriptService->explodeConfigurationForOptionSplit((array)$conf['layout.'][$layoutKey . '.'], count($activeSourceCollections));
|
||||
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
|
||||
|
||||
$isXmlCompliant = DocType::createFromRequest($this->request)->isXmlCompliant();
|
||||
|
||||
// render sources
|
||||
foreach ($activeSourceCollections as $key => $sourceConfiguration) {
|
||||
$sourceLayout = $this->cObj->stdWrapValue('source', $srcLayoutOptionSplitted[$key] ?? []);
|
||||
|
||||
$sourceRenderConfiguration = [
|
||||
'file' => $file,
|
||||
'file.' => $conf['file.'] ?? null,
|
||||
];
|
||||
|
||||
$imageQuality = $this->cObj->stdWrapValue('quality', $sourceConfiguration ?? []);
|
||||
if ($imageQuality) {
|
||||
$sourceRenderConfiguration['file.']['params'] = '-quality ' . (int)$imageQuality;
|
||||
}
|
||||
|
||||
$pixelDensity = (int)$this->cObj->stdWrapValue('pixelDensity', $sourceConfiguration, 1);
|
||||
$dimensionKeys = ['width', 'height', 'maxW', 'minW', 'maxH', 'minH', 'maxWidth', 'maxHeight', 'XY'];
|
||||
foreach ($dimensionKeys as $dimensionKey) {
|
||||
$dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $sourceConfiguration);
|
||||
if ($dimension === '') {
|
||||
$dimension = (string)$this->cObj->stdWrapValue($dimensionKey, $conf['file.'] ?? []);
|
||||
}
|
||||
if ($dimension !== '') {
|
||||
if (str_contains($dimension, 'c') && ($dimensionKey === 'width' || $dimensionKey === 'height')) {
|
||||
$dimensionParts = explode('c', $dimension, 2);
|
||||
$dimension = ((int)$dimensionParts[0] * $pixelDensity) . 'c';
|
||||
if ($dimensionParts[1]) {
|
||||
$dimension .= $dimensionParts[1];
|
||||
}
|
||||
} elseif ($dimensionKey === 'XY') {
|
||||
$dimensionParts = GeneralUtility::intExplode(',', $dimension);
|
||||
$dimension = $dimensionParts[0] * $pixelDensity;
|
||||
if ($dimensionParts[1]) {
|
||||
$dimension .= ',' . $dimensionParts[1] * $pixelDensity;
|
||||
}
|
||||
} else {
|
||||
$dimension = (int)$dimension * $pixelDensity;
|
||||
}
|
||||
$sourceRenderConfiguration['file.'][$dimensionKey] = $dimension;
|
||||
// Remove the stdWrap properties for dimension as they have been processed already above.
|
||||
unset($sourceRenderConfiguration['file.'][$dimensionKey . '.']);
|
||||
}
|
||||
}
|
||||
$imageResource = $this->cObj->getImgResource($sourceRenderConfiguration['file'], $sourceRenderConfiguration['file.']);
|
||||
if ($imageResource !== null) {
|
||||
$sourceConfiguration['width'] = $imageResource->getWidth();
|
||||
$sourceConfiguration['height'] = $imageResource->getHeight();
|
||||
|
||||
$urlPrefix = '';
|
||||
// Prepend 'absRefPrefix' to file path only if file was not processed by FAL, e.g. GIFBUILDER
|
||||
if ($imageResource->getOriginalFile() === null && is_file($imageResource->getFullPath())) {
|
||||
$urlPrefix = GeneralUtility::makeInstance(FrontendUrlPrefix::class)->getUrlPrefix($this->request);
|
||||
}
|
||||
|
||||
$sourceConfiguration['src'] = htmlspecialchars($urlPrefix . $imageResource->getPublicUrl());
|
||||
$sourceConfiguration['selfClosingTagSlash'] = $isXmlCompliant ? ' /' : '';
|
||||
|
||||
$oneSourceCollection = $this->markerTemplateService->substituteMarkerArray($sourceLayout, $sourceConfiguration, '###|###', true, true);
|
||||
|
||||
$sourceCollection .= $eventDispatcher->dispatch(
|
||||
new ModifyImageSourceCollectionEvent($oneSourceCollection, $sourceCollection, (array)$sourceConfiguration, $sourceRenderConfiguration, $this->cObj)
|
||||
)->getSourceCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $sourceCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps the input string by the $wrap value and implements the "linkWrap" data type as well.
|
||||
*
|
||||
* The "linkWrap" data type means that this function will find any integer encapsulated
|
||||
* in {} (curly braces) in the first wrap part and substitute it with the corresponding page
|
||||
* uid from the rootline where the found integer is pointing to the key in the rootline.
|
||||
*
|
||||
* @param string $content Input string
|
||||
* @param string $wrap A string where the first two parts separated by "|" (vertical line) will be wrapped around the input string
|
||||
*/
|
||||
protected function linkWrap(string $content, string $wrap): string
|
||||
{
|
||||
$wrapArr = explode('|', $wrap);
|
||||
if (preg_match('/\\{([0-9]*)\\}/', $wrapArr[0], $reg)) {
|
||||
$localRootLine = $this->request->getAttribute('frontend.page.information')->getLocalRootLine();
|
||||
$uid = $localRootLine[$reg[1]]['uid'] ?? null;
|
||||
if ($uid) {
|
||||
$wrapArr[0] = str_replace($reg[0], $uid, $wrapArr[0]);
|
||||
}
|
||||
}
|
||||
return trim($wrapArr[0]) . $content . trim($wrapArr[1] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstraction method which creates an alt or title parameter for an HTML img, applet, area or input element and the FILE content element.
|
||||
* From the $conf array it implements the properties "altText" and "titleText"
|
||||
*
|
||||
* @param array $conf TypoScript configuration properties
|
||||
* @return string Parameter string containing alt and title parameters (if any)
|
||||
*/
|
||||
protected function getAltParam(array $conf): string
|
||||
{
|
||||
$altText = trim((string)$this->cObj->stdWrapValue('altText', $conf));
|
||||
$titleText = trim((string)$this->cObj->stdWrapValue('titleText', $conf));
|
||||
|
||||
// "alt":
|
||||
$altParam = ' alt="' . htmlspecialchars($altText) . '"';
|
||||
// "title":
|
||||
$emptyTitleHandling = $this->cObj->stdWrapValue('emptyTitleHandling', $conf);
|
||||
// Choices: 'keepEmpty' | 'useAlt' | 'removeAttr'
|
||||
if ($titleText || $emptyTitleHandling === 'keepEmpty') {
|
||||
$altParam .= ' title="' . htmlspecialchars($titleText) . '"';
|
||||
} elseif ($emptyTitleHandling === 'useAlt') {
|
||||
$altParam .= ' title="' . htmlspecialchars($altText) . '"';
|
||||
}
|
||||
return $altParam;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Contains IMG_RESOURCE class object.
|
||||
*/
|
||||
class ImageResourceContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, IMG_RESOURCE
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
$imageResource = $this->cObj->getImgResource($conf['file'] ?? '', $conf['file.'] ?? []);
|
||||
if ($imageResource === null) {
|
||||
return '';
|
||||
}
|
||||
return isset($conf['stdWrap.'])
|
||||
? $this->cObj->stdWrap($imageResource->getPublicUrl(), $conf['stdWrap.'])
|
||||
: $imageResource->getPublicUrl();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Implement cObj "LOAD_REGISTER":
|
||||
* Get latest Register, clone it, set a key/value, push as new latest RegisterStack entry.
|
||||
*
|
||||
* Note the naming "LOAD_REGISTER" is kinda misleading since it rather "loads into" or
|
||||
* sets a new value and pushes as key/value to the stack. "RESTORE_REGISTER" is the
|
||||
* counterpart cObj to get rid of that state again.
|
||||
*/
|
||||
class LoadRegisterContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Does not return any content, it just sets internal data based on the TypoScript properties.
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string Empty string
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
$registerStack = $this->request->getAttribute('frontend.register.stack');
|
||||
$clonedRegister = clone $registerStack->current();
|
||||
if (is_array($conf)) {
|
||||
$isExecuted = [];
|
||||
foreach ($conf as $key => $value) {
|
||||
$key = rtrim($key, '.');
|
||||
if (!isset($isExecuted[$key])) {
|
||||
$registerProperties = $key . '.';
|
||||
if (isset($conf[$key]) && isset($conf[$registerProperties])) {
|
||||
$value = $this->cObj->stdWrap($conf[$key], $conf[$registerProperties]);
|
||||
} elseif (isset($conf[$registerProperties])) {
|
||||
$value = $this->cObj->stdWrap('', $conf[$registerProperties]);
|
||||
}
|
||||
$clonedRegister->set($key, $value);
|
||||
$isExecuted[$key] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
$registerStack->push($clonedRegister);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Menu;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Category\Collection\CategoryCollection;
|
||||
|
||||
/**
|
||||
* Utility class for menus based on category collections of pages.
|
||||
*
|
||||
* Returns all the relevant pages for rendering with a menu content object.
|
||||
* @internal this is only used for internal purposes and solely used for EXT:frontend and not part of TYPO3's Core API.
|
||||
*/
|
||||
class CategoryMenuUtility
|
||||
{
|
||||
/**
|
||||
* @var string Name of the field used for sorting the pages
|
||||
*/
|
||||
protected static $sortingField;
|
||||
|
||||
/**
|
||||
* Collects all pages for the selected categories, sorted according to configuration.
|
||||
*
|
||||
* @param string $selectedCategories Comma-separated list of system categories primary keys
|
||||
* @param array|null $configuration TypoScript configuration for the "special." keyword
|
||||
* @param AbstractMenuContentObject $parentObject Back-reference to the calling object
|
||||
* @return array List of selected pages
|
||||
*/
|
||||
public function collectPages($selectedCategories, $configuration, $parentObject)
|
||||
{
|
||||
$selectedPages = [];
|
||||
$categoriesPerPage = [];
|
||||
// Determine the name of the relation field
|
||||
$relationField = (string)$parentObject->getParentContentObject()->stdWrapValue('relation', $configuration ?? []);
|
||||
// Get the pages for each selected category
|
||||
$selectedCategories = GeneralUtility::intExplode(',', $selectedCategories, true);
|
||||
foreach ($selectedCategories as $aCategory) {
|
||||
$collection = CategoryCollection::load(
|
||||
$aCategory,
|
||||
true,
|
||||
'pages',
|
||||
$relationField
|
||||
);
|
||||
$categoryUid = $collection->getUid();
|
||||
// Loop on the results, overlay each page record found
|
||||
foreach ($collection as $pageItem) {
|
||||
$parentObject->getSysPage()->versionOL('pages', $pageItem, true);
|
||||
if (is_array($pageItem)) {
|
||||
$selectedPages[$pageItem['uid']] = $parentObject->getSysPage()->getLanguageOverlay('pages', $pageItem);
|
||||
// Keep a list of the categories each page belongs to
|
||||
if (!isset($categoriesPerPage[$pageItem['uid']])) {
|
||||
$categoriesPerPage[$pageItem['uid']] = [];
|
||||
}
|
||||
$categoriesPerPage[$pageItem['uid']][] = $categoryUid;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Loop on the selected pages to add the categories they belong to, as comma-separated list of category uid's)
|
||||
// (this makes them available for rendering, if needed)
|
||||
foreach ($selectedPages as $uid => $pageRecord) {
|
||||
$selectedPages[$uid]['_categories'] = implode(',', $categoriesPerPage[$uid]);
|
||||
}
|
||||
|
||||
// Sort the pages according to the sorting property
|
||||
self::$sortingField = (string)$parentObject->getParentContentObject()->stdWrapValue('sorting', $configuration ?? []);
|
||||
$order = (string)$parentObject->getParentContentObject()->stdWrapValue('order', $configuration ?? []);
|
||||
$selectedPages = $this->sortPages($selectedPages, $order);
|
||||
|
||||
return $selectedPages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the selected pages
|
||||
*
|
||||
* If the sorting field is not defined or does not corresponding to an existing field
|
||||
* of the "pages" tables, the list of pages will remain unchanged.
|
||||
*
|
||||
* @param array $pages List of selected pages
|
||||
* @param string $order Order for sorting (should "asc" or "desc")
|
||||
* @return array Sorted list of pages
|
||||
*/
|
||||
protected function sortPages($pages, $order)
|
||||
{
|
||||
// Perform the sorting only if a criterion was actually defined
|
||||
if (!empty(self::$sortingField)) {
|
||||
// Check that the sorting field exists (checking the first record is enough)
|
||||
$firstPage = current($pages);
|
||||
if (isset($firstPage[self::$sortingField])) {
|
||||
// Make sure the order property is either "asc" or "desc" (default is "asc")
|
||||
if (!empty($order)) {
|
||||
$order = strtolower($order);
|
||||
if ($order !== 'desc') {
|
||||
$order = 'asc';
|
||||
}
|
||||
}
|
||||
$sortMultiplier = $order === 'asc' ? 1 : -1;
|
||||
uasort($pages, static function (array $pageA, array $pageB) use ($sortMultiplier): int {
|
||||
return strnatcasecmp($pageA[self::$sortingField], $pageB[self::$sortingField]) * $sortMultiplier;
|
||||
});
|
||||
}
|
||||
}
|
||||
return $pages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Menu\Exception;
|
||||
|
||||
use TYPO3\CMS\Frontend\Exception;
|
||||
|
||||
/**
|
||||
* No such menu type exception
|
||||
*/
|
||||
class NoSuchMenuTypeException extends Exception {}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?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\Menu;
|
||||
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException;
|
||||
|
||||
/**
|
||||
* Factory for menu content objects. Allows overriding the default
|
||||
* types like 'TMENU' with an own implementation (only one possible)
|
||||
* and new types can be registered.
|
||||
* @internal this is only used for internal purposes and solely used for EXT:frontend and not part of TYPO3's Core API.
|
||||
*/
|
||||
class MenuContentObjectFactory implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* Register of TypoScript keys to according render class
|
||||
*/
|
||||
protected array $menuTypeToClassMapping = [
|
||||
'TMENU' => TextMenuContentObject::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Gets a typo script string like 'TMENU' and returns an object of this type
|
||||
*
|
||||
* @throws Exception\NoSuchMenuTypeException
|
||||
*/
|
||||
public function getMenuObjectByType(string $type = ''): AbstractMenuContentObject
|
||||
{
|
||||
$upperCasedClassName = strtoupper($type);
|
||||
if (array_key_exists($upperCasedClassName, $this->menuTypeToClassMapping)) {
|
||||
/** @var AbstractMenuContentObject $object */
|
||||
$object = GeneralUtility::makeInstance($this->menuTypeToClassMapping[$upperCasedClassName]);
|
||||
return $object;
|
||||
}
|
||||
throw new NoSuchMenuTypeException(
|
||||
'Menu type ' . (string)$type . ' has no implementing class.',
|
||||
1363278130
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register new menu type or override existing type
|
||||
*
|
||||
* @param string $type Menu type to be used in TypoScript
|
||||
* @param string $className Class rendering the menu
|
||||
*/
|
||||
public function registerMenuType(string $type, string $className)
|
||||
{
|
||||
$this->menuTypeToClassMapping[strtoupper($type)] = $className;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Menu;
|
||||
|
||||
use TYPO3\CMS\Core\TypoScript\TypoScriptService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Extension class creating text based menus
|
||||
*/
|
||||
class TextMenuContentObject extends AbstractMenuContentObject
|
||||
{
|
||||
/**
|
||||
* Traverses the ->result array of menu items configuration (made by ->generate()) and renders each item.
|
||||
* An instance of ContentObjectRenderer is also made and for each menu item rendered it is loaded with
|
||||
* the record for that page so that any stdWrap properties that applies will have the current menu items record available.
|
||||
*
|
||||
* @return string The HTML for the menu including submenus
|
||||
*/
|
||||
public function writeMenu()
|
||||
{
|
||||
if (empty($this->result)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$register = $this->request->getAttribute('frontend.register.stack')->current();
|
||||
$cObjectForCurrentMenu = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$menuContent = [];
|
||||
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
|
||||
$subMenuObjSuffixes = $typoScriptService->explodeConfigurationForOptionSplit(['sOSuffix' => $this->mconf['submenuObjSuffixes'] ?? null], count($this->result));
|
||||
$explicitSpacerRenderingEnabled = ($this->mconf['SPC'] ?? false);
|
||||
foreach ($this->result as $key => $val) {
|
||||
$register->set('count_HMENU_MENUOBJ', (int)$register->get('count_HMENU_MENUOBJ', 0) + 1);
|
||||
$register->set('count_MENUOBJ', (int)$register->get('count_MENUOBJ', 0) + 1);
|
||||
|
||||
// Initialize the cObj with the page record of the menu item
|
||||
$cObjectForCurrentMenu->setRequest($this->request);
|
||||
$cObjectForCurrentMenu->start($this->menuArr[$key], 'pages');
|
||||
$this->I = [];
|
||||
$this->I['key'] = $key;
|
||||
$this->I['val'] = $val;
|
||||
$this->I['title'] = $this->getPageTitle($this->menuArr[$key]['title'] ?? '', $this->menuArr[$key]['nav_title'] ?? '');
|
||||
$this->I['title.'] = $this->I['val']['stdWrap.'] ?? [];
|
||||
$this->I['title'] = $cObjectForCurrentMenu->stdWrapValue('title', $this->I);
|
||||
$this->I['uid'] = $this->menuArr[$key]['uid'] ?? 0;
|
||||
$this->I['mount_pid'] = $this->menuArr[$key]['mount_pid'] ?? 0;
|
||||
$this->I['pid'] = $this->menuArr[$key]['pid'] ?? 0;
|
||||
$this->I['spacer'] = $this->menuArr[$key]['isSpacer'] ?? false;
|
||||
// Make link tag
|
||||
$this->I['val']['additionalParams'] = $cObjectForCurrentMenu->stdWrapValue('additionalParams', $this->I['val']);
|
||||
$linkResult = $this->link((int)$key, (string)($this->I['val']['altTarget'] ?? ''), ($this->mconf['forceTypeValue'] ?? ''));
|
||||
if ($linkResult === null) {
|
||||
$this->I['val']['doNotLinkIt'] = 1;
|
||||
}
|
||||
// Title attribute of links
|
||||
$titleAttrValue = $cObjectForCurrentMenu->stdWrapValue('ATagTitle', $this->I['val']);
|
||||
if ($linkResult && $titleAttrValue !== '') {
|
||||
$linkResult = $linkResult->withAttribute('title', $titleAttrValue);
|
||||
}
|
||||
$this->I['linkHREF'] = $linkResult;
|
||||
$this->I['val']['doNotLinkIt'] = (bool)$cObjectForCurrentMenu->stdWrapValue('doNotLinkIt', $this->I['val']);
|
||||
// Compile link tag
|
||||
if (!$this->I['spacer'] && !$this->I['val']['doNotLinkIt']) {
|
||||
$this->setATagParts($linkResult);
|
||||
} else {
|
||||
$this->I['A1'] = '';
|
||||
$this->I['A2'] = '';
|
||||
}
|
||||
// ATagBeforeWrap processing:
|
||||
if ($this->I['val']['ATagBeforeWrap'] ?? false) {
|
||||
$wrapPartsBefore = explode('|', $this->I['val']['linkWrap'] ?? '');
|
||||
$wrapPartsAfter = ['', ''];
|
||||
} else {
|
||||
$wrapPartsBefore = ['', ''];
|
||||
$wrapPartsAfter = explode('|', $this->I['val']['linkWrap'] ?? '');
|
||||
}
|
||||
if (($this->I['val']['stdWrap2'] ?? false) || isset($this->I['val']['stdWrap2.'])) {
|
||||
$stdWrap2 = (string)(isset($this->I['val']['stdWrap2.']) ? $cObjectForCurrentMenu->stdWrap('|', $this->I['val']['stdWrap2.']) : '|');
|
||||
$stdWrap2Value = (string)($this->I['val']['stdWrap2'] ?? '|');
|
||||
$stdWrap2Value = $stdWrap2Value !== '' ? $stdWrap2Value : '|';
|
||||
$wrapPartsStdWrap = explode($stdWrap2Value, $stdWrap2);
|
||||
} else {
|
||||
$wrapPartsStdWrap = ['', ''];
|
||||
}
|
||||
// Make before, middle and after parts
|
||||
$this->I['parts'] = [];
|
||||
$this->I['parts']['before'] = $this->getBeforeAfter('before', $cObjectForCurrentMenu);
|
||||
$this->I['parts']['stdWrap2_begin'] = $wrapPartsStdWrap[0];
|
||||
// stdWrap for doNotShowLink
|
||||
$this->I['val']['doNotShowLink'] = $cObjectForCurrentMenu->stdWrapValue('doNotShowLink', $this->I['val']);
|
||||
if (!$this->I['val']['doNotShowLink']) {
|
||||
$this->I['parts']['notATagBeforeWrap_begin'] = $wrapPartsAfter[0];
|
||||
$this->I['parts']['ATag_begin'] = $this->I['A1'];
|
||||
$this->I['parts']['ATagBeforeWrap_begin'] = $wrapPartsBefore[0];
|
||||
$this->I['parts']['title'] = $this->I['title'];
|
||||
$this->I['parts']['ATagBeforeWrap_end'] = $wrapPartsBefore[1] ?? '';
|
||||
$this->I['parts']['ATag_end'] = $this->I['A2'];
|
||||
$this->I['parts']['notATagBeforeWrap_end'] = $wrapPartsAfter[1] ?? '';
|
||||
}
|
||||
$this->I['parts']['stdWrap2_end'] = $wrapPartsStdWrap[1] ?? '';
|
||||
$this->I['parts']['after'] = $this->getBeforeAfter('after', $cObjectForCurrentMenu);
|
||||
// Passing I to a user function
|
||||
if ($this->mconf['IProcFunc'] ?? false) {
|
||||
$this->I = $this->userProcess('IProcFunc', $this->I);
|
||||
}
|
||||
// Merge parts + beforeAllWrap
|
||||
$this->I['theItem'] = implode('', $this->I['parts']);
|
||||
$allWrap = $cObjectForCurrentMenu->stdWrapValue('allWrap', $this->I['val']);
|
||||
$this->I['theItem'] = $cObjectForCurrentMenu->wrap($this->I['theItem'], $allWrap);
|
||||
if ($this->I['val']['subst_elementUid'] ?? false) {
|
||||
$this->I['theItem'] = str_replace('{elementUid}', (string)$this->I['uid'], $this->I['theItem']);
|
||||
}
|
||||
if (is_array($this->I['val']['allStdWrap.'] ?? null)) {
|
||||
$this->I['theItem'] = $cObjectForCurrentMenu->stdWrap($this->I['theItem'], $this->I['val']['allStdWrap.']);
|
||||
}
|
||||
$isSpacerPage = $this->I['spacer'] ?? false;
|
||||
// If rendering of SPACERs is enabled, also allow rendering submenus with Spacers
|
||||
if (!$isSpacerPage || $explicitSpacerRenderingEnabled) {
|
||||
// Add part to the accumulated result + fetch submenus
|
||||
$this->I['theItem'] .= $this->subMenu($this->I['uid'], $subMenuObjSuffixes[$key]['sOSuffix'] ?? '', $key);
|
||||
}
|
||||
$part = $cObjectForCurrentMenu->stdWrapValue('wrapItemAndSub', $this->I['val']);
|
||||
$menuContent[] = $part ? $cObjectForCurrentMenu->wrap($this->I['theItem'], $part) : $this->I['theItem'];
|
||||
}
|
||||
|
||||
$menuContent = implode('', $menuContent);
|
||||
if (is_array($this->mconf['stdWrap.'] ?? null)) {
|
||||
$menuContent = (string)$cObjectForCurrentMenu->stdWrap($menuContent, $this->mconf['stdWrap.']);
|
||||
}
|
||||
return $cObjectForCurrentMenu->wrap($menuContent, $this->mconf['wrap'] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the before* and after* stdWrap for TMENUs
|
||||
* Evaluates:
|
||||
* - before.stdWrap*
|
||||
* - beforeWrap
|
||||
* - after.stdWrap*
|
||||
* - afterWrap
|
||||
*
|
||||
* @param string $pref Can be "before" or "after" and determines which kind of stdWrap to process (basically this is the prefix of the TypoScript properties that are read from the ->I['val'] array
|
||||
* @return string The resulting HTML
|
||||
*/
|
||||
protected function getBeforeAfter(string $pref, ContentObjectRenderer $cObjectForCurrentMenu): string
|
||||
{
|
||||
$processedPref = $cObjectForCurrentMenu->stdWrapValue($pref, $this->I['val']);
|
||||
if (isset($this->I['val'][$pref . 'Wrap'])) {
|
||||
return $cObjectForCurrentMenu->wrap($processedPref, $this->I['val'][$pref . 'Wrap']);
|
||||
}
|
||||
return $processedPref;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\Database\RelationHandler;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\Category\Collection\CategoryCollection;
|
||||
|
||||
/**
|
||||
* Contains RECORDS class object.
|
||||
*/
|
||||
class RecordsContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* List of all items with table and uid information
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $itemArray = [];
|
||||
|
||||
/**
|
||||
* List of all selected records with full data, arranged per table
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $data = [];
|
||||
|
||||
public function __construct(protected readonly TimeTracker $timeTracker) {}
|
||||
|
||||
/**
|
||||
* Rendering the cObject, RECORDS
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
// Reset items and data
|
||||
$this->itemArray = [];
|
||||
$this->data = [];
|
||||
|
||||
$theValue = '';
|
||||
|
||||
$tables = (string)$this->cObj->stdWrapValue('tables', $conf ?? []);
|
||||
if ($tables !== '') {
|
||||
$tablesArray = array_unique(GeneralUtility::trimExplode(',', $tables, true));
|
||||
// Add tables which have a configuration (note that this may create duplicate entries)
|
||||
if (is_array($conf['conf.'] ?? false)) {
|
||||
foreach ($conf['conf.'] as $key => $value) {
|
||||
if (!str_ends_with($key, '.') && !in_array($key, $tablesArray)) {
|
||||
$tablesArray[] = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the data, depending on collection method.
|
||||
// Property "source" is considered more precise and thus takes precedence over "categories"
|
||||
$source = (string)$this->cObj->stdWrapValue('source', $conf ?? []);
|
||||
$categories = (string)$this->cObj->stdWrapValue('categories', $conf ?? []);
|
||||
if ($source !== '') {
|
||||
$this->collectRecordsFromSource($source, $tablesArray);
|
||||
} elseif ($categories !== '') {
|
||||
$relationField = (string)$this->cObj->stdWrapValue('relation', $conf['categories.'] ?? []);
|
||||
$this->collectRecordsFromCategories($categories, $tablesArray, $relationField);
|
||||
}
|
||||
if (!empty($this->itemArray)) {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$cObj->setParent($this->cObj->data, $this->cObj->currentRecord);
|
||||
$pageRepository = $this->getPageRepository();
|
||||
foreach ($this->itemArray as $val) {
|
||||
$row = $this->data[$val['table']][$val['id']] ?? null;
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
// Perform overlays if necessary (records coming from category collections are already overlaid)
|
||||
if ($source !== '') {
|
||||
// Versioning preview
|
||||
$pageRepository->versionOL($val['table'], $row);
|
||||
// Language overlay
|
||||
if (is_array($row)) {
|
||||
$row = $pageRepository->getLanguageOverlay($val['table'], $row);
|
||||
}
|
||||
}
|
||||
// Might be unset during the overlay process
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
if ($this->isRecordsPageAccessible($val['table'], $row, $conf)) {
|
||||
$renderObjName = ($conf['conf.'][$val['table']] ?? false) ? $conf['conf.'][$val['table']] : '<' . $val['table'];
|
||||
$renderObjKey = ($conf['conf.'][$val['table']] ?? false) ? 'conf.' . $val['table'] : '';
|
||||
$renderObjConf = ($conf['conf.'][$val['table'] . '.'] ?? false) ? $conf['conf.'][$val['table'] . '.'] : [];
|
||||
$this->cObj->lastChanged($row['tstamp'] ?? 0);
|
||||
$cObj->setRequest($this->request);
|
||||
$cObj->start($row, $val['table']);
|
||||
$tmpValue = $cObj->cObjGetSingle($renderObjName, $renderObjConf, $renderObjKey);
|
||||
$theValue .= $tmpValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$wrap = $this->cObj->stdWrapValue('wrap', $conf);
|
||||
if ($wrap) {
|
||||
$theValue = $this->cObj->wrap($theValue, $wrap);
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
|
||||
}
|
||||
return $theValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the records page is accessible
|
||||
*/
|
||||
protected function isRecordsPageAccessible(string $table, array $row, array $conf): bool
|
||||
{
|
||||
$pageId = (int)($table === 'pages' ? $row['uid'] : $row['pid']);
|
||||
if ($pageId === $this->request->getAttribute('frontend.page.information')->getId()) {
|
||||
// Access to current page has already been checked before rendering this content object.
|
||||
return true;
|
||||
}
|
||||
if ($this->cObj->stdWrapValue('dontCheckPid', $conf)) {
|
||||
return true;
|
||||
}
|
||||
$validPageId = $this->getPageRepository()->filterAccessiblePageIds([$pageId]);
|
||||
return $validPageId !== [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects records according to the configured source
|
||||
*
|
||||
* @param string $source Source of records
|
||||
* @param array $tables List of tables
|
||||
*/
|
||||
protected function collectRecordsFromSource($source, array $tables)
|
||||
{
|
||||
$loadDB = GeneralUtility::makeInstance(RelationHandler::class);
|
||||
$loadDB->start($source, implode(',', $tables));
|
||||
foreach ($loadDB->tableArray as $table => $v) {
|
||||
$constraints = $this->getPageRepository()->getDefaultConstraints($table);
|
||||
if ($constraints !== []) {
|
||||
$loadDB->additionalWhere[$table] = implode(' AND ', $constraints);
|
||||
}
|
||||
}
|
||||
$this->data = $loadDB->getFromDB();
|
||||
reset($loadDB->itemArray);
|
||||
$this->itemArray = $loadDB->itemArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects records for all selected tables and categories.
|
||||
*
|
||||
* @param string $selectedCategories Comma-separated list of categories
|
||||
* @param array $tables List of tables
|
||||
* @param string $relationField Name of the field containing the categories relation
|
||||
*/
|
||||
protected function collectRecordsFromCategories($selectedCategories, array $tables, $relationField)
|
||||
{
|
||||
$selectedCategories = array_unique(GeneralUtility::intExplode(',', $selectedCategories, true));
|
||||
|
||||
// Loop on all selected tables
|
||||
foreach ($tables as $table) {
|
||||
// Get the records for each selected category
|
||||
$tableRecords = [];
|
||||
$categoriesPerRecord = [];
|
||||
foreach ($selectedCategories as $aCategory) {
|
||||
try {
|
||||
$collection = CategoryCollection::load(
|
||||
$aCategory,
|
||||
true,
|
||||
$table,
|
||||
$relationField
|
||||
);
|
||||
if ($collection->count() > 0) {
|
||||
// Add items to the collection of records for the current table
|
||||
foreach ($collection as $item) {
|
||||
$tableRecords[$item['uid']] = $item;
|
||||
// Keep track of all categories a given item belongs to
|
||||
if (!isset($categoriesPerRecord[$item['uid']])) {
|
||||
$categoriesPerRecord[$item['uid']] = [];
|
||||
}
|
||||
$categoriesPerRecord[$item['uid']][] = $aCategory;
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$message = sprintf(
|
||||
'Could not get records for category id %d. Error: %s (%d)',
|
||||
$aCategory,
|
||||
$e->getMessage(),
|
||||
$e->getCode()
|
||||
);
|
||||
$this->timeTracker->setTSlogMessage($message, LogLevel::WARNING);
|
||||
}
|
||||
}
|
||||
// Store the resulting records into the itemArray and data results array
|
||||
if (!empty($tableRecords)) {
|
||||
$this->data[$table] = [];
|
||||
foreach ($tableRecords as $record) {
|
||||
$this->itemArray[] = [
|
||||
'id' => $record['uid'],
|
||||
'table' => $table,
|
||||
];
|
||||
// Add to the record the categories it belongs to
|
||||
$record['_categories'] = implode(',', $categoriesPerRecord[$record['uid']]);
|
||||
$this->data[$table][$record['uid']] = $record;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* A simple key/value store. See class RegisterStack for more information.
|
||||
*
|
||||
* @internal This class is not part of the TYPO3 Core API
|
||||
*/
|
||||
final class Register
|
||||
{
|
||||
private array $keyValues = [];
|
||||
|
||||
/**
|
||||
* Unable to deal with objects, accepts simple types only.
|
||||
* This is done by intention to not run into issues if this
|
||||
* state needs to be serialized (cached).
|
||||
*/
|
||||
public function set(string $key, string|int|bool|float $value): void
|
||||
{
|
||||
$this->keyValues[$key] = $value;
|
||||
}
|
||||
|
||||
public function get(string $key, string|int|bool|float|null $default = null): string|int|bool|float|null
|
||||
{
|
||||
return $this->keyValues[$key] ?? $default;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* Contains TypoScript "Register" state together with class Register.
|
||||
*
|
||||
* An instance of this class is created during frontend rendering and registered
|
||||
* as Request attribute "frontend.register.stack".
|
||||
*
|
||||
* The TypoScript "register" is a key-value store (implemented by class Register)
|
||||
* combined with a stack. This has probably been invented for menu rendering back
|
||||
* then: content objects in general and TypoScript based menu rendering specifically
|
||||
* can be nested when multiple menu depths/layers are rendered. Each of those layers
|
||||
* may need own values within their scope.
|
||||
*
|
||||
* The two main usages are TypoScript data/getText to access register entries along
|
||||
* with content objects LOAD_REGISTER and RESTORE_REGISTER which push and pop register
|
||||
* objects from the stack and store key/value entries.
|
||||
*
|
||||
* There is one quirk in comparison to a "classic" stack: New Register instances are
|
||||
* typically clones of the underlying Register instance plus new key/values, see class
|
||||
* LoadRegisterContentObject for an implementation: Values can be set on a lower
|
||||
* level register and is still "seen/part of" a register on top. key/values bubble up,
|
||||
* but not down.
|
||||
*
|
||||
* @internal This class is not part of the TYPO3 Core API
|
||||
*/
|
||||
final class RegisterStack
|
||||
{
|
||||
/**
|
||||
* @var Register[]
|
||||
*/
|
||||
private array $registerStack = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->push(new Register());
|
||||
}
|
||||
|
||||
/**
|
||||
* Peek current Register. Does not change stack.
|
||||
*/
|
||||
public function current(): Register
|
||||
{
|
||||
return array_last($this->registerStack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new Register instance to top of stack
|
||||
*/
|
||||
public function push(Register $register): void
|
||||
{
|
||||
$this->registerStack[] = $register;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove top of stack Register and return it.
|
||||
* Re-inits with an empty register if empty to avoid exception or nullable handling
|
||||
* in consumers if there is for example a "RESTORE_REGISTER" cObj too much. As
|
||||
* drawback, consumers never know if there is a leftover pop().
|
||||
*/
|
||||
public function pop(): Register
|
||||
{
|
||||
$register = array_pop($this->registerStack);
|
||||
if (empty($this->registerStack)) {
|
||||
$this->push(new Register());
|
||||
}
|
||||
return $register;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Implement cObj "RESTORE_REGISTER":
|
||||
* As the counterpart of "LOAD_REGISTER", "RESTORE_REGISTER" removes any state
|
||||
* added by latest "LOAD_REGISTER" again.
|
||||
*/
|
||||
class RestoreRegisterContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Does not return any content, it just sets internal data based on the TypoScript properties.
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Empty string
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
$this->request->getAttribute('frontend.register.stack')->pop();
|
||||
return '';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Imaging\Exception\InvalidSvgException;
|
||||
use TYPO3\CMS\Core\Imaging\Svg\SvgDocumentFactory;
|
||||
use TYPO3\CMS\Core\Imaging\Svg\SvgDocumentService;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\SystemResourceDoesNotExistException;
|
||||
use TYPO3\CMS\Core\SystemResource\Exception\SystemResourceException;
|
||||
use TYPO3\CMS\Core\SystemResource\Publishing\SystemResourcePublisherInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\SystemResourceFactory;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\PublicResourceInterface;
|
||||
use TYPO3\CMS\Core\SystemResource\Type\SystemResourceInterface;
|
||||
|
||||
/**
|
||||
* Contains SVG content object.
|
||||
*/
|
||||
class ScalableVectorGraphicsContentObject extends AbstractContentObject
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly SystemResourceFactory $resourceFactory,
|
||||
protected readonly SystemResourcePublisherInterface $resourcePublisher,
|
||||
protected readonly SvgDocumentFactory $svgDocumentFactory,
|
||||
protected readonly SvgDocumentService $svgDocumentService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Rendering the cObject, SVG
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
*/
|
||||
public function render($conf = []): string
|
||||
{
|
||||
$renderMode = $this->cObj->stdWrapValue('renderMode', $conf);
|
||||
|
||||
if ($renderMode === 'inline') {
|
||||
return $this->renderInline($conf);
|
||||
}
|
||||
|
||||
return $this->renderObject($conf);
|
||||
}
|
||||
|
||||
protected function renderInline(array $conf): string
|
||||
{
|
||||
$resource = $this->resolveResource($conf);
|
||||
[$width, $height, $isDefaultWidth, $isDefaultHeight] = $this->getDimensions($conf);
|
||||
|
||||
$content = $svgContent = '';
|
||||
if ($resource instanceof SystemResourceInterface) {
|
||||
try {
|
||||
$svgContent = $resource->getContents();
|
||||
} catch (SystemResourceDoesNotExistException) {
|
||||
}
|
||||
}
|
||||
if ($svgContent !== '') {
|
||||
try {
|
||||
$document = $this->svgDocumentFactory->fromStringAndSanitize($svgContent);
|
||||
if (!$isDefaultWidth) {
|
||||
$document->documentElement->setAttribute('width', (string)$width);
|
||||
}
|
||||
if (!$isDefaultHeight) {
|
||||
$document->documentElement->setAttribute('height', (string)$height);
|
||||
}
|
||||
$content = $this->svgDocumentService->toInlineMarkup($document);
|
||||
} catch (InvalidSvgException) {
|
||||
$content = '';
|
||||
}
|
||||
} else {
|
||||
$value = $this->cObj->stdWrapValue('value', $conf);
|
||||
if (!empty($value)) {
|
||||
$content = [];
|
||||
$content[] = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' . (int)$width . '" height="' . (int)$height . '">';
|
||||
$content[] = $value;
|
||||
$content[] = '</svg>';
|
||||
$content = implode(LF, $content);
|
||||
}
|
||||
}
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the SVG as <object> tag
|
||||
*/
|
||||
protected function renderObject(array $conf): string
|
||||
{
|
||||
$resource = $this->resolveResource($conf);
|
||||
[$width, $height] = $this->getDimensions($conf);
|
||||
$content = [];
|
||||
if ($resource !== null) {
|
||||
$uri = $this->resourcePublisher->generateUri($resource, $this->request);
|
||||
$content[] = '<!--[if IE]>';
|
||||
$content[] = ' <object src="' . htmlspecialchars($uri) . '" classid="image/svg+xml" width="' . (int)$width . '" height="' . (int)$height . '">';
|
||||
$content[] = '<![endif]-->';
|
||||
$content[] = '<!--[if !IE]>-->';
|
||||
$content[] = ' <object data="' . htmlspecialchars($uri) . '" type="image/svg+xml" width="' . (int)$width . '" height="' . (int)$height . '">';
|
||||
$content[] = '<!--<![endif]-->';
|
||||
$content[] = '</object>';
|
||||
}
|
||||
$content = implode(LF, $content);
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function resolveResource(array $conf): ?PublicResourceInterface
|
||||
{
|
||||
try {
|
||||
$resourceIdentifier = (string)$this->cObj->stdWrapValue('src', $conf);
|
||||
return $this->resourceFactory->createPublicResource($resourceIdentifier);
|
||||
} catch (SystemResourceException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getDimensions(array $conf): array
|
||||
{
|
||||
$isDefaultWidth = false;
|
||||
$isDefaultHeight = false;
|
||||
$width = $this->cObj->stdWrapValue('width', $conf);
|
||||
$height = $this->cObj->stdWrapValue('height', $conf);
|
||||
|
||||
if (empty($width)) {
|
||||
$isDefaultWidth = true;
|
||||
$width = 600;
|
||||
}
|
||||
if (empty($height)) {
|
||||
$isDefaultHeight = true;
|
||||
$height = 400;
|
||||
}
|
||||
|
||||
return [$width, $height, $isDefaultWidth, $isDefaultHeight];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Contains TEXT class object.
|
||||
*/
|
||||
class TextContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, TEXT
|
||||
*
|
||||
* @param mixed $conf Array of TypoScript properties (marked as "mixed" currently because we don't know what we're receiving)
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (!is_array($conf)) {
|
||||
return '';
|
||||
}
|
||||
$content = '';
|
||||
if (isset($conf['value'])) {
|
||||
$content = $conf['value'];
|
||||
unset($conf['value']);
|
||||
}
|
||||
if (isset($conf['value.'])) {
|
||||
$content = $this->cObj->stdWrap($content, $conf['value.']);
|
||||
unset($conf['value.']);
|
||||
}
|
||||
if (!empty($conf)) {
|
||||
$content = $this->cObj->stdWrap($content, $conf);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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 Psr\Log\LogLevel;
|
||||
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Contains USER class object.
|
||||
*/
|
||||
class UserContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, USER
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
if (empty($conf)) {
|
||||
$this->getTimeTracker()->setTSlogMessage('USER without configuration.', LogLevel::WARNING);
|
||||
return '';
|
||||
}
|
||||
$content = '';
|
||||
if ($this->cObj->getUserObjectType() === false) {
|
||||
// Render this if we are a delayed non cached object
|
||||
$this->cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER);
|
||||
}
|
||||
$tempContent = $this->cObj->callUserFunction($conf['userFunc'] ?? '', $conf, '');
|
||||
if ($this->cObj->doConvertToUserIntObject) {
|
||||
$this->cObj->doConvertToUserIntObject = false;
|
||||
$content = $this->cObj->cObjGetSingle('USER_INT', $conf);
|
||||
} else {
|
||||
$content .= $tempContent;
|
||||
// Only executed when the element is not converted to USER_INT
|
||||
if (isset($conf['stdWrap.'])) {
|
||||
$content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
|
||||
}
|
||||
}
|
||||
$this->cObj->setUserObjectType(false);
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TimeTracker
|
||||
*/
|
||||
protected function getTimeTracker()
|
||||
{
|
||||
return GeneralUtility::makeInstance(TimeTracker::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\Utility\StringUtility;
|
||||
|
||||
/**
|
||||
* Contains USER_INT class object.
|
||||
*/
|
||||
class UserInternalContentObject extends AbstractContentObject
|
||||
{
|
||||
/**
|
||||
* Rendering the cObject, USER_INT
|
||||
*
|
||||
* @param array $conf Array of TypoScript properties
|
||||
* @return string Output
|
||||
*/
|
||||
public function render($conf = [])
|
||||
{
|
||||
$this->cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER_INT);
|
||||
$substKey = 'INT_SCRIPT.' . md5(StringUtility::getUniqueId());
|
||||
$pageParts = $this->request->getAttribute('frontend.page.parts');
|
||||
$pageParts->addNotCachedContentElement([
|
||||
'substKey' => $substKey,
|
||||
'conf' => $conf,
|
||||
'cObjData' => serialize($this->cObj->getState()),
|
||||
'type' => 'FUNC',
|
||||
]);
|
||||
$this->cObj->setUserObjectType(false);
|
||||
return '<!--' . $substKey . '-->';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user