diff --git a/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php b/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/Domain/Model/Product.php b/packages/vitec/Classes/Domain/Model/Product.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529111828 b/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529111828 new file mode 100755 index 0000000..a913f06 --- /dev/null +++ b/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529111828 @@ -0,0 +1,922 @@ + + */ + protected $categories; + + /** + * Product images + * + * @var ObjectStorage + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $productimage; + + /** + * Downloads + * + * @var ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $downloads; + + + /** + * Open Graph Image + * + * @var FileReference + */ + protected $ogimage; + + /** + * Content element link - Key Features + * + * @var string + */ + protected $contentelement; + + /** + * Content element link - CTA Features + * + * @var string + */ + protected $contentelementcta; + + /** + * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Evomedien\Vitec\Domain\Model\Product> + * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $relatedprodukt; + + +/* --------------------------------------------------------------------- */ + + /** + * Returns the title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Sets the title + * + * @param string $title + * @return void + */ + public function setTitle(string $title) + { + $this->title = $title; + } + + /** + * Returns the video + * + * @return string + */ + public function getVideo() + { + return $this->video; + } + + /** + * Sets the video + * + * @param string $video + * @return void + */ + public function setVideo(string $video) + { + $this->video = $video; + } + + /** + * Returns the slug + * + * @return string + */ + public function getSlug() + { + return $this->slug; + } + + /** + * Sets the slug + * + * @param string $slug + * @return void + */ + public function setSlug(string $slug) + { + $this->slug = $slug; + } + + /** + * Returns the urltitle + * + * @return string + */ + public function getUrltitle() + { + return $this->urltitle; + } + + /** + * Sets the urltitle + * + * @param string $urltitle + * @return void + */ + public function setUrltitle(string $urltitle) + { + $this->urltitle = $urltitle; + } + + + /** + * Returns the seotitle + * + * @return string + */ + public function getSeotitle() + { + return $this->seotitle; + } + + /** + * Sets the seotitle + * + * @param string $seotitle + * @return void + */ + public function setSeotitle(string $seotitle) + { + $this->seotitle = $seotitle; + } + + + /** + * Returns the seometa + * + * @return string $seometa + */ + public function getSeometa() + { + return $this->seometa; + } + + /** + * Sets the seometa + * + * @param string $seometa + * @return void + */ + public function setSeometa($seometa) + { + $this->seometa = $seometa; + } + + /** + * Returns the keywords + * + * @return string $keywords + */ + public function getKeywords() + { + return $this->keywords; + } + + /** + * Sets the keywords + * + * @param string $keywords + * @return void + */ + public function setKeywords($keywords) + { + $this->keywords = $keywords; + } + + /** + * Returns the teaser + * + * @return string $kteasereywords + */ + public function getTeaser() + { + return $this->teaser; + } + + /** + * Sets the teaser + * + * @param string $teaser + * @return void + */ + public function setTeaser($teaser) + { + $this->teaser = $teaser; + } + + /** + * Returns the subtitle + * + * @return string $subtitle + */ + public function getSubtitle() + { + return $this->subtitle; + } + + /** + * Sets the subtitle + * + * @param string $subtitle + * @return void + */ + public function setSubtitle($subtitle) + { + $this->subtitle = $subtitle; + } + + /** + * Returns the hideonapp + * + * @return bool $hideonapp + */ + public function getHideonapp() + { + return $this->hideonapp; + } + + /** + * Sets the hideonapp + * + * @param bool $hideonapp + * @return void + */ + public function setHideonapp($hideonapp) + { + $this->hideonapp = $hideonapp; + } + + /** + * Returns the description + * + * @return bool $description + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the description + * + * @param bool $description + * @return void + */ + public function setDescriptionp($description) + { + $this->description = $description; + } + + /** + * Returns the hideonwebsite + * + * @return bool $hideonwebsite + */ + public function getHideonwebsite() + { + return $this->hideonwebsite; + } + + /** + * Sets the hideonwebsite + * + * @param bool $hideonwebsite + * @return void + */ + public function setHideonwebsite($hideonwebsite) + { + $this->hideonwebsite = $hideonwebsite; + } + + /** + * Returns the hideondatasheets + * + * @return bool $hideondatasheets + */ + public function getHideondatasheets() + { + return $this->hideondatasheets; + } + + /** + * Sets the hideondatasheets + * + * @param bool $hideondatasheets + * @return void + */ + public function setHideondatasheets($hideondatasheets) + { + $this->hideondatasheets = $hideondatasheets; + } + + /** + * Returns the hideonproducts + * + * @return bool $hideonproducts + */ + public function getHideonproducts() + { + return $this->hideonproducts; + } + + /** + * Sets the hideonproducts + * + * @param bool $hideonproducts + * @return void + */ + public function setHideonproducts($hideonproducts) + { + $this->hideonproducts = $hideonproducts; + } + + /** + * Returns the structureddata + * + * @return string $structureddata + */ + public function getStructureddata() + { + return $this->structureddata; + } + + /** + * Sets the structureddata + * + * @param string $structureddata + * @return void + */ + public function setStructureddata($structureddata) + { + $this->structureddata = $structureddata; + } + + /** + * Returns the applications + * + * @return string $applications + */ + public function getApplications() + { + return $this->applications; + } + + /** + * Sets the applications + * + * @param string $applications + * @return void + */ + public function setApplications($applications) + { + $this->applications = $applications; + } + + /** + * Returns the highlights + * + * @return string $highlights + */ + public function getHighlights() + { + return $this->highlights; + } + + /** + * Sets the highlights + * + * @param string $highlights + * @return void + */ + public function setHighlights($highlights) + { + $this->highlights = $highlights; + } + /** + * Returns the shortcutpid + * + * @return string + */ + public function getShortcutpid() + { + return $this->shortcutpid; + } + + /** + * Sets the shortcutpid + * + * @param string $shortcutpid + * @return void + */ + public function setShortcutpid ($shortcutpid) + { + $this->shortcutpid = $shortcutpid; + } + + /** + * Returns the shortcut + * + * @return bool $shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Sets the shortcut + * + * @param bool $shortcut + * @return void + */ + public function setShortcut($shortcut) + { + $this->shortcut = $shortcut; + } + + /** + * Returns the legacy + * + * @return bool $legacy + */ + public function getLegacy() + { + return $this->legacy; + } + + /** + * Sets the legacy + * + * @param bool $legacy + * @return void + */ + public function setLegacy($legacy) + { + $this->legacy = $legacy; + } + + /** + * Returns the supportproduct + * + * @return bool $supportproduct + */ + public function getSupportproduct() + { + return $this->supportproduct; + } + + /** + * Sets the supportproduct + * + * @param bool $supportproduct + * @return void + */ + public function setSupportproduct($supportproduct) + { + $this->supportproduct = $supportproduct; + } + + /** + * Returns the subproduct + * + * @return bool $subproduct + */ + public function getSubproduct() + { + return $this->subproduct; + } + + /** + * Sets the subproduct + * + * @param bool $subproduct + * @return void + */ + public function setSubproduct($subproduct) + { + $this->subproduct = $subproduct; + } + + /** + * Initializes the ObjectStorage for categories + */ + public function __construct() + { + $this->categories = new ObjectStorage(); + $this->productimage = new ObjectStorage(); + $this->downloads = new ObjectStorage(); + } + + /** + * Adds a category + * + * @param Category $category + * @return void + */ + public function addCategory(Category $category) + { + $this->categories->attach($category); + } + + /** + * Removes a category + * + * @param Category $categoryToRemove + * @return void + */ + public function removeCategory(Category $categoryToRemove) + { + $this->categories->detach($categoryToRemove); + } + + /** + * Returns the categories + * + * @return ObjectStorage + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Sets the categories + * + * @param ObjectStorage $categories + * @return void + */ + public function setCategories(ObjectStorage $categories) + { + $this->categories = $categories; + } + + + /** + * Adds a product image + * + * @param FileReference $productimage + * @return void + */ + public function addProductimage(FileReference $productimage) + { + $this->productimage->attach($productimage); + } + + /** + * Removes a product image + * + * @param FileReference $productimageToRemove + * @return void + */ + public function removeProductimage(FileReference $productimageToRemove) + { + $this->productimage->detach($productimageToRemove); + } + + /** + * Returns the product images + * + * @return ObjectStorage + */ + public function getProductimage() + { + return $this->productimage; + } + + /** + * Sets the product images + * + * @param ObjectStorage $productimage + * @return void + */ + public function setProductimage(ObjectStorage $productimage) + { + $this->productimage = $productimage; + } + + + /** + * Adds a download + * + * @param \Evomedien\Vitec\Domain\Model\Download $download + * @return void + */ + public function addDownload(\Evomedien\Vitec\Domain\Model\Download $download): void + { + $this->downloads->attach($download); + } + + /** + * Removes a download + * + * @param \Evomedien\Vitec\Domain\Model\Download $downloadToRemove + * @return void + */ + public function removeDownload(\Evomedien\Vitec\Domain\Model\Download $downloadToRemove): void + { + $this->downloads->detach($downloadToRemove); + } + + /** + * Returns the downloads + * + * @return ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> + */ + public function getDownloads(): ObjectStorage + { + return $this->downloads; + } + + /** + * Sets the downloads + * + * @param ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> $downloads + * @return void + */ + public function setDownloads(ObjectStorage $downloads): void + { + $this->downloads = $downloads; + } + + + /** + * Returns the Open Graph Image + * + * @return FileReference|null + */ + public function getOgimage(): ?FileReference + { + return $this->ogimage; + } + + /** + * Sets the Open Graph Image + * + * @param FileReference $ogimage + * @return void + */ + public function setOgimage(FileReference $ogimage): void + { + $this->ogimage = $ogimage; + } + + /** + * Returns the content element link + * + * @return string + */ + public function getContentelement(): string + { + return $this->contentelement; + } + + /** + * Sets the content element link + * + * @param string $contentelement + * @return void + */ + public function setContentelement(string $contentelement): void + { + $this->contentelement = $contentelement; + } + + /** + * Returns the content element cta link + * + * @return string + */ + public function getContentelementcta(): string + { + return $this->contentelementcta; + } + + /** + * Sets the content element link cta + * + * @param string $contentelementcta + * @return void + */ + public function setContentelementcta(string $contentelementcta): void + { + $this->contentelementcta = $contentelementcta; + } + + /** + * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product> + */ + public function getRelatedprodukt(): \TYPO3\CMS\Extbase\Persistence\ObjectStorage + { + return $this->relatedprodukt; + } + + /** + * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product> $relatedprodukt + */ + public function setRelatedprodukt(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $relatedprodukt): void + { + $this->relatedprodukt = $relatedprodukt; + } +/* --------------------------------------------------------------------- */ + +} \ No newline at end of file diff --git a/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529120751 b/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529120751 new file mode 100755 index 0000000..54a7ae8 --- /dev/null +++ b/packages/vitec/Classes/Domain/Model/Product.php.bak.20260529120751 @@ -0,0 +1,952 @@ + + */ + protected $categories; + + /** + * Product images + * + * @var ObjectStorage + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $productimage; + + /** + * Downloads + * + * @var ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $downloads; + + + /** + * Open Graph Image + * + * @var FileReference + */ + protected $ogimage; + + /** + * Content element link - Key Features + * + * @var string + */ + + /** + * Uploaded / locally selected video file (FAL) + * + * @var \TYPO3\CMS\Extbase\Domain\Model\FileReference|null + */ + protected $videofile; + + protected $contentelement; + + /** + * Content element link - CTA Features + * + * @var string + */ + protected $contentelementcta; + + /** + * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Evomedien\Vitec\Domain\Model\Product> + * @TYPO3\CMS\Extbase\Annotation\ORM\Lazy + * @TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove") + */ + protected $relatedprodukt; + + +/* --------------------------------------------------------------------- */ + + /** + * Returns the title + * + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * Sets the title + * + * @param string $title + * @return void + */ + public function setTitle(string $title) + { + $this->title = $title; + } + + /** + * Returns the video + * + * @return string + */ + public function getVideo() + { + return $this->video; + } + + /** + * Sets the video + * + * @param string $video + * @return void + */ + public function setVideo(string $video) + { + $this->video = $video; + } + + /** + * Returns the slug + * + * @return string + */ + public function getSlug() + { + return $this->slug; + } + + /** + * Sets the slug + * + * @param string $slug + * @return void + */ + public function setSlug(string $slug) + { + $this->slug = $slug; + } + + /** + * Returns the urltitle + * + * @return string + */ + public function getUrltitle() + { + return $this->urltitle; + } + + /** + * Sets the urltitle + * + * @param string $urltitle + * @return void + */ + public function setUrltitle(string $urltitle) + { + $this->urltitle = $urltitle; + } + + + /** + * Returns the seotitle + * + * @return string + */ + public function getSeotitle() + { + return $this->seotitle; + } + + /** + * Sets the seotitle + * + * @param string $seotitle + * @return void + */ + public function setSeotitle(string $seotitle) + { + $this->seotitle = $seotitle; + } + + + /** + * Returns the seometa + * + * @return string $seometa + */ + public function getSeometa() + { + return $this->seometa; + } + + /** + * Sets the seometa + * + * @param string $seometa + * @return void + */ + public function setSeometa($seometa) + { + $this->seometa = $seometa; + } + + /** + * Returns the keywords + * + * @return string $keywords + */ + public function getKeywords() + { + return $this->keywords; + } + + /** + * Sets the keywords + * + * @param string $keywords + * @return void + */ + public function setKeywords($keywords) + { + $this->keywords = $keywords; + } + + /** + * Returns the teaser + * + * @return string $kteasereywords + */ + public function getTeaser() + { + return $this->teaser; + } + + /** + * Sets the teaser + * + * @param string $teaser + * @return void + */ + public function setTeaser($teaser) + { + $this->teaser = $teaser; + } + + /** + * Returns the subtitle + * + * @return string $subtitle + */ + public function getSubtitle() + { + return $this->subtitle; + } + + /** + * Sets the subtitle + * + * @param string $subtitle + * @return void + */ + public function setSubtitle($subtitle) + { + $this->subtitle = $subtitle; + } + + /** + * Returns the hideonapp + * + * @return bool $hideonapp + */ + public function getHideonapp() + { + return $this->hideonapp; + } + + /** + * Sets the hideonapp + * + * @param bool $hideonapp + * @return void + */ + public function setHideonapp($hideonapp) + { + $this->hideonapp = $hideonapp; + } + + /** + * Returns the description + * + * @return bool $description + */ + public function getDescription() + { + return $this->description; + } + + /** + * Sets the description + * + * @param bool $description + * @return void + */ + public function setDescriptionp($description) + { + $this->description = $description; + } + + /** + * Returns the hideonwebsite + * + * @return bool $hideonwebsite + */ + public function getHideonwebsite() + { + return $this->hideonwebsite; + } + + /** + * Sets the hideonwebsite + * + * @param bool $hideonwebsite + * @return void + */ + public function setHideonwebsite($hideonwebsite) + { + $this->hideonwebsite = $hideonwebsite; + } + + /** + * Returns the hideondatasheets + * + * @return bool $hideondatasheets + */ + public function getHideondatasheets() + { + return $this->hideondatasheets; + } + + /** + * Sets the hideondatasheets + * + * @param bool $hideondatasheets + * @return void + */ + public function setHideondatasheets($hideondatasheets) + { + $this->hideondatasheets = $hideondatasheets; + } + + /** + * Returns the hideonproducts + * + * @return bool $hideonproducts + */ + public function getHideonproducts() + { + return $this->hideonproducts; + } + + /** + * Sets the hideonproducts + * + * @param bool $hideonproducts + * @return void + */ + public function setHideonproducts($hideonproducts) + { + $this->hideonproducts = $hideonproducts; + } + + /** + * Returns the structureddata + * + * @return string $structureddata + */ + public function getStructureddata() + { + return $this->structureddata; + } + + /** + * Sets the structureddata + * + * @param string $structureddata + * @return void + */ + public function setStructureddata($structureddata) + { + $this->structureddata = $structureddata; + } + + /** + * Returns the applications + * + * @return string $applications + */ + public function getApplications() + { + return $this->applications; + } + + /** + * Sets the applications + * + * @param string $applications + * @return void + */ + public function setApplications($applications) + { + $this->applications = $applications; + } + + /** + * Returns the highlights + * + * @return string $highlights + */ + public function getHighlights() + { + return $this->highlights; + } + + /** + * Sets the highlights + * + * @param string $highlights + * @return void + */ + public function setHighlights($highlights) + { + $this->highlights = $highlights; + } + /** + * Returns the shortcutpid + * + * @return string + */ + public function getShortcutpid() + { + return $this->shortcutpid; + } + + /** + * Sets the shortcutpid + * + * @param string $shortcutpid + * @return void + */ + public function setShortcutpid ($shortcutpid) + { + $this->shortcutpid = $shortcutpid; + } + + /** + * Returns the shortcut + * + * @return bool $shortcut + */ + public function getShortcut() + { + return $this->shortcut; + } + + /** + * Sets the shortcut + * + * @param bool $shortcut + * @return void + */ + public function setShortcut($shortcut) + { + $this->shortcut = $shortcut; + } + + /** + * Returns the legacy + * + * @return bool $legacy + */ + public function getLegacy() + { + return $this->legacy; + } + + /** + * Sets the legacy + * + * @param bool $legacy + * @return void + */ + public function setLegacy($legacy) + { + $this->legacy = $legacy; + } + + /** + * Returns the supportproduct + * + * @return bool $supportproduct + */ + public function getSupportproduct() + { + return $this->supportproduct; + } + + /** + * Sets the supportproduct + * + * @param bool $supportproduct + * @return void + */ + public function setSupportproduct($supportproduct) + { + $this->supportproduct = $supportproduct; + } + + /** + * Returns the subproduct + * + * @return bool $subproduct + */ + public function getSubproduct() + { + return $this->subproduct; + } + + /** + * Sets the subproduct + * + * @param bool $subproduct + * @return void + */ + public function setSubproduct($subproduct) + { + $this->subproduct = $subproduct; + } + + /** + * Initializes the ObjectStorage for categories + */ + public function __construct() + { + $this->categories = new ObjectStorage(); + $this->productimage = new ObjectStorage(); + $this->downloads = new ObjectStorage(); + } + + /** + * Adds a category + * + * @param Category $category + * @return void + */ + public function addCategory(Category $category) + { + $this->categories->attach($category); + } + + /** + * Removes a category + * + * @param Category $categoryToRemove + * @return void + */ + public function removeCategory(Category $categoryToRemove) + { + $this->categories->detach($categoryToRemove); + } + + /** + * Returns the categories + * + * @return ObjectStorage + */ + public function getCategories() + { + return $this->categories; + } + + /** + * Sets the categories + * + * @param ObjectStorage $categories + * @return void + */ + public function setCategories(ObjectStorage $categories) + { + $this->categories = $categories; + } + + + /** + * Adds a product image + * + * @param FileReference $productimage + * @return void + */ + public function addProductimage(FileReference $productimage) + { + $this->productimage->attach($productimage); + } + + /** + * Removes a product image + * + * @param FileReference $productimageToRemove + * @return void + */ + public function removeProductimage(FileReference $productimageToRemove) + { + $this->productimage->detach($productimageToRemove); + } + + /** + * Returns the product images + * + * @return ObjectStorage + */ + public function getProductimage() + { + return $this->productimage; + } + + /** + * Sets the product images + * + * @param ObjectStorage $productimage + * @return void + */ + public function setProductimage(ObjectStorage $productimage) + { + $this->productimage = $productimage; + } + + + /** + * Adds a download + * + * @param \Evomedien\Vitec\Domain\Model\Download $download + * @return void + */ + public function addDownload(\Evomedien\Vitec\Domain\Model\Download $download): void + { + $this->downloads->attach($download); + } + + /** + * Removes a download + * + * @param \Evomedien\Vitec\Domain\Model\Download $downloadToRemove + * @return void + */ + public function removeDownload(\Evomedien\Vitec\Domain\Model\Download $downloadToRemove): void + { + $this->downloads->detach($downloadToRemove); + } + + /** + * Returns the downloads + * + * @return ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> + */ + public function getDownloads(): ObjectStorage + { + return $this->downloads; + } + + /** + * Sets the downloads + * + * @param ObjectStorage<\Evomedien\Vitec\Domain\Model\Download> $downloads + * @return void + */ + public function setDownloads(ObjectStorage $downloads): void + { + $this->downloads = $downloads; + } + + + /** + * Returns the Open Graph Image + * + * @return FileReference|null + */ + public function getOgimage(): ?FileReference + { + return $this->ogimage; + } + + /** + * Sets the Open Graph Image + * + * @param FileReference $ogimage + * @return void + */ + public function setOgimage(FileReference $ogimage): void + { + $this->ogimage = $ogimage; + } + + /** + * Returns the content element link + * + * @return string + */ + + /** + * Returns the uploaded video file + * + * @return \TYPO3\CMS\Extbase\Domain\Model\FileReference|null + */ + public function getVideofile(): ?\TYPO3\CMS\Extbase\Domain\Model\FileReference + { + return $this->videofile; + } + + /** + * Sets the uploaded video file + * + * @param \TYPO3\CMS\Extbase\Domain\Model\FileReference|null $videofile + * @return void + */ + public function setVideofile(?\TYPO3\CMS\Extbase\Domain\Model\FileReference $videofile): void + { + $this->videofile = $videofile; + } + + public function getContentelement(): string + { + return $this->contentelement; + } + + /** + * Sets the content element link + * + * @param string $contentelement + * @return void + */ + public function setContentelement(string $contentelement): void + { + $this->contentelement = $contentelement; + } + + /** + * Returns the content element cta link + * + * @return string + */ + public function getContentelementcta(): string + { + return $this->contentelementcta; + } + + /** + * Sets the content element link cta + * + * @param string $contentelementcta + * @return void + */ + public function setContentelementcta(string $contentelementcta): void + { + $this->contentelementcta = $contentelementcta; + } + + /** + * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product> + */ + public function getRelatedprodukt(): \TYPO3\CMS\Extbase\Persistence\ObjectStorage + { + return $this->relatedprodukt; + } + + /** + * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\Vendor\Extension\Domain\Model\Product> $relatedprodukt + */ + public function setRelatedprodukt(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $relatedprodukt): void + { + $this->relatedprodukt = $relatedprodukt; + } +/* --------------------------------------------------------------------- */ + +} diff --git a/packages/vitec/Classes/EventListener/ShowFrontendLayoutBanner.php b/packages/vitec/Classes/EventListener/ShowFrontendLayoutBanner.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/Hook/SyncBackendLayoutHook.php b/packages/vitec/Classes/Hook/SyncBackendLayoutHook.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/Service/ContentElementResolver.php b/packages/vitec/Classes/Service/ContentElementResolver.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/Service/ContentElementResolver.php.bak.20260529120325 b/packages/vitec/Classes/Service/ContentElementResolver.php.bak.20260529120325 new file mode 100755 index 0000000..09c7251 --- /dev/null +++ b/packages/vitec/Classes/Service/ContentElementResolver.php.bak.20260529120325 @@ -0,0 +1,238 @@ + [ProductListJsonRenderer::class, 'products'], + 'vitec_productshow' => [ProductShowJsonRenderer::class, 'product'], + 'vitec_usecaselist' => [UsecaseListJsonRenderer::class, 'usecases'], + 'vitec_usecaseshow' => [UsecaseShowJsonRenderer::class, 'usecase'], + ]; + + /** + * Resolve a typolink string to a normalised tt_content JSON element. + * + * Accepted forms: + * - "t3://record?identifier=tt_content&uid=N" + * - "" (legacy: bare tt_content uid) + * - everything else → null (page links etc.) + * + * @return array|null + */ + public static function resolveLink(?string $link): ?array + { + try { + $link = trim((string)$link); + if ($link === '') { + return null; + } + + $uid = self::extractTtContentUid($link); + if ($uid <= 0) { + return null; + } + + $qb = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + $row = $qb + ->select('*') + ->from('tt_content') + ->where( + $qb->expr()->eq('uid', $qb->createNamedParameter($uid, ParameterType::INTEGER)), + $qb->expr()->eq('deleted', 0), + $qb->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + if (!$row) { + return null; + } + + return self::normaliseRecord($row); + } catch (\Throwable $e) { + return null; + } + } + + /** + * Normalise a tt_content DB row to the same envelope shape that + * {@see \Evomedien\Vitec\DataProcessing\ContainerChildrenProcessor} + * emits for container children. VITEC list-plugin children are + * resolved to their headless JSON. + * + * @param array $record + * @return array + */ + public static function normaliseRecord(array $record): array + { + $data = []; + foreach ($record as $field => $value) { + if (in_array($field, self::ENVELOPE, true)) { + continue; + } + if (in_array($field, self::SYSTEM_FIELDS, true)) { + continue; + } + if (self::isEmpty($value) && !in_array($field, self::KEEP_IF_ZERO, true)) { + continue; + } + $data[$field] = self::castValue($field, $value); + } + + self::resolvePluginData($record, $data); + + return [ + 'id' => (int)$record['uid'], + 'type' => (string)$record['CType'], + 'colPos' => (int)($record['colPos'] ?? 0), + 'sorting' => (int)($record['sorting'] ?? 0), + 'appearance' => [ + 'layout' => (string)($record['layout'] ?? ''), + 'frameClass' => (string)($record['frame_class'] ?? 'default'), + 'spaceBefore' => (string)($record['space_before_class'] ?? ''), + 'spaceAfter' => (string)($record['space_after_class'] ?? ''), + ], + 'data' => (object)$data, + ]; + } + + /** + * Extract the tt_content uid from a typolink string. + */ + private static function extractTtContentUid(string $link): int + { + if (ctype_digit($link)) { + return (int)$link; + } + + if (!str_starts_with($link, 't3://record')) { + return 0; + } + + $parts = parse_url($link); + if (empty($parts['query'])) { + return 0; + } + + $params = []; + parse_str((string)$parts['query'], $params); + + $identifier = (string)($params['identifier'] ?? ''); + $uid = (int)($params['uid'] ?? 0); + + if ($identifier !== 'tt_content' || $uid <= 0) { + return 0; + } + + return $uid; + } + + /** + * If the record is a VITEC list-plugin, run its renderer for THIS row + * and inject the decoded result under its key. Drops raw pi_flexform. + * + * @param array $record + * @param array $data + */ + private static function resolvePluginData(array $record, array &$data): void + { + $listType = (string)($record['list_type'] ?? ''); + if ($listType === '' || !isset(self::PLUGIN_RENDERERS[$listType])) { + return; + } + + try { + [$rendererClass, $jsonKey] = self::PLUGIN_RENDERERS[$listType]; + $renderer = GeneralUtility::makeInstance($rendererClass); + + if (!method_exists($renderer, 'renderForRecord')) { + return; + } + + $json = $renderer->renderForRecord($record); + if ($json === '' || $json === null) { + return; + } + + $decoded = json_decode($json, true); + if ($decoded === null && json_last_error() !== JSON_ERROR_NONE) { + return; + } + + $data[$jsonKey] = $decoded; + unset($data['pi_flexform']); + } catch (\Throwable $e) { + // leave raw data on failure + } + } + + private static function isEmpty(mixed $value): bool + { + return $value === null || $value === '' || $value === 0 || $value === '0'; + } + + private static function castValue(string $field, mixed $value): mixed + { + if (in_array($field, ['header_layout'], true)) { + return (int)$value; + } + return $value; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529111828 b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529111828 new file mode 100755 index 0000000..fd48d4e --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529111828 @@ -0,0 +1,463 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + // Not a product-list page: emit nothing so headless removes the key. + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-list plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement tt_content row of the plugin + */ + public function renderForRecord(array $contentElement): string + { + try { + // Parse FlexForm of THIS element + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $categoryUids = array_filter( + array_map('intval', explode(',', (string)($settings['categories'] ?? ''))) + ); + $debugMode = (bool)($settings['debug'] ?? false); + $allProducts = (bool)($settings['allproducts'] ?? false); + + // Extbase repositories don't work in UserFunc context — direct query. + $productQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productQuery = $productQueryBuilder + ->select('p.*') + ->from('tx_vitec_domain_model_product', 'p') + ->where( + $productQueryBuilder->expr()->eq('p.deleted', 0), + $productQueryBuilder->expr()->eq('p.hidden', 0), + $productQueryBuilder->expr()->eq('p.legacy', 0), + $productQueryBuilder->expr()->eq('p.supportproduct', 0), + $productQueryBuilder->expr()->eq('p.hideonwebsite', 0), + $productQueryBuilder->expr()->eq('p.hideonproducts', 0), + $productQueryBuilder->expr()->eq('p.subproduct', 0) + ); + + if (!empty($categoryUids) && !$allProducts) { + $productQuery + ->join( + 'p', + 'sys_category_record_mm', + 'mm', + 'mm.uid_foreign = p.uid AND mm.tablenames = ' . $productQueryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . ' AND mm.fieldname = ' . $productQueryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->andWhere( + $productQueryBuilder->expr()->in('mm.uid_local', $productQueryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY)) + ) + ->groupBy('p.uid'); + } + + $products = $productQuery->executeQuery()->fetchAllAssociative(); + + $productsData = []; + foreach ($products as $product) { + $productsData[] = $this->serializeProduct($product); + } + + if ($debugMode) { + return json_encode([ + 'products' => $productsData, + 'debug' => [ + 'pageId' => (int)($GLOBALS['TSFE']->id ?? 0), + 'categoryUids' => $categoryUids, + 'productCount' => count($productsData), + 'settings' => $settings, + ], + ]); + } + + return json_encode($productsData); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * Serialize a single product DB row to the full headless JSON structure. + * + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => (string)($product['contentelement'] ?? ''), + 'contentelementcta' => (string)($product['contentelementcta'] ?? ''), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('sfr.uid', 'sfr.uid_local', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->executeQuery() + ->fetchAllAssociative(); + + $imageService = GeneralUtility::makeInstance(ImageService::class); + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + + $images = []; + foreach ($fileReferences as $fileRefData) { + try { + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $sizes = [ + 'small' => ['width' => 400, 'height' => null], + 'medium' => ['width' => 800, 'height' => null], + 'large' => ['width' => 1200, 'height' => null], + 'xlarge' => ['width' => 1600, 'height' => null], + ]; + + $srcset = []; + foreach ($sizes as $sizeName => $dimensions) { + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + [ + 'width' => $dimensions['width'], + 'height' => $dimensions['height'], + 'crop' => $fileRefData['crop'] ?? null + ] + ); + + $imageUri = $imageService->getImageUri($processedImage); + $srcset[] = [ + 'url' => $imageUri, + 'width' => $dimensions['width'], + 'descriptor' => $dimensions['width'] . 'w' + ]; + } + + $defaultProcessed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ); + + $images[] = [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($defaultProcessed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type') + ] + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRefData = $queryBuilder + ->select('sfr.uid', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRefData) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $processed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200, 'crop' => $fileRefData['crop'] ?? null] + ); + + return [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($processed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type'), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(static function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'] ?? '', + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529115809 b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529115809 new file mode 100755 index 0000000..8aa4f9c --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529115809 @@ -0,0 +1,507 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + // Not a product-list page: emit nothing so headless removes the key. + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-list plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement tt_content row of the plugin + */ + public function renderForRecord(array $contentElement): string + { + try { + // Parse FlexForm of THIS element + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $categoryUids = array_filter( + array_map('intval', explode(',', (string)($settings['categories'] ?? ''))) + ); + $debugMode = (bool)($settings['debug'] ?? false); + $allProducts = (bool)($settings['allproducts'] ?? false); + + // Extbase repositories don't work in UserFunc context — direct query. + $productQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productQuery = $productQueryBuilder + ->select('p.*') + ->from('tx_vitec_domain_model_product', 'p') + ->where( + $productQueryBuilder->expr()->eq('p.deleted', 0), + $productQueryBuilder->expr()->eq('p.hidden', 0), + $productQueryBuilder->expr()->eq('p.legacy', 0), + $productQueryBuilder->expr()->eq('p.supportproduct', 0), + $productQueryBuilder->expr()->eq('p.hideonwebsite', 0), + $productQueryBuilder->expr()->eq('p.hideonproducts', 0), + $productQueryBuilder->expr()->eq('p.subproduct', 0) + ); + + if (!empty($categoryUids) && !$allProducts) { + $productQuery + ->join( + 'p', + 'sys_category_record_mm', + 'mm', + 'mm.uid_foreign = p.uid AND mm.tablenames = ' . $productQueryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . ' AND mm.fieldname = ' . $productQueryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->andWhere( + $productQueryBuilder->expr()->in('mm.uid_local', $productQueryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY)) + ) + ->groupBy('p.uid'); + } + + $products = $productQuery->executeQuery()->fetchAllAssociative(); + + $productsData = []; + foreach ($products as $product) { + $productsData[] = $this->serializeProduct($product); + } + + if ($debugMode) { + return json_encode([ + 'products' => $productsData, + 'debug' => [ + 'pageId' => (int)($GLOBALS['TSFE']->id ?? 0), + 'categoryUids' => $categoryUids, + 'productCount' => count($productsData), + 'settings' => $settings, + ], + ]); + } + + return json_encode($productsData); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * Serialize a single product DB row to the full headless JSON structure. + * + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => (string)($product['contentelement'] ?? ''), + 'contentelementcta' => (string)($product['contentelementcta'] ?? ''), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'videofile' => $this->getProductVideoFile($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('sfr.uid', 'sfr.uid_local', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->executeQuery() + ->fetchAllAssociative(); + + $imageService = GeneralUtility::makeInstance(ImageService::class); + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + + $images = []; + foreach ($fileReferences as $fileRefData) { + try { + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $sizes = [ + 'small' => ['width' => 400, 'height' => null], + 'medium' => ['width' => 800, 'height' => null], + 'large' => ['width' => 1200, 'height' => null], + 'xlarge' => ['width' => 1600, 'height' => null], + ]; + + $srcset = []; + foreach ($sizes as $sizeName => $dimensions) { + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + [ + 'width' => $dimensions['width'], + 'height' => $dimensions['height'], + 'crop' => $fileRefData['crop'] ?? null + ] + ); + + $imageUri = $imageService->getImageUri($processedImage); + $srcset[] = [ + 'url' => $imageUri, + 'width' => $dimensions['width'], + 'descriptor' => $dimensions['width'] . 'w' + ]; + } + + $defaultProcessed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ); + + $images[] = [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($defaultProcessed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type') + ] + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRefData = $queryBuilder + ->select('sfr.uid', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRefData) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $processed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200, 'crop' => $fileRefData['crop'] ?? null] + ); + + return [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($processed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type'), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + + /** + * Resolve the uploaded video file (single FAL reference, fieldname=videofile). + * + * @return array|null + */ + protected function getProductVideoFile(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $row = $queryBuilder + ->select('fr.uid', 'fr.title', 'fr.description', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $queryBuilder->expr()->eq('fr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.fieldname', $queryBuilder->createNamedParameter('videofile', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.uid_foreign', $queryBuilder->createNamedParameter($productUid, \Doctrine\DBAL\ParameterType::INTEGER)), + $queryBuilder->expr()->eq('fr.deleted', 0), + $queryBuilder->expr()->eq('fr.hidden', 0), + $queryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$row) { + return null; + } + + return [ + 'uid' => (int)$row['file_uid'], + 'name' => (string)($row['name'] ?? ''), + 'url' => '/fileadmin' . ($row['identifier'] ?? ''), + 'size' => (int)($row['size'] ?? 0), + 'extension' => (string)($row['extension'] ?? ''), + 'mimeType' => (string)($row['mime_type'] ?? ''), + 'title' => (string)($row['title'] ?? ''), + 'description' => (string)($row['description'] ?? ''), + ]; + } + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(static function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'] ?? '', + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529120751 b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529120751 new file mode 100755 index 0000000..fd1915c --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php.bak.20260529120751 @@ -0,0 +1,507 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productlist', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + // Not a product-list page: emit nothing so headless removes the key. + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-list plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement tt_content row of the plugin + */ + public function renderForRecord(array $contentElement): string + { + try { + // Parse FlexForm of THIS element + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $categoryUids = array_filter( + array_map('intval', explode(',', (string)($settings['categories'] ?? ''))) + ); + $debugMode = (bool)($settings['debug'] ?? false); + $allProducts = (bool)($settings['allproducts'] ?? false); + + // Extbase repositories don't work in UserFunc context — direct query. + $productQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productQuery = $productQueryBuilder + ->select('p.*') + ->from('tx_vitec_domain_model_product', 'p') + ->where( + $productQueryBuilder->expr()->eq('p.deleted', 0), + $productQueryBuilder->expr()->eq('p.hidden', 0), + $productQueryBuilder->expr()->eq('p.legacy', 0), + $productQueryBuilder->expr()->eq('p.supportproduct', 0), + $productQueryBuilder->expr()->eq('p.hideonwebsite', 0), + $productQueryBuilder->expr()->eq('p.hideonproducts', 0), + $productQueryBuilder->expr()->eq('p.subproduct', 0) + ); + + if (!empty($categoryUids) && !$allProducts) { + $productQuery + ->join( + 'p', + 'sys_category_record_mm', + 'mm', + 'mm.uid_foreign = p.uid AND mm.tablenames = ' . $productQueryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . ' AND mm.fieldname = ' . $productQueryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->andWhere( + $productQueryBuilder->expr()->in('mm.uid_local', $productQueryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY)) + ) + ->groupBy('p.uid'); + } + + $products = $productQuery->executeQuery()->fetchAllAssociative(); + + $productsData = []; + foreach ($products as $product) { + $productsData[] = $this->serializeProduct($product); + } + + if ($debugMode) { + return json_encode([ + 'products' => $productsData, + 'debug' => [ + 'pageId' => (int)($GLOBALS['TSFE']->id ?? 0), + 'categoryUids' => $categoryUids, + 'productCount' => count($productsData), + 'settings' => $settings, + ], + ]); + } + + return json_encode($productsData); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * Serialize a single product DB row to the full headless JSON structure. + * + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelement'] ?? '')), + 'contentelementcta' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelementcta'] ?? '')), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'videofile' => $this->getProductVideoFile($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('sfr.uid', 'sfr.uid_local', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->executeQuery() + ->fetchAllAssociative(); + + $imageService = GeneralUtility::makeInstance(ImageService::class); + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + + $images = []; + foreach ($fileReferences as $fileRefData) { + try { + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $sizes = [ + 'small' => ['width' => 400, 'height' => null], + 'medium' => ['width' => 800, 'height' => null], + 'large' => ['width' => 1200, 'height' => null], + 'xlarge' => ['width' => 1600, 'height' => null], + ]; + + $srcset = []; + foreach ($sizes as $sizeName => $dimensions) { + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + [ + 'width' => $dimensions['width'], + 'height' => $dimensions['height'], + 'crop' => $fileRefData['crop'] ?? null + ] + ); + + $imageUri = $imageService->getImageUri($processedImage); + $srcset[] = [ + 'url' => $imageUri, + 'width' => $dimensions['width'], + 'descriptor' => $dimensions['width'] . 'w' + ]; + } + + $defaultProcessed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ); + + $images[] = [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($defaultProcessed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type') + ] + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRefData = $queryBuilder + ->select('sfr.uid', 'sfr.title', 'sfr.description', 'sfr.alternative', 'sfr.crop') + ->from('sys_file_reference', 'sfr') + ->where( + $queryBuilder->expr()->eq('sfr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('sfr.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('sfr.deleted', 0), + $queryBuilder->expr()->eq('sfr.hidden', 0) + ) + ->orderBy('sfr.sorting_foreign') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRefData) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject((int)$fileRefData['uid']); + + $processed = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200, 'crop' => $fileRefData['crop'] ?? null] + ); + + return [ + 'uid' => (int)$fileRefData['uid'], + 'url' => $imageService->getImageUri($processed), + 'title' => $fileRefData['title'] ?? '', + 'alternative' => $fileRefData['alternative'] ?? '', + 'description' => $fileRefData['description'] ?? '', + 'properties' => [ + 'width' => $fileReference->getProperty('width'), + 'height' => $fileReference->getProperty('height'), + 'mimeType' => $fileReference->getProperty('mime_type'), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + + /** + * Resolve the uploaded video file (single FAL reference, fieldname=videofile). + * + * @return array|null + */ + protected function getProductVideoFile(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $row = $queryBuilder + ->select('fr.uid', 'fr.title', 'fr.description', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $queryBuilder->expr()->eq('fr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.fieldname', $queryBuilder->createNamedParameter('videofile', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.uid_foreign', $queryBuilder->createNamedParameter($productUid, \Doctrine\DBAL\ParameterType::INTEGER)), + $queryBuilder->expr()->eq('fr.deleted', 0), + $queryBuilder->expr()->eq('fr.hidden', 0), + $queryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$row) { + return null; + } + + return [ + 'uid' => (int)$row['file_uid'], + 'name' => (string)($row['name'] ?? ''), + 'url' => '/fileadmin' . ($row['identifier'] ?? ''), + 'size' => (int)($row['size'] ?? 0), + 'extension' => (string)($row['extension'] ?? ''), + 'mimeType' => (string)($row['mime_type'] ?? ''), + 'title' => (string)($row['title'] ?? ''), + 'description' => (string)($row['description'] ?? ''), + ]; + } + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(static function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'] ?? '', + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529111828 b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529111828 new file mode 100755 index 0000000..a7ae85b --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529111828 @@ -0,0 +1,470 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productshow', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-show plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement + */ + public function renderForRecord(array $contentElement): string + { + try { + $pageId = (int)($GLOBALS['TSFE']->id ?? 0); + + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $productUid = (int)($settings['product'] ?? 0); + $layout = (int)($settings['layout'] ?? 0); + $debugMode = (bool)($settings['debug'] ?? false); + + if (!$productUid) { + $routeParams = $GLOBALS['TYPO3_REQUEST']->getQueryParams(); + $productParam = $routeParams['tx_vitec_productshow']['product'] ?? null; + + if ($productParam) { + if (!is_numeric($productParam)) { + $slugQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productBySlug = $slugQueryBuilder + ->select('uid') + ->from('tx_vitec_domain_model_product') + ->where( + $slugQueryBuilder->expr()->eq('slug', $slugQueryBuilder->createNamedParameter($productParam)), + $slugQueryBuilder->expr()->eq('deleted', 0), + $slugQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + $productUid = (int)($productBySlug['uid'] ?? 0); + } else { + $productUid = (int)$productParam; + } + } + } + + if (!$productUid) { + return $debugMode + ? json_encode(['error' => 'No product selected or found', 'debug' => ['settings' => $settings]]) + : ''; + } + + $productQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $product = $productQueryBuilder + ->select('*') + ->from('tx_vitec_domain_model_product') + ->where( + $productQueryBuilder->expr()->eq('uid', $productQueryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $productQueryBuilder->expr()->eq('deleted', 0), + $productQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + if (!$product) { + return $debugMode + ? json_encode(['error' => 'Product not found', 'debug' => ['productUid' => $productUid]]) + : ''; + } + + $response = [ + 'product' => $this->serializeProduct($product), + 'layout' => $layout, + 'settings' => [ + 'layout' => $layout, + ], + ]; + + if ($debugMode) { + $response['debug'] = [ + 'pageId' => $pageId, + 'productUid' => $productUid, + 'layout' => $layout, + 'settings' => $settings, + ]; + } + + return json_encode($response); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => (string)($product['contentelement'] ?? ''), + 'contentelementcta' => (string)($product['contentelementcta'] ?? ''), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($fileReferences)) { + return []; + } + + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $images = []; + + foreach ($fileReferences as $fileRef) { + try { + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => '1874c', 'height' => '625c'] + ); + + $srcset = []; + foreach ([400, 800, 1200, 1600] as $width) { + $processedVariant = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => $width . 'c', 'height' => (int)($width / 3) . 'c'] + ); + $srcset[] = [ + 'url' => $imageService->getImageUri($processedVariant), + 'width' => $width, + 'descriptor' => $width . 'w', + ]; + } + + $images[] = [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRef) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200] + ); + + return [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'], + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529115809 b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529115809 new file mode 100755 index 0000000..ef54bcf --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529115809 @@ -0,0 +1,514 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productshow', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-show plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement + */ + public function renderForRecord(array $contentElement): string + { + try { + $pageId = (int)($GLOBALS['TSFE']->id ?? 0); + + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $productUid = (int)($settings['product'] ?? 0); + $layout = (int)($settings['layout'] ?? 0); + $debugMode = (bool)($settings['debug'] ?? false); + + if (!$productUid) { + $routeParams = $GLOBALS['TYPO3_REQUEST']->getQueryParams(); + $productParam = $routeParams['tx_vitec_productshow']['product'] ?? null; + + if ($productParam) { + if (!is_numeric($productParam)) { + $slugQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productBySlug = $slugQueryBuilder + ->select('uid') + ->from('tx_vitec_domain_model_product') + ->where( + $slugQueryBuilder->expr()->eq('slug', $slugQueryBuilder->createNamedParameter($productParam)), + $slugQueryBuilder->expr()->eq('deleted', 0), + $slugQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + $productUid = (int)($productBySlug['uid'] ?? 0); + } else { + $productUid = (int)$productParam; + } + } + } + + if (!$productUid) { + return $debugMode + ? json_encode(['error' => 'No product selected or found', 'debug' => ['settings' => $settings]]) + : ''; + } + + $productQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $product = $productQueryBuilder + ->select('*') + ->from('tx_vitec_domain_model_product') + ->where( + $productQueryBuilder->expr()->eq('uid', $productQueryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $productQueryBuilder->expr()->eq('deleted', 0), + $productQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + if (!$product) { + return $debugMode + ? json_encode(['error' => 'Product not found', 'debug' => ['productUid' => $productUid]]) + : ''; + } + + $response = [ + 'product' => $this->serializeProduct($product), + 'layout' => $layout, + 'settings' => [ + 'layout' => $layout, + ], + ]; + + if ($debugMode) { + $response['debug'] = [ + 'pageId' => $pageId, + 'productUid' => $productUid, + 'layout' => $layout, + 'settings' => $settings, + ]; + } + + return json_encode($response); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => (string)($product['contentelement'] ?? ''), + 'contentelementcta' => (string)($product['contentelementcta'] ?? ''), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'videofile' => $this->getProductVideoFile($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($fileReferences)) { + return []; + } + + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $images = []; + + foreach ($fileReferences as $fileRef) { + try { + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => '1874c', 'height' => '625c'] + ); + + $srcset = []; + foreach ([400, 800, 1200, 1600] as $width) { + $processedVariant = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => $width . 'c', 'height' => (int)($width / 3) . 'c'] + ); + $srcset[] = [ + 'url' => $imageService->getImageUri($processedVariant), + 'width' => $width, + 'descriptor' => $width . 'w', + ]; + } + + $images[] = [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRef) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200] + ); + + return [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + + /** + * Resolve the uploaded video file (single FAL reference, fieldname=videofile). + * + * @return array|null + */ + protected function getProductVideoFile(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $row = $queryBuilder + ->select('fr.uid', 'fr.title', 'fr.description', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $queryBuilder->expr()->eq('fr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.fieldname', $queryBuilder->createNamedParameter('videofile', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.uid_foreign', $queryBuilder->createNamedParameter($productUid, \Doctrine\DBAL\ParameterType::INTEGER)), + $queryBuilder->expr()->eq('fr.deleted', 0), + $queryBuilder->expr()->eq('fr.hidden', 0), + $queryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$row) { + return null; + } + + return [ + 'uid' => (int)$row['file_uid'], + 'name' => (string)($row['name'] ?? ''), + 'url' => '/fileadmin' . ($row['identifier'] ?? ''), + 'size' => (int)($row['size'] ?? 0), + 'extension' => (string)($row['extension'] ?? ''), + 'mimeType' => (string)($row['mime_type'] ?? ''), + 'title' => (string)($row['title'] ?? ''), + 'description' => (string)($row['description'] ?? ''), + ]; + } + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'], + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529120751 b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529120751 new file mode 100755 index 0000000..5b86e32 --- /dev/null +++ b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php.bak.20260529120751 @@ -0,0 +1,514 @@ +id ?? 0); + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tt_content'); + + $contentElements = $queryBuilder + ->select('*') + ->from('tt_content') + ->where( + $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pageId, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('list_type', $queryBuilder->createNamedParameter('vitec_productshow', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($contentElements)) { + return ''; + } + + return $this->renderForRecord($contentElements[0]); + } + + /** + * Render exactly the given tt_content row (the product-show plugin element). + * Exception-safe: returns '' on any failure. + * + * @param array $contentElement + */ + public function renderForRecord(array $contentElement): string + { + try { + $pageId = (int)($GLOBALS['TSFE']->id ?? 0); + + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + + $productUid = (int)($settings['product'] ?? 0); + $layout = (int)($settings['layout'] ?? 0); + $debugMode = (bool)($settings['debug'] ?? false); + + if (!$productUid) { + $routeParams = $GLOBALS['TYPO3_REQUEST']->getQueryParams(); + $productParam = $routeParams['tx_vitec_productshow']['product'] ?? null; + + if ($productParam) { + if (!is_numeric($productParam)) { + $slugQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $productBySlug = $slugQueryBuilder + ->select('uid') + ->from('tx_vitec_domain_model_product') + ->where( + $slugQueryBuilder->expr()->eq('slug', $slugQueryBuilder->createNamedParameter($productParam)), + $slugQueryBuilder->expr()->eq('deleted', 0), + $slugQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + $productUid = (int)($productBySlug['uid'] ?? 0); + } else { + $productUid = (int)$productParam; + } + } + } + + if (!$productUid) { + return $debugMode + ? json_encode(['error' => 'No product selected or found', 'debug' => ['settings' => $settings]]) + : ''; + } + + $productQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $product = $productQueryBuilder + ->select('*') + ->from('tx_vitec_domain_model_product') + ->where( + $productQueryBuilder->expr()->eq('uid', $productQueryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $productQueryBuilder->expr()->eq('deleted', 0), + $productQueryBuilder->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAssociative(); + + if (!$product) { + return $debugMode + ? json_encode(['error' => 'Product not found', 'debug' => ['productUid' => $productUid]]) + : ''; + } + + $response = [ + 'product' => $this->serializeProduct($product), + 'layout' => $layout, + 'settings' => [ + 'layout' => $layout, + ], + ]; + + if ($debugMode) { + $response['debug'] = [ + 'pageId' => $pageId, + 'productUid' => $productUid, + 'layout' => $layout, + 'settings' => $settings, + ]; + } + + return json_encode($response); + } catch (\Throwable $e) { + return ''; + } + } + + /** + * @param array $product + * @return array + */ + protected function serializeProduct(array $product): array + { + $uid = (int)$product['uid']; + + return [ + 'uid' => $uid, + + 'title' => (string)($product['title'] ?? ''), + 'slug' => (string)($product['slug'] ?? ''), + 'urltitle' => (string)($product['urltitle'] ?? ''), + 'seotitle' => (string)($product['seotitle'] ?? ''), + 'seometa' => (string)($product['seometa'] ?? ''), + 'keywords' => (string)($product['keywords'] ?? ''), + 'structureddata' => (string)($product['structureddata'] ?? ''), + 'teaser' => (string)($product['teaser'] ?? ''), + 'subtitle' => (string)($product['subtitle'] ?? ''), + 'video' => (string)($product['video'] ?? ''), + 'applications' => (string)($product['applications'] ?? ''), + 'description' => (string)($product['description'] ?? ''), + 'highlights' => (string)($product['highlights'] ?? ''), + 'shortcutpid' => (string)($product['shortcutpid'] ?? ''), + 'contentelement' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelement'] ?? '')), + 'contentelementcta' => \Evomedien\Vitec\Service\ContentElementResolver::resolveLink((string)($product['contentelementcta'] ?? '')), + + 'hideonapp' => (bool)($product['hideonapp'] ?? false), + 'hideonwebsite' => (bool)($product['hideonwebsite'] ?? false), + 'hideondatasheets' => (bool)($product['hideondatasheets'] ?? false), + 'hideonproducts' => (bool)($product['hideonproducts'] ?? false), + 'shortcut' => (bool)($product['shortcut'] ?? false), + 'legacy' => (bool)($product['legacy'] ?? false), + 'supportproduct' => (bool)($product['supportproduct'] ?? false), + 'subproduct' => (bool)($product['subproduct'] ?? false), + + 'link' => '/product/' . (string)($product['slug'] ?? ''), + + 'categories' => $this->getProductCategories($uid), + 'images' => $this->getProductImages($uid), + 'downloads' => $this->getProductDownloads($uid), + 'ogimage' => $this->getProductOgImage($uid), + 'videofile' => $this->getProductVideoFile($uid), + 'relatedprodukt' => $this->getRelatedProducts($uid), + ]; + } + + protected function getProductImages(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileReferences = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('productimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($fileReferences)) { + return []; + } + + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $images = []; + + foreach ($fileReferences as $fileRef) { + try { + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => '1874c', 'height' => '625c'] + ); + + $srcset = []; + foreach ([400, 800, 1200, 1600] as $width) { + $processedVariant = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => $width . 'c', 'height' => (int)($width / 3) . 'c'] + ); + $srcset[] = [ + 'url' => $imageService->getImageUri($processedVariant), + 'width' => $width, + 'descriptor' => $width . 'w', + ]; + } + + $images[] = [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'srcset' => $srcset, + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + continue; + } + } + + return $images; + } + + /** + * @return array|null + */ + protected function getProductOgImage(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $queryBuilder + ->select('*') + ->from('sys_file_reference') + ->where( + $queryBuilder->expr()->eq('uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING)), + $queryBuilder->expr()->eq('fieldname', $queryBuilder->createNamedParameter('ogimage', ParameterType::STRING)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + ) + ->orderBy('sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$fileRef) { + return null; + } + + try { + $resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class); + $imageService = GeneralUtility::makeInstance(ImageService::class); + $fileReference = $resourceFactory->getFileReferenceObject($fileRef['uid']); + $originalFile = $fileReference->getOriginalFile(); + + $processedImage = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 1200] + ); + + return [ + 'uid' => $fileRef['uid'], + 'url' => $imageService->getImageUri($processedImage), + 'title' => $fileReference->getTitle() ?: '', + 'alternative' => $fileReference->getAlternative() ?: '', + 'description' => $fileReference->getDescription() ?: '', + 'properties' => [ + 'width' => $originalFile->getProperty('width'), + 'height' => $originalFile->getProperty('height'), + 'mimeType' => $originalFile->getMimeType(), + ], + ]; + } catch (\Exception $e) { + return null; + } + } + + + /** + * Resolve the uploaded video file (single FAL reference, fieldname=videofile). + * + * @return array|null + */ + protected function getProductVideoFile(int $productUid): ?array + { + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $row = $queryBuilder + ->select('fr.uid', 'fr.title', 'fr.description', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $queryBuilder->expr()->eq('fr.tablenames', $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.fieldname', $queryBuilder->createNamedParameter('videofile', \Doctrine\DBAL\ParameterType::STRING)), + $queryBuilder->expr()->eq('fr.uid_foreign', $queryBuilder->createNamedParameter($productUid, \Doctrine\DBAL\ParameterType::INTEGER)), + $queryBuilder->expr()->eq('fr.deleted', 0), + $queryBuilder->expr()->eq('fr.hidden', 0), + $queryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if (!$row) { + return null; + } + + return [ + 'uid' => (int)$row['file_uid'], + 'name' => (string)($row['name'] ?? ''), + 'url' => '/fileadmin' . ($row['identifier'] ?? ''), + 'size' => (int)($row['size'] ?? 0), + 'extension' => (string)($row['extension'] ?? ''), + 'mimeType' => (string)($row['mime_type'] ?? ''), + 'title' => (string)($row['title'] ?? ''), + 'description' => (string)($row['description'] ?? ''), + ]; + } + protected function getProductCategories(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + + $categories = $queryBuilder + ->select('c.uid', 'c.title', 'c.description') + ->from('sys_category', 'c') + ->join( + 'c', + 'sys_category_record_mm', + 'mm', + 'mm.uid_local = c.uid AND mm.tablenames = ' . + $queryBuilder->createNamedParameter('tx_vitec_domain_model_product', ParameterType::STRING) . + ' AND mm.fieldname = ' . + $queryBuilder->createNamedParameter('categories', ParameterType::STRING) + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_foreign', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('c.deleted', 0), + $queryBuilder->expr()->eq('c.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + return array_map(function ($cat) { + return [ + 'uid' => (int)$cat['uid'], + 'title' => $cat['title'], + 'description' => $cat['description'] ?? '', + ]; + }, $categories); + } + + protected function getProductDownloads(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_download'); + + $downloads = $queryBuilder + ->select('d.*') + ->from('tx_vitec_domain_model_download', 'd') + ->join( + 'd', + 'tx_vitec_product_download_mm', + 'mm', + 'mm.uid_foreign = d.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('d.deleted', 0), + $queryBuilder->expr()->eq('d.hidden', 0), + $queryBuilder->expr()->eq('d.hideonwebsite', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($downloads as $download) { + $fileInfo = null; + + if (!empty($download['file'])) { + $fileQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('sys_file_reference'); + + $fileRef = $fileQueryBuilder + ->select('fr.uid', 'f.uid as file_uid', 'f.identifier', 'f.name', 'f.size', 'f.extension', 'f.mime_type') + ->from('sys_file_reference', 'fr') + ->join('fr', 'sys_file', 'f', 'fr.uid_local = f.uid') + ->where( + $fileQueryBuilder->expr()->eq('fr.uid_foreign', $fileQueryBuilder->createNamedParameter((int)$download['uid'], ParameterType::INTEGER)), + $fileQueryBuilder->expr()->eq('fr.tablenames', $fileQueryBuilder->createNamedParameter('tx_vitec_domain_model_download', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.fieldname', $fileQueryBuilder->createNamedParameter('file', ParameterType::STRING)), + $fileQueryBuilder->expr()->eq('fr.deleted', 0), + $fileQueryBuilder->expr()->eq('f.missing', 0) + ) + ->orderBy('fr.sorting_foreign', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + if ($fileRef) { + $fileInfo = [ + 'uid' => (int)$fileRef['file_uid'], + 'name' => $fileRef['name'], + 'url' => '/fileadmin' . $fileRef['identifier'], + 'size' => (int)$fileRef['size'], + 'extension' => $fileRef['extension'], + 'mimeType' => $fileRef['mime_type'] ?? '', + ]; + } + } + + $result[] = [ + 'uid' => (int)$download['uid'], + 'title' => $download['title'] ?? '', + 'slug' => $download['slug'] ?? '', + 'teaser' => $download['teaser'] ?? '', + 'description' => $download['description'] ?? '', + 'keywords' => $download['keywords'] ?? '', + 'icon' => $download['icon'] ?? '', + 'file' => $fileInfo, + ]; + } + + return $result; + } + + protected function getRelatedProducts(int $productUid): array + { + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_product'); + + $related = $queryBuilder + ->select('p.uid', 'p.title', 'p.slug', 'p.subtitle', 'p.teaser', 'p.description') + ->from('tx_vitec_domain_model_product', 'p') + ->join( + 'p', + 'tx_vitec_product_related_mm', + 'mm', + 'mm.uid_foreign = p.uid' + ) + ->where( + $queryBuilder->expr()->eq('mm.uid_local', $queryBuilder->createNamedParameter($productUid, ParameterType::INTEGER)), + $queryBuilder->expr()->eq('p.deleted', 0), + $queryBuilder->expr()->eq('p.hidden', 0) + ) + ->orderBy('mm.sorting', 'ASC') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($related as $rel) { + $relUid = (int)$rel['uid']; + $result[] = [ + 'uid' => $relUid, + 'title' => (string)($rel['title'] ?? ''), + 'slug' => (string)($rel['slug'] ?? ''), + 'subtitle' => (string)($rel['subtitle'] ?? ''), + 'teaser' => (string)($rel['teaser'] ?? ''), + 'description' => (string)($rel['description'] ?? ''), + 'link' => '/product/' . (string)($rel['slug'] ?? ''), + 'images' => $this->getProductImages($relUid), + ]; + } + + return $result; + } +} diff --git a/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php b/packages/vitec/Classes/UserFunc/UsecaseListJsonRenderer.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/UsecaseShowJsonRenderer.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/View/BackendLayoutDataProvider.php b/packages/vitec/Classes/View/BackendLayoutDataProvider.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Classes/View/VitecBackendLayoutView.php b/packages/vitec/Classes/View/VitecBackendLayoutView.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/FlexForms/Container.xml b/packages/vitec/Configuration/FlexForms/Container.xml old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/Icons.php b/packages/vitec/Configuration/Icons.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/pages.php b/packages/vitec/Configuration/TCA/Overrides/pages.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_25_25_25_25.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_25_25_25_25.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_33_33.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_33_33.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_66.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_33_66.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_50_50.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_50_50.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_66_33.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_cols_66_33.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_container.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_container.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_shared.php b/packages/vitec/Configuration/TCA/Overrides/tt_content_vitec_shared.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529111828 b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529111828 new file mode 100755 index 0000000..90850fe --- /dev/null +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529111828 @@ -0,0 +1,541 @@ + [ + 'title' => 'VITEC Product', + 'label' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'versioningWS' => true, + 'languageField' => 'sys_language_uid', + 'transOrigPointerField' => 'l10n_parent', + 'transOrigDiffSourceField' => 'l10n_diffsource', + 'delete' => 'deleted', + 'enablecolumns' => [ + 'disabled' => 'hidden', + 'starttime' => 'starttime', + 'endtime' => 'endtime', + ], + 'searchFields' => 'title,slug', + 'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_product.gif', + 'security' => [ + 'ignorePageTypeRestriction' => true, + ], + ], + 'types' => [ + '1' => ['showitem' => 'title, subtitle, slug, teaser, description, applications, highlights, contentelement, contentelementcta, downloads, + --div--;SEO, seotitle, urltitle, seometa, keywords, structureddata, + --div--;Images and Videos, productimage, ogimage, video, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories, + --div--;Visibility, hideonapp, hideonwebsite, hideondatasheets, hideonproducts, shortcut, shortcutpid, + --div--;Misc, legacy, supportproduct, subproduct, relatedprodukt, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime'], + ], + 'columns' => [ + 'sys_language_uid' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language', + 'config' => [ + 'type' => 'language', + ], + ], + 'l10n_parent' => [ + 'displayCond' => 'FIELD:sys_language_uid:>:0', + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'default' => 0, + 'items' => [ + ['', 0], + ], + 'foreign_table' => 'tx_vitec_domain_model_product', + 'foreign_table_where' => 'AND {#tx_vitec_domain_model_product}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_product}.{#sys_language_uid} IN (-1,0)', + ], + ], + 'l10n_diffsource' => [ + 'config' => [ + 'type' => 'passthrough', + ], + ], + 'hidden' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => true + ] + ], + ], + ], + 'starttime' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime,int', + 'default' => 0, + 'behaviour' => [ + 'allowLanguageSynchronization' => true + ] + ], + ], + 'endtime' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime,int', + 'default' => 0, + 'range' => [ + 'upper' => mktime(0, 0, 0, 1, 1, 2038) + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true + ] + ], + ], + 'categories' => [ + 'config' => [ + 'type' => 'category' + ] + ], + 'title' => [ + 'exclude' => false, + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title.description', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'required' => true, + 'default' => '' + ], + ], + 'slug' => [ + 'exclude' => false, + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug.description', + 'config' => [ + 'type' => 'slug', + 'size' => 50, + 'generatorOptions' => [ + 'fields' => ['title'], // TODO: adjust this field to the one you want to use + 'fieldSeparator' => '-', + 'replacements' => [ + '/' => '', + ], + ], + 'fallbackCharacter' => '-', + 'eval' => 'uniqueInPid', + ], + + ], + 'seotitle' => [ + 'exclude' => false, + 'label' => 'SEO-Title', + 'description' => 'SEO specific title, used for sharing options', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'urltitle' => [ + 'exclude' => false, + 'label' => 'URL-Title', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.urltitle.description', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'video' => [ + 'exclude' => false, + 'label' => 'Product Video - just the Youtube Clip ID', + 'description' => 'Product Video - just the Youtube Clip ID', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'downloads' => [ + 'exclude' => true, + 'label' => 'Downloads', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectMultipleSideBySide', + 'foreign_table' => 'tx_vitec_domain_model_download', + 'MM' => 'tx_vitec_product_download_mm', // Define the MM table for the relation + 'size' => 10, + 'maxitems' => 9999, + 'minitems' => 0, + 'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering + ], + ], + 'structureddata' => [ + 'exclude' => true, + 'label' => 'Structured Data in JSON Format', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'keywords' => [ + 'exclude' => true, + 'label' => 'Keywords', + 'description' => 'Keywords for SEO, separated by commas', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'seometa' => [ + 'exclude' => true, + 'label' => 'SEO Meta Description', + 'description' => 'SEO specific meta description, used for sharing options', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'teaser' => [ + 'exclude' => true, + 'label' => 'Short Teaser (optional)', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + + 'subtitle' => [ + 'exclude' => true, + 'label' => 'Subtitle', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'hideonapp' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on App', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'hideonwebsite' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Website', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'hideonproducts' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Product Section', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'hideondatasheets' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Datasheets Page', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'applications' => [ + 'exclude' => true, + 'label' => 'Applications', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + 'description' => [ + 'exclude' => true, + 'label' => 'Description', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + + 'highlights' => [ + 'exclude' => true, + 'label' => 'Highlights', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + + 'shortcut' => [ + 'exclude' => true, + 'label' => 'Does this Product have a custom Page', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'shortcutpid' => [ + 'exclude' => false, + 'label' => 'Page ID', + 'description' => 'PID of the custom page', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + + 'legacy' => [ + 'exclude' => true, + 'label' => 'Is this a legacy produc?', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'supportproduct' => [ + 'exclude' => true, + 'label' => 'Is this a support product?', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'subproduct' => [ + 'exclude' => true, + 'label' => 'Product is a sub-products', + 'description' => 'This Product will not be shown as a main product', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'relatedprodukt' => [ + 'exclude' => true, + 'label' => 'Related Products', + 'description' => 'Select related products for this product', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectMultipleSideBySide', + 'foreign_table' => 'tx_vitec_domain_model_product', // Reference the same table + 'MM' => 'tx_vitec_product_related_mm', // Define the MM table for the relation + 'size' => 10, + 'maxitems' => 9999, + 'minitems' => 0, + 'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering + ], + ], + 'productimage' => [ + 'exclude' => true, + 'label' => 'Product Image(s)', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'sys_file_reference', + 'foreign_field' => 'uid_foreign', + 'foreign_sortby' => 'sorting_foreign', + 'foreign_table_field' => 'tablenames', + 'foreign_match_fields' => [ + 'fieldname' => 'productimage', + ], + 'appearance' => [ + 'collapseAll' => true, + 'levelLinksPosition' => 'top', + 'showSynchronizationLink' => true, + 'showPossibleLocalizationRecords' => true, + 'showAllLocalizationLink' => true, + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, + ], + 'filter' => [ + [ + 'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren', + 'parameters' => [ + 'allowedFileExtensions' => 'jpg,jpeg,png,gif', + ], + ], + ], + 'maxitems' => 10, + 'minitems' => 0, + ], + ], + + 'ogimage' => [ + 'exclude' => true, + 'label' => 'Open Graph Image', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'sys_file_reference', + 'foreign_field' => 'uid_foreign', + 'foreign_sortby' => 'sorting_foreign', + 'foreign_table_field' => 'tablenames', + 'foreign_match_fields' => [ + 'fieldname' => 'ogimage', + ], + 'appearance' => [ + 'collapseAll' => true, + 'levelLinksPosition' => 'top', + 'showSynchronizationLink' => true, + 'showPossibleLocalizationRecords' => true, + 'showAllLocalizationLink' => true, + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, + ], + 'filter' => [ + [ + 'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren', + 'parameters' => [ + 'allowedFileExtensions' => 'jpg,jpeg,png,gif,webp', + ], + ], + ], + 'maxitems' => 1, // Allow only one image + 'minitems' => 0, + ], + ], + + 'contentelement' => [ + 'exclude' => true, + 'label' => 'Select Content Element for Key Features Section', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputLink', + 'softref' => 'typolink', + 'eval' => 'trim', + 'fieldControl' => [ + 'linkPopup' => [ + 'options' => [ + 'title' => 'Select Content Element', + 'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types + 'blindLinkFields' => 'class,params', // Disable unnecessary fields + ], + ], + ], + ], + ], + + 'contentelementcta' => [ + 'exclude' => true, + 'label' => 'Select Content Element for CTA Section', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputLink', + 'softref' => 'typolink', + 'eval' => 'trim', + 'fieldControl' => [ + 'linkPopup' => [ + 'options' => [ + 'title' => 'Select Content Element', + 'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types + 'blindLinkFields' => 'class,params', // Disable unnecessary fields + ], + ], + ], + ], + ], + +/* ----------------------------------------------------- */ + ], +]; \ No newline at end of file diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529120751 b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529120751 new file mode 100755 index 0000000..b77d023 --- /dev/null +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php.bak.20260529120751 @@ -0,0 +1,576 @@ + [ + 'title' => 'VITEC Product', + 'label' => 'title', + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'cruser_id' => 'cruser_id', + 'versioningWS' => true, + 'languageField' => 'sys_language_uid', + 'transOrigPointerField' => 'l10n_parent', + 'transOrigDiffSourceField' => 'l10n_diffsource', + 'delete' => 'deleted', + 'enablecolumns' => [ + 'disabled' => 'hidden', + 'starttime' => 'starttime', + 'endtime' => 'endtime', + ], + 'searchFields' => 'title,slug', + 'iconfile' => 'EXT:vitec/Resources/Public/Icons/tx_vitec_domain_model_product.gif', + 'security' => [ + 'ignorePageTypeRestriction' => true, + ], + ], + 'types' => [ + '1' => ['showitem' => 'title, subtitle, slug, teaser, description, applications, highlights, contentelement, contentelementcta, downloads, + --div--;SEO, seotitle, urltitle, seometa, keywords, structureddata, + --div--;Images and Videos, productimage, ogimage, video, videofile, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories, + --div--;Visibility, hideonapp, hideonwebsite, hideondatasheets, hideonproducts, shortcut, shortcutpid, + --div--;Misc, legacy, supportproduct, subproduct, relatedprodukt, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, hidden, starttime, endtime'], + ], + 'columns' => [ + 'sys_language_uid' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.language', + 'config' => [ + 'type' => 'language', + ], + ], + 'l10n_parent' => [ + 'displayCond' => 'FIELD:sys_language_uid:>:0', + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.l18n_parent', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'default' => 0, + 'items' => [ + ['', 0], + ], + 'foreign_table' => 'tx_vitec_domain_model_product', + 'foreign_table_where' => 'AND {#tx_vitec_domain_model_product}.{#pid}=###CURRENT_PID### AND {#tx_vitec_domain_model_product}.{#sys_language_uid} IN (-1,0)', + ], + ], + 'l10n_diffsource' => [ + 'config' => [ + 'type' => 'passthrough', + ], + ], + 'hidden' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.visible', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => true + ] + ], + ], + ], + 'starttime' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.starttime', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime,int', + 'default' => 0, + 'behaviour' => [ + 'allowLanguageSynchronization' => true + ] + ], + ], + 'endtime' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.endtime', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputDateTime', + 'eval' => 'datetime,int', + 'default' => 0, + 'range' => [ + 'upper' => mktime(0, 0, 0, 1, 1, 2038) + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true + ] + ], + ], + 'categories' => [ + 'config' => [ + 'type' => 'category' + ] + ], + 'title' => [ + 'exclude' => false, + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.title.description', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'required' => true, + 'default' => '' + ], + ], + 'slug' => [ + 'exclude' => false, + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.slug.description', + 'config' => [ + 'type' => 'slug', + 'size' => 50, + 'generatorOptions' => [ + 'fields' => ['title'], // TODO: adjust this field to the one you want to use + 'fieldSeparator' => '-', + 'replacements' => [ + '/' => '', + ], + ], + 'fallbackCharacter' => '-', + 'eval' => 'uniqueInPid', + ], + + ], + 'seotitle' => [ + 'exclude' => false, + 'label' => 'SEO-Title', + 'description' => 'SEO specific title, used for sharing options', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'urltitle' => [ + 'exclude' => false, + 'label' => 'URL-Title', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:tx_vitec_domain_model_product.urltitle.description', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'video' => [ + 'exclude' => false, + 'label' => 'Product Video - just the Youtube Clip ID', + 'description' => 'Product Video - just the Youtube Clip ID', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'downloads' => [ + 'exclude' => true, + 'label' => 'Downloads', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectMultipleSideBySide', + 'foreign_table' => 'tx_vitec_domain_model_download', + 'MM' => 'tx_vitec_product_download_mm', // Define the MM table for the relation + 'size' => 10, + 'maxitems' => 9999, + 'minitems' => 0, + 'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering + ], + ], + 'structureddata' => [ + 'exclude' => true, + 'label' => 'Structured Data in JSON Format', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'keywords' => [ + 'exclude' => true, + 'label' => 'Keywords', + 'description' => 'Keywords for SEO, separated by commas', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'seometa' => [ + 'exclude' => true, + 'label' => 'SEO Meta Description', + 'description' => 'SEO specific meta description, used for sharing options', + 'config' => [ + 'type' => 'text', + 'cols' => 40, + 'rows' => 15, + 'eval' => 'trim' + ] + ], + 'teaser' => [ + 'exclude' => true, + 'label' => 'Short Teaser (optional)', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + + 'subtitle' => [ + 'exclude' => true, + 'label' => 'Subtitle', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + 'hideonapp' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on App', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'hideonwebsite' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Website', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'hideonproducts' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Product Section', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'hideondatasheets' => [ + 'exclude' => true, + 'label' => 'If checked - Product will not be shown on Datasheets Page', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'applications' => [ + 'exclude' => true, + 'label' => 'Applications', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + 'description' => [ + 'exclude' => true, + 'label' => 'Description', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + + 'highlights' => [ + 'exclude' => true, + 'label' => 'Highlights', + 'config' => [ + 'type' => 'text', + 'enableRichtext' => 'true', + 'eval' => 'trim', + 'default' => '' + ], + 'defaultExtras' => 'richtext:rte_transform[mode=ts_css]' + ], + + 'shortcut' => [ + 'exclude' => true, + 'label' => 'Does this Product have a custom Page', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'shortcutpid' => [ + 'exclude' => false, + 'label' => 'Page ID', + 'description' => 'PID of the custom page', + 'config' => [ + 'type' => 'input', + 'size' => 30, + 'eval' => 'trim', + 'default' => '' + ], + ], + + 'legacy' => [ + 'exclude' => true, + 'label' => 'Is this a legacy produc?', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'supportproduct' => [ + 'exclude' => true, + 'label' => 'Is this a support product?', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + + 'subproduct' => [ + 'exclude' => true, + 'label' => 'Product is a sub-products', + 'description' => 'This Product will not be shown as a main product', + 'config' => [ + 'type' => 'check', + 'renderType' => 'checkboxToggle', + 'items' => [ + [ + 0 => '', + 1 => '', + 'invertStateDisplay' => false + ] + ], + ], + ], + 'relatedprodukt' => [ + 'exclude' => true, + 'label' => 'Related Products', + 'description' => 'Select related products for this product', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectMultipleSideBySide', + 'foreign_table' => 'tx_vitec_domain_model_product', // Reference the same table + 'MM' => 'tx_vitec_product_related_mm', // Define the MM table for the relation + 'size' => 10, + 'maxitems' => 9999, + 'minitems' => 0, + 'enableMultiSelectFilterTextfield' => true, // Optional: Adds a search box for filtering + ], + ], + 'productimage' => [ + 'exclude' => true, + 'label' => 'Product Image(s)', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'sys_file_reference', + 'foreign_field' => 'uid_foreign', + 'foreign_sortby' => 'sorting_foreign', + 'foreign_table_field' => 'tablenames', + 'foreign_match_fields' => [ + 'fieldname' => 'productimage', + ], + 'appearance' => [ + 'collapseAll' => true, + 'levelLinksPosition' => 'top', + 'showSynchronizationLink' => true, + 'showPossibleLocalizationRecords' => true, + 'showAllLocalizationLink' => true, + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, + ], + 'filter' => [ + [ + 'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren', + 'parameters' => [ + 'allowedFileExtensions' => 'jpg,jpeg,png,gif', + ], + ], + ], + 'maxitems' => 10, + 'minitems' => 0, + ], + ], + + 'ogimage' => [ + 'exclude' => true, + 'label' => 'Open Graph Image', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'sys_file_reference', + 'foreign_field' => 'uid_foreign', + 'foreign_sortby' => 'sorting_foreign', + 'foreign_table_field' => 'tablenames', + 'foreign_match_fields' => [ + 'fieldname' => 'ogimage', + ], + 'appearance' => [ + 'collapseAll' => true, + 'levelLinksPosition' => 'top', + 'showSynchronizationLink' => true, + 'showPossibleLocalizationRecords' => true, + 'showAllLocalizationLink' => true, + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, + ], + 'filter' => [ + [ + 'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren', + 'parameters' => [ + 'allowedFileExtensions' => 'jpg,jpeg,png,gif,webp', + ], + ], + ], + 'maxitems' => 1, // Allow only one image + 'minitems' => 0, + ], + ], + + 'videofile' => [ + 'exclude' => true, + 'label' => 'Video File (Upload or Local)', + 'config' => [ + 'type' => 'inline', + 'foreign_table' => 'sys_file_reference', + 'foreign_field' => 'uid_foreign', + 'foreign_sortby' => 'sorting_foreign', + 'foreign_table_field' => 'tablenames', + 'foreign_match_fields' => [ + 'fieldname' => 'videofile', + ], + 'appearance' => [ + 'collapseAll' => true, + 'levelLinksPosition' => 'top', + 'showSynchronizationLink' => true, + 'showPossibleLocalizationRecords' => true, + 'showAllLocalizationLink' => true, + ], + 'behaviour' => [ + 'allowLanguageSynchronization' => true, + ], + 'filter' => [ + [ + 'userFunc' => \TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter::class . '->filterInlineChildren', + 'parameters' => [ + 'allowedFileExtensions' => 'mp4,webm,ogv,mov,m4v', + ], + ], + ], + 'maxitems' => 1, + 'minitems' => 0, + ], + ], + + 'contentelement' => [ + 'exclude' => true, + 'label' => 'Select Content Element for Key Features Section', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputLink', + 'softref' => 'typolink', + 'eval' => 'trim', + 'fieldControl' => [ + 'linkPopup' => [ + 'options' => [ + 'title' => 'Select Content Element', + 'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types + 'blindLinkFields' => 'class,params', // Disable unnecessary fields + ], + ], + ], + ], + ], + + 'contentelementcta' => [ + 'exclude' => true, + 'label' => 'Select Content Element for CTA Section', + 'config' => [ + 'type' => 'input', + 'renderType' => 'inputLink', + 'softref' => 'typolink', + 'eval' => 'trim', + 'fieldControl' => [ + 'linkPopup' => [ + 'options' => [ + 'title' => 'Select Content Element', + 'blindLinkOptions' => 'mail,folder,url', // Disable unnecessary link types + 'blindLinkFields' => 'class,params', // Disable unnecessary fields + ], + ], + ], + ], + ], + +/* ----------------------------------------------------- */ + ], +]; diff --git a/packages/vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript b/packages/vitec/Configuration/TypoScript/Headless/vitec_containers.typoscript old mode 100644 new mode 100755 diff --git a/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript b/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Private/Language/locallang_containers.xlf b/packages/vitec/Resources/Private/Language/locallang_containers.xlf old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Private/Language/locallang_pages.xlf b/packages/vitec/Resources/Private/Language/locallang_pages.xlf old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-cols-25-25-25-25.svg b/packages/vitec/Resources/Public/Icons/vitec-cols-25-25-25-25.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-cols-33-33-33.svg b/packages/vitec/Resources/Public/Icons/vitec-cols-33-33-33.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-cols-33-66.svg b/packages/vitec/Resources/Public/Icons/vitec-cols-33-66.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-cols-50-50.svg b/packages/vitec/Resources/Public/Icons/vitec-cols-50-50.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-cols-66-33.svg b/packages/vitec/Resources/Public/Icons/vitec-cols-66-33.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/Resources/Public/Icons/vitec-container.svg b/packages/vitec/Resources/Public/Icons/vitec-container.svg old mode 100644 new mode 100755 diff --git a/packages/vitec/ext_localconf.php b/packages/vitec/ext_localconf.php old mode 100644 new mode 100755 diff --git a/packages/vitec/ext_tables.sql b/packages/vitec/ext_tables.sql old mode 100644 new mode 100755 diff --git a/packages/vitec/ext_tables.sql.bak.20260529111828 b/packages/vitec/ext_tables.sql.bak.20260529111828 new file mode 100755 index 0000000..8e6ed68 --- /dev/null +++ b/packages/vitec/ext_tables.sql.bak.20260529111828 @@ -0,0 +1,171 @@ +CREATE TABLE tx_vitec_domain_model_product ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + PRIMARY KEY (uid), + urltitle varchar(255) DEFAULT '' NOT NULL, + seotitle varchar(255) DEFAULT '' NOT NULL, + seometa text, + keywords text, + productimage int(11) DEFAULT '0' NOT NULL, + structureddata text, + teaser varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + applications text, + highlights text, + description text, + image INTEGER, + relatedimage INTEGER, + ogimage INTEGER, + legacy tinyint(4) DEFAULT '0' NOT NULL, + supportproduct tinyint(4) DEFAULT '0' NOT NULL, + subproduct tinyint(4) DEFAULT '0' NOT NULL, + cta varchar(255) DEFAULT '' NOT NULL, + sorting1 smallint(5) NOT NULL, + sorting2 smallint(5) NOT NULL, + sorting3 smallint(5) NOT NULL, + sorting4 smallint(5) NOT NULL, + sorting5 smallint(5) NOT NULL, + links text, + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + hideondatasheets smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonproducts smallint(5) unsigned DEFAULT '0' NOT NULL, + shortcut tinyint(4) DEFAULT '0' NOT NULL, + shortcutpid smallint(5) NOT NULL, + video varchar(255) DEFAULT '' NOT NULL, + key1 varchar(255) DEFAULT '' NOT NULL, + key2 varchar(255) DEFAULT '' NOT NULL, + key3 varchar(255) DEFAULT '' NOT NULL, + apptext1 varchar(255) DEFAULT '' NOT NULL, + apptext2 varchar(255) DEFAULT '' NOT NULL, + apptext3 varchar(255) DEFAULT '' NOT NULL, + productlayout int(11) DEFAULT '0' NOT NULL, + contentelement varchar(255) DEFAULT '' NOT NULL, + contentelementcta varchar(255) DEFAULT '' NOT NULL, + KEY parent (pid) +); +CREATE TABLE tx_vitec_domain_model_download ( + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) NOT NULL DEFAULT '', + keywords varchar(255) NOT NULL DEFAULT '', + description text, + file int(11) unsigned NOT NULL DEFAULT '0', + sort1 varchar(255) NOT NULL DEFAULT '', + sort2 varchar(255) NOT NULL DEFAULT '', + sort3 varchar(255) NOT NULL DEFAULT '', + private_download smallint(1) unsigned NOT NULL DEFAULT '0', + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + hideondatasheets smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonproducts smallint(5) unsigned DEFAULT '0' NOT NULL, + icon varchar(255) NOT NULL DEFAULT '', + filepath varchar(255) NOT NULL DEFAULT '', + fileprefix varchar(255) NOT NULL DEFAULT '', + useolddl smallint(1) unsigned NOT NULL DEFAULT '0' +); + +CREATE TABLE tx_vitec_product_download_mm ( + uid_local int(11) DEFAULT '0' NOT NULL, + uid_foreign int(11) DEFAULT '0' NOT NULL, + sorting int(11) DEFAULT '0' NOT NULL, + sorting_foreign int(11) DEFAULT '0' NOT NULL, + KEY uid_local (uid_local), + KEY uid_foreign (uid_foreign) +); + +CREATE TABLE tx_vitec_product_related_mm ( + uid_local INT(11) NOT NULL, + uid_foreign INT(11) NOT NULL, + sorting INT(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid_local, uid_foreign) +); + +CREATE TABLE tx_vitec_domain_model_usecase ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + description text, + caseimage INTEGER, + logoimage INTEGER, + singlepid varchar(255) DEFAULT '' NOT NULL, + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid) +); + +CREATE TABLE tx_vitec_domain_model_solution ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + sys_language_uid int(11) DEFAULT '0' NOT NULL, + l10n_parent int(11) DEFAULT '0' NOT NULL, + l10n_diffsource mediumblob, + title varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + description text, + image int(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid), + KEY language (l10n_parent, sys_language_uid) +); + +CREATE TABLE tx_vitec_domain_model_market ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + sys_language_uid int(11) DEFAULT '0' NOT NULL, + l10n_parent int(11) DEFAULT '0' NOT NULL, + l10n_diffsource mediumblob, + title varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + description text, + image int(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid), + KEY language (l10n_parent, sys_language_uid) +); + +ALTER TABLE sys_category +ADD class VARCHAR(255) DEFAULT '' NOT NULL, +ADD filetype VARCHAR(255) DEFAULT '' NOT NULL, +ADD type VARCHAR(255) DEFAULT '' NOT NULL; + + +CREATE TABLE tt_content ( + tx_vitec_bg_variant VARCHAR(20) DEFAULT 'none' NOT NULL +); \ No newline at end of file diff --git a/packages/vitec/ext_tables.sql.bak.20260529120751 b/packages/vitec/ext_tables.sql.bak.20260529120751 new file mode 100755 index 0000000..ecc77ff --- /dev/null +++ b/packages/vitec/ext_tables.sql.bak.20260529120751 @@ -0,0 +1,172 @@ +CREATE TABLE tx_vitec_domain_model_product ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + PRIMARY KEY (uid), + urltitle varchar(255) DEFAULT '' NOT NULL, + seotitle varchar(255) DEFAULT '' NOT NULL, + seometa text, + keywords text, + productimage int(11) DEFAULT '0' NOT NULL, + structureddata text, + teaser varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + applications text, + highlights text, + description text, + image INTEGER, + relatedimage INTEGER, + ogimage INTEGER, + legacy tinyint(4) DEFAULT '0' NOT NULL, + supportproduct tinyint(4) DEFAULT '0' NOT NULL, + subproduct tinyint(4) DEFAULT '0' NOT NULL, + cta varchar(255) DEFAULT '' NOT NULL, + sorting1 smallint(5) NOT NULL, + sorting2 smallint(5) NOT NULL, + sorting3 smallint(5) NOT NULL, + sorting4 smallint(5) NOT NULL, + sorting5 smallint(5) NOT NULL, + links text, + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + hideondatasheets smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonproducts smallint(5) unsigned DEFAULT '0' NOT NULL, + shortcut tinyint(4) DEFAULT '0' NOT NULL, + shortcutpid smallint(5) NOT NULL, + video varchar(255) DEFAULT '' NOT NULL, + key1 varchar(255) DEFAULT '' NOT NULL, + key2 varchar(255) DEFAULT '' NOT NULL, + key3 varchar(255) DEFAULT '' NOT NULL, + apptext1 varchar(255) DEFAULT '' NOT NULL, + apptext2 varchar(255) DEFAULT '' NOT NULL, + apptext3 varchar(255) DEFAULT '' NOT NULL, + productlayout int(11) DEFAULT '0' NOT NULL, + contentelement varchar(255) DEFAULT '' NOT NULL, + contentelementcta varchar(255) DEFAULT '' NOT NULL, + videofile int(11) unsigned DEFAULT '0' NOT NULL, + KEY parent (pid) +); +CREATE TABLE tx_vitec_domain_model_download ( + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) NOT NULL DEFAULT '', + keywords varchar(255) NOT NULL DEFAULT '', + description text, + file int(11) unsigned NOT NULL DEFAULT '0', + sort1 varchar(255) NOT NULL DEFAULT '', + sort2 varchar(255) NOT NULL DEFAULT '', + sort3 varchar(255) NOT NULL DEFAULT '', + private_download smallint(1) unsigned NOT NULL DEFAULT '0', + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + hideondatasheets smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonproducts smallint(5) unsigned DEFAULT '0' NOT NULL, + icon varchar(255) NOT NULL DEFAULT '', + filepath varchar(255) NOT NULL DEFAULT '', + fileprefix varchar(255) NOT NULL DEFAULT '', + useolddl smallint(1) unsigned NOT NULL DEFAULT '0' +); + +CREATE TABLE tx_vitec_product_download_mm ( + uid_local int(11) DEFAULT '0' NOT NULL, + uid_foreign int(11) DEFAULT '0' NOT NULL, + sorting int(11) DEFAULT '0' NOT NULL, + sorting_foreign int(11) DEFAULT '0' NOT NULL, + KEY uid_local (uid_local), + KEY uid_foreign (uid_foreign) +); + +CREATE TABLE tx_vitec_product_related_mm ( + uid_local INT(11) NOT NULL, + uid_foreign INT(11) NOT NULL, + sorting INT(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid_local, uid_foreign) +); + +CREATE TABLE tx_vitec_domain_model_usecase ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + title varchar(255) DEFAULT '' NOT NULL, + slug varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + description text, + caseimage INTEGER, + logoimage INTEGER, + singlepid varchar(255) DEFAULT '' NOT NULL, + hideonapp smallint(5) unsigned DEFAULT '0' NOT NULL, + hideonwebsite smallint(5) unsigned DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid) +); + +CREATE TABLE tx_vitec_domain_model_solution ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + sys_language_uid int(11) DEFAULT '0' NOT NULL, + l10n_parent int(11) DEFAULT '0' NOT NULL, + l10n_diffsource mediumblob, + title varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + description text, + image int(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid), + KEY language (l10n_parent, sys_language_uid) +); + +CREATE TABLE tx_vitec_domain_model_market ( + uid int(11) NOT NULL auto_increment, + pid int(11) DEFAULT '0' NOT NULL, + tstamp int(11) DEFAULT '0' NOT NULL, + crdate int(11) DEFAULT '0' NOT NULL, + cruser_id int(11) DEFAULT '0' NOT NULL, + deleted tinyint(4) DEFAULT '0' NOT NULL, + hidden tinyint(4) DEFAULT '0' NOT NULL, + starttime int(11) DEFAULT '0' NOT NULL, + endtime int(11) DEFAULT '0' NOT NULL, + sys_language_uid int(11) DEFAULT '0' NOT NULL, + l10n_parent int(11) DEFAULT '0' NOT NULL, + l10n_diffsource mediumblob, + title varchar(255) DEFAULT '' NOT NULL, + subtitle varchar(255) DEFAULT '' NOT NULL, + teaser varchar(255) DEFAULT '' NOT NULL, + description text, + image int(11) DEFAULT '0' NOT NULL, + PRIMARY KEY (uid), + KEY parent (pid), + KEY language (l10n_parent, sys_language_uid) +); + +ALTER TABLE sys_category +ADD class VARCHAR(255) DEFAULT '' NOT NULL, +ADD filetype VARCHAR(255) DEFAULT '' NOT NULL, +ADD type VARCHAR(255) DEFAULT '' NOT NULL; + + +CREATE TABLE tt_content ( + tx_vitec_bg_variant VARCHAR(20) DEFAULT 'none' NOT NULL +);