Files
typo3-13/packages/ep_theme/Classes/Preview/ContentPreviewRenderer.php
T
frommeandClaude Opus 5.5 0b17a24edd feat: page module previews for teasers, disrupter, disturber, map and lineup
The TYPO3 10 drawItem hooks become one preview renderer registered per
CType in TCA; it renders the old templates from a46918d with the same
variables (f:format.html is not allowed in the backend: f:sanitize.html).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-24 12:56:22 +02:00

121 lines
5.8 KiB
PHP

<?php
declare(strict_types=1);
namespace EP\EpTheme\Preview;
use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Resource\FileRepository;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\View\ViewFactoryData;
use TYPO3\CMS\Core\View\ViewFactoryInterface;
/**
* Page module previews of the theme's content elements, rendered from
* Templates/PreviewRenderer/<CType>.html as the TYPO3 10 drawItem hooks did
* (Classes/Hooks/PageLayoutView in a46918d), with the same template variables.
* Registered per CType in TCA (['types'][<CType>]['previewRenderer']).
*/
class ContentPreviewRenderer extends StandardContentPreviewRenderer
{
/**
* CType => [field holding the related uid, related table, template variable]
*/
protected const RELATED_RECORDS = [
'teaser_country' => ['tx_eptheme_context_country', 'tx_epproducts_domain_model_country', 'country'],
'teaser_region' => ['tx_eptheme_context_region', 'tx_epproducts_domain_model_region', 'region'],
'teaser_city' => ['tx_eptheme_context_city', 'tx_epproducts_domain_model_city', 'city'],
'teaser_hotel' => ['tx_eptheme_context_hotel', 'tx_epproducts_domain_model_hotel', 'hotel'],
'teaser_concept' => ['tx_eptheme_context_concept', 'tx_epproducts_domain_model_concept', 'concept'],
'teaser_product' => ['tx_eptheme_context_product', 'tx_epproducts_domain_model_product', 'product'],
'teaser_booking_link' => ['tx_eptheme_context_product', 'tx_epproducts_domain_model_product', 'product'],
];
public function renderPageModulePreviewContent(GridColumnItem $item): string
{
$row = $item->getRecord();
$cType = (string)$row['CType'];
$template = GeneralUtility::getFileAbsFileName(
'EXT:ep_theme/Resources/Private/Templates/PreviewRenderer/' . GeneralUtility::underscoredToUpperCamelCase($cType) . '.html'
);
if (!is_file($template)) {
return parent::renderPageModulePreviewContent($item);
}
$images = $this->getImages((int)$row['uid'], 'tt_content', 'image');
$variables = [
'data' => $row,
'flexForm' => GeneralUtility::makeInstance(FlexFormService::class)->convertFlexFormContentToArray((string)($row['pi_flexform'] ?? '')),
'firstImage' => $images[0] ?? null,
'images' => $images,
];
$variables = array_replace($variables, $this->getAssignmentsForTemplate($cType, $row));
$view = GeneralUtility::makeInstance(ViewFactoryInterface::class)->create(new ViewFactoryData(
templatePathAndFilename: $template,
request: $item->getContext()->getCurrentRequest(),
));
$view->assignMultiple($variables);
return $view->render();
}
protected function getAssignmentsForTemplate(string $cType, array $row): array
{
if (isset(self::RELATED_RECORDS[$cType])) {
[$field, $table, $name] = self::RELATED_RECORDS[$cType];
$uid = (int)($row[$field] ?? 0);
$assignments = [
'images' => $this->getImages($uid, $table, 'teaser_images'),
$name => $uid > 0 ? BackendUtility::getRecord($table, $uid) : null,
];
if ($cType === 'teaser_product') {
$hotelUid = (int)($row['tx_eptheme_context_hotel'] ?? 0);
$assignments['hotel'] = $hotelUid > 0 ? BackendUtility::getRecord('tx_epproducts_domain_model_hotel', $hotelUid) : null;
}
return $assignments;
}
switch ($cType) {
case 'gmap':
$regionUid = (int)($row['tx_eptheme_context_region'] ?? 0);
$content = $regionUid > 0
? BackendUtility::getRecord('tx_epproducts_domain_model_region', $regionUid)
: BackendUtility::getRecord('tx_epproducts_domain_model_hotel', (int)($row['tx_eptheme_context_hotel'] ?? 0));
return ['content' => $content];
case 'disrupter':
// tx_eptheme_disrupter is an MM relation; the field itself only holds the count
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_eptheme_ttcontent_tx_eptheme_disrupter_mm');
$disrupterUid = (int)$queryBuilder->select('uid_foreign')
->from('tx_eptheme_ttcontent_tx_eptheme_disrupter_mm')
->where($queryBuilder->expr()->eq('uid_local', $queryBuilder->createNamedParameter((int)$row['uid'])))
->orderBy('sorting')
->setMaxResults(1)
->executeQuery()
->fetchOne();
return ['content' => $disrupterUid > 0 ? BackendUtility::getRecord('tx_eptheme_domain_model_disrupter', $disrupterUid) : null];
case 'lineup':
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_eptheme_domain_model_lineup');
$rows = $queryBuilder->select('*')
->from('tx_eptheme_domain_model_lineup')
->where($queryBuilder->expr()->eq('tt_content', $queryBuilder->createNamedParameter((int)$row['uid'])))
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
return ['rows' => $rows];
}
return [];
}
protected function getImages(int $uid, string $table, string $field): array
{
if ($uid <= 0) {
return [];
}
return GeneralUtility::makeInstance(FileRepository::class)->findByRelation($table, $field, $uid);
}
}