From 55f7f82a4269d40e4882c39a82416a7e798efe4d Mon Sep 17 00:00:00 2001 From: Oliver Rasche Date: Fri, 11 Sep 2026 15:28:32 +0200 Subject: [PATCH] Show success stories below the product lists, linked on the category sys_category gains a story selection and a layout (Options tab, alongside the images field EXT:news puts there), so one editorial decision serves every product list that renders the category. ProductListJsonRenderer emits them beside the products in the 7.3 list envelope; each card carries forCategories - the product categories it came from - so the front end hides it with the same ?cat=/?subcat= filter it applies to the products. Stories are collected over the category and its descendants and deduplicated. Story links follow the existing rule (detail page's parent path plus slug, which SuccessStoryPathRewrite maps back), taking the page from the element's FlexForm or the new site setting vitec.storyDetailPid. Spec raised to 1.17 with clause 7.15.2.1; the header had been left at 1.12 although entries up to 1.15 already existed, so today's two entries were renumbered to 1.16 and 1.17. --- composer.lock | 608 +++++++++--------- config/sites/vitec/settings.yaml | 1 + .../UserFunc/ProductListJsonRenderer.php | 143 ++++ .../Configuration/FlexForms/Productlist.xml | 10 + .../Sets/Vitecset/settings.definitions.yaml | 7 + .../TCA/Overrides/sys_category.php | 52 ++ .../Headless-JSON-Architecture.md | 50 +- .../Private/Language/locallang_db.xlf | 12 + packages/vitec/ext_tables.sql | 9 + public/_frontend/.htaccess | 54 +- public/_frontend/index.html | 4 +- 11 files changed, 632 insertions(+), 318 deletions(-) diff --git a/composer.lock b/composer.lock index 96a09ff..0caf40c 100644 --- a/composer.lock +++ b/composer.lock @@ -1564,16 +1564,16 @@ }, { "name": "friendsoftypo3/content-blocks", - "version": "2.4.8", + "version": "2.4.9", "source": { "type": "git", "url": "https://github.com/FriendsOfTYPO3/content-blocks.git", - "reference": "47ffa2db718907eff883728e7fcaf99511dcb74d" + "reference": "c4eef30dcdb1a5676f259174da1186caef116f67" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfTYPO3/content-blocks/zipball/47ffa2db718907eff883728e7fcaf99511dcb74d", - "reference": "47ffa2db718907eff883728e7fcaf99511dcb74d", + "url": "https://api.github.com/repos/FriendsOfTYPO3/content-blocks/zipball/c4eef30dcdb1a5676f259174da1186caef116f67", + "reference": "c4eef30dcdb1a5676f259174da1186caef116f67", "shasum": "" }, "require": { @@ -1585,10 +1585,10 @@ "symfony/finder": "^7.4", "symfony/var-exporter": "^7.4 || ^8.0", "symfony/yaml": "^7.4", - "typo3/cms-backend": "^14.3.5", - "typo3/cms-core": "^14.3.5", - "typo3/cms-fluid": "^14.3.5", - "typo3/cms-frontend": "^14.3.5" + "typo3/cms-backend": "^14.3.7", + "typo3/cms-core": "^14.3.7", + "typo3/cms-fluid": "^14.3.7", + "typo3/cms-frontend": "^14.3.7" }, "replace": { "contentblocks/content-blocks": "*" @@ -1599,15 +1599,15 @@ "friendsofphp/php-cs-fixer": "^3.22", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", - "typo3/cms-extbase": "^14.3.5", - "typo3/cms-extensionmanager": "^14.3.5", - "typo3/cms-filelist": "^14.3.5", - "typo3/cms-install": "^14.3.5", - "typo3/cms-lowlevel": "^14.3.5", - "typo3/cms-rte-ckeditor": "^14.3.5", - "typo3/cms-tstemplate": "^14.3.5", - "typo3/cms-workspaces": "^14.3.5", - "typo3/coding-standards": "0.8.x-dev", + "typo3/cms-extbase": "^14.3.7", + "typo3/cms-extensionmanager": "^14.3.7", + "typo3/cms-filelist": "^14.3.7", + "typo3/cms-install": "^14.3.7", + "typo3/cms-lowlevel": "^14.3.7", + "typo3/cms-rte-ckeditor": "^14.3.7", + "typo3/cms-tstemplate": "^14.3.7", + "typo3/cms-workspaces": "^14.3.7", + "typo3/coding-standards": "^0.9", "typo3/testing-framework": "^9.5" }, "type": "typo3-cms-extension", @@ -1620,7 +1620,7 @@ "symfony/var-exporter": "" } }, - "version": "2.4.8", + "version": "2.4.9", "web-dir": ".Build/public", "extension-key": "content_blocks" } @@ -1649,7 +1649,7 @@ "issues": "https://github.com/FriendsOfTYPO3/content-blocks/issues", "source": "https://github.com/FriendsOfTYPO3/content-blocks" }, - "time": "2026-07-22T14:34:41+00:00" + "time": "2026-09-09T14:14:58+00:00" }, { "name": "friendsoftypo3/headless", @@ -1854,26 +1854,27 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.15.5", + "version": "8.2.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a" + "reference": "93939470950a9b11e2e84204166ef5e048c55fe4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee80339fd9177ba44c49cdb653ff02a4d1106b9a", - "reference": "ee80339fd9177ba44c49cdb653ff02a4d1106b9a", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/93939470950a9b11e2e84204166ef5e048c55fe4", + "reference": "93939470950a9b11e2e84204166ef5e048c55fe4", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.5.3", - "guzzlehttp/psr7": "^2.13.1", - "php": "^7.2.5 || ^8.0", + "guzzlehttp/promises": "^3.0.2", + "guzzlehttp/psr7": "^3.1", + "php": "^7.4 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.25" + "psr/http-factory": "^1.0", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php82": "^1.27" }, "provide": { "psr/http-client-implementation": "1.0" @@ -1881,10 +1882,10 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "guzzle/client-integration-tests": "3.0.3", - "guzzlehttp/test-server": "^0.7", + "guzzle/client-integration-tests": "4.0.1", + "guzzlehttp/test-server": "^1.0.1", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.52 || ^9.6.34", + "phpunit/phpunit": "^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1900,9 +1901,6 @@ } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" } @@ -1962,7 +1960,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.15.5" + "source": "https://github.com/guzzle/guzzle/tree/8.2.0" }, "funding": [ { @@ -1978,29 +1976,28 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:21:06+00:00" + "time": "2026-09-06T13:55:09+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.5.3", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1" + "reference": "42118e66a53c492effaf92bc357e931985d5c6f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/cde49999552d185d64715fe9c1f77a2aadd2f9f1", - "reference": "cde49999552d185d64715fe9c1f77a2aadd2f9f1", + "url": "https://api.github.com/repos/guzzle/promises/zipball/42118e66a53c492effaf92bc357e931985d5c6f9", + "reference": "42118e66a53c492effaf92bc357e931985d5c6f9", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.52 || ^9.6.34" + "phpunit/phpunit": "^9.6.34" }, "type": "library", "extra": { @@ -2046,7 +2043,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.5.3" + "source": "https://github.com/guzzle/promises/tree/3.0.2" }, "funding": [ { @@ -2062,39 +2059,39 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:11:28+00:00" + "time": "2026-08-24T10:00:26+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.13.1", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "95e7828100de18b4e269fb1703be530082d5166d" + "reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/95e7828100de18b4e269fb1703be530082d5166d", - "reference": "95e7828100de18b4e269fb1703be530082d5166d", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a3059ba1a84c9139c4ae03cf0f45bea276c97c74", + "reference": "a3059ba1a84c9139c4ae03cf0f45bea276c97c74", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0", - "psr/http-factory": "^1.0", - "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0", - "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.25" + "php": "^7.4 || ^8.0", + "psr/http-factory": "^1.1", + "psr/http-message": "^2.0", + "symfony/polyfill-php80": "^1.25", + "symfony/polyfill-php82": "^1.27" }, "provide": { - "psr/http-factory-implementation": "1.0", - "psr/http-message-implementation": "1.0" + "psr/http-factory-implementation": "1.1", + "psr/http-message-implementation": "2.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "http-interop/http-factory-tests": "1.1.0", "jshttp/mime-db": "1.54.0.1", - "phpunit/phpunit": "^8.5.52 || ^9.6.34" + "php-http/psr7-integration-tests": "^1.5.1", + "phpunit/phpunit": "^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -2165,7 +2162,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.13.1" + "source": "https://github.com/guzzle/psr7/tree/3.1.0" }, "funding": [ { @@ -2181,7 +2178,7 @@ "type": "tidelift" } ], - "time": "2026-08-24T09:13:11+00:00" + "time": "2026-08-24T11:02:13+00:00" }, { "name": "halaxa/json-machine", @@ -2247,16 +2244,16 @@ }, { "name": "justinrainbow/json-schema", - "version": "6.11.0", + "version": "6.12.0", "source": { "type": "git", "url": "https://github.com/jsonrainbow/json-schema.git", - "reference": "7e420a943a6fbc95e60e3cf67acfbee85b3b4da7" + "reference": "8ef236a1a37df364b518b7411a4b5bb95b040d79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/7e420a943a6fbc95e60e3cf67acfbee85b3b4da7", - "reference": "7e420a943a6fbc95e60e3cf67acfbee85b3b4da7", + "url": "https://api.github.com/repos/jsonrainbow/json-schema/zipball/8ef236a1a37df364b518b7411a4b5bb95b040d79", + "reference": "8ef236a1a37df364b518b7411a4b5bb95b040d79", "shasum": "" }, "require": { @@ -2306,9 +2303,9 @@ ], "support": { "issues": "https://github.com/jsonrainbow/json-schema/issues", - "source": "https://github.com/jsonrainbow/json-schema/tree/6.11.0" + "source": "https://github.com/jsonrainbow/json-schema/tree/6.12.0" }, - "time": "2026-08-21T10:30:42+00:00" + "time": "2026-09-04T12:54:20+00:00" }, { "name": "lolli42/finediff", @@ -3982,50 +3979,6 @@ }, "time": "2021-10-29T13:26:27+00:00" }, - { - "name": "ralouphie/getallheaders", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", - "shasum": "" - }, - "require": { - "php": ">=5.6" - }, - "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/getallheaders.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" - } - ], - "description": "A polyfill for getallheaders.", - "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" - }, - "time": "2019-03-08T08:55:37+00:00" - }, { "name": "react/promise", "version": "v3.3.0", @@ -6469,6 +6422,86 @@ ], "time": "2026-05-26T12:45:58+00:00" }, + { + "name": "symfony/polyfill-php82", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php82.git", + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", + "reference": "002dc0cfe5fd4ed6033d48f27d4f19a486c4b04b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php82\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.2+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php82/tree/v1.38.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-05-26T12:45:58+00:00" + }, { "name": "symfony/polyfill-php83", "version": "v1.41.0", @@ -8031,23 +8064,23 @@ }, { "name": "typo3/cms-backend", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/backend.git", - "reference": "a19fb339ad606185754b569f810d77edefb256d8" + "reference": "6106c22f6f9c93c16acf2e5052229e1e817b7fba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/backend/zipball/a19fb339ad606185754b569f810d77edefb256d8", - "reference": "a19fb339ad606185754b569f810d77edefb256d8", + "url": "https://api.github.com/repos/TYPO3-CMS/backend/zipball/6106c22f6f9c93c16acf2e5052229e1e817b7fba", + "reference": "6106c22f6f9c93c16acf2e5052229e1e817b7fba", "shasum": "" }, "require": { "ext-intl": "*", "ext-libxml": "*", "psr/event-dispatcher": "^1.0", - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8120,24 +8153,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-belog", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/belog.git", - "reference": "668ccbbea2bb4bc51c66e9ec54b325538ecf96bd" + "reference": "f783869a2814e4e538152ba3ffc560b667684c4e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/belog/zipball/668ccbbea2bb4bc51c66e9ec54b325538ecf96bd", - "reference": "668ccbbea2bb4bc51c66e9ec54b325538ecf96bd", + "url": "https://api.github.com/repos/TYPO3-CMS/belog/zipball/f783869a2814e4e538152ba3ffc560b667684c4e", + "reference": "f783869a2814e4e538152ba3ffc560b667684c4e", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8187,24 +8220,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-beuser", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/beuser.git", - "reference": "43afb0acfe144d5b322ae504bb307f56b3fbf072" + "reference": "6673e35faf4ebb89a9b5a42b1ac7c6ac58573cb2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/beuser/zipball/43afb0acfe144d5b322ae504bb307f56b3fbf072", - "reference": "43afb0acfe144d5b322ae504bb307f56b3fbf072", + "url": "https://api.github.com/repos/TYPO3-CMS/beuser/zipball/6673e35faf4ebb89a9b5a42b1ac7c6ac58573cb2", + "reference": "6673e35faf4ebb89a9b5a42b1ac7c6ac58573cb2", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8254,7 +8287,7 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-cli", @@ -8363,16 +8396,16 @@ }, { "name": "typo3/cms-core", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/core.git", - "reference": "3615717fdcf4e069ac2da346a533d97a86a3d2a9" + "reference": "18071e452396230c34e23554298591ada6dc4c4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/core/zipball/3615717fdcf4e069ac2da346a533d97a86a3d2a9", - "reference": "3615717fdcf4e069ac2da346a533d97a86a3d2a9", + "url": "https://api.github.com/repos/TYPO3-CMS/core/zipball/18071e452396230c34e23554298591ada6dc4c4a", + "reference": "18071e452396230c34e23554298591ada6dc4c4a", "shasum": "" }, "require": { @@ -8381,7 +8414,7 @@ "composer-runtime-api": "^2.1", "composer/class-map-generator": "^1.7.2", "composer/semver": "^3.4", - "doctrine/dbal": "~4.4.3", + "doctrine/dbal": "~4.4.4", "doctrine/lexer": "^3.0.1", "egulias/email-validator": "^4.0", "enshrined/svg-sanitize": "~0.22", @@ -8397,8 +8430,9 @@ "ext-tokenizer": "*", "ext-xml": "*", "firebase/php-jwt": "^6.10.2 || ^7.0.5", - "guzzlehttp/guzzle": "^7.15.2", - "guzzlehttp/psr7": "^2.10.2", + "guzzlehttp/guzzle": "^7.15.2 || ^8.0", + "guzzlehttp/promises": "^2.0.3 || ^3.0", + "guzzlehttp/psr7": "^2.10.2 || ^3.0", "lolli42/finediff": "^1.1.2", "masterminds/html5": "^2.10.0", "php": "^8.2", @@ -8437,7 +8471,7 @@ "typo3/cms-cli": "^3.1.3", "typo3/cms-composer-installers": "^5.0.2", "typo3/html-sanitizer": "^2.3.2", - "typo3fluid/fluid": "^5.3.1" + "typo3fluid/fluid": "^5.3.2" }, "conflict": { "hoa/core": "*", @@ -8521,28 +8555,28 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-dashboard", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/dashboard.git", - "reference": "9a26e3a9ef375d1107bedead7bd6531625fd799f" + "reference": "e8fdeb94880a256f86db7a53e12ff0553aec559b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/dashboard/zipball/9a26e3a9ef375d1107bedead7bd6531625fd799f", - "reference": "9a26e3a9ef375d1107bedead7bd6531625fd799f", + "url": "https://api.github.com/repos/TYPO3-CMS/dashboard/zipball/e8fdeb94880a256f86db7a53e12ff0553aec559b", + "reference": "e8fdeb94880a256f86db7a53e12ff0553aec559b", "shasum": "" }, "require": { - "typo3/cms-backend": "14.3.6", - "typo3/cms-core": "14.3.6", - "typo3/cms-extbase": "14.3.6", - "typo3/cms-fluid": "14.3.6", - "typo3/cms-frontend": "14.3.6" + "typo3/cms-backend": "14.3.7", + "typo3/cms-core": "14.3.7", + "typo3/cms-extbase": "14.3.7", + "typo3/cms-fluid": "14.3.7", + "typo3/cms-frontend": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8593,20 +8627,20 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-extbase", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/extbase.git", - "reference": "866a40995d9d30687268dd73f16bd56eb1c30548" + "reference": "68375ff116d0943dd56245949ddc3da7fb7bae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/extbase/zipball/866a40995d9d30687268dd73f16bd56eb1c30548", - "reference": "866a40995d9d30687268dd73f16bd56eb1c30548", + "url": "https://api.github.com/repos/TYPO3-CMS/extbase/zipball/68375ff116d0943dd56245949ddc3da7fb7bae9c", + "reference": "68375ff116d0943dd56245949ddc3da7fb7bae9c", "shasum": "" }, "require": { @@ -8618,7 +8652,7 @@ "symfony/property-access": "^7.4.8", "symfony/property-info": "^7.4.15", "symfony/validator": "^7.4.8", - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8679,25 +8713,25 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-extensionmanager", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/extensionmanager.git", - "reference": "c946412b584e55c33a6120b108f133a982b4618f" + "reference": "eb39b8b0e95747a5a4e70c48e3f655a38be5490b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/extensionmanager/zipball/c946412b584e55c33a6120b108f133a982b4618f", - "reference": "c946412b584e55c33a6120b108f133a982b4618f", + "url": "https://api.github.com/repos/TYPO3-CMS/extensionmanager/zipball/eb39b8b0e95747a5a4e70c48e3f655a38be5490b", + "reference": "eb39b8b0e95747a5a4e70c48e3f655a38be5490b", "shasum": "" }, "require": { "ext-libxml": "*", - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8749,24 +8783,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-felogin", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/felogin.git", - "reference": "ed0e21f43f04151276da0c61a715ea68978a8665" + "reference": "4d8d043c0d08394aab375eb5dc665062d9f251b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/felogin/zipball/ed0e21f43f04151276da0c61a715ea68978a8665", - "reference": "ed0e21f43f04151276da0c61a715ea68978a8665", + "url": "https://api.github.com/repos/TYPO3-CMS/felogin/zipball/4d8d043c0d08394aab375eb5dc665062d9f251b5", + "reference": "4d8d043c0d08394aab375eb5dc665062d9f251b5", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8816,24 +8850,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-filelist", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/filelist.git", - "reference": "0b4a61275e3a7b377a047abaec10bd09ecd28709" + "reference": "7b7acdf948aaf79844d45905f4b59d93a2f13b07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/filelist/zipball/0b4a61275e3a7b377a047abaec10bd09ecd28709", - "reference": "0b4a61275e3a7b377a047abaec10bd09ecd28709", + "url": "https://api.github.com/repos/TYPO3-CMS/filelist/zipball/7b7acdf948aaf79844d45905f4b59d93a2f13b07", + "reference": "7b7acdf948aaf79844d45905f4b59d93a2f13b07", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -8885,28 +8919,28 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-fluid", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/fluid.git", - "reference": "34adb02bd7eb87417ef2804da19c649f343df8af" + "reference": "66831b8b7c35b535dd4df958777d084d70fa4d24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/fluid/zipball/34adb02bd7eb87417ef2804da19c649f343df8af", - "reference": "34adb02bd7eb87417ef2804da19c649f343df8af", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid/zipball/66831b8b7c35b535dd4df958777d084d70fa4d24", + "reference": "66831b8b7c35b535dd4df958777d084d70fa4d24", "shasum": "" }, "require": { "symfony/dependency-injection": "^7.4.8", "symfony/finder": "^7.4.8", - "typo3/cms-core": "14.3.6", - "typo3/cms-extbase": "14.3.6", - "typo3fluid/fluid": "^5.3.1" + "typo3/cms-core": "14.3.7", + "typo3/cms-extbase": "14.3.7", + "typo3fluid/fluid": "^5.3.2" }, "conflict": { "typo3/cms": "*" @@ -8959,26 +8993,26 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-fluid-styled-content", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/fluid_styled_content.git", - "reference": "44b71d3be1e32b40ae4b67ea370faeb5d0a3b53f" + "reference": "9a57ba7cdae1e60cfe3d7c8c535b6927b3d30fe7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/fluid_styled_content/zipball/44b71d3be1e32b40ae4b67ea370faeb5d0a3b53f", - "reference": "44b71d3be1e32b40ae4b67ea370faeb5d0a3b53f", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid_styled_content/zipball/9a57ba7cdae1e60cfe3d7c8c535b6927b3d30fe7", + "reference": "9a57ba7cdae1e60cfe3d7c8c535b6927b3d30fe7", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6", - "typo3/cms-fluid": "14.3.6", - "typo3/cms-frontend": "14.3.6" + "typo3/cms-core": "14.3.7", + "typo3/cms-fluid": "14.3.7", + "typo3/cms-frontend": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9028,27 +9062,27 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-form", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/form.git", - "reference": "b465f891ab33c715c9ad2dd45675660dd17672a7" + "reference": "d3e48ac5a023c6e79f072af7c71ebac0be6dbda1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/form/zipball/b465f891ab33c715c9ad2dd45675660dd17672a7", - "reference": "b465f891ab33c715c9ad2dd45675660dd17672a7", + "url": "https://api.github.com/repos/TYPO3-CMS/form/zipball/d3e48ac5a023c6e79f072af7c71ebac0be6dbda1", + "reference": "d3e48ac5a023c6e79f072af7c71ebac0be6dbda1", "shasum": "" }, "require": { "psr/http-message": "^1.1 || ^2.0", "symfony/expression-language": "^7.4.8", - "typo3/cms-core": "14.3.6", - "typo3/cms-frontend": "14.3.6" + "typo3/cms-core": "14.3.7", + "typo3/cms-frontend": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9103,25 +9137,25 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-frontend", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/frontend.git", - "reference": "5794317270a8b7560755b226127f745dc33a8753" + "reference": "3fc8a4d3b7d968b426c7536102b868ce23410eb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/frontend/zipball/5794317270a8b7560755b226127f745dc33a8753", - "reference": "5794317270a8b7560755b226127f745dc33a8753", + "url": "https://api.github.com/repos/TYPO3-CMS/frontend/zipball/3fc8a4d3b7d968b426c7536102b868ce23410eb7", + "reference": "3fc8a4d3b7d968b426c7536102b868ce23410eb7", "shasum": "" }, "require": { "ext-libxml": "*", - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9181,24 +9215,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-impexp", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/impexp.git", - "reference": "c5eaabd9fd6456f26d8420699b8284cc9f9433fb" + "reference": "ccbf36a2bbc3a1d1c62fd5c0bf213e27e3ff26fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/impexp/zipball/c5eaabd9fd6456f26d8420699b8284cc9f9433fb", - "reference": "c5eaabd9fd6456f26d8420699b8284cc9f9433fb", + "url": "https://api.github.com/repos/TYPO3-CMS/impexp/zipball/ccbf36a2bbc3a1d1c62fd5c0bf213e27e3ff26fa", + "reference": "ccbf36a2bbc3a1d1c62fd5c0bf213e27e3ff26fa", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9248,24 +9282,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-info", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/info.git", - "reference": "bfc112bc518167ef88da7e7b453cd545e7c901a5" + "reference": "9c1e82d8cc5f230e970877e8ab321a3fc123be71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/info/zipball/bfc112bc518167ef88da7e7b453cd545e7c901a5", - "reference": "bfc112bc518167ef88da7e7b453cd545e7c901a5", + "url": "https://api.github.com/repos/TYPO3-CMS/info/zipball/9c1e82d8cc5f230e970877e8ab321a3fc123be71", + "reference": "9c1e82d8cc5f230e970877e8ab321a3fc123be71", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9318,31 +9352,31 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-install", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/install.git", - "reference": "445d83327c8e4de51e80da151d2e70714bc30930" + "reference": "5b36d108ce0048d2e20792a65476fe14d3aee484" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/install/zipball/445d83327c8e4de51e80da151d2e70714bc30930", - "reference": "445d83327c8e4de51e80da151d2e70714bc30930", + "url": "https://api.github.com/repos/TYPO3-CMS/install/zipball/5b36d108ce0048d2e20792a65476fe14d3aee484", + "reference": "5b36d108ce0048d2e20792a65476fe14d3aee484", "shasum": "" }, "require": { - "doctrine/dbal": "~4.4.3", - "guzzlehttp/promises": "^2.0.3", + "doctrine/dbal": "~4.4.4", + "guzzlehttp/promises": "^2.0.3 || ^3.0", "nikic/php-parser": "^5.4.0", "symfony/finder": "^7.4.8", "symfony/http-foundation": "^7.4.13", - "typo3/cms-core": "14.3.6", - "typo3/cms-extbase": "14.3.6", - "typo3/cms-fluid": "14.3.6" + "typo3/cms-core": "14.3.7", + "typo3/cms-extbase": "14.3.7", + "typo3/cms-fluid": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9395,24 +9429,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-reactions", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/reactions.git", - "reference": "48384bfd14f2c63b2f5010cb7904122caf93a5d1" + "reference": "4f19877a23bda5ce9ce3258e085612a8219f4be2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/reactions/zipball/48384bfd14f2c63b2f5010cb7904122caf93a5d1", - "reference": "48384bfd14f2c63b2f5010cb7904122caf93a5d1", + "url": "https://api.github.com/repos/TYPO3-CMS/reactions/zipball/4f19877a23bda5ce9ce3258e085612a8219f4be2", + "reference": "4f19877a23bda5ce9ce3258e085612a8219f4be2", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9462,24 +9496,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-recycler", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/recycler.git", - "reference": "72fa6a7c18b044d487726dd73c23544974e16393" + "reference": "4c10966afed492b3b71276771c243fa042df9966" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/recycler/zipball/72fa6a7c18b044d487726dd73c23544974e16393", - "reference": "72fa6a7c18b044d487726dd73c23544974e16393", + "url": "https://api.github.com/repos/TYPO3-CMS/recycler/zipball/4c10966afed492b3b71276771c243fa042df9966", + "reference": "4c10966afed492b3b71276771c243fa042df9966", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9532,24 +9566,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-reports", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/reports.git", - "reference": "4e28680d94dd21f149a045a847ccb4f42b452843" + "reference": "3080d816d2ecb641ed33ba2f85d8fe1c22398dcc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/reports/zipball/4e28680d94dd21f149a045a847ccb4f42b452843", - "reference": "4e28680d94dd21f149a045a847ccb4f42b452843", + "url": "https://api.github.com/repos/TYPO3-CMS/reports/zipball/3080d816d2ecb641ed33ba2f85d8fe1c22398dcc", + "reference": "3080d816d2ecb641ed33ba2f85d8fe1c22398dcc", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9602,24 +9636,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-rte-ckeditor", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/rte_ckeditor.git", - "reference": "01facd6eae020faa648d1bb335220fd5a1117120" + "reference": "b257f3319f05cfe041e57147311123ae04570f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/rte_ckeditor/zipball/01facd6eae020faa648d1bb335220fd5a1117120", - "reference": "01facd6eae020faa648d1bb335220fd5a1117120", + "url": "https://api.github.com/repos/TYPO3-CMS/rte_ckeditor/zipball/b257f3319f05cfe041e57147311123ae04570f46", + "reference": "b257f3319f05cfe041e57147311123ae04570f46", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9669,24 +9703,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-scheduler", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/scheduler.git", - "reference": "85a73bf9edf817856bdc4c3e84bfe72a1e73ea4e" + "reference": "4cee4b59a210f117a3b12d02c9747bb7af6fbd42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/85a73bf9edf817856bdc4c3e84bfe72a1e73ea4e", - "reference": "85a73bf9edf817856bdc4c3e84bfe72a1e73ea4e", + "url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/4cee4b59a210f117a3b12d02c9747bb7af6fbd42", + "reference": "4cee4b59a210f117a3b12d02c9747bb7af6fbd42", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9733,26 +9767,26 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-seo", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/seo.git", - "reference": "0e7a5b0ab289ea8032e05300bb8d6a6929f86b97" + "reference": "f094412c7c90851c330a1777903be1525eb34fd1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/seo/zipball/0e7a5b0ab289ea8032e05300bb8d6a6929f86b97", - "reference": "0e7a5b0ab289ea8032e05300bb8d6a6929f86b97", + "url": "https://api.github.com/repos/TYPO3-CMS/seo/zipball/f094412c7c90851c330a1777903be1525eb34fd1", + "reference": "f094412c7c90851c330a1777903be1525eb34fd1", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6", - "typo3/cms-extbase": "14.3.6", - "typo3/cms-frontend": "14.3.6" + "typo3/cms-core": "14.3.7", + "typo3/cms-extbase": "14.3.7", + "typo3/cms-frontend": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9805,24 +9839,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-sys-note", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/sys_note.git", - "reference": "5fb786d976ebbdff039a726860a5b660e152bd89" + "reference": "3e7f535da5536c0721794cfab40579ae1f0839cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/sys_note/zipball/5fb786d976ebbdff039a726860a5b660e152bd89", - "reference": "5fb786d976ebbdff039a726860a5b660e152bd89", + "url": "https://api.github.com/repos/TYPO3-CMS/sys_note/zipball/3e7f535da5536c0721794cfab40579ae1f0839cb", + "reference": "3e7f535da5536c0721794cfab40579ae1f0839cb", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9875,24 +9909,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-tstemplate", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/tstemplate.git", - "reference": "4b8e78335bc6792e71879b4e3bb52e56072e76d0" + "reference": "170a81430c146123ac88d868625f9344329960c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/tstemplate/zipball/4b8e78335bc6792e71879b4e3bb52e56072e76d0", - "reference": "4b8e78335bc6792e71879b4e3bb52e56072e76d0", + "url": "https://api.github.com/repos/TYPO3-CMS/tstemplate/zipball/170a81430c146123ac88d868625f9344329960c9", + "reference": "170a81430c146123ac88d868625f9344329960c9", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -9942,24 +9976,24 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-viewpage", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/viewpage.git", - "reference": "839c8442f56cbde023defe750a1f1c37c592c785" + "reference": "71d80ee87905e641b61cd2f8d9b043a4970532b2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/viewpage/zipball/839c8442f56cbde023defe750a1f1c37c592c785", - "reference": "839c8442f56cbde023defe750a1f1c37c592c785", + "url": "https://api.github.com/repos/TYPO3-CMS/viewpage/zipball/71d80ee87905e641b61cd2f8d9b043a4970532b2", + "reference": "71d80ee87905e641b61cd2f8d9b043a4970532b2", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -10009,25 +10043,25 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/cms-webhooks", - "version": "v14.3.6", + "version": "v14.3.7", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/webhooks.git", - "reference": "d7b094a4f5e6aed485ba981a62aaf2f9980c3383" + "reference": "9311ce04f27db1600d625762ac30ff11ed2bf279" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/webhooks/zipball/d7b094a4f5e6aed485ba981a62aaf2f9980c3383", - "reference": "d7b094a4f5e6aed485ba981a62aaf2f9980c3383", + "url": "https://api.github.com/repos/TYPO3-CMS/webhooks/zipball/9311ce04f27db1600d625762ac30ff11ed2bf279", + "reference": "9311ce04f27db1600d625762ac30ff11ed2bf279", "shasum": "" }, "require": { "symfony/uid": "^7.4.8", - "typo3/cms-core": "14.3.6" + "typo3/cms-core": "14.3.7" }, "conflict": { "typo3/cms": "*" @@ -10077,7 +10111,7 @@ "type": "membership" } ], - "time": "2026-08-11T07:21:33+00:00" + "time": "2026-09-08T09:17:25+00:00" }, { "name": "typo3/html-sanitizer", @@ -10266,10 +10300,10 @@ }, "prefer-stable": false, "prefer-lowest": false, - "platform": [], - "platform-dev": [], + "platform": {}, + "platform-dev": {}, "platform-overrides": { "php": "8.4.0" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.9.0" } diff --git a/config/sites/vitec/settings.yaml b/config/sites/vitec/settings.yaml index 74c2daa..cae08f2 100644 --- a/config/sites/vitec/settings.yaml +++ b/config/sites/vitec/settings.yaml @@ -3,3 +3,4 @@ my.example.setting: Test menu.footer.pageUids: '1,2,3' menu.meta.pageUids: '5' megamenu.contentUid: 10366 +vitec.storyDetailPid: 26 diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php index 1671ae0..ff1a0de 100755 --- a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php @@ -204,6 +204,14 @@ class ProductListJsonRenderer 'layout' => (string)($settings['layout'] ?? '0'), 'showToolbar' => (bool)($settings['showtoolbar'] ?? false), 'products' => $productsData, + // Success stories the editor attached to these categories + // (sys_category, Options tab). They ride along with the list + // so the front end can hide them with the same category + // filter it applies to the products. + 'successStories' => $this->successStoriesFor( + $allProducts ? [] : $categoryUids, + (int)($settings['storysinglepid'] ?? 0) + ), ]; if ($debugMode) { @@ -222,6 +230,141 @@ class ProductListJsonRenderer } } + /** + * Success stories attached to the given categories, in the order the + * categories were resolved and, inside one category, in the order the + * editor arranged them. A story linked from several of the categories + * appears once and carries all of them in `forCategories`, which is what + * the front end filters on: the product finder writes ?cat= / ?subcat=, + * and a card stays visible while one of its categories is selected. + * + * Cards use the shared story shape (UsecaseSerializer), so the front end + * can render them with the same component as every other story list. + * `detailUrl` follows the same rule as the story list: the story's own + * detail page wins, otherwise the Single PID configured on this element + * plus the slug - and null when neither is available. + * + * Returned in the list envelope of 7.3 - `layout` plus the array - so the + * front end treats these cards like every other story list. The layout is + * an editorial setting on the category, next to the selection itself. + * + * @param int[] $categoryUids + * @return array{layout:string,stories:array>} + */ + private function successStoriesFor(array $categoryUids, int $singlePid): array + { + if ($categoryUids === []) { + return ['layout' => 'grid', 'stories' => []]; + } + + $queryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('sys_category'); + $rows = $queryBuilder->select('uid', 'tx_vitec_success_stories', 'tx_vitec_success_stories_layout')->from('sys_category') + ->where( + $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($categoryUids, Connection::PARAM_INT_ARRAY)), + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('hidden', 0) + )->executeQuery()->fetchAllAssociative(); + + $listByCategory = []; + $layoutByCategory = []; + foreach ($rows as $row) { + $listByCategory[(int)$row['uid']] = (string)($row['tx_vitec_success_stories'] ?? ''); + $layoutByCategory[(int)$row['uid']] = (string)($row['tx_vitec_success_stories_layout'] ?? '') ?: 'grid'; + } + + $order = []; + $forCategories = []; + $layout = 'grid'; + foreach ($categoryUids as $categoryUid) { + foreach (explode(',', $listByCategory[(int)$categoryUid] ?? '') as $raw) { + $storyUid = (int)trim($raw); + if ($storyUid <= 0) { + continue; + } + if ($order === []) { + // The first category that actually contributes stories sets + // the layout - that is the top-most one the element is + // configured with, which is where an editor expects the + // setting to live. + $layout = $layoutByCategory[(int)$categoryUid] ?? 'grid'; + } + if (!in_array($storyUid, $order, true)) { + $order[] = $storyUid; + } + $forCategories[$storyUid][] = (int)$categoryUid; + } + } + if ($order === []) { + return ['layout' => 'grid', 'stories' => []]; + } + + $storyQueryBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\ConnectionPool::class) + ->getQueryBuilderForTable('tx_vitec_domain_model_usecase'); + $storyRows = $storyQueryBuilder->select('*')->from('tx_vitec_domain_model_usecase') + ->where( + $storyQueryBuilder->expr()->in('uid', $storyQueryBuilder->createNamedParameter($order, Connection::PARAM_INT_ARRAY)), + $storyQueryBuilder->expr()->eq('deleted', 0), + $storyQueryBuilder->expr()->eq('hidden', 0) + )->executeQuery()->fetchAllAssociative(); + + $byUid = []; + foreach ($storyRows as $storyRow) { + $byUid[(int)$storyRow['uid']] = $storyRow; + } + + // Same base as the story list: the detail page's PARENT path, because + // SuccessStoryPathRewrite maps /success-stories/ onto the detail + // subpage at request time. Falls back to the site setting + // `vitec.storyDetailPid`, so the editor does not have to configure the + // page on every single product list. + if ($singlePid <= 0) { + $singlePid = $this->storyDetailPidFromSite(); + } + $detailBase = ''; + if ($singlePid > 0) { + $detailPath = rtrim(\Evomedien\Vitec\Service\LinkResolver::pageUrl($singlePid) ?? '', '/'); + $parent = str_contains($detailPath, '/') ? substr($detailPath, 0, (int)strrpos($detailPath, '/')) : ''; + $detailBase = $parent !== '' ? $parent : $detailPath; + } + + $serializer = GeneralUtility::makeInstance(\Evomedien\Vitec\Service\UsecaseSerializer::class); + $stories = []; + foreach ($order as $storyUid) { + if (!isset($byUid[$storyUid])) { + continue; // hidden or deleted meanwhile - simply drops out + } + $item = $serializer->serializeListItem($byUid[$storyUid]); + if (($item['detailUrl'] ?? null) === null && $detailBase !== '' && ($item['slug'] ?? '') !== '') { + $item['detailUrl'] = $detailBase . '/' . ltrim((string)$item['slug'], '/'); + } + // NOT the story's own topic categories (those stay in `categories`) + // but the product categories it was attached to. + $item['forCategories'] = array_values(array_unique($forCategories[$storyUid])); + $stories[] = $item; + } + + return ['layout' => $layout, 'stories' => $stories]; + } + + /** + * Site-wide fallback for the success story detail page, 0 when unset. + * Read from the site settings rather than hard-coded, so the page can be + * moved without touching code. + */ + private function storyDetailPidFromSite(): int + { + try { + $site = ($GLOBALS['TYPO3_REQUEST'] ?? null)?->getAttribute('site'); + if ($site === null || !method_exists($site, 'getSettings')) { + return 0; + } + return (int)$site->getSettings()->get('vitec.storyDetailPid', 0); + } catch (\Throwable) { + return 0; + } + } + /** * The given category uids plus every descendant category uid, DEPTH first * over sys_category.parent with siblings in sys_category.sorting order - diff --git a/packages/vitec/Configuration/FlexForms/Productlist.xml b/packages/vitec/Configuration/FlexForms/Productlist.xml index edc5de2..af4a7c0 100644 --- a/packages/vitec/Configuration/FlexForms/Productlist.xml +++ b/packages/vitec/Configuration/FlexForms/Productlist.xml @@ -100,6 +100,16 @@ 0 + + + Needed only so the success-story cards below this list get a link. Pick the same story detail page the Success Stories list uses. Leave empty and the cards render without a link. + + group + pages + 1 + 1 + + diff --git a/packages/vitec/Configuration/Sets/Vitecset/settings.definitions.yaml b/packages/vitec/Configuration/Sets/Vitecset/settings.definitions.yaml index abdc1d5..beabfc6 100644 --- a/packages/vitec/Configuration/Sets/Vitecset/settings.definitions.yaml +++ b/packages/vitec/Configuration/Sets/Vitecset/settings.definitions.yaml @@ -29,6 +29,13 @@ settings: type: bool default: false + vitec.storyDetailPid: + label: 'Success story detail page' + description: 'The page that renders a single success story. Used to build story links where no page is configured on the element itself - the story URL is that page''s parent path plus the story slug, which is what SuccessStoryPathRewrite expects. 0 = no fallback.' + category: VITEC + type: int + default: 0 + menu.footer.pageUids: label: 'Footer menu — page UIDs' description: 'Comma-separated list of page UIDs to display in the footer menu (in order).' diff --git a/packages/vitec/Configuration/TCA/Overrides/sys_category.php b/packages/vitec/Configuration/TCA/Overrides/sys_category.php index 801a2ae..91f3c95 100644 --- a/packages/vitec/Configuration/TCA/Overrides/sys_category.php +++ b/packages/vitec/Configuration/TCA/Overrides/sys_category.php @@ -32,6 +32,47 @@ $customSysCategoryColumns = [ 'eval' => 'trim', ], ], + // Success stories shown below the product list of this category. Lives on + // the category rather than on the plugin so one editorial decision serves + // every list that renders the category (decision 2026-09-11). + 'tx_vitec_success_stories' => [ + 'exclude' => true, + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:sys_category.tx_vitec_success_stories', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:sys_category.tx_vitec_success_stories.description', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectMultipleSideBySide', + 'foreign_table' => 'tx_vitec_domain_model_usecase', + // No ORDER BY in foreign_table_where: TYPO3 appends its own + // conditions after this fragment, which turns an ORDER BY here + // into a syntax error. Sorting belongs in `sortItems`. + 'foreign_table_where' => 'AND {#tx_vitec_domain_model_usecase}.{#sys_language_uid} IN (0,-1)', + 'sortItems' => ['label' => 'asc'], + 'size' => 8, + 'minitems' => 0, + 'maxitems' => 20, + 'enableMultiSelectFilterTextfield' => true, + ], + ], + // Same vocabulary as the story and market lists (grid|list|carousel|50-50), + // so the front end renders these cards with the component it already has. + 'tx_vitec_success_stories_layout' => [ + 'exclude' => true, + 'displayCond' => 'FIELD:tx_vitec_success_stories:REQ:true', + 'label' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:sys_category.tx_vitec_success_stories_layout', + 'description' => 'LLL:EXT:vitec/Resources/Private/Language/locallang_db.xlf:sys_category.tx_vitec_success_stories_layout.description', + 'config' => [ + 'type' => 'select', + 'renderType' => 'selectSingle', + 'default' => 'grid', + 'items' => [ + ['label' => 'Grid', 'value' => 'grid'], + ['label' => 'List', 'value' => 'list'], + ['label' => 'Carousel', 'value' => 'carousel'], + ['label' => '50 / 50', 'value' => '50-50'], + ], + ], + ], ]; // Add the fields to the TCA @@ -43,4 +84,15 @@ $customSysCategoryColumns = [ 'class, filetype, type', '', 'after:description' +); + +// Success stories go into the existing "Options" tab. That tab comes from +// EXT:news (it holds `images`), so anchoring after that field puts our field +// INSIDE it - adding our own --div--;...options would produce a second tab of +// the same name. EXT:news loads before EXT:vitec, so the anchor exists. +\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes( + 'sys_category', + 'tx_vitec_success_stories, tx_vitec_success_stories_layout', + '', + 'after:images' ); \ No newline at end of file diff --git a/packages/vitec/Documentation/Headless-JSON-Architecture.md b/packages/vitec/Documentation/Headless-JSON-Architecture.md index 6c2e2ae..c85e2ef 100755 --- a/packages/vitec/Documentation/Headless-JSON-Architecture.md +++ b/packages/vitec/Documentation/Headless-JSON-Architecture.md @@ -3,7 +3,7 @@ | | | |---|---| | **Document identifier** | EVO‑VITEC‑HL‑001 | -| **Version** | 1.15 | +| **Version** | 1.17 | | **Status** | Released | | **Date** | 2026‑09‑11 | | **Applies to** | `evomedien/vitec` on TYPO3 v14.3 (headless) | @@ -26,9 +26,10 @@ | 1.10 | 2026‑08‑18 | **Interface change (additive).** `tx_vitec_domain_model_product` gained five fields: `heroimage` (multiple FAL images, detail payload only), the richtext fields `description2`, `capabilities` and `textrelatedproducts`, and `portfolio` (TCA `link`) — emitted as a **resolved URL** through the new `LinkResolver::typolinkUrl()`. The product payloads are specified for the first time (7.15). Links in `contentelement` / `contentelementcta` that point at a **container** now resolve its children (`items`, page‑level shape) and `background` (7.4). Two record link handlers (`download`, `product`) added to the link browser, resolved server‑side per the new Clause 9.12; new middleware `vitec/download-file` streams `/download/file/` as a forced download (5.3), file lookup consolidated into `DownloadFileResolver` — first step towards the B‑4 target (10.2). Backend‑only: `relatedprodukt` moved from the Misc tab to General. Editorial: the document footer had been stuck at v1.7 since v1.8. | | 1.11 | 2026‑08‑21 | **New interface: site search.** Apache Solr 10 (dedicated VPS behind an HTTPS reverse proxy) with `apache-solr-for-typo3/solr` 14.0.0-RC1. The EXT:solr results plugin `solr_pi_results` on the search page is rendered headless by `SearchJsonRenderer` (payload key `search`) - request/response contract in the new Clause 7.16. Indexed corpus: pages plus product, market (`detail_page` only), use-case, news and download records; result `type` vocabulary `page\|product\|market\|story\|news\|download`. Downloads gained the canonical route `/download/` (uid route kept for the record links) and `private_download` is now enforced by `DownloadFileResolver` (5.3, 9.12). The VITEC Set now declares the solr set as a dependency - overriding a foreign set’s TypoScript requires loading after it (5.2). Editorial: the header table had been stuck at v1.9 since v1.10. | | 1.12 | 2026‑08‑24 | **Interface change (additive).** The search endpoint (7.16) gained a type filter and facet counts: request parameter `filter` (a value from the `type` vocabulary; the natural name `type` is unavailable - it is TYPO3’s reserved page-type parameter), response keys `filter` (active filter or null) and `facets.type` (per-type document counts with `active` flags; counts stay complete while a filter is active, except when the filtered result is empty). Editorial baseline of 76 managed synonyms imported into `core_en` (codecs, acquired-brand names such as `exterity => avedia`, UK/US spellings, common misspellings) - synonyms apply at query time, no re-index. New autocomplete endpoint: the EXT:solr suggest plugin as lean JSON page type 7384, deliberately USER_INT (7.16). | -| 1.13 | 2026‑09‑11 | **New plugin** `vitec_productfinder` (`ProductFinderJsonRenderer`, payload key `productfinder`): the product category hierarchy on its own, as the data source for the filter selects above the product lists (7.15.3, 8). No change to `vitec_productlist` — filtering happens client side over the products already delivered, keyed on the category uids this payload carries. | | 1.14 | 2026‑09‑11 | **Interface change (additive).** Secondary search facets: stories index their market titles (`market_stringM`, MM relation), products their sys_category titles (`category_stringM`); the search endpoint accepts `market` and `category` GET parameters (values verbatim from `facets.[].value`; an unknown value simply yields zero results) and echoes them as `activeFacets`. Visibility fix: the usecases queue now honours `no_index`/`hideonwebsite`, products defensively `hideonwebsite` - and the excluded story’s document was removed explicitly, because re-indexing never deletes. | | 1.15 | 2026‑09‑11 | **Corpus change (additive, index side).** Download documents now carry the extracted file text: Solr 10 has no ExtractingRequestHandler, so a dedicated Apache Tika container on the Solr VPS (Caddy route `/tika/*`, own basic-auth credential) extracts PDF/Office contents during indexing (`TikaDownloadContentIndexer` on `BeforeDocumentIsProcessedForIndexingEvent`, fail-soft per 9.5, cached in `var/tika-cache` keyed on path+size+mtime). Datasheet specifications are now searchable ("625i", "genlock"). No interface change. | +| 1.16 | 2026‑09‑11 | **New plugin** `vitec_productfinder` (`ProductFinderJsonRenderer`, payload key `productfinder`): the product category hierarchy on its own, as the data source for the filter selects above the product lists (7.15.3, 8). No change to `vitec_productlist` — filtering happens client side over the products already delivered, keyed on the category uids this payload carries. | +| 1.17 | 2026‑09‑11 | **Interface change (additive).** The product list payload gained `successStories` (7.15.2.1): stories an editor attaches to a `sys_category` (tab *Options*, with its own layout), emitted in the list envelope beside `products`. Each card carries `forCategories` — the product categories it came from — so the front end can hide it with the same `?cat=`/`?subcat=` filter it applies to the products. Story links reuse the 7.5 rule, with the detail page taken from the element or the new site setting `vitec.storyDetailPid`. | This document is drafted in the style of, and adopts the terminology conventions of, ISO/IEC/IEEE 42010 (architecture description), ISO/IEC/IEEE 26514 (information for @@ -907,6 +908,51 @@ rarely shows. The `layout` vocabulary remains `0`–`3` (see the v1.9 note). --- +#### 7.15.2.1 Success stories below a product list + +The list payload carries a second array beside `products`: the success stories +an editor attached to the categories this element renders. The link lives on +`sys_category` (tab *Options*), not on the plugin, so one decision serves every +list that shows the category. + +```json +"successStories": { + "layout": "grid", + "stories": [ + { + "uid": 12, + "title": "Abu Dhabi Islamic Bank", + "detailUrl": "/success-stories/abu-dhabi-islamic-bank", + "cardImage": { }, + "categories": [ ], + "forCategories": [58] + } + ] +} +``` + +Envelope per 7.3 (`layout` plus the array); `layout` uses the story-list +vocabulary `grid|list|carousel|50-50` and comes from the first category that +contributes stories. Cards are the shared story shape of 7.5, so the same +front-end component renders them. + +`forCategories` is the addition: the **product** categories the story was +attached to — not to be confused with `categories`, which stays the story's own +topic categories. The front end filters on it, using the same `?cat=` / +`?subcat=` parameters the product finder (7.15.3) writes: a card stays visible +while one of its categories is selected. Stories are collected over the +configured categories **and their descendants**, deduplicated, each carrying +every category it came from. An element configured to show all products has no +category context and returns the empty envelope. + +`detailUrl` follows 7.5: the story's own `detail_page` wins; otherwise the URL +is built from the detail page's parent path plus the story slug — the form +`SuccessStoryPathRewrite` maps back. That page is taken from the element's +FlexForm, falling back to the site setting `vitec.storyDetailPid`, and +`detailUrl` is `null` when neither is set. + +--- + #### 7.15.3 Product Finder payload (`vitec_productfinder`, key `productfinder`) The filter above the product lists is fed by its own element. It carries the diff --git a/packages/vitec/Resources/Private/Language/locallang_db.xlf b/packages/vitec/Resources/Private/Language/locallang_db.xlf index 09be3f9..cad63b9 100644 --- a/packages/vitec/Resources/Private/Language/locallang_db.xlf +++ b/packages/vitec/Resources/Private/Language/locallang_db.xlf @@ -27,6 +27,18 @@ Slug + + Success Stories + + + Shown as cards below the product list of this category. The order set here is the order they appear in. + + + Layout + + + Display variant for these story cards - same options as the success story list. + diff --git a/packages/vitec/ext_tables.sql b/packages/vitec/ext_tables.sql index 157e4c0..10674e5 100755 --- a/packages/vitec/ext_tables.sql +++ b/packages/vitec/ext_tables.sql @@ -362,3 +362,12 @@ CREATE TABLE tx_vitec_product_workbook ( tstamp int(11) DEFAULT '0' NOT NULL, PRIMARY KEY (product_uid) ); + +# +# Success stories shown below the product list of a category (2026-09-11). +# Comma-separated usecase uids, order = the editor's arrangement. +# +CREATE TABLE sys_category ( + tx_vitec_success_stories varchar(1024) DEFAULT '' NOT NULL, + tx_vitec_success_stories_layout varchar(30) DEFAULT 'grid' NOT NULL +); diff --git a/public/_frontend/.htaccess b/public/_frontend/.htaccess index 15135c6..52d7f05 100644 --- a/public/_frontend/.htaccess +++ b/public/_frontend/.htaccess @@ -1,27 +1,27 @@ -RewriteEngine On -RewriteBase /_frontend/ - -RewriteRule ^index\.html$ - [L] - -RewriteCond %{REQUEST_FILENAME} -f [OR] -RewriteCond %{REQUEST_FILENAME} -d -RewriteRule ^ - [L] - -RewriteRule ^ index.html [L] - -# ── Cache headers ──────────────────────────────────────────────────────────── - -# index.html: never cache — always fetch fresh so the browser picks up -# the latest hashed JS/CSS filenames after a deploy. - - Header set Cache-Control "no-store, no-cache, must-revalidate" - Header set Pragma "no-cache" - Header set Expires "0" - - -# Hashed assets (JS, CSS, fonts, images inside /assets/): -# Vite appends a content hash to every filename, so these are immutable. -# Cache them for 1 year — a new deploy produces new filenames automatically. - - Header set Cache-Control "public, max-age=31536000, immutable" - +RewriteEngine On +RewriteBase /_frontend/ + +RewriteRule ^index\.html$ - [L] + +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d +RewriteRule ^ - [L] + +RewriteRule ^ index.html [L] + +# ── Cache headers ──────────────────────────────────────────────────────────── + +# index.html: never cache — always fetch fresh so the browser picks up +# the latest hashed JS/CSS filenames after a deploy. + + Header set Cache-Control "no-store, no-cache, must-revalidate" + Header set Pragma "no-cache" + Header set Expires "0" + + +# Hashed assets (JS, CSS, fonts, images inside /assets/): +# Vite appends a content hash to every filename, so these are immutable. +# Cache them for 1 year — a new deploy produces new filenames automatically. + + Header set Cache-Control "public, max-age=31536000, immutable" + diff --git a/public/_frontend/index.html b/public/_frontend/index.html index 7625da5..9ef3c62 100644 --- a/public/_frontend/index.html +++ b/public/_frontend/index.html @@ -25,8 +25,8 @@ VITEC - - + +