feat: ep_products plugins are content element types of their own

Preparation for TYPO3 14, which drops list_type. The plugins are
registered as CTypes named like their former list_type values
(epproducts_product_detail etc.); the form keeps the fields of the
"list" type, with the plugin options in place of the plugin selector. As content
element types they show the E&P logo of the extension manager
(icon ep-products-plugin) instead of the generic plugin icon.

The upgrade wizard epProductsPluginListTypeToCType moves the elements
and the tt_content:list_type permissions of backend groups. Until it has
run, list_type elements render through the new types (alias block in
ext_localconf.php), so the deploy alone changes nothing. Retired plugins
stay "list" elements.

The content-only page type, the content manifest and the two container
columns limited to "list" now treat the epproducts_* types as plugins.

Crawl of 1,912 URLs identical for old code and new code before and after
the wizard; rehearsed again on the prod dump of 2026-11-01 (1,924 URLs,
API snapshot identical). Rollout and rollback SQL: .ddev/plans/PLUGINS-CTYPE-ROLLOUT.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
frommeandClaude Opus 5.5 committed 2026-11-01 14:31:35 +01:00
1 parent 518f29b160
commit 81a8013e8a
7 files changed
+238 -160

No files matched your search

@@ -254,7 +254,7 @@ class ManifestBuilder
'pid',
'sys_language_uid',
'MAX(tstamp) AS tstamp',
"SUM(CASE WHEN deleted = 0 AND hidden = 0 AND CType <> 'list' THEN 1 ELSE 0 END) AS text_elements"
"SUM(CASE WHEN deleted = 0 AND hidden = 0 AND CType <> 'list' AND CType NOT LIKE 'epproducts\\_%' THEN 1 ELSE 0 END) AS text_elements"
)
->from('tt_content')->groupBy('pid', 'sys_language_uid')->executeQuery()
->fetchAllAssociative()
@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace EP\EpProducts\Upgrades;
use TYPO3\CMS\Install\Attribute\UpgradeWizard;
use TYPO3\CMS\Install\Updates\AbstractListTypeToCTypeUpdate;
/**
* Turns the ep_products plugin elements (CType "list") into content elements of their own type, as TYPO3 14
* no longer knows list_type. The types keep the plugin signatures as names. Also rewrites the
* tt_content:list_type permissions of backend groups. Retired plugins stay "list" elements.
*/
#[UpgradeWizard('epProductsPluginListTypeToCType')]
final class PluginListTypeToCTypeUpdate extends AbstractListTypeToCTypeUpdate
{
private const array PLUGINS = [
'epproducts_searchresult',
'epproducts_travelinfo',
'epproducts_watchlist',
'epproducts_product_detail',
'epproducts_product_teasergroup',
'epproducts_product_pricetable',
'epproducts_snowreport_list',
'epproducts_country_detail',
'epproducts_region_teasergroup',
'epproducts_region_detail',
'epproducts_city_teasergroup',
'epproducts_city_detail',
'epproducts_hotel_teasergroup',
'epproducts_hotel_teasergroup_nodate',
'epproducts_hotel_detail',
'epproducts_hotel_detail_event',
'epproducts_concept_teasergroup',
'epproducts_concept_detail',
'epproducts_teammembers',
'epproducts_faq',
'epproducts_newsletter_subscription',
'epproducts_maps',
'epproducts_pricetable',
];
protected function getListTypeToCTypeMapping(): array
{
return array_combine(self::PLUGINS, self::PLUGINS);
}
public function getTitle(): string
{
return 'E&P Produkte: Plugins als eigene Inhaltstypen';
}
public function getDescription(): string
{
return 'Migrates the ep_products plugin elements from list_type to CType, including backend group permissions.';
}
}
@@ -9,4 +9,9 @@ return [
'provider' => SvgIconProvider::class,
'source' => 'EXT:ep_products/Resources/Public/Icons/logo.svg',
],
// Content element types of the plugins, the logo of the extension manager
'ep-products-plugin' => [
'provider' => SvgIconProvider::class,
'source' => 'EXT:ep_products/Resources/Public/Icons/Extension.svg',
],
];
@@ -12,247 +12,199 @@ call_user_func(function (): void {
ExtensionUtility::registerPlugin(
'EpProducts',
'searchresult',
'Suchergebnis'
'Suchergebnis',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'travelinfo',
'Reiseinformationen'
'Reiseinformationen',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'watchlist',
'Merkliste'
'Merkliste',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'product_detail',
'Produkt: Details'
'Produkt: Details',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'product_teasergroup',
'Produkt: Teaser'
'Produkt: Teaser',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'product_pricetable',
'Buchungstabelle'
'Buchungstabelle',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'snowreport_list',
'Schneehöhen'
'Schneehöhen',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'country_detail',
'Land'
'Land',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'region_teasergroup',
'Gebiet: Teaser'
'Gebiet: Teaser',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'region_detail',
'Gebiet: Details'
'Gebiet: Details',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'city_teasergroup',
'Ort: Teaser'
'Ort: Teaser',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'city_detail',
'Ort: Details'
'Ort: Details',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_teasergroup',
'Hotel: Teaser'
'Hotel: Teaser',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_teasergroup_nodate',
'Hotel: Teaser (terminunabhängig)'
'Hotel: Teaser (terminunabhängig)',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_detail',
'Hotel: Details'
'Hotel: Details',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'hotel_detail_event',
'Hotel: Details (Event)'
'Hotel: Details (Event)',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'concept_teasergroup',
'Konzept: Teaser'
'Konzept: Teaser',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'concept_detail',
'Konzept: Details'
'Konzept: Details',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'teammembers',
'Team'
'Team',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'faq',
'FAQ'
'FAQ',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'newsletter_subscription',
'Newsletter-/RA-Registrierung'
'Newsletter-/RA-Registrierung',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'maps',
'Übersichtskarte Hotels/Produkte'
'Übersichtskarte Hotels/Produkte',
'ep-products-plugin'
);
ExtensionUtility::registerPlugin(
'EpProducts',
'pricetable',
'Preistabelle'
'Preistabelle',
'ep-products-plugin'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_product_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_product_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_pricetable'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_product_pricetable',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_pricetable.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_snowreport_list'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_snowreport_list',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_snowreport.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_country_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_country_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_country.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_region_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_region_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_region_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_region_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_city_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_city_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_city_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_city_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_teasergroup'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_teasergroup',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_teasergroup_nodate'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_teasergroup_nodate',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser_date_independent.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_hotel_detail_event'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_hotel_detail_event',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail_event.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_concept_detail'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_concept_detail',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_concept_detail.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_faq'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_faq',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_faq.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_maps'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_maps',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_maps.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_pricetable'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_pricetable',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_pricetable.xml'
);
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_teammembers'] = 'pi_flexform';
ExtensionManagementUtility::addPiFlexFormValue(
'epproducts_teammembers',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_teammembers.xml'
);
// Content element types since 2026-10 (upgrade wizard epProductsPluginListTypeToCType); the form keeps the
// fields of the former "list" type, with the plugin options in place of the plugin selector
$flexForms = [
'epproducts_product_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_detail.xml',
'epproducts_product_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_teaser.xml',
'epproducts_product_pricetable' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_pricetable.xml',
'epproducts_snowreport_list' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_snowreport.xml',
'epproducts_country_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_country.xml',
'epproducts_region_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_teaser.xml',
'epproducts_region_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_detail.xml',
'epproducts_city_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_teaser.xml',
'epproducts_city_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_city_detail.xml',
'epproducts_hotel_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser.xml',
'epproducts_hotel_teasergroup_nodate' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser_date_independent.xml',
'epproducts_hotel_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail.xml',
'epproducts_hotel_detail_event' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail_event.xml',
'epproducts_concept_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_concept_detail.xml',
'epproducts_faq' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_faq.xml',
'epproducts_maps' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_maps.xml',
'epproducts_pricetable' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_pricetable.xml',
'epproducts_teammembers' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_teammembers.xml',
];
foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $item) {
$cType = (string)($item['value'] ?? '');
if (!str_starts_with($cType, 'epproducts_')) {
continue;
}
$GLOBALS['TCA']['tt_content']['types'][$cType]['showitem'] = str_replace(
'list_type;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:list_type_formlabel,',
isset($flexForms[$cType]) ? 'pi_flexform,' : '',
$GLOBALS['TCA']['tt_content']['types']['list']['showitem']
);
if (isset($flexForms[$cType])) {
ExtensionManagementUtility::addPiFlexFormValue('*', $flexForms[$cType], $cType);
}
}
});
+82 -25
View File
@@ -1,6 +1,7 @@
<?php
use TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use EP\EpProducts\Controller\ProductController;
use EP\EpProducts\Controller\CityController;
use EP\EpProducts\Controller\ConceptController;
@@ -54,7 +55,9 @@ $boot = function (): void {
'product_detail',
[
ProductController::class => 'detail,hotelList',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -62,7 +65,9 @@ $boot = function (): void {
'product_teasergroup',
[
ProductController::class => 'teasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -70,7 +75,9 @@ $boot = function (): void {
'product_pricetable',
[
ProductController::class => 'pricetable',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -78,7 +85,9 @@ $boot = function (): void {
'country_detail',
[
CountryController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -86,7 +95,9 @@ $boot = function (): void {
'region_teasergroup',
[
RegionController::class => 'teasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -94,7 +105,9 @@ $boot = function (): void {
'region_detail',
[
RegionController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -102,7 +115,9 @@ $boot = function (): void {
'city_teasergroup',
[
CityController::class => 'teasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -110,7 +125,9 @@ $boot = function (): void {
'city_detail',
[
CityController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -118,7 +135,9 @@ $boot = function (): void {
'hotel_teasergroup',
[
HotelController::class => 'teasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -126,7 +145,9 @@ $boot = function (): void {
'hotel_teasergroup_nodate',
[
HotelController::class => 'dateIndependentTeasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -134,7 +155,9 @@ $boot = function (): void {
'hotel_detail',
[
HotelController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -142,7 +165,9 @@ $boot = function (): void {
'hotel_detail_event',
[
HotelController::class => 'detailEvent',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -150,7 +175,9 @@ $boot = function (): void {
'faq',
[
FaqController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -158,7 +185,9 @@ $boot = function (): void {
'concept_teasergroup',
[
ConceptController::class => 'teasergroup',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -166,7 +195,9 @@ $boot = function (): void {
'concept_detail',
[
ConceptController::class => 'detail',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -174,7 +205,9 @@ $boot = function (): void {
'teammembers',
[
TeamController::class => 'list,show',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -185,7 +218,8 @@ $boot = function (): void {
],
[
NewsletterController::class => 'subscriptionForm,subscriptionFormSubmit',
]
],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -193,7 +227,9 @@ $boot = function (): void {
'maps',
[
MapController::class => 'general',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -201,7 +237,9 @@ $boot = function (): void {
'pricetable',
[
PricetableController::class => 'event',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -223,7 +261,8 @@ $boot = function (): void {
],
[
SearchController::class => 'searchresult',
]
],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
// The search bar in the page header (lib.searchBar), not a content element
@@ -232,7 +271,9 @@ $boot = function (): void {
'searchbar',
[
SearchbarController::class => 'index',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -240,7 +281,9 @@ $boot = function (): void {
'watchlist',
[
WatchlistController::class => 'list',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -248,7 +291,9 @@ $boot = function (): void {
'travelinfo',
[
TravelinfoController::class => 'travelInfo,travelCode',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -256,7 +301,9 @@ $boot = function (): void {
'snowreport_list',
[
SnowreportController::class => 'list',
]
],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
ExtensionUtility::configurePlugin(
@@ -276,9 +323,19 @@ $boot = function (): void {
AjaxWatchlistController::class => 'list',
AjaxTableController::class => 'datestable,pricetable,pricetableHtml,eventPricetable',
AjaxTeaserPopupController::class => 'concept',
]
],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
// Elements not yet migrated by the upgrade wizard epProductsPluginListTypeToCType (deploy first, then run
// the wizard) still render: list_type plugin => content element type of the same name
$listTypeAliases = '';
foreach (array_keys($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions']['EpProducts']['plugins']) as $pluginName) {
$signature = 'epproducts_' . strtolower((string)$pluginName);
$listTypeAliases .= 'tt_content.list.20.' . $signature . ' < tt_content.' . $signature . ".20\n";
}
ExtensionManagementUtility::addTypoScript('ep_products', 'setup', $listTypeAliases, 'defaultContentRendering');
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = '_gl';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ref';
@@ -32,13 +32,18 @@ call_user_func(function (): void {
);
// Column layouts, migrated from gridelements in 2026 (commit 4172ed9). colPos = 200 + 10 × layout + column
// ep_products plugins are content element types of their own since 2026-10; "list" stays for retired plugins
$plugins = implode(',', array_filter(
array_map(static fn(array $item): string => (string)($item['value'] ?? ''), $GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items']),
static fn(string $cType): bool => str_starts_with($cType, 'epproducts_')
));
$menus = 'menu_abstract,menu_categorized_content,menu_categorized_pages,menu_recently_updated,menu_related_pages,menu_section_pages,menu_sitemap,menu_sitemap_pages';
$columnContainers = [
'ep_container_halves' => ['Spalten 50/50', 'Zwei gleich breite Spalten', 'halves', [[201], [202]]],
'ep_container_thirds' => ['Spalten 33/33/33', 'Drei gleich breite Spalten', 'thirds', [[211], [212], [213]]],
'ep_container_columns_quarters' => ['Spalten 25/25/25/25', 'Vier gleich breite Spalten', 'quarters', [[221], [222], [223], [224]]],
'ep_container_half_and_two_quarters' => ['Spalten 50/25/25', 'Eine breite und zwei schmale Spalten', 'half_and_two_quarters', [[231, 'textmedia,teaser,list,form_formframework'], [232], [233]]],
'ep_container_two_quarters_and_half' => ['Spalten 25/25/50', 'Zwei schmale und eine breite Spalte', 'two_quarters_and_half', [[241], [242], [243, 'textmedia,teaser,list,gmap,nlform,form_formframework,menu_pages,menu_subpages,menu_section']]],
'ep_container_half_and_two_quarters' => ['Spalten 50/25/25', 'Eine breite und zwei schmale Spalten', 'half_and_two_quarters', [[231, 'textmedia,teaser,list,form_formframework,' . $plugins], [232], [233]]],
'ep_container_two_quarters_and_half' => ['Spalten 25/25/50', 'Zwei schmale und eine breite Spalte', 'two_quarters_and_half', [[241], [242], [243, 'textmedia,teaser,list,gmap,nlform,form_formframework,menu_pages,menu_subpages,menu_section,' . $plugins]]],
];
// No container inside these columns (topLevelLayout of the gridelements configuration)
$containers = implode(',', array_merge(['ep_container_quarters', 'ep_container_teasergroup'], array_keys($columnContainers)));
@@ -6,9 +6,10 @@
# Plugins (the booking widget on product detail pages), forms, sub-page menus and teaser cards
# are left out: MyE&P has trip data first-hand, and the pages and records the cards link to are
# in the feed in their own right. The filter applies to top-level elements only; a plugin nested
# in a grid or container element is still rendered by its parent.
# in a grid or container element is still rendered by its parent. The ep_products plugins are content
# element types of their own (epproducts_*); "list" in the setting covers the retired plugins.
lib.contentOnly < lib.dynamicContent
lib.contentOnly.20.select.where = colPos={register:colPos} AND CType NOT IN ({$plugin.tx_eptheme.settings.contentOnlyExcludedCTypes})
lib.contentOnly.20.select.where = colPos={register:colPos} AND CType NOT IN ({$plugin.tx_eptheme.settings.contentOnlyExcludedCTypes}) AND CType NOT LIKE 'epproducts_%'
tx_eptheme_content_only = PAGE
tx_eptheme_content_only {