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', 'pid',
'sys_language_uid', 'sys_language_uid',
'MAX(tstamp) AS tstamp', '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() ->from('tt_content')->groupBy('pid', 'sys_language_uid')->executeQuery()
->fetchAllAssociative() ->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, 'provider' => SvgIconProvider::class,
'source' => 'EXT:ep_products/Resources/Public/Icons/logo.svg', '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( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'searchresult', 'searchresult',
'Suchergebnis' 'Suchergebnis',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'travelinfo', 'travelinfo',
'Reiseinformationen' 'Reiseinformationen',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'watchlist', 'watchlist',
'Merkliste' 'Merkliste',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'product_detail', 'product_detail',
'Produkt: Details' 'Produkt: Details',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'product_teasergroup', 'product_teasergroup',
'Produkt: Teaser' 'Produkt: Teaser',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'product_pricetable', 'product_pricetable',
'Buchungstabelle' 'Buchungstabelle',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'snowreport_list', 'snowreport_list',
'Schneehöhen' 'Schneehöhen',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'country_detail', 'country_detail',
'Land' 'Land',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'region_teasergroup', 'region_teasergroup',
'Gebiet: Teaser' 'Gebiet: Teaser',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'region_detail', 'region_detail',
'Gebiet: Details' 'Gebiet: Details',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'city_teasergroup', 'city_teasergroup',
'Ort: Teaser' 'Ort: Teaser',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'city_detail', 'city_detail',
'Ort: Details' 'Ort: Details',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'hotel_teasergroup', 'hotel_teasergroup',
'Hotel: Teaser' 'Hotel: Teaser',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'hotel_teasergroup_nodate', 'hotel_teasergroup_nodate',
'Hotel: Teaser (terminunabhängig)' 'Hotel: Teaser (terminunabhängig)',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'hotel_detail', 'hotel_detail',
'Hotel: Details' 'Hotel: Details',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'hotel_detail_event', 'hotel_detail_event',
'Hotel: Details (Event)' 'Hotel: Details (Event)',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'concept_teasergroup', 'concept_teasergroup',
'Konzept: Teaser' 'Konzept: Teaser',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'concept_detail', 'concept_detail',
'Konzept: Details' 'Konzept: Details',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'teammembers', 'teammembers',
'Team' 'Team',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'faq', 'faq',
'FAQ' 'FAQ',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'newsletter_subscription', 'newsletter_subscription',
'Newsletter-/RA-Registrierung' 'Newsletter-/RA-Registrierung',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'maps', 'maps',
'Übersichtskarte Hotels/Produkte' 'Übersichtskarte Hotels/Produkte',
'ep-products-plugin'
); );
ExtensionUtility::registerPlugin( ExtensionUtility::registerPlugin(
'EpProducts', 'EpProducts',
'pricetable', 'pricetable',
'Preistabelle' 'Preistabelle',
'ep-products-plugin'
); );
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_detail'] = 'pi_flexform'; // Content element types since 2026-10 (upgrade wizard epProductsPluginListTypeToCType); the form keeps the
ExtensionManagementUtility::addPiFlexFormValue( // fields of the former "list" type, with the plugin options in place of the plugin selector
'epproducts_product_detail', $flexForms = [
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_detail.xml' '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',
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_teasergroup'] = 'pi_flexform'; 'epproducts_snowreport_list' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_snowreport.xml',
ExtensionManagementUtility::addPiFlexFormValue( 'epproducts_country_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_country.xml',
'epproducts_product_teasergroup', 'epproducts_region_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_teaser.xml',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_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',
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_product_pricetable'] = 'pi_flexform'; 'epproducts_hotel_teasergroup' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser.xml',
ExtensionManagementUtility::addPiFlexFormValue( 'epproducts_hotel_teasergroup_nodate' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_teaser_date_independent.xml',
'epproducts_product_pricetable', 'epproducts_hotel_detail' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_hotel_detail.xml',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_product_pricetable.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',
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_snowreport_list'] = 'pi_flexform'; 'epproducts_maps' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_maps.xml',
ExtensionManagementUtility::addPiFlexFormValue( 'epproducts_pricetable' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_pricetable.xml',
'epproducts_snowreport_list', 'epproducts_teammembers' => 'FILE:EXT:ep_products/Configuration/FlexForms/flexform_teammembers.xml',
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_snowreport.xml' ];
); foreach ($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'] as $item) {
$cType = (string)($item['value'] ?? '');
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_addlist']['epproducts_country_detail'] = 'pi_flexform'; if (!str_starts_with($cType, 'epproducts_')) {
ExtensionManagementUtility::addPiFlexFormValue( continue;
'epproducts_country_detail', }
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_country.xml' $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']['subtypes_addlist']['epproducts_region_teasergroup'] = 'pi_flexform'; $GLOBALS['TCA']['tt_content']['types']['list']['showitem']
ExtensionManagementUtility::addPiFlexFormValue( );
'epproducts_region_teasergroup', if (isset($flexForms[$cType])) {
'FILE:EXT:ep_products/Configuration/FlexForms/flexform_region_teaser.xml' ExtensionManagementUtility::addPiFlexFormValue('*', $flexForms[$cType], $cType);
); }
}
$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'
);
}); });
+82 -25
View File
@@ -1,6 +1,7 @@
<?php <?php
use TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask; use TYPO3\CMS\Scheduler\Task\TableGarbageCollectionTask;
use TYPO3\CMS\Extbase\Utility\ExtensionUtility; use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use EP\EpProducts\Controller\ProductController; use EP\EpProducts\Controller\ProductController;
use EP\EpProducts\Controller\CityController; use EP\EpProducts\Controller\CityController;
use EP\EpProducts\Controller\ConceptController; use EP\EpProducts\Controller\ConceptController;
@@ -54,7 +55,9 @@ $boot = function (): void {
'product_detail', 'product_detail',
[ [
ProductController::class => 'detail,hotelList', ProductController::class => 'detail,hotelList',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -62,7 +65,9 @@ $boot = function (): void {
'product_teasergroup', 'product_teasergroup',
[ [
ProductController::class => 'teasergroup', ProductController::class => 'teasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -70,7 +75,9 @@ $boot = function (): void {
'product_pricetable', 'product_pricetable',
[ [
ProductController::class => 'pricetable', ProductController::class => 'pricetable',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -78,7 +85,9 @@ $boot = function (): void {
'country_detail', 'country_detail',
[ [
CountryController::class => 'detail', CountryController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -86,7 +95,9 @@ $boot = function (): void {
'region_teasergroup', 'region_teasergroup',
[ [
RegionController::class => 'teasergroup', RegionController::class => 'teasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -94,7 +105,9 @@ $boot = function (): void {
'region_detail', 'region_detail',
[ [
RegionController::class => 'detail', RegionController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -102,7 +115,9 @@ $boot = function (): void {
'city_teasergroup', 'city_teasergroup',
[ [
CityController::class => 'teasergroup', CityController::class => 'teasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -110,7 +125,9 @@ $boot = function (): void {
'city_detail', 'city_detail',
[ [
CityController::class => 'detail', CityController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -118,7 +135,9 @@ $boot = function (): void {
'hotel_teasergroup', 'hotel_teasergroup',
[ [
HotelController::class => 'teasergroup', HotelController::class => 'teasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -126,7 +145,9 @@ $boot = function (): void {
'hotel_teasergroup_nodate', 'hotel_teasergroup_nodate',
[ [
HotelController::class => 'dateIndependentTeasergroup', HotelController::class => 'dateIndependentTeasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -134,7 +155,9 @@ $boot = function (): void {
'hotel_detail', 'hotel_detail',
[ [
HotelController::class => 'detail', HotelController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -142,7 +165,9 @@ $boot = function (): void {
'hotel_detail_event', 'hotel_detail_event',
[ [
HotelController::class => 'detailEvent', HotelController::class => 'detailEvent',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -150,7 +175,9 @@ $boot = function (): void {
'faq', 'faq',
[ [
FaqController::class => 'detail', FaqController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -158,7 +185,9 @@ $boot = function (): void {
'concept_teasergroup', 'concept_teasergroup',
[ [
ConceptController::class => 'teasergroup', ConceptController::class => 'teasergroup',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -166,7 +195,9 @@ $boot = function (): void {
'concept_detail', 'concept_detail',
[ [
ConceptController::class => 'detail', ConceptController::class => 'detail',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -174,7 +205,9 @@ $boot = function (): void {
'teammembers', 'teammembers',
[ [
TeamController::class => 'list,show', TeamController::class => 'list,show',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -185,7 +218,8 @@ $boot = function (): void {
], ],
[ [
NewsletterController::class => 'subscriptionForm,subscriptionFormSubmit', NewsletterController::class => 'subscriptionForm,subscriptionFormSubmit',
] ],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -193,7 +227,9 @@ $boot = function (): void {
'maps', 'maps',
[ [
MapController::class => 'general', MapController::class => 'general',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -201,7 +237,9 @@ $boot = function (): void {
'pricetable', 'pricetable',
[ [
PricetableController::class => 'event', PricetableController::class => 'event',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -223,7 +261,8 @@ $boot = function (): void {
], ],
[ [
SearchController::class => 'searchresult', SearchController::class => 'searchresult',
] ],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
// The search bar in the page header (lib.searchBar), not a content element // The search bar in the page header (lib.searchBar), not a content element
@@ -232,7 +271,9 @@ $boot = function (): void {
'searchbar', 'searchbar',
[ [
SearchbarController::class => 'index', SearchbarController::class => 'index',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -240,7 +281,9 @@ $boot = function (): void {
'watchlist', 'watchlist',
[ [
WatchlistController::class => 'list', WatchlistController::class => 'list',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -248,7 +291,9 @@ $boot = function (): void {
'travelinfo', 'travelinfo',
[ [
TravelinfoController::class => 'travelInfo,travelCode', TravelinfoController::class => 'travelInfo,travelCode',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -256,7 +301,9 @@ $boot = function (): void {
'snowreport_list', 'snowreport_list',
[ [
SnowreportController::class => 'list', SnowreportController::class => 'list',
] ],
[],
ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
); );
ExtensionUtility::configurePlugin( ExtensionUtility::configurePlugin(
@@ -276,9 +323,19 @@ $boot = function (): void {
AjaxWatchlistController::class => 'list', AjaxWatchlistController::class => 'list',
AjaxTableController::class => 'datestable,pricetable,pricetableHtml,eventPricetable', AjaxTableController::class => 'datestable,pricetable,pricetableHtml,eventPricetable',
AjaxTeaserPopupController::class => 'concept', 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'][] = '_gl';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid'; $GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'gclid';
$GLOBALS['TYPO3_CONF_VARS']['FE']['cacheHash']['excludedParameters'][] = 'ref'; $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 // 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'; $menus = 'menu_abstract,menu_categorized_content,menu_categorized_pages,menu_recently_updated,menu_related_pages,menu_section_pages,menu_sitemap,menu_sitemap_pages';
$columnContainers = [ $columnContainers = [
'ep_container_halves' => ['Spalten 50/50', 'Zwei gleich breite Spalten', 'halves', [[201], [202]]], '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_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_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_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']]], '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) // No container inside these columns (topLevelLayout of the gridelements configuration)
$containers = implode(',', array_merge(['ep_container_quarters', 'ep_container_teasergroup'], array_keys($columnContainers))); $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 # 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 # 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 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 < 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 = PAGE
tx_eptheme_content_only { tx_eptheme_content_only {