Files
orascheandClaude Fable 5.1 20bcbeb731 Page content check for markets and solutions (vitec_pagecheck)
Sibling of the product check (decision Harry 2026-11-13): new content element vitec_pagecheck with a FlexForm scope (markets | solutions) and PageCheckService/PageCheckJsonRenderer. Overview = one row per page of the tree (11 markets + 24 sub-markets, 8 solution groups + 39 sub-solutions) with 18 checks taken from the page elements (hero image and subline, lead, intro text/image/CTA, cards with visuals, testimonials, products, insights text/cover/CTA, logos, CTA banner), the page properties (meta description, OG image, page media) and the market/solution record behind the page; columns a page type does not have come as ok null. ?check=<pageUid> delivers the page detail (elements in page order with texts, links, images, FlexForm settings; SEO, media, record, children). Always USER_INT, check is cHash-excluded already. New command vitec:pagecheck-pages created /markets/market-check (608) and /solutions/solution-check (609) from the product-check page properties; both live on DEV, rendering is front end work.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-13 11:44:41 +01:00

291 lines
15 KiB
PHP

<?php
declare(strict_types=1);
defined('TYPO3') or die();
use TYPO3\CMS\Extbase\Utility\ExtensionUtility;
(static function (): void {
$registerPluginWithFlexForm = static function (
string $pluginName,
string $pluginTitle,
string $flexFormFile,
?string $iconIdentifier = null
): void {
ExtensionUtility::registerPlugin(
'Vitec',
$pluginName,
$pluginTitle,
$iconIdentifier ?? 'content-plugin',
'plugins',
'',
$flexFormFile
);
};
$registerPluginWithFlexForm('Productlist', 'Show Products by Category', 'FILE:EXT:vitec/Configuration/FlexForms/Productlist.xml', 'vitec-plugin-productlist');
$registerPluginWithFlexForm('Productfinder', 'Product Finder (filter data)', 'FILE:EXT:vitec/Configuration/FlexForms/Productfinder.xml', 'vitec-plugin-productlist');
$registerPluginWithFlexForm('Productfeature', 'Featured Product Card', 'FILE:EXT:vitec/Configuration/FlexForms/Productfeature.xml', 'vitec-plugin-productlist');
// Product content audit as page content (2026-11-12): no FlexForm, the
// element has nothing to configure - data comes from ProductCheckService.
ExtensionUtility::registerPlugin(
'Vitec',
'Productcheck',
'Product Content Check (audit table)',
'vitec-plugin-productlist',
'plugins',
'Live audit of every product record: one row per product, one check per content field, details per row. Place once on /products/product-check.'
);
// Page content audit for the market and solution trees (2026-11-13):
// scope (markets | solutions) via FlexForm, data from PageCheckService.
$registerPluginWithFlexForm('Pagecheck', 'Page Content Check (markets / solutions audit)', 'FILE:EXT:vitec/Configuration/FlexForms/Pagecheck.xml', 'vitec-plugin-productlist');
$registerPluginWithFlexForm('Simplecard', 'Simple Card', 'FILE:EXT:vitec/Configuration/FlexForms/Simplecard.xml', 'vitec-plugin-simplecard');
$registerPluginWithFlexForm('Productshow', 'Show Single Product', 'FILE:EXT:vitec/Configuration/FlexForms/Productshow.xml', 'vitec-plugin-productshow');
$registerPluginWithFlexForm('Usecaseshow', 'Single Success Story', 'FILE:EXT:vitec/Configuration/FlexForms/Usecase.xml', 'vitec-plugin-usecaseshow');
$registerPluginWithFlexForm('Marketshow', 'Show Single Market', 'FILE:EXT:vitec/Configuration/FlexForms/Market.xml', 'vitec-plugin-marketshow');
$registerPluginWithFlexForm('Solutionshow', 'Show Single Solution', 'FILE:EXT:vitec/Configuration/FlexForms/Solution.xml', 'vitec-plugin-solutionshow');
$registerPluginWithFlexForm('Usecaselist', 'Shows List of all Success Stories', 'FILE:EXT:vitec/Configuration/FlexForms/Usecaselist.xml', 'vitec-plugin-usecaselist');
$registerPluginWithFlexForm('Downloadcard', 'Download Card', 'FILE:EXT:vitec/Configuration/FlexForms/Downloadcard.xml', 'vitec-plugin-downloadcard');
$registerPluginWithFlexForm('Downloadcardcollection', 'Download Card Collection', 'FILE:EXT:vitec/Configuration/FlexForms/Downloadcardcollection.xml', 'vitec-plugin-downloadcardcollection');
$registerPluginWithFlexForm('Datasheets', 'Datasheets', 'FILE:EXT:vitec/Configuration/FlexForms/Datasheets.xml');
$registerPluginWithFlexForm('Eventlist', 'Event List', 'FILE:EXT:vitec/Configuration/FlexForms/Eventlist.xml', 'vitec-plugin-eventlist');
// Locations: lives in the "VITEC" wizard group, titled "VITEC Locations".
$GLOBALS['TCA']['tt_content']['columns']['CType']['config']['itemGroups']['vitec']
= 'LLL:EXT:vitec/Resources/Private/Language/locallang_containers.xlf:group.header';
// VITEC Forms — own wizard tab for the three form plugins.
$GLOBALS['TCA']['tt_content']['columns']['CType']['config']['itemGroups']['vitec_forms']
= 'VITEC Forms';
ExtensionUtility::registerPlugin(
'Vitec',
'Contactform',
'VITEC Contact Form',
'vitec-plugin-contactform',
'vitec_forms',
'Contact form — email or Salesforce delivery.',
'FILE:EXT:vitec/Configuration/FlexForms/Contactform.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Demoform',
'VITEC Request Demo',
'vitec-plugin-demoform',
'vitec_forms',
'Demo request form — email or Salesforce delivery.',
'FILE:EXT:vitec/Configuration/FlexForms/Demoform.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Helpdeskform',
'VITEC HelpDesk Access',
'vitec-plugin-helpdeskform',
'vitec_forms',
'Request access to the online helpdesk — email or Salesforce delivery.',
'FILE:EXT:vitec/Configuration/FlexForms/Helpdeskform.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Modelcard',
'VITEC Card',
'vitec-plugin-modelcard',
'vitec',
'One card for a Product, Success Story, Market or Solution — all data from the model.',
'FILE:EXT:vitec/Configuration/FlexForms/Modelcard.xml'
);
// Collapsible sub-market section previews (2026-10-02)
$GLOBALS['TCA']['tt_content']['types']['vitec_usecaselist']['previewRenderer']
= \Evomedien\Vitec\Preview\SubmarketPluginPreview::class;
$GLOBALS['TCA']['tt_content']['types']['vitec_productfeature']['previewRenderer']
= \Evomedien\Vitec\Preview\SubmarketPluginPreview::class;
$GLOBALS['TCA']['tt_content']['types']['vitec_productlist']['previewRenderer']
= \Evomedien\Vitec\Preview\SubmarketPluginPreview::class;
$GLOBALS['TCA']['tt_content']['types']['vitec_modelcard']['previewRenderer']
= \Evomedien\Vitec\Preview\ModelcardPreviewRenderer::class;
ExtensionUtility::registerPlugin(
'Vitec',
'Customerlogos',
'VITEC Customer Logos',
'vitec-plugin-customerlogos',
'vitec',
'Customer logos in list, grid, carousel or marquee layout.',
'FILE:EXT:vitec/Configuration/FlexForms/Customerlogos.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Locationlist',
'VITEC Locations',
'vitec-plugin-locationlist',
'vitec',
'Locations as grid, list or interactive map.',
'FILE:EXT:vitec/Configuration/FlexForms/Locationlist.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Marketlist',
'VITEC Market List',
'vitec-plugin-marketlist',
'vitec',
'All markets as grid, list or carousel.',
'FILE:EXT:vitec/Configuration/FlexForms/Marketlist.xml'
);
ExtensionUtility::registerPlugin(
'Vitec',
'Solutionlist',
'VITEC Solution List',
'vitec-plugin-solutionlist',
'vitec',
'All solutions as grid, list or carousel.',
'FILE:EXT:vitec/Configuration/FlexForms/Solutionlist.xml'
);
// Change frame_class to allow multiple selections.
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['renderType'] = 'selectCheckBox';
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['maxitems'] = 999;
// Add custom frame_class options for Vitec.
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'] = array_merge(
$GLOBALS['TCA']['tt_content']['columns']['frame_class']['config']['items'],
[
['label' => 'Vitec: Full Width', 'value' => 'vitec-full-width'],
['label' => 'Vitec: Centered Container', 'value' => 'vitec-centered'],
['label' => 'Vitec: Card Style', 'value' => 'vitec-card'],
['label' => 'Vitec: Dark Background', 'value' => 'vitec-dark'],
['label' => 'Vitec: Highlight Box', 'value' => 'vitec-highlight'],
// Background variants, ordered light to dark. Mutually exclusive by
// intent, but frame_class is a multi-select here (selectCheckBox,
// see above) and stays that way on purpose -- a restriction would
// have to be built for every other class too. If an editor ticks two
// backgrounds, both end up comma-separated in the JSON `frameClass`
// and the frontend decides which one wins.
//
// `vitec-bg-graphite`, `-blue` and `-midnight` were already styled in
// the frontend bundle but had no backend option; `-white` and
// `-light-grey` still need their CSS.
//
// The older 'Vitec: Dark Background' (vitec-dark) above stays as it
// is: existing content depends on it, and it is the most heavily
// styled class of them all.
['label' => 'Vitec: White Background', 'value' => 'vitec-bg-white'],
['label' => 'Vitec: Light-Grey Background', 'value' => 'vitec-bg-light-grey'],
['label' => 'Vitec: VITEC-Graphite Background', 'value' => 'vitec-bg-graphite'],
['label' => 'Vitec: VITEC-Blue Background', 'value' => 'vitec-bg-blue'],
['label' => 'Vitec: VITEC-Midnight Background', 'value' => 'vitec-bg-midnight'],
]
);
// Inline parent pointer: content elements owned by a Success Story record.
$GLOBALS['TCA']['tt_content']['columns']['tx_vitec_usecase_content'] = [
'config' => ['type' => 'passthrough'],
];
// The standard TYPO3 "Appearance" tab for the Content Blocks elements.
//
// Content Blocks builds each element's showitem itself
// (TcaGenerator::getContentElementStandardShowItem) and appends only the
// "Extended" tab -- Appearance is never part of it. The Extbase plugins are
// unaffected: ExtensionManagementUtility::addPlugin() seeds their type from
// tt_content types['header'], which carries the tab already.
//
// Until now that meant an editor could not reach layout, frame_class
// (including the Vitec classes registered above), spaceBefore/spaceAfter,
// sectionIndex or linkToTop on a Content Block at all.
//
// Deliberately NOT declared as a palette in every config.yaml: palettes live
// in TCA under a global identifier, so a second definition of `frames` would
// collide with the Core one -- the same trap that took the page module down
// on 2026-08-10. Pointing at the Core palettes keeps a single definition.
//
// This file runs after Content Blocks, which generates its TCA on
// BeforeTcaOverridesEvent.
$appearanceTab = '--div--;core.form.tabs:appearance,'
. '--palette--;;frames,'
. '--palette--;;appearanceLinks';
$extendedTab = '--div--;core.form.tabs:extended';
foreach ($GLOBALS['TCA']['tt_content']['types'] as $cType => &$typeConfig) {
// Own elements only, and only those still missing the tab -- that skips
// the plugins and makes a repeated run a no-op.
if (!str_starts_with((string)$cType, 'vitec_')
|| !isset($typeConfig['showitem'])
|| str_contains($typeConfig['showitem'], '--palette--;;frames')
) {
continue;
}
// Content Blocks puts "Extended" last, so Appearance goes in front of it
// and the tab order matches the Core content elements. Should a future
// version drop that marker, append rather than silently do nothing.
if (str_contains($typeConfig['showitem'], $extendedTab)) {
$typeConfig['showitem'] = str_replace(
$extendedTab,
$appearanceTab . ',' . $extendedTab,
$typeConfig['showitem']
);
} else {
$typeConfig['showitem'] = rtrim($typeConfig['showitem'], ", \t\n\r") . ',' . $appearanceTab;
}
}
unset($typeConfig);
// Shared Content-Block select columns (fix 2026-09-25).
//
// `layout_variant` and `background_variant` are defined by BOTH vitec/card
// and vitec/cta-banner - same identifier under the vendor prefix, so both
// blocks write the same tt_content columns. Content Blocks merges such
// diverging item lists INDEX-wise into the base column and generates no
// per-type items override: the card-only values `vertical`, `horizontal`,
// `horizontal_reverse` and background `none` did not exist as items at
// all, and FormEngine showed "[ Missing label ]" for exactly those stored
// values (hero-section / intro-paragraph dodged the trap long ago with
// `hero_` / `intro_` prefixes).
//
// Fixed here WITHOUT renaming columns (existing data and the JSON keys
// `layout_variant` / `background_variant` stay): the base column carries
// the short cta-banner lists, vitec_card gets its full lists through
// columnsOverrides. FormEngine and the DataHandler both respect
// columnsOverrides; the per-type merge is array_replace_recursive, so an
// items override can only EXTEND the base list, never shorten it - which
// holds (9/7 items over 3/5). New blocks must not reuse these identifiers
// with diverging items; prefix them instead.
$GLOBALS['TCA']['tt_content']['columns']['vitec_layout_variant']['config']['items'] = [
['label' => 'Centered (text over background)', 'value' => 'centered'],
['label' => 'Split (text left, CTAs right)', 'value' => 'split'],
['label' => 'Stacked (text top, CTAs below)', 'value' => 'stacked'],
];
$GLOBALS['TCA']['tt_content']['columns']['vitec_background_variant']['config']['items'] = [
['label' => 'Orange', 'value' => 'orange'],
['label' => 'Blue', 'value' => 'blue'],
['label' => 'Graphite', 'value' => 'graphite'],
['label' => 'Midnight', 'value' => 'midnight'],
['label' => 'Light (neutral)', 'value' => 'light'],
];
$GLOBALS['TCA']['tt_content']['types']['vitec_card']['columnsOverrides']['vitec_layout_variant']['config']['items'] = [
['label' => 'Vertical (image top, content below)', 'value' => 'vertical'],
['label' => 'Horizontal (image left, content right)', 'value' => 'horizontal'],
['label' => 'Horizontal Reverse (content left, image right)', 'value' => 'horizontal_reverse'],
['label' => 'Image Overlay (text over image)', 'value' => 'image_overlay'],
['label' => 'Text Only (no image)', 'value' => 'text_only'],
['label' => 'Icon Card (icon focused, text below)', 'value' => 'icon_card'],
['label' => 'Feature (large highlighted card)', 'value' => 'feature'],
['label' => 'Compact (small card, tight spacing)', 'value' => 'compact'],
['label' => 'Flip Card (front / back)', 'value' => 'flipcard'],
];
$GLOBALS['TCA']['tt_content']['types']['vitec_card']['columnsOverrides']['vitec_background_variant']['config']['items'] = [
['label' => 'None', 'value' => 'none'],
['label' => 'Orange', 'value' => 'orange'],
['label' => 'Blue', 'value' => 'blue'],
['label' => 'Graphite', 'value' => 'graphite'],
['label' => 'Midnight', 'value' => 'midnight'],
['label' => 'Light (neutral)', 'value' => 'light'],
['label' => 'Dark (neutral)', 'value' => 'dark'],
];
})();