754 lines
27 KiB
PHP
754 lines
27 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Evomedien\Vitec\UserFunc;
|
|
|
|
use Doctrine\DBAL\Connection;
|
|
use Doctrine\DBAL\ArrayParameterType;
|
|
use Doctrine\DBAL\ParameterType;
|
|
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
|
use TYPO3\CMS\Core\Database\ConnectionPool;
|
|
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
|
use TYPO3\CMS\Core\Service\FlexFormService;
|
|
use Evomedien\Vitec\Service\RteResolver;
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
|
use TYPO3\CMS\Extbase\Service\ImageService;
|
|
|
|
/**
|
|
* Headless JSON renderer for EXT:news content elements.
|
|
*
|
|
* The renderer supports the news list/detail plugin variants used in this
|
|
* project and emits a stable JSON structure for the frontend.
|
|
*
|
|
* Exception-safe: returns '' on any failure.
|
|
*/
|
|
final class NewsJsonRenderer
|
|
{
|
|
private const NEWS_CTYPES = [
|
|
'news_pi1',
|
|
'news_newsliststicky',
|
|
'news_newsselectedlist',
|
|
'news_newsdetail',
|
|
'news_newsdatemenu',
|
|
'news_categorylist',
|
|
'news_newssearchform',
|
|
'news_newssearchresult',
|
|
'news_taglist',
|
|
];
|
|
|
|
private const TEMPLATE_LAYOUTS = [
|
|
'1' => 'Compact List',
|
|
'2' => 'Featured Story',
|
|
'3' => 'Editorial Grid',
|
|
'4' => 'Detail Spotlight',
|
|
];
|
|
|
|
private const TEMPLATE_LAYOUT_DEFAULTS = [
|
|
'news_pi1' => '1',
|
|
'news_newsliststicky' => '1',
|
|
'news_newsselectedlist' => '1',
|
|
'news_newsdetail' => '2',
|
|
'news_newsdatemenu' => '3',
|
|
'news_categorylist' => '3',
|
|
'news_newssearchform' => '4',
|
|
'news_newssearchresult' => '4',
|
|
'news_taglist' => '3',
|
|
];
|
|
|
|
#[AsAllowedCallable]
|
|
public function render(string $content, array $conf): string
|
|
{
|
|
try {
|
|
$row = is_array($this->cObj->data ?? null) ? $this->cObj->data : null;
|
|
if ($row !== null && in_array((string)($row['CType'] ?? ''), self::NEWS_CTYPES, true)) {
|
|
return $this->renderForRecord($row);
|
|
}
|
|
|
|
$pageId = $this->resolveCurrentPageId();
|
|
if ($pageId <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tt_content');
|
|
|
|
$ctypeConstraints = [];
|
|
foreach (self::NEWS_CTYPES as $ctype) {
|
|
$ctypeConstraints[] = $queryBuilder->expr()->eq(
|
|
'CType',
|
|
$queryBuilder->createNamedParameter($ctype, ParameterType::STRING)
|
|
);
|
|
}
|
|
|
|
$contentElements = $queryBuilder
|
|
->select('*')
|
|
->from('tt_content')
|
|
->where(
|
|
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->or(...$ctypeConstraints),
|
|
$queryBuilder->expr()->eq('deleted', 0),
|
|
$queryBuilder->expr()->eq('hidden', 0)
|
|
)
|
|
->orderBy('sorting', 'ASC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
|
|
if ($contentElements === []) {
|
|
return '';
|
|
}
|
|
|
|
return $this->renderForRecord($contentElements[0]);
|
|
} catch (\Throwable $e) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Render exactly the given news tt_content row.
|
|
*
|
|
* @param array<string,mixed> $contentElement
|
|
*/
|
|
public function renderForRecord(array $contentElement): string
|
|
{
|
|
try {
|
|
$flexFormService = GeneralUtility::makeInstance(FlexFormService::class);
|
|
$flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? '');
|
|
$settings = $flexFormData['settings'] ?? [];
|
|
|
|
$type = (string)($contentElement['CType'] ?? '');
|
|
$mode = $type === 'news_newsdetail' ? 'detail' : 'list';
|
|
$debug = (bool)($settings['debug'] ?? false);
|
|
|
|
if ($mode === 'detail') {
|
|
$news = $this->resolveDetailNews($contentElement, $settings);
|
|
$response = [
|
|
'mode' => 'detail',
|
|
'news' => $news !== null ? $this->serializeNews($news, $settings) : null,
|
|
'settings' => $this->buildSettingsPayload($contentElement, $settings),
|
|
];
|
|
|
|
if ($debug) {
|
|
$response['debug'] = [
|
|
'contentElementUid' => (int)($contentElement['uid'] ?? 0),
|
|
'settings' => $settings,
|
|
];
|
|
}
|
|
|
|
return json_encode($response) ?: '';
|
|
}
|
|
|
|
$newsRows = $this->fetchNewsList($contentElement, $settings);
|
|
$items = array_map(fn (array $newsRow): array => $this->serializeNews($newsRow, $settings), $newsRows);
|
|
|
|
$response = [
|
|
'mode' => 'list',
|
|
'items' => $items,
|
|
'settings' => $this->buildSettingsPayload($contentElement, $settings),
|
|
];
|
|
|
|
if ($debug) {
|
|
$response['debug'] = [
|
|
'contentElementUid' => (int)($contentElement['uid'] ?? 0),
|
|
'count' => count($items),
|
|
'settings' => $settings,
|
|
];
|
|
}
|
|
|
|
return json_encode($response) ?: '';
|
|
} catch (\Throwable $e) {
|
|
return json_encode([
|
|
'mode' => 'error',
|
|
'message' => $e->getMessage(),
|
|
]) ?: '';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $contentElement
|
|
* @param array<string,mixed> $settings
|
|
* @return array<string,mixed>|null
|
|
*/
|
|
private function resolveDetailNews(array $contentElement, array $settings): ?array
|
|
{
|
|
$newsUid = $this->resolveNewsUid((string)($settings['singleNews'] ?? ''));
|
|
if ($newsUid <= 0) {
|
|
$queryParams = $GLOBALS['TYPO3_REQUEST']?->getQueryParams() ?? [];
|
|
$newsParam = $queryParams['tx_news_pi1']['news'] ?? null;
|
|
$newsUid = $this->resolveNewsUid((string)($newsParam ?? ''));
|
|
}
|
|
|
|
if ($newsUid > 0) {
|
|
$news = $this->fetchNewsByUid($newsUid, (int)($contentElement['sys_language_uid'] ?? 0));
|
|
if ($news !== null) {
|
|
return $news;
|
|
}
|
|
}
|
|
|
|
$list = $this->fetchNewsList($contentElement, $settings, 1);
|
|
return $list[0] ?? null;
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $contentElement
|
|
* @param array<string,mixed> $settings
|
|
* @param int|null $limitOverride
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function fetchNewsList(array $contentElement, array $settings, ?int $limitOverride = null): array
|
|
{
|
|
$selectedNews = $this->parseUidList((string)($settings['selectedList'] ?? ''));
|
|
$storagePids = $this->parseUidList((string)($settings['startingpoint'] ?? ''));
|
|
if ($storagePids === []) {
|
|
$storagePids = [(int)($contentElement['pid'] ?? 0)];
|
|
}
|
|
|
|
$categoryUids = $this->parseUidList((string)($settings['categories'] ?? ''));
|
|
$includeSubCategories = (bool)($settings['includeSubCategories'] ?? false);
|
|
$tagUids = $this->parseUidList((string)($settings['tags'] ?? ''));
|
|
$limit = $limitOverride ?? (int)($settings['limit'] ?? 0);
|
|
$orderDirection = strtoupper((string)($settings['orderDirection'] ?? 'DESC')) === 'ASC' ? 'ASC' : 'DESC';
|
|
$topNewsFirst = (bool)($settings['topNewsFirst'] ?? false);
|
|
$searchTerm = $this->resolveSearchTerm((string)($contentElement['CType'] ?? ''));
|
|
$languageUid = (int)($contentElement['sys_language_uid'] ?? 0);
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_news');
|
|
|
|
$rows = $queryBuilder
|
|
->select('n.*')
|
|
->from('tx_news_domain_model_news', 'n')
|
|
->where(
|
|
$queryBuilder->expr()->eq('n.deleted', 0),
|
|
$queryBuilder->expr()->eq('n.hidden', 0),
|
|
$queryBuilder->expr()->in('n.pid', $queryBuilder->createNamedParameter($storagePids, ArrayParameterType::INTEGER))
|
|
)
|
|
->orderBy('n.istopnews', $topNewsFirst ? 'DESC' : 'ASC')
|
|
->addOrderBy('n.datetime', 'DESC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
|
|
if ($languageUid > 0) {
|
|
$rows = array_values(array_filter($rows, static fn (array $row): bool => (int)($row['sys_language_uid'] ?? 0) === $languageUid));
|
|
} else {
|
|
$rows = array_values(array_filter($rows, static fn (array $row): bool => in_array((int)($row['sys_language_uid'] ?? 0), [0, -1], true)));
|
|
}
|
|
|
|
if ($selectedNews !== []) {
|
|
$selectedLookup = array_fill_keys($selectedNews, true);
|
|
$rows = array_values(array_filter($rows, static fn (array $row): bool => isset($selectedLookup[(int)($row['uid'] ?? 0)])));
|
|
}
|
|
|
|
if ($categoryUids !== []) {
|
|
$categoryLookup = $this->newsUidsForCategories($categoryUids, $includeSubCategories);
|
|
if ($categoryLookup === [] && !$includeSubCategories) {
|
|
$categoryLookup = $this->newsUidsForCategories($this->expandCategoryUids($categoryUids), true);
|
|
}
|
|
|
|
if ($categoryLookup !== []) {
|
|
$rows = array_values(array_filter($rows, static fn (array $row): bool => isset($categoryLookup[(int)($row['uid'] ?? 0)])));
|
|
}
|
|
}
|
|
|
|
if ($tagUids !== []) {
|
|
$tagLookup = $this->newsUidsForTags($tagUids);
|
|
if ($tagLookup !== []) {
|
|
$rows = array_values(array_filter($rows, static fn (array $row): bool => isset($tagLookup[(int)($row['uid'] ?? 0)])));
|
|
}
|
|
}
|
|
|
|
if ($searchTerm !== '') {
|
|
$needle = mb_strtolower($searchTerm);
|
|
$rows = array_values(array_filter($rows, static function (array $row) use ($needle): bool {
|
|
$haystack = mb_strtolower((string)($row['title'] ?? '') . ' ' . (string)($row['teaser'] ?? '') . ' ' . (string)($row['path_segment'] ?? ''));
|
|
return str_contains($haystack, $needle);
|
|
}));
|
|
}
|
|
|
|
if ($limit > 0) {
|
|
$rows = array_slice($rows, 0, $limit);
|
|
}
|
|
|
|
return $rows;
|
|
}
|
|
|
|
/**
|
|
* @param int $newsUid
|
|
* @param int $languageUid
|
|
* @return array<string,mixed>|null
|
|
*/
|
|
private function fetchNewsByUid(int $newsUid, int $languageUid): ?array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_news');
|
|
|
|
$query = $queryBuilder
|
|
->select('n.*')
|
|
->from('tx_news_domain_model_news', 'n')
|
|
->where(
|
|
$queryBuilder->expr()->eq('n.uid', $queryBuilder->createNamedParameter($newsUid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('n.deleted', 0),
|
|
$queryBuilder->expr()->eq('n.hidden', 0)
|
|
);
|
|
|
|
if ($languageUid > 0) {
|
|
$query->andWhere(
|
|
$queryBuilder->expr()->eq('n.sys_language_uid', $queryBuilder->createNamedParameter($languageUid, ParameterType::INTEGER))
|
|
);
|
|
}
|
|
|
|
$news = $query->executeQuery()->fetchAssociative();
|
|
return is_array($news) ? $news : null;
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $news
|
|
* @param array<string,mixed> $settings
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function serializeNews(array $news, array $settings = []): array
|
|
{
|
|
$uid = (int)($news['uid'] ?? 0);
|
|
$pathSegment = (string)($news['path_segment'] ?? '');
|
|
$urls = $this->buildNewsUrls($pathSegment, $settings);
|
|
|
|
return [
|
|
'uid' => $uid,
|
|
'title' => (string)($news['title'] ?? ''),
|
|
'alternativeTitle' => (string)($news['alternative_title'] ?? ''),
|
|
'pathSegment' => $pathSegment,
|
|
'detailUrl' => $urls['detailUrl'],
|
|
'canonicalUrl' => $urls['canonicalUrl'],
|
|
'teaser' => (string)($news['teaser'] ?? ''),
|
|
'bodytext' => RteResolver::html($news['bodytext'] ?? ''),
|
|
'datetime' => (int)($news['datetime'] ?? 0),
|
|
'archive' => (int)($news['archive'] ?? 0),
|
|
'istopnews' => (bool)($news['istopnews'] ?? false),
|
|
'author' => (string)($news['author'] ?? ''),
|
|
'authorEmail' => (string)($news['author_email'] ?? ''),
|
|
'categories' => $this->resolveCategories($uid),
|
|
'tags' => $this->resolveTags($uid),
|
|
'media' => $this->resolveFiles('tx_news_domain_model_news', 'fal_media', $uid),
|
|
'relatedFiles' => $this->resolveFiles('tx_news_domain_model_news', 'fal_related_files', $uid),
|
|
'related' => $this->resolveRelatedNews($uid),
|
|
'relatedLinks' => $this->resolveRelatedLinks($uid),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $settings
|
|
* @return array{detailUrl:string, canonicalUrl:string}
|
|
*/
|
|
private function buildNewsUrls(string $pathSegment, array $settings): array
|
|
{
|
|
$pathSegment = trim($pathSegment, '/');
|
|
if ($pathSegment === '') {
|
|
return ['detailUrl' => '', 'canonicalUrl' => ''];
|
|
}
|
|
|
|
$detailPid = (int)($settings['detailPid'] ?? 0);
|
|
if ($detailPid <= 0) {
|
|
$detailPid = $this->resolveCurrentPageId();
|
|
}
|
|
$detailPageSlug = $detailPid > 0 ? $this->resolvePageSlug($detailPid) : '';
|
|
if ($detailPageSlug === '') {
|
|
return ['detailUrl' => '', 'canonicalUrl' => ''];
|
|
}
|
|
|
|
$detailUrl = rtrim($detailPageSlug, '/') . '/' . $pathSegment;
|
|
$canonicalUrl = $this->absoluteUrl($detailUrl);
|
|
|
|
return [
|
|
'detailUrl' => $detailUrl,
|
|
'canonicalUrl' => $canonicalUrl,
|
|
];
|
|
}
|
|
|
|
private function resolvePageSlug(int $pageId): string
|
|
{
|
|
if ($pageId <= 0) {
|
|
return '';
|
|
}
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('pages');
|
|
|
|
$row = $queryBuilder
|
|
->select('slug')
|
|
->from('pages')
|
|
->where(
|
|
$queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('deleted', 0),
|
|
$queryBuilder->expr()->eq('hidden', 0)
|
|
)
|
|
->executeQuery()
|
|
->fetchAssociative();
|
|
|
|
return (string)($row['slug'] ?? '');
|
|
}
|
|
|
|
private function absoluteUrl(string $path): string
|
|
{
|
|
$path = '/' . ltrim($path, '/');
|
|
|
|
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
|
if ($request !== null) {
|
|
$normalizedParams = $request->getAttribute('normalizedParams');
|
|
if ($normalizedParams !== null) {
|
|
return rtrim($normalizedParams->getSiteUrl(), '/') . $path;
|
|
}
|
|
}
|
|
|
|
$site = $request?->getAttribute('site');
|
|
if ($site !== null) {
|
|
return rtrim((string)$site->getBase(), '/') . $path;
|
|
}
|
|
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function resolveCategories(int $newsUid): array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('sys_category');
|
|
|
|
return $queryBuilder
|
|
->select('c.uid', 'c.title')
|
|
->from('sys_category', 'c')
|
|
->join(
|
|
'c',
|
|
'sys_category_record_mm',
|
|
'mm',
|
|
'mm.uid_local = c.uid'
|
|
)
|
|
->where(
|
|
$queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($newsUid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('mm.tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', ParameterType::STRING)),
|
|
$queryBuilder->expr()->eq('mm.fieldname', $queryBuilder->createNamedParameter('categories', ParameterType::STRING))
|
|
)
|
|
->orderBy('c.title', 'ASC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function resolveTags(int $newsUid): array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_tag');
|
|
|
|
return $queryBuilder
|
|
->select('t.uid', 't.title')
|
|
->from('tx_news_domain_model_tag', 't')
|
|
->join(
|
|
't',
|
|
'tx_news_domain_model_news_tag_mm',
|
|
'mm',
|
|
'mm.uid_foreign = t.uid'
|
|
)
|
|
->where(
|
|
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($newsUid, ParameterType::INTEGER))
|
|
)
|
|
->orderBy('t.title', 'ASC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $categoryUids
|
|
* @return array<int,true>
|
|
*/
|
|
private function newsUidsForCategories(array $categoryUids, bool $includeSubCategories): array
|
|
{
|
|
if ($categoryUids === []) {
|
|
return [];
|
|
}
|
|
|
|
$categories = $includeSubCategories ? $this->expandCategoryUids($categoryUids) : $categoryUids;
|
|
if ($categories === []) {
|
|
return [];
|
|
}
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('sys_category_record_mm');
|
|
|
|
$uids = $queryBuilder
|
|
->select('uid_foreign')
|
|
->from('sys_category_record_mm')
|
|
->where(
|
|
$queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_news_domain_model_news', ParameterType::STRING)),
|
|
$queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('categories', ParameterType::STRING)),
|
|
$queryBuilder->expr()->in('uid_local', $queryBuilder->createNamedParameter($categories, ArrayParameterType::INTEGER))
|
|
)
|
|
->executeQuery()
|
|
->fetchFirstColumn();
|
|
|
|
return array_fill_keys(array_map('intval', $uids), true);
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $tagUids
|
|
* @return array<int,true>
|
|
*/
|
|
private function newsUidsForTags(array $tagUids): array
|
|
{
|
|
if ($tagUids === []) {
|
|
return [];
|
|
}
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_news_tag_mm');
|
|
|
|
$uids = $queryBuilder
|
|
->select('uid_local')
|
|
->from('tx_news_domain_model_news_tag_mm')
|
|
->where(
|
|
$queryBuilder->expr()->in('uid_foreign', $queryBuilder->createNamedParameter($tagUids, ArrayParameterType::INTEGER))
|
|
)
|
|
->executeQuery()
|
|
->fetchFirstColumn();
|
|
|
|
return array_fill_keys(array_map('intval', $uids), true);
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function resolveRelatedNews(int $newsUid): array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_news');
|
|
|
|
return $queryBuilder
|
|
->select('n.uid', 'n.title', 'n.path_segment')
|
|
->from('tx_news_domain_model_news', 'n')
|
|
->join(
|
|
'n',
|
|
'tx_news_domain_model_news_related_mm',
|
|
'mm',
|
|
'mm.uid_foreign = n.uid'
|
|
)
|
|
->where(
|
|
$queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($newsUid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('n.deleted', 0),
|
|
$queryBuilder->expr()->eq('n.hidden', 0)
|
|
)
|
|
->orderBy('n.datetime', 'DESC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function resolveRelatedLinks(int $newsUid): array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_link');
|
|
|
|
return $queryBuilder
|
|
->select('l.uid', 'l.title', 'l.description', 'l.uri')
|
|
->from('tx_news_domain_model_link', 'l')
|
|
->where(
|
|
$queryBuilder->expr()->eq('l.parent', $queryBuilder->createNamedParameter($newsUid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('l.deleted', 0),
|
|
$queryBuilder->expr()->eq('l.hidden', 0)
|
|
)
|
|
->orderBy('l.sorting', 'ASC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
}
|
|
|
|
/**
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
private function resolveFiles(string $tableName, string $fieldName, int $uid): array
|
|
{
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('sys_file_reference');
|
|
|
|
$rows = $queryBuilder
|
|
->select('uid')
|
|
->from('sys_file_reference')
|
|
->where(
|
|
$queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($uid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter($tableName, ParameterType::STRING)),
|
|
$queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter($fieldName, ParameterType::STRING)),
|
|
$queryBuilder->expr()->eq('deleted', 0),
|
|
$queryBuilder->expr()->eq('hidden', 0)
|
|
)
|
|
->orderBy('sorting_foreign', 'ASC')
|
|
->executeQuery()
|
|
->fetchAllAssociative();
|
|
|
|
if ($rows === []) {
|
|
return [];
|
|
}
|
|
|
|
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
|
|
$imageService = GeneralUtility::makeInstance(ImageService::class);
|
|
$files = [];
|
|
|
|
foreach ($rows as $row) {
|
|
try {
|
|
$fileReference = $resourceFactory->getFileReferenceObject((int)$row['uid']);
|
|
$processed = $imageService->applyProcessingInstructions($fileReference, ['width' => 1400, 'fileExtension' => 'webp']);
|
|
|
|
$files[] = [
|
|
'uid' => (int)$row['uid'],
|
|
'url' => $imageService->getImageUri($processed),
|
|
];
|
|
} catch (\Throwable $e) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return $files;
|
|
}
|
|
|
|
private function resolveNewsUid(string $rawValue): int
|
|
{
|
|
$rawValue = trim($rawValue);
|
|
if ($rawValue === '') {
|
|
return 0;
|
|
}
|
|
|
|
if (ctype_digit($rawValue)) {
|
|
return (int)$rawValue;
|
|
}
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('tx_news_domain_model_news');
|
|
|
|
$row = $queryBuilder
|
|
->select('uid')
|
|
->from('tx_news_domain_model_news')
|
|
->where(
|
|
$queryBuilder->expr()->eq('path_segment', $queryBuilder->createNamedParameter($rawValue, ParameterType::STRING)),
|
|
$queryBuilder->expr()->eq('deleted', 0),
|
|
$queryBuilder->expr()->eq('hidden', 0)
|
|
)
|
|
->executeQuery()
|
|
->fetchAssociative();
|
|
|
|
return (int)($row['uid'] ?? 0);
|
|
}
|
|
|
|
/**
|
|
* @return list<int>
|
|
*/
|
|
private function parseUidList(string $rawValue): array
|
|
{
|
|
$values = array_filter(array_map('trim', explode(',', $rawValue)));
|
|
|
|
return array_values(array_filter(array_map(static fn (string $value): int => (int)$value, $values)));
|
|
}
|
|
|
|
/**
|
|
* @param list<int> $categoryUids
|
|
* @return list<int>
|
|
*/
|
|
private function expandCategoryUids(array $categoryUids): array
|
|
{
|
|
if ($categoryUids === []) {
|
|
return [];
|
|
}
|
|
|
|
$allUids = array_fill_keys($categoryUids, true);
|
|
$queue = $categoryUids;
|
|
|
|
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
|
|
->getQueryBuilderForTable('sys_category');
|
|
|
|
while ($queue !== []) {
|
|
$currentUid = array_shift($queue);
|
|
if ($currentUid === null) {
|
|
continue;
|
|
}
|
|
|
|
$children = $queryBuilder
|
|
->select('uid')
|
|
->from('sys_category')
|
|
->where(
|
|
$queryBuilder->expr()->eq('parent', $queryBuilder->createNamedParameter($currentUid, ParameterType::INTEGER)),
|
|
$queryBuilder->expr()->eq('deleted', 0)
|
|
)
|
|
->executeQuery()
|
|
->fetchFirstColumn();
|
|
|
|
foreach ($children as $childUid) {
|
|
$childUid = (int)$childUid;
|
|
if ($childUid <= 0 || isset($allUids[$childUid])) {
|
|
continue;
|
|
}
|
|
|
|
$allUids[$childUid] = true;
|
|
$queue[] = $childUid;
|
|
}
|
|
}
|
|
|
|
return array_values(array_map('intval', array_keys($allUids)));
|
|
}
|
|
|
|
private function resolveCurrentPageId(): int
|
|
{
|
|
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
|
if ($request !== null) {
|
|
$pageInfo = $request->getAttribute('frontend.page.information');
|
|
if ($pageInfo !== null) {
|
|
return (int)$pageInfo->getId();
|
|
}
|
|
}
|
|
|
|
return (int)($GLOBALS['TSFE']->id ?? 0);
|
|
}
|
|
|
|
/**
|
|
* @param array<string,mixed> $contentElement
|
|
* @param array<string,mixed> $settings
|
|
* @return array<string,mixed>
|
|
*/
|
|
private function buildSettingsPayload(array $contentElement, array $settings): array
|
|
{
|
|
$payload = $settings;
|
|
$payload['type'] = (string)($contentElement['CType'] ?? '');
|
|
$payload['detailPid'] = (int)($settings['detailPid'] ?? 0);
|
|
$payload['listPid'] = (int)($settings['listPid'] ?? 0);
|
|
$payload['templateLayout'] = $this->resolveTemplateLayoutValue((string)($contentElement['CType'] ?? ''), (string)($settings['templateLayout'] ?? ''));
|
|
$payload['templateLayoutLabel'] = self::TEMPLATE_LAYOUTS[$payload['templateLayout']] ?? '';
|
|
$payload['templateLayouts'] = array_map(
|
|
static fn (string $label, string $key): array => ['key' => $key, 'label' => $label],
|
|
self::TEMPLATE_LAYOUTS,
|
|
array_keys(self::TEMPLATE_LAYOUTS)
|
|
);
|
|
|
|
return $payload;
|
|
}
|
|
|
|
private function resolveTemplateLayoutValue(string $type, string $value): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value !== '' && isset(self::TEMPLATE_LAYOUTS[$value])) {
|
|
return $value;
|
|
}
|
|
|
|
return self::TEMPLATE_LAYOUT_DEFAULTS[$type] ?? '';
|
|
}
|
|
|
|
private function resolveSearchTerm(string $type): string
|
|
{
|
|
if ($type !== 'news_newssearchresult') {
|
|
return '';
|
|
}
|
|
|
|
$queryParams = $GLOBALS['TYPO3_REQUEST']?->getQueryParams() ?? [];
|
|
$search = $queryParams['tx_news_pi1']['search']['sword'] ?? '';
|
|
|
|
return trim((string)$search);
|
|
}
|
|
} |