Markets List and Detail Plugin
This commit is contained in:
@@ -57,4 +57,18 @@ class MarketController extends ActionController
|
||||
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* action list
|
||||
*
|
||||
* Extbase registration target for the Marketlist plugin. In headless mode
|
||||
* the JSON output is produced by MarketListJsonRenderer, not by this
|
||||
* controller — same arrangement as LocationController::listAction().
|
||||
*
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function listAction(): ResponseInterface
|
||||
{
|
||||
return $this->htmlResponse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use Evomedien\Vitec\UserFunc\ProductListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\ProductShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\MarketListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\MarketShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\SolutionShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\DownloadcardJsonRenderer;
|
||||
@@ -110,6 +111,7 @@ final class ContainerChildrenProcessor implements DataProcessorInterface
|
||||
'vitec_usecaselist' => [UsecaseListJsonRenderer::class, 'usecases'],
|
||||
'vitec_usecaseshow' => [UsecaseShowJsonRenderer::class, 'usecase'],
|
||||
'vitec_marketshow' => [MarketShowJsonRenderer::class, 'market'],
|
||||
'vitec_marketlist' => [MarketListJsonRenderer::class, 'markets'],
|
||||
'vitec_solutionshow' => [SolutionShowJsonRenderer::class, 'solution'],
|
||||
'vitec_downloadcard' => [DownloadcardJsonRenderer::class, 'downloadcard'],
|
||||
'vitec_downloadcardcollection' => [DownloadcardcollectionJsonRenderer::class, 'downloadcardcollection'],
|
||||
|
||||
@@ -13,6 +13,18 @@ class Market extends AbstractEntity
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $slug = '';
|
||||
|
||||
/**
|
||||
* Page uid presenting this market (TCA type "group", allowed: pages).
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $detailPage = 0;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -65,6 +77,38 @@ class Market extends AbstractEntity
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug(): string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slug
|
||||
*/
|
||||
public function setSlug(string $slug): void
|
||||
{
|
||||
$this->slug = $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDetailPage(): int
|
||||
{
|
||||
return $this->detailPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $detailPage
|
||||
*/
|
||||
public function setDetailPage(int $detailPage): void
|
||||
{
|
||||
$this->detailPage = $detailPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,11 @@ class Solution extends AbstractEntity
|
||||
*/
|
||||
protected $title = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $slug = '';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
@@ -65,6 +70,22 @@ class Solution extends AbstractEntity
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getSlug(): string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $slug
|
||||
*/
|
||||
public function setSlug(string $slug): void
|
||||
{
|
||||
$this->slug = $slug;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ use Evomedien\Vitec\UserFunc\ProductListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\ProductShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\UsecaseListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\UsecaseShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\MarketListJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\MarketShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\SolutionShowJsonRenderer;
|
||||
use Evomedien\Vitec\UserFunc\DownloadcardJsonRenderer;
|
||||
@@ -75,6 +76,7 @@ final class ContentElementResolver
|
||||
'vitec_usecaselist' => [UsecaseListJsonRenderer::class, 'usecases'],
|
||||
'vitec_usecaseshow' => [UsecaseShowJsonRenderer::class, 'usecase'],
|
||||
'vitec_marketshow' => [MarketShowJsonRenderer::class, 'market'],
|
||||
'vitec_marketlist' => [MarketListJsonRenderer::class, 'markets'],
|
||||
'vitec_solutionshow' => [SolutionShowJsonRenderer::class, 'solution'],
|
||||
'vitec_downloadcard' => [DownloadcardJsonRenderer::class, 'downloadcard'],
|
||||
'vitec_downloadcardcollection' => [DownloadcardcollectionJsonRenderer::class, 'downloadcardcollection'],
|
||||
|
||||
56
packages/vitec/Classes/Service/RteResolver.php
Normal file
56
packages/vitec/Classes/Service/RteResolver.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\Service;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* Turns raw richtext (RTE) database content into frontend-ready HTML.
|
||||
*
|
||||
* TYPO3 stores RTE fields with unresolved internal references: internal links as
|
||||
* `<a href="t3://page?uid=12">`, legacy content as `<link>` tags, images with
|
||||
* relative paths. Fluid resolves those through `parseFunc` when it renders; a
|
||||
* headless UserFunc renderer that hands the raw column straight to JSON does
|
||||
* not, and the frontend receives dead links.
|
||||
*
|
||||
* The two upstream packages already close this gap for the output they own:
|
||||
* `friendsoftypo3/headless` applies `parseFunc =< lib.parseFunc_RTE` to the core
|
||||
* text elements via TypoScript, and `nb-headless-content-blocks` calls
|
||||
* `parseFunc($value, null, '< lib.parseFunc_RTE')` for every Content Block field
|
||||
* whose TCA has `enableRichtext`. This class closes it for the VITEC UserFunc
|
||||
* renderers, using exactly the same call.
|
||||
*
|
||||
* Static by design: the conversion is stateless, and the call sites are payload
|
||||
* array literals where a `GeneralUtility::makeInstance(...)->` prefix would add
|
||||
* only noise. Same rationale as `CropVariants::firstImage()` and
|
||||
* `FormDefinitions::get()`.
|
||||
*
|
||||
* Fail-soft per architecture spec clause 9.5: when parsing is impossible — most
|
||||
* notably outside a frontend request, where no TypoScript setup exists — the raw
|
||||
* value is returned. Content is never lost; at worst it stays unresolved.
|
||||
*/
|
||||
final class RteResolver
|
||||
{
|
||||
/**
|
||||
* @param mixed $value raw column value; null and non-strings are tolerated
|
||||
*/
|
||||
public static function html(mixed $value): string
|
||||
{
|
||||
$raw = (string)($value ?? '');
|
||||
if (trim($raw) === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
$parsed = GeneralUtility::makeInstance(ContentObjectRenderer::class)
|
||||
->parseFunc($raw, null, '< lib.parseFunc_RTE');
|
||||
|
||||
return $parsed !== '' ? $parsed : $raw;
|
||||
} catch (\Throwable $e) {
|
||||
return $raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ use TYPO3\CMS\Core\Core\Environment;
|
||||
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;
|
||||
|
||||
@@ -261,7 +262,7 @@ class DatasheetsJsonRenderer
|
||||
'title' => (string)($download['title'] ?? ''),
|
||||
'slug' => (string)($download['slug'] ?? ''),
|
||||
'teaser' => (string)($download['teaser'] ?? ''),
|
||||
'description' => (string)($download['description'] ?? ''),
|
||||
'description' => RteResolver::html($download['description'] ?? ''),
|
||||
'tstamp' => (int)($download['tstamp'] ?? 0),
|
||||
'filetype' => $filetype,
|
||||
'file' => $this->getDownloadFile($uid, (string)($download['fileprefix'] ?? ''), $filetype),
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\DBAL\ParameterType;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||
use Evomedien\Vitec\Service\RteResolver;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
@@ -254,7 +255,7 @@ class DownloadcardJsonRenderer
|
||||
'title' => (string)($download['title'] ?? ''),
|
||||
'slug' => (string)($download['slug'] ?? ''),
|
||||
'teaser' => (string)($download['teaser'] ?? ''),
|
||||
'description' => (string)($download['description'] ?? ''),
|
||||
'description' => RteResolver::html($download['description'] ?? ''),
|
||||
'keywords' => (string)($download['keywords'] ?? ''),
|
||||
'icon' => (string)($download['icon'] ?? ''),
|
||||
'filepath' => (string)($download['filepath'] ?? ''),
|
||||
|
||||
@@ -12,6 +12,7 @@ use TYPO3\CMS\Core\Database\Connection;
|
||||
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;
|
||||
|
||||
@@ -258,7 +259,7 @@ class DownloadcardcollectionJsonRenderer
|
||||
'title' => (string)($download['title'] ?? ''),
|
||||
'slug' => (string)($download['slug'] ?? ''),
|
||||
'teaser' => (string)($download['teaser'] ?? ''),
|
||||
'description' => (string)($download['description'] ?? ''),
|
||||
'description' => RteResolver::html($download['description'] ?? ''),
|
||||
'keywords' => (string)($download['keywords'] ?? ''),
|
||||
'icon' => (string)($download['icon'] ?? ''),
|
||||
'filepath' => (string)($download['filepath'] ?? ''),
|
||||
|
||||
@@ -9,6 +9,7 @@ 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;
|
||||
|
||||
@@ -195,7 +196,7 @@ class EventlistJsonRenderer
|
||||
'title' => (string)($event['title'] ?? ''),
|
||||
'slug' => (string)($event['slug'] ?? ''),
|
||||
'teaser' => (string)($event['teaser'] ?? ''),
|
||||
'description' => (string)($event['description'] ?? ''),
|
||||
'description' => RteResolver::html($event['description'] ?? ''),
|
||||
'eventstart' => $start > 0 ? date('Y-m-d', $start) : null,
|
||||
'eventend' => $end > 0 ? date('Y-m-d', $end) : null,
|
||||
'venue' => (string)($event['venue'] ?? ''),
|
||||
|
||||
@@ -8,6 +8,7 @@ use Doctrine\DBAL\ParameterType;
|
||||
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||
use Evomedien\Vitec\Service\RteResolver;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
@@ -82,7 +83,7 @@ class LocationsJsonRenderer
|
||||
$settings = $flexFormData['settings'] ?? [];
|
||||
|
||||
$variant = (string)($settings['variant'] ?? 'grid');
|
||||
$mapText = trim((string)($settings['maptext'] ?? ''));
|
||||
$mapText = RteResolver::html($settings['maptext'] ?? '');
|
||||
$debugMode = (bool)($settings['debug'] ?? false);
|
||||
|
||||
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE);
|
||||
|
||||
196
packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php
Normal file
196
packages/vitec/Classes/UserFunc/MarketListJsonRenderer.php
Normal file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Evomedien\Vitec\UserFunc;
|
||||
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Evomedien\Vitec\Service\RteResolver;
|
||||
use Evomedien\Vitec\Service\UsecaseSerializer;
|
||||
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* UserFunc: render all VITEC markets as JSON (headless).
|
||||
*
|
||||
* Output under content.markets:
|
||||
* { "layout": "grid|list|carousel", "markets": [ … ] }
|
||||
*
|
||||
* Selection semantics (FlexForm `settings.markets`):
|
||||
* - nothing selected -> ALL visible markets, alphabetical by title
|
||||
* (tx_vitec_domain_model_market has no `sorting` column)
|
||||
* - markets selected -> exactly those, in the order the editor arranged
|
||||
* them in the FlexForm
|
||||
*
|
||||
* The page carrying the plugin is never used as a filter.
|
||||
*
|
||||
* Each item uses the same shape as the card payload (ModelcardJsonRenderer,
|
||||
* model type "market"), so the frontend can render list and single card with
|
||||
* one component. Image resolution is delegated to UsecaseSerializer::image()
|
||||
* rather than re-implemented inline (architecture spec, clause 9.2).
|
||||
*
|
||||
* `render()` = top-level plugin / page discovery. `renderForRecord()` = one
|
||||
* specific tt_content row (reused by ContainerChildrenProcessor for nested
|
||||
* plugins). Exception-safe.
|
||||
*/
|
||||
class MarketListJsonRenderer
|
||||
{
|
||||
private const TABLE = 'tx_vitec_domain_model_market';
|
||||
private const CTYPE = 'vitec_marketlist';
|
||||
|
||||
#[AsAllowedCallable]
|
||||
public function render(string $content, array $conf): string
|
||||
{
|
||||
$row = is_array($this->cObj->data ?? null) ? $this->cObj->data : null;
|
||||
if ($row && (string)($row['CType'] ?? '') === self::CTYPE) {
|
||||
return $this->renderForRecord($row);
|
||||
}
|
||||
|
||||
$pageId = 0;
|
||||
$request = $GLOBALS['TYPO3_REQUEST'] ?? null;
|
||||
if ($request !== null) {
|
||||
$pageInfo = $request->getAttribute('frontend.page.information');
|
||||
if ($pageInfo !== null) {
|
||||
$pageId = (int)$pageInfo->getId();
|
||||
}
|
||||
}
|
||||
if ($pageId <= 0) {
|
||||
$pageId = (int)($GLOBALS['TSFE']->id ?? 0);
|
||||
}
|
||||
if ($pageId <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
|
||||
$ces = $qb
|
||||
->select('*')
|
||||
->from('tt_content')
|
||||
->where(
|
||||
$qb->expr()->eq('pid', $qb->createNamedParameter($pageId, ParameterType::INTEGER)),
|
||||
$qb->expr()->eq('CType', $qb->createNamedParameter(self::CTYPE, ParameterType::STRING)),
|
||||
$qb->expr()->eq('deleted', 0),
|
||||
$qb->expr()->eq('hidden', 0)
|
||||
)
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
if (empty($ces)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->renderForRecord($ces[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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'] ?? [];
|
||||
|
||||
$layout = (string)($settings['layout'] ?? 'grid');
|
||||
$debugMode = (bool)($settings['debug'] ?? false);
|
||||
$selectedUids = GeneralUtility::intExplode(',', (string)($settings['markets'] ?? ''), true);
|
||||
|
||||
// Always fetch the full (small) table once — that way a selected
|
||||
// record that has meanwhile been hidden or deleted simply drops out.
|
||||
$qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE);
|
||||
$rows = $qb
|
||||
->select('*')
|
||||
->from(self::TABLE)
|
||||
->where(
|
||||
$qb->expr()->eq('deleted', 0),
|
||||
$qb->expr()->eq('hidden', 0)
|
||||
)
|
||||
->orderBy('title', 'ASC')
|
||||
->executeQuery()
|
||||
->fetchAllAssociative();
|
||||
|
||||
$serializer = GeneralUtility::makeInstance(UsecaseSerializer::class);
|
||||
|
||||
if ($selectedUids === []) {
|
||||
$markets = array_map(
|
||||
fn(array $r): array => $this->serializeMarket($r, $serializer),
|
||||
$rows
|
||||
);
|
||||
} else {
|
||||
// Keep the editor's FlexForm order. A SQL IN() would return the
|
||||
// rows in storage order, so the sequence is rebuilt here.
|
||||
$byUid = [];
|
||||
foreach ($rows as $r) {
|
||||
$byUid[(int)$r['uid']] = $r;
|
||||
}
|
||||
$markets = [];
|
||||
foreach ($selectedUids as $uid) {
|
||||
if (isset($byUid[$uid])) {
|
||||
$markets[] = $this->serializeMarket($byUid[$uid], $serializer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = [
|
||||
'layout' => $layout,
|
||||
'markets' => $markets,
|
||||
];
|
||||
|
||||
if ($debugMode) {
|
||||
$response['debug'] = [
|
||||
'count' => count($markets),
|
||||
'selected' => $selectedUids,
|
||||
'settings' => $settings,
|
||||
];
|
||||
}
|
||||
|
||||
return (string)json_encode($response);
|
||||
} catch (\Throwable $e) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $r
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
private function serializeMarket(array $r, UsecaseSerializer $serializer): array
|
||||
{
|
||||
$uid = (int)$r['uid'];
|
||||
|
||||
return [
|
||||
'uid' => $uid,
|
||||
'title' => (string)($r['title'] ?? ''),
|
||||
'slug' => (string)($r['slug'] ?? ''),
|
||||
'subtitle' => (string)($r['subtitle'] ?? ''),
|
||||
'teaser' => (string)($r['teaser'] ?? ''),
|
||||
'description' => RteResolver::html($r['description'] ?? ''),
|
||||
'detailUrl' => $this->detailUrl((int)($r['detail_page'] ?? 0)),
|
||||
'image' => $serializer->image($uid, 'image', self::TABLE, true),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `detail_page` uid to a URL. The frontend cannot do anything
|
||||
* with a raw page uid, so the link is built server side — same convention
|
||||
* as `headerLink` and LocationsJsonRenderer.
|
||||
*
|
||||
* Returns null when no page is set or the link cannot be resolved.
|
||||
*/
|
||||
private function detailUrl(int $pageUid): ?string
|
||||
{
|
||||
if ($pageUid <= 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$url = $cObj->typoLink_URL(['parameter' => (string)$pageUid]);
|
||||
return $url !== '' ? $url : null;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,10 @@ use Doctrine\DBAL\ParameterType;
|
||||
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;
|
||||
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
|
||||
|
||||
/**
|
||||
* UserFunc to render a single market as JSON for headless output.
|
||||
@@ -155,9 +157,11 @@ class MarketShowJsonRenderer
|
||||
return [
|
||||
'uid' => $uid,
|
||||
'title' => (string)($market['title'] ?? ''),
|
||||
'slug' => (string)($market['slug'] ?? ''),
|
||||
'subtitle' => (string)($market['subtitle'] ?? ''),
|
||||
'teaser' => (string)($market['teaser'] ?? ''),
|
||||
'description' => (string)($market['description'] ?? ''),
|
||||
'description' => RteResolver::html($market['description'] ?? ''),
|
||||
'detailUrl' => $this->detailUrl((int)($market['detail_page'] ?? 0)),
|
||||
'categories' => $this->getMarketCategories($uid),
|
||||
'image' => $this->getMarketImage($uid),
|
||||
];
|
||||
@@ -267,4 +271,25 @@ class MarketShowJsonRenderer
|
||||
];
|
||||
}, $categories);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the `detail_page` uid to a URL. The frontend cannot do anything
|
||||
* with a raw page uid, so the link is built server side — same convention
|
||||
* as `headerLink` and LocationsJsonRenderer.
|
||||
*
|
||||
* Returns null when no page is set or the link cannot be resolved.
|
||||
*/
|
||||
private function detailUrl(int $pageUid): ?string
|
||||
{
|
||||
if ($pageUid <= 0) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
|
||||
$url = $cObj->typoLink_URL(['parameter' => (string)$pageUid]);
|
||||
return $url !== '' ? $url : null;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Evomedien\Vitec\UserFunc;
|
||||
|
||||
use Doctrine\DBAL\ParameterType;
|
||||
use Evomedien\Vitec\Service\RteResolver;
|
||||
use Evomedien\Vitec\Service\UsecaseSerializer;
|
||||
use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
@@ -166,9 +167,10 @@ class ModelcardJsonRenderer
|
||||
return [
|
||||
'uid' => $uid,
|
||||
'title' => (string)($row['title'] ?? ''),
|
||||
'slug' => (string)($row['slug'] ?? ''),
|
||||
'subtitle' => (string)($row['subtitle'] ?? ''),
|
||||
'teaser' => (string)($row['teaser'] ?? ''),
|
||||
'description' => (string)($row['description'] ?? ''),
|
||||
'description' => RteResolver::html($row['description'] ?? ''),
|
||||
'image' => $serializer->image($uid, 'image', self::MODEL_TABLES[$modelType], true),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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;
|
||||
|
||||
@@ -319,7 +320,7 @@ final class NewsJsonRenderer
|
||||
'detailUrl' => $urls['detailUrl'],
|
||||
'canonicalUrl' => $urls['canonicalUrl'],
|
||||
'teaser' => (string)($news['teaser'] ?? ''),
|
||||
'bodytext' => (string)($news['bodytext'] ?? ''),
|
||||
'bodytext' => RteResolver::html($news['bodytext'] ?? ''),
|
||||
'datetime' => (int)($news['datetime'] ?? 0),
|
||||
'archive' => (int)($news['archive'] ?? 0),
|
||||
'istopnews' => (bool)($news['istopnews'] ?? false),
|
||||
|
||||
@@ -16,6 +16,7 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
use TYPO3\CMS\Core\Resource\FileReference;
|
||||
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;
|
||||
|
||||
@@ -177,9 +178,9 @@ class ProductListJsonRenderer
|
||||
'teaser' => (string)($product['teaser'] ?? ''),
|
||||
'subtitle' => (string)($product['subtitle'] ?? ''),
|
||||
'video' => (string)($product['video'] ?? ''),
|
||||
'applications' => (string)($product['applications'] ?? ''),
|
||||
'description' => (string)($product['description'] ?? ''),
|
||||
'highlights' => (string)($product['highlights'] ?? ''),
|
||||
'applications' => RteResolver::html($product['applications'] ?? ''),
|
||||
'description' => RteResolver::html($product['description'] ?? ''),
|
||||
'highlights' => RteResolver::html($product['highlights'] ?? ''),
|
||||
'shortcutpid' => (string)($product['shortcutpid'] ?? ''),
|
||||
'contentelement' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelement'] ?? '')),
|
||||
'contentelementcta' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelementcta'] ?? '')),
|
||||
@@ -697,7 +698,7 @@ class ProductListJsonRenderer
|
||||
'title' => $download['title'] ?? '',
|
||||
'slug' => $download['slug'] ?? '',
|
||||
'teaser' => $download['teaser'] ?? '',
|
||||
'description' => $download['description'] ?? '',
|
||||
'description' => RteResolver::html($download['description'] ?? ''),
|
||||
'keywords' => $download['keywords'] ?? '',
|
||||
'icon' => $download['icon'] ?? '',
|
||||
'fileprefix' => $fileprefix,
|
||||
@@ -741,7 +742,7 @@ class ProductListJsonRenderer
|
||||
'slug' => (string)($rel['slug'] ?? ''),
|
||||
'subtitle' => (string)($rel['subtitle'] ?? ''),
|
||||
'teaser' => (string)($rel['teaser'] ?? ''),
|
||||
'description' => (string)($rel['description'] ?? ''),
|
||||
'description' => RteResolver::html($rel['description'] ?? ''),
|
||||
'link' => '/product/' . (string)($rel['slug'] ?? ''),
|
||||
'images' => $this->getProductImages($relUid),
|
||||
];
|
||||
|
||||
@@ -10,6 +10,7 @@ use TYPO3\CMS\Core\Attribute\AsAllowedCallable;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Service\FlexFormService;
|
||||
use Evomedien\Vitec\Service\RteResolver;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Resource\ResourceFactory;
|
||||
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
|
||||
@@ -192,9 +193,9 @@ class ProductShowJsonRenderer
|
||||
'teaser' => (string)($product['teaser'] ?? ''),
|
||||
'subtitle' => (string)($product['subtitle'] ?? ''),
|
||||
'video' => (string)($product['video'] ?? ''),
|
||||
'applications' => (string)($product['applications'] ?? ''),
|
||||
'description' => (string)($product['description'] ?? ''),
|
||||
'highlights' => (string)($product['highlights'] ?? ''),
|
||||
'applications' => RteResolver::html($product['applications'] ?? ''),
|
||||
'description' => RteResolver::html($product['description'] ?? ''),
|
||||
'highlights' => RteResolver::html($product['highlights'] ?? ''),
|
||||
'shortcutpid' => (string)($product['shortcutpid'] ?? ''),
|
||||
'contentelement' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelement'] ?? '')),
|
||||
'contentelementcta' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelementcta'] ?? '')),
|
||||
@@ -769,7 +770,7 @@ class ProductShowJsonRenderer
|
||||
'title' => $download['title'] ?? '',
|
||||
'slug' => $download['slug'] ?? '',
|
||||
'teaser' => $download['teaser'] ?? '',
|
||||
'description' => $download['description'] ?? '',
|
||||
'description' => RteResolver::html($download['description'] ?? ''),
|
||||
'keywords' => $download['keywords'] ?? '',
|
||||
'icon' => $download['icon'] ?? '',
|
||||
'fileprefix' => $fileprefix,
|
||||
@@ -813,7 +814,7 @@ class ProductShowJsonRenderer
|
||||
'slug' => (string)($rel['slug'] ?? ''),
|
||||
'subtitle' => (string)($rel['subtitle'] ?? ''),
|
||||
'teaser' => (string)($rel['teaser'] ?? ''),
|
||||
'description' => (string)($rel['description'] ?? ''),
|
||||
'description' => RteResolver::html($rel['description'] ?? ''),
|
||||
'link' => '/product/' . (string)($rel['slug'] ?? ''),
|
||||
'images' => $this->getProductImages($relUid),
|
||||
];
|
||||
|
||||
@@ -10,6 +10,7 @@ use Doctrine\DBAL\ParameterType;
|
||||
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;
|
||||
|
||||
@@ -155,9 +156,10 @@ class SolutionShowJsonRenderer
|
||||
return [
|
||||
'uid' => $uid,
|
||||
'title' => (string)($solution['title'] ?? ''),
|
||||
'slug' => (string)($solution['slug'] ?? ''),
|
||||
'subtitle' => (string)($solution['subtitle'] ?? ''),
|
||||
'teaser' => (string)($solution['teaser'] ?? ''),
|
||||
'description' => (string)($solution['description'] ?? ''),
|
||||
'description' => RteResolver::html($solution['description'] ?? ''),
|
||||
'categories' => $this->getSolutionCategories($uid),
|
||||
'image' => $this->getSolutionImage($uid),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user