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

@@ -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 {