diff --git a/composer.lock b/composer.lock index fc01367..9c62fc1 100644 --- a/composer.lock +++ b/composer.lock @@ -164,16 +164,16 @@ }, { "name": "christian-riesen/base32", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/ChristianRiesen/base32.git", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894" + "reference": "03082b946c111eb6ab41a98626fee33360c5232c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/2e82dab3baa008e24a505649b0d583c31d31e894", - "reference": "2e82dab3baa008e24a505649b0d583c31d31e894", + "url": "https://api.github.com/repos/ChristianRiesen/base32/zipball/03082b946c111eb6ab41a98626fee33360c5232c", + "reference": "03082b946c111eb6ab41a98626fee33360c5232c", "shasum": "" }, "require": { @@ -217,22 +217,22 @@ ], "support": { "issues": "https://github.com/ChristianRiesen/base32/issues", - "source": "https://github.com/ChristianRiesen/base32/tree/1.6.0" + "source": "https://github.com/ChristianRiesen/base32/tree/1.6.1" }, - "time": "2021-02-26T10:19:33+00:00" + "time": "2026-07-11T19:29:43+00:00" }, { "name": "composer/ca-bundle", - "version": "1.5.12", + "version": "1.5.13", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "00a2f4201641d5c53f7fc0195e6c8d9fcc321a78" + "reference": "c008272789979f709f7fcb32c2ecf1d2db5e84e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/00a2f4201641d5c53f7fc0195e6c8d9fcc321a78", - "reference": "00a2f4201641d5c53f7fc0195e6c8d9fcc321a78", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/c008272789979f709f7fcb32c2ecf1d2db5e84e5", + "reference": "c008272789979f709f7fcb32c2ecf1d2db5e84e5", "shasum": "" }, "require": { @@ -279,7 +279,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.12" + "source": "https://github.com/composer/ca-bundle/tree/1.5.13" }, "funding": [ { @@ -291,7 +291,7 @@ "type": "github" } ], - "time": "2026-05-19T11:26:22+00:00" + "time": "2026-07-18T12:35:13+00:00" }, { "name": "composer/class-map-generator", @@ -887,16 +887,16 @@ }, { "name": "doctrine/dbal", - "version": "4.4.3", + "version": "4.4.4", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "61e730f1658814821a85f2402c945f3883407dec" + "reference": "fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/61e730f1658814821a85f2402c945f3883407dec", - "reference": "61e730f1658814821a85f2402c945f3883407dec", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce", + "reference": "fb9e0ffe15e1590e24dc61c0c0a23f9a33ee42ce", "shasum": "" }, "require": { @@ -973,7 +973,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/4.4.3" + "source": "https://github.com/doctrine/dbal/tree/4.4.4" }, "funding": [ { @@ -989,7 +989,7 @@ "type": "tidelift" } ], - "time": "2026-03-20T08:52:12+00:00" + "time": "2026-07-21T14:34:40+00:00" }, { "name": "doctrine/deprecations", @@ -1404,16 +1404,16 @@ }, { "name": "friendsoftypo3/content-blocks", - "version": "2.4.7", + "version": "2.4.8", "source": { "type": "git", "url": "https://github.com/FriendsOfTYPO3/content-blocks.git", - "reference": "9f2b13b5f605cd6576b9a1dec10b559c59af3c66" + "reference": "47ffa2db718907eff883728e7fcaf99511dcb74d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfTYPO3/content-blocks/zipball/9f2b13b5f605cd6576b9a1dec10b559c59af3c66", - "reference": "9f2b13b5f605cd6576b9a1dec10b559c59af3c66", + "url": "https://api.github.com/repos/FriendsOfTYPO3/content-blocks/zipball/47ffa2db718907eff883728e7fcaf99511dcb74d", + "reference": "47ffa2db718907eff883728e7fcaf99511dcb74d", "shasum": "" }, "require": { @@ -1425,10 +1425,10 @@ "symfony/finder": "^7.4", "symfony/var-exporter": "^7.4 || ^8.0", "symfony/yaml": "^7.4", - "typo3/cms-backend": "^14.3", - "typo3/cms-core": "^14.3", - "typo3/cms-fluid": "^14.3", - "typo3/cms-frontend": "^14.3" + "typo3/cms-backend": "^14.3.5", + "typo3/cms-core": "^14.3.5", + "typo3/cms-fluid": "^14.3.5", + "typo3/cms-frontend": "^14.3.5" }, "replace": { "contentblocks/content-blocks": "*" @@ -1439,14 +1439,14 @@ "friendsofphp/php-cs-fixer": "^3.22", "phpstan/phpstan": "^2.1", "phpstan/phpstan-phpunit": "^2.0", - "typo3/cms-extbase": "^14.3", - "typo3/cms-extensionmanager": "^14.3", - "typo3/cms-filelist": "^14.3", - "typo3/cms-install": "^14.3", - "typo3/cms-lowlevel": "^14.3", - "typo3/cms-rte-ckeditor": "^14.3", - "typo3/cms-tstemplate": "^14.3", - "typo3/cms-workspaces": "^14.3", + "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/testing-framework": "^9.5" }, @@ -1460,7 +1460,7 @@ "symfony/var-exporter": "" } }, - "version": "2.4.7", + "version": "2.4.8", "web-dir": ".Build/public", "extension-key": "content_blocks" } @@ -1489,7 +1489,7 @@ "issues": "https://github.com/FriendsOfTYPO3/content-blocks/issues", "source": "https://github.com/FriendsOfTYPO3/content-blocks" }, - "time": "2026-06-25T15:33:34+00:00" + "time": "2026-07-22T14:34:41+00:00" }, { "name": "friendsoftypo3/headless", @@ -1686,22 +1686,22 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.13.2", + "version": "7.15.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "bcd989ad36c92d42a3715379af91f2defee5b8dd" + "reference": "744101956d78b7c1384d0cbf379db13e859167bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/bcd989ad36c92d42a3715379af91f2defee5b8dd", - "reference": "bcd989ad36c92d42a3715379af91f2defee5b8dd", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/744101956d78b7c1384d0cbf379db13e859167bf", + "reference": "744101956d78b7c1384d0cbf379db13e859167bf", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^2.5", - "guzzlehttp/psr7": "^2.12.3", + "guzzlehttp/promises": "^2.5.1", + "guzzlehttp/psr7": "^2.13", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", @@ -1714,7 +1714,7 @@ "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", "guzzle/client-integration-tests": "3.0.3", - "guzzlehttp/test-server": "^0.6", + "guzzlehttp/test-server": "^0.7", "php-http/message-factory": "^1.1", "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" @@ -1794,7 +1794,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.13.2" + "source": "https://github.com/guzzle/guzzle/tree/7.15.2" }, "funding": [ { @@ -1810,20 +1810,20 @@ "type": "tidelift" } ], - "time": "2026-07-05T19:00:11+00:00" + "time": "2026-07-26T23:23:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.5.0", + "version": "2.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", - "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", + "url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29", + "reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29", "shasum": "" }, "require": { @@ -1878,7 +1878,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.5.0" + "source": "https://github.com/guzzle/promises/tree/2.5.1" }, "funding": [ { @@ -1894,20 +1894,20 @@ "type": "tidelift" } ], - "time": "2026-06-02T12:23:43+00:00" + "time": "2026-07-08T15:48:39+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.12.3", + "version": "2.13.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d" + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d", - "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dad89620b7a6edb60c15858442eb2e408b45d8f4", + "reference": "dad89620b7a6edb60c15858442eb2e408b45d8f4", "shasum": "" }, "require": { @@ -1997,7 +1997,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.12.3" + "source": "https://github.com/guzzle/psr7/tree/2.13.0" }, "funding": [ { @@ -2013,7 +2013,7 @@ "type": "tidelift" } ], - "time": "2026-06-23T15:21:08+00:00" + "time": "2026-07-16T22:23:49+00:00" }, { "name": "justinrainbow/json-schema", @@ -5988,6 +5988,86 @@ ], "time": "2026-05-26T12:51:13+00:00" }, + { + "name": "symfony/polyfill-php85", + "version": "v1.38.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php85.git", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "reference": "ba2ba04f3352cfa2dcbbcb90aee13ed967f505b1", + "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\\Php85\\": "" + }, + "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.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php85/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-26T02:25:22+00:00" + }, { "name": "symfony/polyfill-uuid", "version": "v1.37.0", @@ -7310,23 +7390,23 @@ }, { "name": "typo3/cms-backend", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/backend.git", - "reference": "f842410118ea059292dc697e3c085e5b12ea41f0" + "reference": "dc6163417a8a41b46790ec37cb5f59e5d6c45e73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/backend/zipball/f842410118ea059292dc697e3c085e5b12ea41f0", - "reference": "f842410118ea059292dc697e3c085e5b12ea41f0", + "url": "https://api.github.com/repos/TYPO3-CMS/backend/zipball/dc6163417a8a41b46790ec37cb5f59e5d6c45e73", + "reference": "dc6163417a8a41b46790ec37cb5f59e5d6c45e73", "shasum": "" }, "require": { "ext-intl": "*", "ext-libxml": "*", "psr/event-dispatcher": "^1.0", - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -7382,7 +7462,7 @@ "role": "Developer" } ], - "description": "TYPO3 CMS backend", + "description": "TYPO3 CMS Backend", "homepage": "https://typo3.community/", "support": { "chat": "https://typo3.community/meet/slack/", @@ -7399,24 +7479,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-belog", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/belog.git", - "reference": "e5f33784c536e43929104e378d872cafbb5c3afd" + "reference": "428858d2a07d4560a11e13aae40f0208961c2c00" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/belog/zipball/e5f33784c536e43929104e378d872cafbb5c3afd", - "reference": "e5f33784c536e43929104e378d872cafbb5c3afd", + "url": "https://api.github.com/repos/TYPO3-CMS/belog/zipball/428858d2a07d4560a11e13aae40f0208961c2c00", + "reference": "428858d2a07d4560a11e13aae40f0208961c2c00", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -7466,24 +7546,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-beuser", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/beuser.git", - "reference": "124498b0bcf090e1cde666f9f6f80d34322bdd1e" + "reference": "9cee11be80a34a23dd4d5f8a816119039462771d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/beuser/zipball/124498b0bcf090e1cde666f9f6f80d34322bdd1e", - "reference": "124498b0bcf090e1cde666f9f6f80d34322bdd1e", + "url": "https://api.github.com/repos/TYPO3-CMS/beuser/zipball/9cee11be80a34a23dd4d5f8a816119039462771d", + "reference": "9cee11be80a34a23dd4d5f8a816119039462771d", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -7533,7 +7613,7 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-cli", @@ -7642,16 +7722,16 @@ }, { "name": "typo3/cms-core", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/core.git", - "reference": "e59ef7a3348e439bf103140e01614f1bc403d373" + "reference": "2c739f805ba6e05d3864f3cbdfdf7fbecd8ca0ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/core/zipball/e59ef7a3348e439bf103140e01614f1bc403d373", - "reference": "e59ef7a3348e439bf103140e01614f1bc403d373", + "url": "https://api.github.com/repos/TYPO3-CMS/core/zipball/2c739f805ba6e05d3864f3cbdfdf7fbecd8ca0ea", + "reference": "2c739f805ba6e05d3864f3cbdfdf7fbecd8ca0ea", "shasum": "" }, "require": { @@ -7676,8 +7756,8 @@ "ext-tokenizer": "*", "ext-xml": "*", "firebase/php-jwt": "^6.10.2 || ^7.0.5", - "guzzlehttp/guzzle": "^7.9.2", - "guzzlehttp/psr7": "^2.9.0", + "guzzlehttp/guzzle": "^7.12.1", + "guzzlehttp/psr7": "^2.10.2", "lolli42/finediff": "^1.1.2", "masterminds/html5": "^2.10.0", "php": "^8.2", @@ -7703,6 +7783,8 @@ "symfony/mime": "^7.4.12", "symfony/options-resolver": "^7.4.8", "symfony/polyfill-php83": "^1.36.0", + "symfony/polyfill-php84": "^1.38", + "symfony/polyfill-php85": "^1.38", "symfony/process": "^7.4.8", "symfony/rate-limiter": "^7.4.8", "symfony/routing": "^7.4.13", @@ -7797,28 +7879,28 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-dashboard", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/dashboard.git", - "reference": "0dfdb48e21dfd7e48785b775aa0265c981ca43b1" + "reference": "9fb42e261187767e4ffb6ce99c6390ea85b1c415" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/dashboard/zipball/0dfdb48e21dfd7e48785b775aa0265c981ca43b1", - "reference": "0dfdb48e21dfd7e48785b775aa0265c981ca43b1", + "url": "https://api.github.com/repos/TYPO3-CMS/dashboard/zipball/9fb42e261187767e4ffb6ce99c6390ea85b1c415", + "reference": "9fb42e261187767e4ffb6ce99c6390ea85b1c415", "shasum": "" }, "require": { - "typo3/cms-backend": "14.3.4", - "typo3/cms-core": "14.3.4", - "typo3/cms-extbase": "14.3.4", - "typo3/cms-fluid": "14.3.4", - "typo3/cms-frontend": "14.3.4" + "typo3/cms-backend": "14.3.5", + "typo3/cms-core": "14.3.5", + "typo3/cms-extbase": "14.3.5", + "typo3/cms-fluid": "14.3.5", + "typo3/cms-frontend": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -7869,20 +7951,20 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-extbase", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/extbase.git", - "reference": "3d41793cdb2dcae4a3490f859af6a999e5cc5627" + "reference": "1f50f2faa83364387af8a921d51bc72a8580eb9f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/extbase/zipball/3d41793cdb2dcae4a3490f859af6a999e5cc5627", - "reference": "3d41793cdb2dcae4a3490f859af6a999e5cc5627", + "url": "https://api.github.com/repos/TYPO3-CMS/extbase/zipball/1f50f2faa83364387af8a921d51bc72a8580eb9f", + "reference": "1f50f2faa83364387af8a921d51bc72a8580eb9f", "shasum": "" }, "require": { @@ -7894,7 +7976,7 @@ "symfony/property-access": "^7.4.8", "symfony/property-info": "^7.4.8", "symfony/validator": "^7.4.8", - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -7955,25 +8037,25 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-extensionmanager", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/extensionmanager.git", - "reference": "a3f272a35cc3938324f5b9fc7a8131ed5f731851" + "reference": "536178e47137d4db8b855649d89e8eb35fe3bd22" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/extensionmanager/zipball/a3f272a35cc3938324f5b9fc7a8131ed5f731851", - "reference": "a3f272a35cc3938324f5b9fc7a8131ed5f731851", + "url": "https://api.github.com/repos/TYPO3-CMS/extensionmanager/zipball/536178e47137d4db8b855649d89e8eb35fe3bd22", + "reference": "536178e47137d4db8b855649d89e8eb35fe3bd22", "shasum": "" }, "require": { "ext-libxml": "*", - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8025,24 +8107,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-felogin", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/felogin.git", - "reference": "d45a4987e58186b3806829ca0e715b148ea6b721" + "reference": "26a9e4204966798c8fa05f9a03b069688ba72390" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/felogin/zipball/d45a4987e58186b3806829ca0e715b148ea6b721", - "reference": "d45a4987e58186b3806829ca0e715b148ea6b721", + "url": "https://api.github.com/repos/TYPO3-CMS/felogin/zipball/26a9e4204966798c8fa05f9a03b069688ba72390", + "reference": "26a9e4204966798c8fa05f9a03b069688ba72390", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8092,24 +8174,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-filelist", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/filelist.git", - "reference": "8bc262bbd3577a5535317941e141b0975803a4ca" + "reference": "efbc1c8a9822f977bf49db476f51b94246631029" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/filelist/zipball/8bc262bbd3577a5535317941e141b0975803a4ca", - "reference": "8bc262bbd3577a5535317941e141b0975803a4ca", + "url": "https://api.github.com/repos/TYPO3-CMS/filelist/zipball/efbc1c8a9822f977bf49db476f51b94246631029", + "reference": "efbc1c8a9822f977bf49db476f51b94246631029", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8161,27 +8243,27 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-fluid", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/fluid.git", - "reference": "8f50a943ac2a22532379591c97b570119c8e4bae" + "reference": "2859efa69f30ea632e43ec5a8a7385ffb863e036" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/fluid/zipball/8f50a943ac2a22532379591c97b570119c8e4bae", - "reference": "8f50a943ac2a22532379591c97b570119c8e4bae", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid/zipball/2859efa69f30ea632e43ec5a8a7385ffb863e036", + "reference": "2859efa69f30ea632e43ec5a8a7385ffb863e036", "shasum": "" }, "require": { "symfony/dependency-injection": "^7.4.8", "symfony/finder": "^7.4.8", - "typo3/cms-core": "14.3.4", - "typo3/cms-extbase": "14.3.4", + "typo3/cms-core": "14.3.5", + "typo3/cms-extbase": "14.3.5", "typo3fluid/fluid": "^5.3.1" }, "conflict": { @@ -8235,26 +8317,26 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-fluid-styled-content", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/fluid_styled_content.git", - "reference": "b08e119753ab90e15f015135cdfd65d47bbdbbf6" + "reference": "4fa54be5e51328f7e908d5a79649824a5da2f56e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/fluid_styled_content/zipball/b08e119753ab90e15f015135cdfd65d47bbdbbf6", - "reference": "b08e119753ab90e15f015135cdfd65d47bbdbbf6", + "url": "https://api.github.com/repos/TYPO3-CMS/fluid_styled_content/zipball/4fa54be5e51328f7e908d5a79649824a5da2f56e", + "reference": "4fa54be5e51328f7e908d5a79649824a5da2f56e", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4", - "typo3/cms-fluid": "14.3.4", - "typo3/cms-frontend": "14.3.4" + "typo3/cms-core": "14.3.5", + "typo3/cms-fluid": "14.3.5", + "typo3/cms-frontend": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8304,27 +8386,27 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-form", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/form.git", - "reference": "3307aae5867a885cd239226c9dda2791ad711f56" + "reference": "97d4c3d0b8f9bf1d3d00ba8facb442511a339442" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/form/zipball/3307aae5867a885cd239226c9dda2791ad711f56", - "reference": "3307aae5867a885cd239226c9dda2791ad711f56", + "url": "https://api.github.com/repos/TYPO3-CMS/form/zipball/97d4c3d0b8f9bf1d3d00ba8facb442511a339442", + "reference": "97d4c3d0b8f9bf1d3d00ba8facb442511a339442", "shasum": "" }, "require": { "psr/http-message": "^1.1 || ^2.0", "symfony/expression-language": "^7.4.8", - "typo3/cms-core": "14.3.4", - "typo3/cms-frontend": "14.3.4" + "typo3/cms-core": "14.3.5", + "typo3/cms-frontend": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8379,25 +8461,25 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-frontend", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/frontend.git", - "reference": "bd865d76c5b63a5c0b1f0325b32292e975ad7c38" + "reference": "222a74b6ecae6be99f056f6939091db4b6beb240" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/frontend/zipball/bd865d76c5b63a5c0b1f0325b32292e975ad7c38", - "reference": "bd865d76c5b63a5c0b1f0325b32292e975ad7c38", + "url": "https://api.github.com/repos/TYPO3-CMS/frontend/zipball/222a74b6ecae6be99f056f6939091db4b6beb240", + "reference": "222a74b6ecae6be99f056f6939091db4b6beb240", "shasum": "" }, "require": { "ext-libxml": "*", - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8457,24 +8539,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-impexp", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/impexp.git", - "reference": "b7644c24e3cb118a91be1eafa8810eb33cd94ddf" + "reference": "346236cc3157af112c52a2d31e380521db0a5b5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/impexp/zipball/b7644c24e3cb118a91be1eafa8810eb33cd94ddf", - "reference": "b7644c24e3cb118a91be1eafa8810eb33cd94ddf", + "url": "https://api.github.com/repos/TYPO3-CMS/impexp/zipball/346236cc3157af112c52a2d31e380521db0a5b5b", + "reference": "346236cc3157af112c52a2d31e380521db0a5b5b", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8524,24 +8606,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-info", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/info.git", - "reference": "b530430877339b2a16c30ba7b78b8c88454dd7a1" + "reference": "6b19609dd439c2070d14c5037a83e3b95c8c08c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/info/zipball/b530430877339b2a16c30ba7b78b8c88454dd7a1", - "reference": "b530430877339b2a16c30ba7b78b8c88454dd7a1", + "url": "https://api.github.com/repos/TYPO3-CMS/info/zipball/6b19609dd439c2070d14c5037a83e3b95c8c08c6", + "reference": "6b19609dd439c2070d14c5037a83e3b95c8c08c6", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8594,20 +8676,20 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-install", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/install.git", - "reference": "a4c5bd2e9a598d5d45975312ae097df2a33ef05d" + "reference": "fd964477a16b906ef817c45bbf61bbb0d58788ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/install/zipball/a4c5bd2e9a598d5d45975312ae097df2a33ef05d", - "reference": "a4c5bd2e9a598d5d45975312ae097df2a33ef05d", + "url": "https://api.github.com/repos/TYPO3-CMS/install/zipball/fd964477a16b906ef817c45bbf61bbb0d58788ee", + "reference": "fd964477a16b906ef817c45bbf61bbb0d58788ee", "shasum": "" }, "require": { @@ -8616,9 +8698,9 @@ "nikic/php-parser": "^5.4.0", "symfony/finder": "^7.4.8", "symfony/http-foundation": "^7.4.13", - "typo3/cms-core": "14.3.4", - "typo3/cms-extbase": "14.3.4", - "typo3/cms-fluid": "14.3.4" + "typo3/cms-core": "14.3.5", + "typo3/cms-extbase": "14.3.5", + "typo3/cms-fluid": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8671,24 +8753,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-reactions", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/reactions.git", - "reference": "700233067b941381d551fa8a0662c92977ba9ceb" + "reference": "d631fd081515dc83ff0b82402930e3f11bfb49ba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/reactions/zipball/700233067b941381d551fa8a0662c92977ba9ceb", - "reference": "700233067b941381d551fa8a0662c92977ba9ceb", + "url": "https://api.github.com/repos/TYPO3-CMS/reactions/zipball/d631fd081515dc83ff0b82402930e3f11bfb49ba", + "reference": "d631fd081515dc83ff0b82402930e3f11bfb49ba", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8738,24 +8820,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-recycler", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/recycler.git", - "reference": "e34ad4cf0561f2e441248c52243457adef3a9bac" + "reference": "aef17058b7a838ca23f74673732c27161cee1ea0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/recycler/zipball/e34ad4cf0561f2e441248c52243457adef3a9bac", - "reference": "e34ad4cf0561f2e441248c52243457adef3a9bac", + "url": "https://api.github.com/repos/TYPO3-CMS/recycler/zipball/aef17058b7a838ca23f74673732c27161cee1ea0", + "reference": "aef17058b7a838ca23f74673732c27161cee1ea0", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8808,24 +8890,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-rte-ckeditor", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/rte_ckeditor.git", - "reference": "1e649567cec0c67a3acef5fc2a680f861c6ed146" + "reference": "056f77dab6350067c52691e113144fdd41340713" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/rte_ckeditor/zipball/1e649567cec0c67a3acef5fc2a680f861c6ed146", - "reference": "1e649567cec0c67a3acef5fc2a680f861c6ed146", + "url": "https://api.github.com/repos/TYPO3-CMS/rte_ckeditor/zipball/056f77dab6350067c52691e113144fdd41340713", + "reference": "056f77dab6350067c52691e113144fdd41340713", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8875,24 +8957,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-scheduler", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/scheduler.git", - "reference": "b5cb0dfbd2e52fc6903cc9e695d7536e55489321" + "reference": "534e433d84ff618bdf17e9d8518016ad3b719101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/b5cb0dfbd2e52fc6903cc9e695d7536e55489321", - "reference": "b5cb0dfbd2e52fc6903cc9e695d7536e55489321", + "url": "https://api.github.com/repos/TYPO3-CMS/scheduler/zipball/534e433d84ff618bdf17e9d8518016ad3b719101", + "reference": "534e433d84ff618bdf17e9d8518016ad3b719101", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -8939,26 +9021,26 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-seo", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/seo.git", - "reference": "ed5e9f465e1379adfcff0abedbfe1d68b96f6bdc" + "reference": "e9c10a916a93ce7970aadbb5cec6cc3a170e63ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/seo/zipball/ed5e9f465e1379adfcff0abedbfe1d68b96f6bdc", - "reference": "ed5e9f465e1379adfcff0abedbfe1d68b96f6bdc", + "url": "https://api.github.com/repos/TYPO3-CMS/seo/zipball/e9c10a916a93ce7970aadbb5cec6cc3a170e63ce", + "reference": "e9c10a916a93ce7970aadbb5cec6cc3a170e63ce", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4", - "typo3/cms-extbase": "14.3.4", - "typo3/cms-frontend": "14.3.4" + "typo3/cms-core": "14.3.5", + "typo3/cms-extbase": "14.3.5", + "typo3/cms-frontend": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -9011,24 +9093,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-sys-note", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/sys_note.git", - "reference": "14543f7367fb92adbacc96a889ff602fe559a979" + "reference": "02e5f699794c2da762ab3c705226082586d70958" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/sys_note/zipball/14543f7367fb92adbacc96a889ff602fe559a979", - "reference": "14543f7367fb92adbacc96a889ff602fe559a979", + "url": "https://api.github.com/repos/TYPO3-CMS/sys_note/zipball/02e5f699794c2da762ab3c705226082586d70958", + "reference": "02e5f699794c2da762ab3c705226082586d70958", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -9081,24 +9163,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-tstemplate", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/tstemplate.git", - "reference": "caa2a8e1a373a8ae5b8cba0ea8e8944bffce83f4" + "reference": "c04c795a48856775fec7daf71bdf8e953f6d91b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/tstemplate/zipball/caa2a8e1a373a8ae5b8cba0ea8e8944bffce83f4", - "reference": "caa2a8e1a373a8ae5b8cba0ea8e8944bffce83f4", + "url": "https://api.github.com/repos/TYPO3-CMS/tstemplate/zipball/c04c795a48856775fec7daf71bdf8e953f6d91b3", + "reference": "c04c795a48856775fec7daf71bdf8e953f6d91b3", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -9148,24 +9230,24 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-viewpage", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/viewpage.git", - "reference": "519ebbb6b6da69b13bd2a7a3651c0ae1e8ece3ee" + "reference": "e50a2d25a46461aebf7ea64e29e27edc11a5da1e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/viewpage/zipball/519ebbb6b6da69b13bd2a7a3651c0ae1e8ece3ee", - "reference": "519ebbb6b6da69b13bd2a7a3651c0ae1e8ece3ee", + "url": "https://api.github.com/repos/TYPO3-CMS/viewpage/zipball/e50a2d25a46461aebf7ea64e29e27edc11a5da1e", + "reference": "e50a2d25a46461aebf7ea64e29e27edc11a5da1e", "shasum": "" }, "require": { - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -9215,25 +9297,25 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/cms-webhooks", - "version": "v14.3.4", + "version": "v14.3.5", "source": { "type": "git", "url": "https://github.com/TYPO3-CMS/webhooks.git", - "reference": "e72dac3ec0378c4f5094987ffdc08ba07f68cb41" + "reference": "9bad1109e9da1151c1b1a770426d679240651e72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/TYPO3-CMS/webhooks/zipball/e72dac3ec0378c4f5094987ffdc08ba07f68cb41", - "reference": "e72dac3ec0378c4f5094987ffdc08ba07f68cb41", + "url": "https://api.github.com/repos/TYPO3-CMS/webhooks/zipball/9bad1109e9da1151c1b1a770426d679240651e72", + "reference": "9bad1109e9da1151c1b1a770426d679240651e72", "shasum": "" }, "require": { "symfony/uid": "^7.4.8", - "typo3/cms-core": "14.3.4" + "typo3/cms-core": "14.3.5" }, "conflict": { "typo3/cms": "*" @@ -9283,7 +9365,7 @@ "type": "membership" } ], - "time": "2026-06-16T08:52:34+00:00" + "time": "2026-07-14T12:26:05+00:00" }, { "name": "typo3/html-sanitizer", @@ -9471,10 +9553,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/fix-asset-perms.sh b/fix-asset-perms.sh index 03a17c2..5413bc8 100755 --- a/fix-asset-perms.sh +++ b/fix-asset-perms.sh @@ -17,8 +17,8 @@ cd "$(dirname "$0")" || exit 1 echo "[perms] Project root: $(pwd)" echo "[perms] Setting files 644 / directories 755 under Resources/Public + _assets ..." -find public/_assets packages/vitec/Resources/Public -type f -exec chmod 644 {} \; 2>/dev/null -find public/_assets packages/vitec/Resources/Public -type d -exec chmod 755 {} \; 2>/dev/null +find public/_assets public/fileadmin/icons packages/vitec/Resources/Public -type f -exec chmod 644 {} \; 2>/dev/null +find public/_assets public/fileadmin/icons packages/vitec/Resources/Public -type d -exec chmod 755 {} \; 2>/dev/null echo "[perms] Flushing TYPO3 caches ..." if [ -x vendor/bin/typo3 ]; then diff --git a/packages/vitec/Classes/Controller/FormController.php b/packages/vitec/Classes/Controller/FormController.php new file mode 100755 index 0000000..4d1f03b --- /dev/null +++ b/packages/vitec/Classes/Controller/FormController.php @@ -0,0 +1,22 @@ +htmlResponse(); + } +} diff --git a/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php b/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php index 285afd7..e06b939 100755 --- a/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php +++ b/packages/vitec/Classes/DataProcessing/ContainerChildrenProcessor.php @@ -15,6 +15,7 @@ use Evomedien\Vitec\UserFunc\DatasheetsJsonRenderer; use Evomedien\Vitec\UserFunc\EventlistJsonRenderer; use Evomedien\Vitec\UserFunc\LocationsJsonRenderer; use Evomedien\Vitec\UserFunc\CustomerlogosJsonRenderer; +use Evomedien\Vitec\UserFunc\FormsJsonRenderer; use Evomedien\Vitec\UserFunc\ModelcardJsonRenderer; use Evomedien\Vitec\UserFunc\NewsJsonRenderer; use TYPO3\CMS\Core\Database\Connection; @@ -117,6 +118,9 @@ final class ContainerChildrenProcessor implements DataProcessorInterface 'vitec_locationlist' => [LocationsJsonRenderer::class, 'locations'], 'vitec_customerlogos' => [CustomerlogosJsonRenderer::class, 'customerlogos'], 'vitec_modelcard' => [ModelcardJsonRenderer::class, 'card'], + 'vitec_contactform' => [FormsJsonRenderer::class, 'form'], + 'vitec_demoform' => [FormsJsonRenderer::class, 'form'], + 'vitec_helpdeskform' => [FormsJsonRenderer::class, 'form'], 'news_pi1' => [NewsJsonRenderer::class, 'news'], 'news_newsliststicky' => [NewsJsonRenderer::class, 'news'], 'news_newsselectedlist' => [NewsJsonRenderer::class, 'news'], diff --git a/packages/vitec/Classes/Forms/Delivery/DeliveryInterface.php b/packages/vitec/Classes/Forms/Delivery/DeliveryInterface.php new file mode 100755 index 0000000..02deb4d --- /dev/null +++ b/packages/vitec/Classes/Forms/Delivery/DeliveryInterface.php @@ -0,0 +1,19 @@ + $payload validated, filtered field values + * @param array $settings FlexForm settings of the form plugin + */ + public function deliver(string $formKey, array $payload, array $settings): void; +} diff --git a/packages/vitec/Classes/Forms/Delivery/EmailDelivery.php b/packages/vitec/Classes/Forms/Delivery/EmailDelivery.php new file mode 100755 index 0000000..4cea164 --- /dev/null +++ b/packages/vitec/Classes/Forms/Delivery/EmailDelivery.php @@ -0,0 +1,48 @@ + $value) { + $lines[] = str_pad($field . ':', 16) . $value; + } + + $mail = GeneralUtility::makeInstance(MailMessage::class); + $mail->to(...$recipients) + ->subject($subject) + ->text("Form: $formKey\n\n" . implode("\n", $lines)); + + $submitterEmail = trim((string)($payload['email'] ?? '')); + if ($submitterEmail !== '' && filter_var($submitterEmail, FILTER_VALIDATE_EMAIL)) { + $mail->replyTo($submitterEmail); + } + + if (!$mail->send()) { + throw new \RuntimeException('MailMessage::send() reported failure.'); + } + } +} diff --git a/packages/vitec/Classes/Forms/Delivery/SalesforceDelivery.php b/packages/vitec/Classes/Forms/Delivery/SalesforceDelivery.php new file mode 100755 index 0000000..c54e5a5 --- /dev/null +++ b/packages/vitec/Classes/Forms/Delivery/SalesforceDelivery.php @@ -0,0 +1,25 @@ + Salesforce field mapping. Until then any + * submission with delivery=salesforce is stored with status "failed" and + * this message — no data is lost. + */ +final class SalesforceDelivery implements DeliveryInterface +{ + public function deliver(string $formKey, array $payload, array $settings): void + { + throw new \RuntimeException( + 'Salesforce delivery is prepared but not configured yet — submission kept in the log.' + ); + } +} diff --git a/packages/vitec/Classes/Forms/FormDefinitions.php b/packages/vitec/Classes/Forms/FormDefinitions.php new file mode 100755 index 0000000..6ee731b --- /dev/null +++ b/packages/vitec/Classes/Forms/FormDefinitions.php @@ -0,0 +1,190 @@ + form key */ + public const CTYPE_MAP = [ + 'vitec_contactform' => 'contact', + 'vitec_demoform' => 'demo', + 'vitec_helpdeskform' => 'helpdesk', + ]; + + private const SOLUTION_OPTIONS = [ + 'IPTV Distribution', + 'Digital Signage', + 'Video Streaming', + 'Video Contribution', + 'Situational Awareness and ISR', + 'Remote Production', + 'Encoders & Decoders', + 'Custom Design Service', + ]; + + /** + * @return array|null + */ + public static function get(string $formKey): ?array + { + $all = self::all(); + return $all[$formKey] ?? null; + } + + /** + * @return array> + */ + public static function all(): array + { + $contactFields = [ + self::text('firstName', 'First Name', true), + self::text('lastName', 'Last Name', true), + self::text('company', 'Company', true), + self::field('email', 'email', 'Email', true), + self::field('phone', 'tel', 'Phone', true), + self::text('street', 'Street', false), + self::text('city', 'City', false), + self::text('zip', 'Zip / Postal Code', false), + self::countrySelect(), + self::select('state', 'State', false, [], 'usStates'), + self::select('solution', 'Solution of Interest', false, self::SOLUTION_OPTIONS), + self::field('message', 'textarea', 'Message', false), + ]; + + return [ + 'contact' => [ + 'formKey' => 'contact', + 'title' => 'Contact VITEC', + 'fields' => $contactFields, + ], + 'demo' => [ + 'formKey' => 'demo', + 'title' => 'Request a Demo', + 'fields' => $contactFields, + ], + 'helpdesk' => [ + 'formKey' => 'helpdesk', + 'title' => 'Request Access to Online HelpDesk', + 'fields' => [ + self::text('firstName', 'First Name', true), + self::text('lastName', 'Last Name', true), + self::text('company', 'Company', true), + self::field('phone', 'tel', 'Phone Number', false), + self::field('email', 'email', 'Email Address', true), + self::countrySelect(true), + self::text('product', 'Product', false), + self::text('serialNumber', 'Serial Number', false), + ], + ], + ]; + } + + /** + * Validate a submission payload against the definition. + * + * @param array $payload + * @return array field => error (empty = valid) + */ + public static function validate(string $formKey, array $payload): array + { + $definition = self::get($formKey); + if ($definition === null) { + return ['_form' => 'Unknown form']; + } + + $errors = []; + foreach ($definition['fields'] as $field) { + $name = $field['name']; + $value = trim((string)($payload[$name] ?? '')); + + if (($field['required'] ?? false) && $value === '') { + $errors[$name] = 'This field is required.'; + continue; + } + if ($value === '') { + continue; + } + if ($field['type'] === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) { + $errors[$name] = 'Please enter a valid email address.'; + } + if (mb_strlen($value) > 5000) { + $errors[$name] = 'Value is too long.'; + } + } + + return $errors; + } + + /** + * Reduce a raw payload to the defined fields (drops everything unknown). + * + * @param array $payload + * @return array + */ + public static function filterPayload(string $formKey, array $payload): array + { + $definition = self::get($formKey); + if ($definition === null) { + return []; + } + $out = []; + foreach ($definition['fields'] as $field) { + $out[$field['name']] = trim((string)($payload[$field['name']] ?? '')); + } + return $out; + } + + // ------------------------------------------------------------ field DSL + + /** @return array */ + private static function field(string $name, string $type, string $label, bool $required): array + { + return ['name' => $name, 'type' => $type, 'label' => $label, 'required' => $required]; + } + + /** @return array */ + private static function text(string $name, string $label, bool $required): array + { + return self::field($name, 'text', $label, $required); + } + + /** + * @param array $options + * @return array + */ + private static function select(string $name, string $label, bool $required, array $options, string $optionsSource = ''): array + { + $field = self::field($name, 'select', $label, $required); + if ($options !== []) { + $field['options'] = $options; + } + if ($optionsSource !== '') { + // The frontend supplies this list (e.g. ISO countries) — keeps + // the definition lean and the lists consistent app-wide. + $field['optionsSource'] = $optionsSource; + } + return $field; + } + + /** @return array */ + private static function countrySelect(bool $required = false): array + { + return self::select('country', 'Country', $required, [], 'countries'); + } +} diff --git a/packages/vitec/Classes/Middleware/FormSubmissionMiddleware.php b/packages/vitec/Classes/Middleware/FormSubmissionMiddleware.php new file mode 100755 index 0000000..2bc4d92 --- /dev/null +++ b/packages/vitec/Classes/Middleware/FormSubmissionMiddleware.php @@ -0,0 +1,170 @@ + + * + * Accepts JSON (or form-encoded) submissions from the React frontend for + * the VITEC form plugins (contact | demo | helpdesk): + * + * 1. honeypot check (silently accepted, nothing stored) + * 2. server-side validation against FormDefinitions (single source of truth) + * 3. submission stored in tx_vitec_form_submission (backup log) + * 4. delivery via strategy: email (active) or salesforce (prepared stub) — + * configured in the FlexForm of the form's plugin element + * + * A stored-but-undelivered submission still responds success:true — the + * data is safe in the log and the delivery status is visible in the backend. + */ +final class FormSubmissionMiddleware implements MiddlewareInterface +{ + private const PATH_PATTERN = '#^/api/vitec/form/([a-z]+)/?$#'; + + public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface + { + $path = $request->getUri()->getPath(); + if (preg_match(self::PATH_PATTERN, $path, $m) !== 1) { + return $handler->handle($request); + } + $formKey = $m[1]; + + if (strtoupper($request->getMethod()) !== 'POST') { + return new JsonResponse(['success' => false, 'errors' => ['_form' => 'POST only']], 405); + } + if (FormDefinitions::get($formKey) === null) { + return new JsonResponse(['success' => false, 'errors' => ['_form' => 'Unknown form']], 404); + } + + try { + $payload = $this->parseBody($request); + + // Honeypot: pretend success, store nothing. + if (trim((string)($payload[FormDefinitions::HONEYPOT_FIELD] ?? '')) !== '') { + return new JsonResponse(['success' => true]); + } + + $errors = FormDefinitions::validate($formKey, $payload); + if ($errors !== []) { + return new JsonResponse(['success' => false, 'errors' => $errors], 422); + } + + $clean = FormDefinitions::filterPayload($formKey, $payload); + $settings = $this->pluginSettings($formKey); + $method = (string)($settings['delivery'] ?? 'email'); + + $submissionUid = $this->store($formKey, $clean, $method); + + try { + $this->delivery($method)->deliver($formKey, $clean, $settings); + $this->updateStatus($submissionUid, 'sent', ''); + } catch (\Throwable $e) { + $this->updateStatus($submissionUid, 'failed', $e->getMessage()); + } + + // Data is safely logged either way. + return new JsonResponse(['success' => true]); + } catch (\Throwable $e) { + return new JsonResponse(['success' => false, 'errors' => ['_form' => 'Unexpected error']], 500); + } + } + + /** @return array */ + private function parseBody(ServerRequestInterface $request): array + { + $contentType = $request->getHeaderLine('Content-Type'); + if (str_contains($contentType, 'application/json')) { + $decoded = json_decode((string)$request->getBody(), true); + return is_array($decoded) ? $decoded : []; + } + $parsed = $request->getParsedBody(); + return is_array($parsed) ? $parsed : []; + } + + /** + * Delivery settings come from the FlexForm of the (first) plugin element + * of this form type — one configuration per form type. + * + * @return array + */ + private function pluginSettings(string $formKey): array + { + try { + $ctype = array_search($formKey, FormDefinitions::CTYPE_MAP, true); + if ($ctype === false) { + return []; + } + $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content'); + $row = $qb + ->select('pi_flexform') + ->from('tt_content') + ->where( + $qb->expr()->eq('CType', $qb->createNamedParameter($ctype)), + $qb->expr()->eq('deleted', 0), + $qb->expr()->eq('hidden', 0) + ) + ->orderBy('uid', 'ASC') + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + if (!$row) { + return []; + } + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $data = $flexFormService->convertFlexFormContentToArray((string)$row['pi_flexform']); + return $data['settings'] ?? []; + } catch (\Throwable $e) { + return []; + } + } + + private function delivery(string $method): DeliveryInterface + { + return $method === 'salesforce' + ? GeneralUtility::makeInstance(SalesforceDelivery::class) + : GeneralUtility::makeInstance(EmailDelivery::class); + } + + /** @param array $payload */ + private function store(string $formKey, array $payload, string $method): int + { + $conn = GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable('tx_vitec_form_submission'); + $conn->insert('tx_vitec_form_submission', [ + 'pid' => 0, + 'crdate' => time(), + 'tstamp' => time(), + 'form_key' => $formKey, + 'payload' => (string)json_encode($payload, JSON_UNESCAPED_UNICODE), + 'delivery_method' => $method, + 'delivery_status' => 'pending', + ]); + return (int)$conn->lastInsertId(); + } + + private function updateStatus(int $uid, string $status, string $error): void + { + GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable('tx_vitec_form_submission') + ->update( + 'tx_vitec_form_submission', + ['delivery_status' => $status, 'delivery_error' => mb_substr($error, 0, 1000), 'tstamp' => time()], + ['uid' => $uid] + ); + } +} diff --git a/packages/vitec/Classes/Service/ContentElementResolver.php b/packages/vitec/Classes/Service/ContentElementResolver.php index d8a0930..ca49065 100755 --- a/packages/vitec/Classes/Service/ContentElementResolver.php +++ b/packages/vitec/Classes/Service/ContentElementResolver.php @@ -17,6 +17,7 @@ use Evomedien\Vitec\UserFunc\DatasheetsJsonRenderer; use Evomedien\Vitec\UserFunc\EventlistJsonRenderer; use Evomedien\Vitec\UserFunc\LocationsJsonRenderer; use Evomedien\Vitec\UserFunc\CustomerlogosJsonRenderer; +use Evomedien\Vitec\UserFunc\FormsJsonRenderer; use Evomedien\Vitec\UserFunc\ModelcardJsonRenderer; use Evomedien\Vitec\UserFunc\NewsJsonRenderer; use TYPO3\CMS\Core\Database\ConnectionPool; @@ -82,6 +83,9 @@ final class ContentElementResolver 'vitec_locationlist' => [LocationsJsonRenderer::class, 'locations'], 'vitec_customerlogos' => [CustomerlogosJsonRenderer::class, 'customerlogos'], 'vitec_modelcard' => [ModelcardJsonRenderer::class, 'card'], + 'vitec_contactform' => [FormsJsonRenderer::class, 'form'], + 'vitec_demoform' => [FormsJsonRenderer::class, 'form'], + 'vitec_helpdeskform' => [FormsJsonRenderer::class, 'form'], 'news_pi1' => [NewsJsonRenderer::class, 'news'], 'news_newsliststicky' => [NewsJsonRenderer::class, 'news'], 'news_newsselectedlist' => [NewsJsonRenderer::class, 'news'], diff --git a/packages/vitec/Classes/Service/UsecaseSerializer.php b/packages/vitec/Classes/Service/UsecaseSerializer.php index 1d37844..4af152a 100755 --- a/packages/vitec/Classes/Service/UsecaseSerializer.php +++ b/packages/vitec/Classes/Service/UsecaseSerializer.php @@ -9,6 +9,7 @@ use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Service\ImageService; +use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection; /** * Serialises a Success Story (tx_vitec_domain_model_usecase) DB row to the @@ -43,7 +44,7 @@ final class UsecaseSerializer 'slug' => (string)($u['slug'] ?? ''), 'subtitle' => (string)($u['subtitle'] ?? ''), 'teaser' => (string)($u['teaser'] ?? ''), - 'cardImage' => $this->image($uid, 'card_image'), + 'cardImage' => $this->image($uid, 'card_image', null, true), 'customerLogo' => $this->image($uid, 'customer_logo'), 'markets' => $this->relationMulti('tx_vitec_usecase_market_mm', 'tx_vitec_domain_model_market', $uid), 'categories' => $this->categories($uid), @@ -100,7 +101,7 @@ final class UsecaseSerializer * * @return array|null */ - public function image(int $recordUid, string $fieldName, ?string $table = null): ?array + public function image(int $recordUid, string $fieldName, ?string $table = null, bool $withCropVariants = false): ?array { try { $table = $table ?? self::TABLE; @@ -142,43 +143,92 @@ final class UsecaseSerializer ]; } + $cropString = (string)($ref['crop'] ?? ''); + $payload = $this->processImagePayload($imageService, $fileReference, $cropString !== '' ? $cropString : null); - $srcset = []; - foreach (self::IMAGE_WIDTHS as $width) { - $variant = $imageService->applyProcessingInstructions( - $fileReference, - ['width' => $width, 'crop' => $ref['crop'] ?? null] - ); - $srcset[] = [ - 'url' => $imageService->getImageUri($variant), - 'width' => $width, - 'descriptor' => $width . 'w', - ]; - } - - $default = $imageService->applyProcessingInstructions( - $fileReference, - ['width' => 800, 'crop' => $ref['crop'] ?? null] - ); - - return [ + $result = [ 'uid' => (int)$ref['uid'], - 'url' => $imageService->getImageUri($default), + 'url' => $payload['url'], 'title' => (string)($ref['title'] ?? ''), 'alternative' => (string)($ref['alternative'] ?? ''), 'description' => (string)($ref['description'] ?? ''), - 'srcset' => $srcset, + 'srcset' => $payload['srcset'], 'properties' => [ 'width' => $fileReference->getProperty('width'), 'height' => $fileReference->getProperty('height'), 'mimeType' => $fileReference->getProperty('mime_type'), ], ]; + + // Zusatz-Zuschnitte (Card 4:3 / Large Card 16:9) nur fuer das "erste Bild" + // der karten-faehigen Modelle; faellt auf das Vollbild zurueck, wenn keine + // Variante gesetzt ist. + if ($withCropVariants) { + $result['crops'] = $this->cropVariantPayloads($imageService, $fileReference, $cropString); + } + + return $result; } catch (\Throwable $e) { return null; } } + /** + * Build {url, srcset} for one crop input (crop JSON string, absolute Area + * or null = full image). srcset steps whose processing would fall back to + * the original file are skipped (avoids raw-/403-leaks for sources that are + * smaller than the requested width). + * + * @param string|\TYPO3\CMS\Core\Imaging\ImageManipulation\Area|null $crop + * @return array{url:string,srcset:array>} + */ + private function processImagePayload(ImageService $imageService, \TYPO3\CMS\Core\Resource\FileReference $fileReference, $crop): array + { + $srcset = []; + foreach (self::IMAGE_WIDTHS as $width) { + $variant = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => $width, 'crop' => $crop, 'fileExtension' => 'webp'] + ); + if ($variant->usesOriginalFile()) { + continue; + } + $srcset[] = [ + 'url' => $imageService->getImageUri($variant), + 'width' => $width, + 'descriptor' => $width . 'w', + ]; + } + + $default = $imageService->applyProcessingInstructions( + $fileReference, + ['width' => 800, 'crop' => $crop, 'fileExtension' => 'webp'] + ); + + return [ + 'url' => $imageService->getImageUri($default), + 'srcset' => $srcset, + ]; + } + + /** + * card + largeCard crop-variant payloads for a model's first image. + * Each variant falls back to the full image when no explicit crop is set. + * + * @return array>}> + */ + private function cropVariantPayloads(ImageService $imageService, \TYPO3\CMS\Core\Resource\FileReference $fileReference, string $cropString): array + { + $collection = CropVariantCollection::create($cropString); + $out = []; + foreach (['card', 'largeCard'] as $variantKey) { + $area = $collection->getCropArea($variantKey); + $crop = $area->isEmpty() ? null : $area->makeAbsoluteBasedOnFile($fileReference); + $out[$variantKey] = $this->processImagePayload($imageService, $fileReference, $crop); + } + return $out; + } + /** * All FAL images of one field (multi-reference), same shape as image(). * @@ -260,13 +310,16 @@ final class UsecaseSerializer foreach (self::IMAGE_WIDTHS as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $ref['crop'] ?? null] + ['width' => $width, 'crop' => $ref['crop'] ?? null, 'fileExtension' => 'webp'] ); + if ($variant->usesOriginalFile()) { + continue; // Quelle zu klein fuer diese Breite: kein Raw-/403-Leak + } $srcset[] = ['url' => $imageService->getImageUri($variant), 'width' => $width, 'descriptor' => $width . 'w']; } $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 800, 'crop' => $ref['crop'] ?? null] + ['width' => 800, 'crop' => $ref['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/Tca/CropVariants.php b/packages/vitec/Classes/Tca/CropVariants.php new file mode 100755 index 0000000..23e224d --- /dev/null +++ b/packages/vitec/Classes/Tca/CropVariants.php @@ -0,0 +1,56 @@ + \Evomedien\Vitec\Tca\CropVariants::firstImage(), + */ +final class CropVariants +{ + /** + * @return array> + */ + public static function firstImage(): array + { + return [ + 'default' => [ + 'title' => 'Default', + 'allowedAspectRatios' => [ + 'NaN' => ['title' => 'Free', 'value' => 0.0], + '16:9' => ['title' => '16:9', 'value' => 16 / 9], + '4:3' => ['title' => '4:3', 'value' => 4 / 3], + '1:1' => ['title' => '1:1', 'value' => 1.0], + ], + ], + 'card' => [ + 'title' => 'Card (4:3)', + 'allowedAspectRatios' => [ + '4:3' => ['title' => '4:3', 'value' => 4 / 3], + 'NaN' => ['title' => 'Free', 'value' => 0.0], + ], + ], + 'largeCard' => [ + 'title' => 'Large Card (16:9)', + 'allowedAspectRatios' => [ + '16:9' => ['title' => '16:9', 'value' => 16 / 9], + 'NaN' => ['title' => 'Free', 'value' => 0.0], + ], + ], + ]; + } +} diff --git a/packages/vitec/Classes/UserFunc/CustomerlogosJsonRenderer.php b/packages/vitec/Classes/UserFunc/CustomerlogosJsonRenderer.php index a8c23e9..94eb3c2 100755 --- a/packages/vitec/Classes/UserFunc/CustomerlogosJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/CustomerlogosJsonRenderer.php @@ -204,13 +204,13 @@ class CustomerlogosJsonRenderer foreach (self::IMAGE_WIDTHS as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $ref['crop'] ?? null] + ['width' => $width, 'crop' => $ref['crop'] ?? null, 'fileExtension' => 'webp'] ); $srcset[] = ['url' => $imageService->getImageUri($variant), 'width' => $width, 'descriptor' => $width . 'w']; } $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 400, 'crop' => $ref['crop'] ?? null] + ['width' => 400, 'crop' => $ref['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/DatasheetsJsonRenderer.php b/packages/vitec/Classes/UserFunc/DatasheetsJsonRenderer.php index cbd3a75..26b9683 100755 --- a/packages/vitec/Classes/UserFunc/DatasheetsJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/DatasheetsJsonRenderer.php @@ -338,7 +338,7 @@ class DatasheetsJsonRenderer $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 400, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 400, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/DownloadcardcollectionJsonRenderer.php b/packages/vitec/Classes/UserFunc/DownloadcardcollectionJsonRenderer.php index 64219da..61283e8 100755 --- a/packages/vitec/Classes/UserFunc/DownloadcardcollectionJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/DownloadcardcollectionJsonRenderer.php @@ -211,7 +211,7 @@ class DownloadcardcollectionJsonRenderer foreach ([400, 800, 1200, 1600] as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $fileRefData['crop'] ?? null] + ['width' => $width, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); $srcset[] = [ 'url' => $imageService->getImageUri($variant), @@ -222,7 +222,7 @@ class DownloadcardcollectionJsonRenderer $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/EventlistJsonRenderer.php b/packages/vitec/Classes/UserFunc/EventlistJsonRenderer.php index 0bd9bc1..b261b7c 100755 --- a/packages/vitec/Classes/UserFunc/EventlistJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/EventlistJsonRenderer.php @@ -247,7 +247,7 @@ class EventlistJsonRenderer foreach ([400, 800] as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $fileRefData['crop'] ?? null] + ['width' => $width, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); $srcset[] = [ 'url' => $imageService->getImageUri($variant), @@ -258,7 +258,7 @@ class EventlistJsonRenderer $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 400, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 400, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/FaviconsJsonRenderer.php b/packages/vitec/Classes/UserFunc/FaviconsJsonRenderer.php new file mode 100755 index 0000000..b377da6 --- /dev/null +++ b/packages/vitec/Classes/UserFunc/FaviconsJsonRenderer.php @@ -0,0 +1,47 @@ +/ head tags from the CMS instead of + * hard-coding them. The icon files live under public/fileadmin/icons/ and are + * referenced root-relative — identical convention to the image URLs emitted by + * UsecaseSerializer::image() (getImageUri also returns /fileadmin/... paths). + * If the frontend ever runs on a separate origin from the CMS, it must prefix + * these hrefs with the CMS base URL, exactly as it does for image URLs. + * + * Static by nature (same for every page) — no DB access, exception-free. + */ +final class FaviconsJsonRenderer +{ + private const BASE = '/fileadmin/icons'; + + /** Browser UI / address-bar tint (VITEC navy). */ + private const THEME_COLOR = '#26358C'; + + #[AsAllowedCallable] + public function render(string $content, array $conf): string + { + $payload = [ + 'themeColor' => self::THEME_COLOR, + 'manifest' => self::BASE . '/site.webmanifest', + // Ready-to-render descriptors, ordered most- to least-specific. + 'links' => [ + ['rel' => 'icon', 'type' => 'image/x-icon', 'href' => self::BASE . '/favicon.ico'], + ['rel' => 'icon', 'type' => 'image/png', 'sizes' => '32x32', 'href' => self::BASE . '/favicon-32.png'], + ['rel' => 'icon', 'type' => 'image/png', 'sizes' => '16x16', 'href' => self::BASE . '/favicon-16.png'], + ['rel' => 'apple-touch-icon', 'sizes' => '180x180', 'href' => self::BASE . '/apple-touch-icon.png'], + ['rel' => 'manifest', 'href' => self::BASE . '/site.webmanifest'], + ], + ]; + + return json_encode($payload) ?: ''; + } +} diff --git a/packages/vitec/Classes/UserFunc/FormsJsonRenderer.php b/packages/vitec/Classes/UserFunc/FormsJsonRenderer.php new file mode 100755 index 0000000..9907def --- /dev/null +++ b/packages/vitec/Classes/UserFunc/FormsJsonRenderer.php @@ -0,0 +1,108 @@ +cObj->data ?? null) ? $this->cObj->data : null; + if ($row && isset(FormDefinitions::CTYPE_MAP[(string)($row['CType'] ?? '')])) { + return $this->renderForRecord($row); + } + + $pageId = 0; + $request = $GLOBALS['TYPO3_REQUEST'] ?? null; + if ($request !== null) { + $pageInfo = $request->getAttribute('frontend.page.information'); + if ($pageInfo !== null) { + $pageId = (int)$pageInfo->getId(); + } + } + if ($pageId <= 0) { + $pageId = (int)($GLOBALS['TSFE']->id ?? 0); + } + if ($pageId <= 0) { + return ''; + } + + $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content'); + $ces = $qb + ->select('*') + ->from('tt_content') + ->where( + $qb->expr()->eq('pid', $qb->createNamedParameter($pageId, ParameterType::INTEGER)), + $qb->expr()->in('CType', $qb->createNamedParameter( + array_keys(FormDefinitions::CTYPE_MAP), + \TYPO3\CMS\Core\Database\Connection::PARAM_STR_ARRAY + )), + $qb->expr()->eq('deleted', 0), + $qb->expr()->eq('hidden', 0) + ) + ->executeQuery() + ->fetchAllAssociative(); + + if (empty($ces)) { + return ''; + } + + return $this->renderForRecord($ces[0]); + } + + /** + * @param array $contentElement + */ + public function renderForRecord(array $contentElement): string + { + try { + $formKey = FormDefinitions::CTYPE_MAP[(string)($contentElement['CType'] ?? '')] ?? ''; + $definition = FormDefinitions::get($formKey); + if ($definition === null) { + return ''; + } + + $flexFormService = GeneralUtility::makeInstance(FlexFormService::class); + $flexFormData = $flexFormService->convertFlexFormContentToArray($contentElement['pi_flexform'] ?? ''); + $settings = $flexFormData['settings'] ?? []; + $debugMode = (bool)($settings['debug'] ?? false); + + $response = [ + 'formKey' => $definition['formKey'], + 'title' => $definition['title'], + 'endpoint' => '/api/vitec/form/' . $definition['formKey'], + 'honeypot' => FormDefinitions::HONEYPOT_FIELD, + 'fields' => $definition['fields'], + ]; + + if ($debugMode) { + $response['debug'] = ['settings' => $settings]; + } + + return (string)json_encode($response); + } catch (\Throwable $e) { + return ''; + } + } +} diff --git a/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php index 19a28e4..a790e9f 100755 --- a/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/MarketShowJsonRenderer.php @@ -201,7 +201,7 @@ class MarketShowJsonRenderer foreach ([400, 800, 1200, 1600] as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $fileRefData['crop'] ?? null] + ['width' => $width, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); $srcset[] = [ 'url' => $imageService->getImageUri($variant), @@ -212,7 +212,7 @@ class MarketShowJsonRenderer $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php b/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php index 8f84f5e..762b1a7 100755 --- a/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/ModelcardJsonRenderer.php @@ -157,8 +157,8 @@ class ModelcardJsonRenderer 'slug' => (string)($row['slug'] ?? ''), 'subtitle' => (string)($row['subtitle'] ?? ''), 'teaser' => (string)($row['teaser'] ?? ''), - 'image' => $serializer->image($uid, 'image', 'tx_vitec_domain_model_product') - ?? $serializer->image($uid, 'productimage', 'tx_vitec_domain_model_product'), + 'image' => $serializer->image($uid, 'image', 'tx_vitec_domain_model_product', true) + ?? $serializer->image($uid, 'productimage', 'tx_vitec_domain_model_product', true), ]; } @@ -169,7 +169,7 @@ class ModelcardJsonRenderer 'subtitle' => (string)($row['subtitle'] ?? ''), 'teaser' => (string)($row['teaser'] ?? ''), 'description' => (string)($row['description'] ?? ''), - 'image' => $serializer->image($uid, 'image', self::MODEL_TABLES[$modelType]), + 'image' => $serializer->image($uid, 'image', self::MODEL_TABLES[$modelType], true), ]; } } diff --git a/packages/vitec/Classes/UserFunc/NewsJsonRenderer.php b/packages/vitec/Classes/UserFunc/NewsJsonRenderer.php index 8e09ee1..d4da1a3 100644 --- a/packages/vitec/Classes/UserFunc/NewsJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/NewsJsonRenderer.php @@ -596,7 +596,7 @@ final class NewsJsonRenderer foreach ($rows as $row) { try { $fileReference = $resourceFactory->getFileReferenceObject((int)$row['uid']); - $processed = $imageService->applyProcessingInstructions($fileReference, ['width' => 1400]); + $processed = $imageService->applyProcessingInstructions($fileReference, ['width' => 1400, 'fileExtension' => 'webp']); $files[] = [ 'uid' => (int)$row['uid'], diff --git a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php index 4b56db9..1ce637b 100755 --- a/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/ProductListJsonRenderer.php @@ -246,7 +246,8 @@ class ProductListJsonRenderer [ 'width' => $dimensions['width'], 'height' => $dimensions['height'], - 'crop' => $fileRefData['crop'] ?? null + 'crop' => $fileRefData['crop'] ?? null, + 'fileExtension' => 'webp' ] ); @@ -260,7 +261,7 @@ class ProductListJsonRenderer $defaultProcessed = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); $images[] = [ @@ -318,7 +319,7 @@ class ProductListJsonRenderer $processed = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 1200, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 1200, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php index 5f57403..4696961 100755 --- a/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/ProductShowJsonRenderer.php @@ -327,14 +327,14 @@ class ProductShowJsonRenderer $processedImage = $imageService->applyProcessingInstructions( $fileReference, - ['width' => '1874c', 'height' => '625c'] + ['width' => '1874c', 'height' => '625c', 'fileExtension' => 'webp'] ); $srcset = []; foreach ([400, 800, 1200, 1600] as $width) { $processedVariant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width . 'c', 'height' => (int)($width / 3) . 'c'] + ['width' => $width . 'c', 'height' => (int)($width / 3) . 'c', 'fileExtension' => 'webp'] ); $srcset[] = [ 'url' => $imageService->getImageUri($processedVariant), @@ -399,7 +399,7 @@ class ProductShowJsonRenderer $processedImage = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 1200] + ['width' => 1200, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php b/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php index aa77f4d..133dc8a 100755 --- a/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php +++ b/packages/vitec/Classes/UserFunc/SolutionShowJsonRenderer.php @@ -201,7 +201,7 @@ class SolutionShowJsonRenderer foreach ([400, 800, 1200, 1600] as $width) { $variant = $imageService->applyProcessingInstructions( $fileReference, - ['width' => $width, 'crop' => $fileRefData['crop'] ?? null] + ['width' => $width, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); $srcset[] = [ 'url' => $imageService->getImageUri($variant), @@ -212,7 +212,7 @@ class SolutionShowJsonRenderer $default = $imageService->applyProcessingInstructions( $fileReference, - ['width' => 800, 'crop' => $fileRefData['crop'] ?? null] + ['width' => 800, 'crop' => $fileRefData['crop'] ?? null, 'fileExtension' => 'webp'] ); return [ diff --git a/packages/vitec/Configuration/FlexForms/Contactform.xml b/packages/vitec/Configuration/FlexForms/Contactform.xml new file mode 100755 index 0000000..be52e45 --- /dev/null +++ b/packages/vitec/Configuration/FlexForms/Contactform.xml @@ -0,0 +1,65 @@ + + + + 1 + + + + + Contact Form Settings + array + + + + reload + + select + selectSingle + + + + email + + + + salesforce + + + email + + + + + + Comma-separated email addresses. + FIELD:settings.delivery:=:email + + input + 50 + trim + + + + + + FIELD:settings.delivery:=:email + + input + 50 + trim + VITEC website: contact request + + + + + + + check + 0 + + + + + + + diff --git a/packages/vitec/Configuration/FlexForms/Demoform.xml b/packages/vitec/Configuration/FlexForms/Demoform.xml new file mode 100755 index 0000000..b84713c --- /dev/null +++ b/packages/vitec/Configuration/FlexForms/Demoform.xml @@ -0,0 +1,65 @@ + + + + 1 + + + + + Request Demo Settings + array + + + + reload + + select + selectSingle + + + + email + + + + salesforce + + + email + + + + + + Comma-separated email addresses. + FIELD:settings.delivery:=:email + + input + 50 + trim + + + + + + FIELD:settings.delivery:=:email + + input + 50 + trim + VITEC website: demo request + + + + + + + check + 0 + + + + + + + diff --git a/packages/vitec/Configuration/FlexForms/Helpdeskform.xml b/packages/vitec/Configuration/FlexForms/Helpdeskform.xml new file mode 100755 index 0000000..98c77e1 --- /dev/null +++ b/packages/vitec/Configuration/FlexForms/Helpdeskform.xml @@ -0,0 +1,65 @@ + + + + 1 + + + + + HelpDesk Access Settings + array + + + + reload + + select + selectSingle + + + + email + + + + salesforce + + + email + + + + + + Comma-separated email addresses. + FIELD:settings.delivery:=:email + + input + 50 + trim + + + + + + FIELD:settings.delivery:=:email + + input + 50 + trim + VITEC website: helpdesk access request + + + + + + + check + 0 + + + + + + + diff --git a/packages/vitec/Configuration/Icons.php b/packages/vitec/Configuration/Icons.php index afa33f7..6615d2a 100755 --- a/packages/vitec/Configuration/Icons.php +++ b/packages/vitec/Configuration/Icons.php @@ -88,4 +88,16 @@ return [ 'provider' => SvgIconProvider::class, 'source' => 'EXT:vitec/Resources/Public/Icons/vitec-plugin-modelcard.svg', ], + 'vitec-plugin-contactform' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:vitec/Resources/Public/Icons/vitec-plugin-contactform.svg', + ], + 'vitec-plugin-demoform' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:vitec/Resources/Public/Icons/vitec-plugin-demoform.svg', + ], + 'vitec-plugin-helpdeskform' => [ + 'provider' => SvgIconProvider::class, + 'source' => 'EXT:vitec/Resources/Public/Icons/vitec-plugin-helpdeskform.svg', + ], ]; diff --git a/packages/vitec/Configuration/RequestMiddlewares.php b/packages/vitec/Configuration/RequestMiddlewares.php index 78eda3a..740ce85 100755 --- a/packages/vitec/Configuration/RequestMiddlewares.php +++ b/packages/vitec/Configuration/RequestMiddlewares.php @@ -7,6 +7,15 @@ declare(strict_types=1); */ return [ 'frontend' => [ + 'vitec/form-submission' => [ + 'target' => \Evomedien\Vitec\Middleware\FormSubmissionMiddleware::class, + 'after' => [ + 'typo3/cms-core/normalized-params-attribute', + ], + 'before' => [ + 'typo3/cms-frontend/site', + ], + ], 'vitec/success-story-path-rewrite' => [ 'target' => \Evomedien\Vitec\Middleware\SuccessStoryPathRewrite::class, 'after' => [ diff --git a/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript b/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript index 16b1442..d1cce82 100755 --- a/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript +++ b/packages/vitec/Configuration/Sets/Vitecset/setup.typoscript @@ -161,6 +161,21 @@ tt_content { } } + vitec_contactform < lib.contentElementWithHeader + vitec_contactform { + fields { + content { + fields { + form = USER + form.userFunc = Evomedien\Vitec\UserFunc\FormsJsonRenderer->render + } + } + } + } + + vitec_demoform < tt_content.vitec_contactform + vitec_helpdeskform < tt_content.vitec_contactform + vitec_modelcard < lib.contentElementWithHeader vitec_modelcard { fields { diff --git a/packages/vitec/Configuration/TCA/Overrides/tt_content.php b/packages/vitec/Configuration/TCA/Overrides/tt_content.php index bc9ca31..1f4af79 100644 --- a/packages/vitec/Configuration/TCA/Overrides/tt_content.php +++ b/packages/vitec/Configuration/TCA/Overrides/tt_content.php @@ -38,6 +38,37 @@ use TYPO3\CMS\Extbase\Utility\ExtensionUtility; // 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', diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_market.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_market.php index 12420ce..1cc90ff 100644 --- a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_market.php +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_market.php @@ -164,6 +164,11 @@ return [ 'foreign_selector' => 'uid_local', 'overrideChildTca' => [ 'columns' => [ + 'crop' => [ + 'config' => [ + 'cropVariants' => \Evomedien\Vitec\Tca\CropVariants::firstImage(), + ], + ], 'uid_local' => [ 'config' => [ 'appearance' => [ diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php index 28b788e..5a5efe0 100755 --- a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_product.php @@ -443,6 +443,15 @@ return [ 'foreign_match_fields' => [ 'fieldname' => 'productimage', ], + 'overrideChildTca' => [ + 'columns' => [ + 'crop' => [ + 'config' => [ + 'cropVariants' => \Evomedien\Vitec\Tca\CropVariants::firstImage(), + ], + ], + ], + ], 'appearance' => [ 'collapseAll' => true, 'levelLinksPosition' => 'top', diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php index 566f9b7..14dd45b 100644 --- a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_solution.php @@ -164,6 +164,11 @@ return [ 'foreign_selector' => 'uid_local', 'overrideChildTca' => [ 'columns' => [ + 'crop' => [ + 'config' => [ + 'cropVariants' => \Evomedien\Vitec\Tca\CropVariants::firstImage(), + ], + ], 'uid_local' => [ 'config' => [ 'appearance' => [ diff --git a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_usecase.php b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_usecase.php index 07ba32c..8a16493 100644 --- a/packages/vitec/Configuration/TCA/tx_vitec_domain_model_usecase.php +++ b/packages/vitec/Configuration/TCA/tx_vitec_domain_model_usecase.php @@ -117,7 +117,21 @@ return [ // --------------------------------------------------------- List view 'card_image' => [ 'label' => 'Card Image', - 'config' => ['type' => 'file', 'maxitems' => 1, 'allowed' => 'common-image-types'], + 'config' => [ + 'type' => 'file', + 'maxitems' => 1, + 'allowed' => 'common-image-types', + // Zusatz-Zuschnitte fuer Card-/Large-Card-Darstellung (freier Crop bleibt moeglich). + 'overrideChildTca' => [ + 'columns' => [ + 'crop' => [ + 'config' => [ + 'cropVariants' => \Evomedien\Vitec\Tca\CropVariants::firstImage(), + ], + ], + ], + ], + ], ], 'customer_logo' => [ 'label' => 'Customer Logo', diff --git a/packages/vitec/Configuration/TCA/tx_vitec_form_submission.php b/packages/vitec/Configuration/TCA/tx_vitec_form_submission.php new file mode 100755 index 0000000..ba3b4c0 --- /dev/null +++ b/packages/vitec/Configuration/TCA/tx_vitec_form_submission.php @@ -0,0 +1,41 @@ + [ + 'title' => 'VITEC Form Submission', + 'label' => 'form_key', + 'label_alt' => 'delivery_status', + 'label_alt_force' => true, + 'tstamp' => 'tstamp', + 'crdate' => 'crdate', + 'default_sortby' => 'crdate DESC', + 'rootLevel' => 1, + 'readOnly' => true, + 'iconfile' => 'EXT:vitec/Resources/Public/Icons/vitec-plugin-contactform.svg', + 'security' => ['ignorePageTypeRestriction' => true], + ], + 'types' => [ + '1' => ['showitem' => 'form_key, delivery_method, delivery_status, delivery_error, payload'], + ], + 'columns' => [ + 'form_key' => [ + 'label' => 'Form', + 'config' => ['type' => 'input', 'readOnly' => true], + ], + 'payload' => [ + 'label' => 'Data (JSON)', + 'config' => ['type' => 'text', 'rows' => 12, 'readOnly' => true], + ], + 'delivery_method' => [ + 'label' => 'Delivery', + 'config' => ['type' => 'input', 'readOnly' => true], + ], + 'delivery_status' => [ + 'label' => 'Status', + 'config' => ['type' => 'input', 'readOnly' => true], + ], + 'delivery_error' => [ + 'label' => 'Delivery Error', + 'config' => ['type' => 'text', 'rows' => 3, 'readOnly' => true], + ], + ], +]; diff --git a/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript b/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript index 4cebe2b..67f0419 100755 --- a/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript +++ b/packages/vitec/Configuration/TypoScript/Headless/vitec_menus.typoscript @@ -72,3 +72,10 @@ lib.metaMenu { page.10.fields.mainNavigation =< lib.mainNavigation page.10.fields.footerMenu =< lib.footerMenu page.10.fields.metaMenu =< lib.metaMenu + +# ----------------------------------------------------------------------------- +# Favicon / app-icon set (site-wide) — see FaviconsJsonRenderer +# Icon files: public/fileadmin/icons/ +# ----------------------------------------------------------------------------- +page.10.fields.favicons = USER +page.10.fields.favicons.userFunc = Evomedien\Vitec\UserFunc\FaviconsJsonRenderer->render diff --git a/packages/vitec/Resources/Public/Css/backend-container-collapse.css b/packages/vitec/Resources/Public/Css/backend-container-collapse.css index 8d3e19b..74f0049 100644 --- a/packages/vitec/Resources/Public/Css/backend-container-collapse.css +++ b/packages/vitec/Resources/Public/Css/backend-container-collapse.css @@ -20,3 +20,15 @@ .t3-page-ce.vitec-ce-collapsed .vitec-collapse-toggle typo3-backend-icon { transform: rotate(180deg); } + +/* Collapsed: the element's own preview header lives in the CE header row — + full width on its own line below the icon/button row. */ +.t3-page-ce-header:has(.vitec-preview-header--in-header) { + flex-wrap: wrap; +} +.element-preview-header.vitec-preview-header--in-header { + flex-basis: 100%; + width: 100%; + order: 10; + margin-top: 0.25rem; +} diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-contactform.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-contactform.svg new file mode 100644 index 0000000..1bb0a47 --- /dev/null +++ b/packages/vitec/Resources/Public/Icons/vitec-plugin-contactform.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-customerlogos.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-customerlogos.svg old mode 100755 new mode 100644 diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-demoform.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-demoform.svg new file mode 100644 index 0000000..4f74aa2 --- /dev/null +++ b/packages/vitec/Resources/Public/Icons/vitec-plugin-demoform.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-helpdeskform.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-helpdeskform.svg new file mode 100644 index 0000000..5a11781 --- /dev/null +++ b/packages/vitec/Resources/Public/Icons/vitec-plugin-helpdeskform.svg @@ -0,0 +1,4 @@ + + + + diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-locationlist.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-locationlist.svg old mode 100755 new mode 100644 diff --git a/packages/vitec/Resources/Public/Icons/vitec-plugin-modelcard.svg b/packages/vitec/Resources/Public/Icons/vitec-plugin-modelcard.svg old mode 100755 new mode 100644 diff --git a/packages/vitec/Resources/Public/Images/favicon-32.png b/packages/vitec/Resources/Public/Images/favicon-32.png new file mode 100644 index 0000000..7915d50 Binary files /dev/null and b/packages/vitec/Resources/Public/Images/favicon-32.png differ diff --git a/packages/vitec/Resources/Public/Images/favicon.ico b/packages/vitec/Resources/Public/Images/favicon.ico new file mode 100644 index 0000000..87e5cb8 Binary files /dev/null and b/packages/vitec/Resources/Public/Images/favicon.ico differ diff --git a/packages/vitec/Resources/Public/Javascript/container-collapse.js b/packages/vitec/Resources/Public/Javascript/container-collapse.js index e61a74b..3d1fbb4 100644 --- a/packages/vitec/Resources/Public/Javascript/container-collapse.js +++ b/packages/vitec/Resources/Public/Javascript/container-collapse.js @@ -118,14 +118,70 @@ class VitecContainerCollapse { if (this.isCollapsed(uid)) { ce.classList.add('vitec-ce-collapsed'); } + this.syncPreviewHeader(ce); button.addEventListener('click', (event) => { event.preventDefault(); event.stopPropagation(); const collapsed = ce.classList.toggle('vitec-ce-collapsed'); this.setCollapsed(uid, collapsed); + this.syncPreviewHeader(ce); }); } + + /** + * Keep the element's own `.element-preview-header` visible while collapsed: + * it is moved into the CE header row (after `.t3-page-ce-header-right`) and + * moved back to its original spot (placeholder marker) on expand. + */ + syncPreviewHeader(ce) { + const element = ce.querySelector(':scope > .t3-page-ce-element'); + if (!element) { + return; + } + const header = element.querySelector(':scope > .t3-page-ce-header'); + const body = element.querySelector(':scope > .t3-page-ce-body'); + if (!header || !body) { + return; + } + + const collapsed = ce.classList.contains('vitec-ce-collapsed'); + + if (collapsed) { + // Find THIS element's preview header (not one of a nested child CE). + let preview = null; + for (const candidate of body.querySelectorAll('.element-preview-header')) { + if (candidate.closest('.t3-page-ce') === ce) { + preview = candidate; + break; + } + } + if (!preview || header.contains(preview)) { + return; + } + const placeholder = document.createElement('span'); + placeholder.className = 'vitec-preview-header-placeholder'; + placeholder.hidden = true; + preview.before(placeholder); + preview.classList.add('vitec-preview-header--in-header'); + const headerRight = header.querySelector('.t3-page-ce-header-right'); + if (headerRight) { + headerRight.insertAdjacentElement('afterend', preview); + } else { + header.appendChild(preview); + } + } else { + const preview = header.querySelector('.element-preview-header.vitec-preview-header--in-header'); + const placeholder = body.querySelector('.vitec-preview-header-placeholder'); + if (preview && placeholder) { + preview.classList.remove('vitec-preview-header--in-header'); + placeholder.replaceWith(preview); + } else if (preview) { + preview.classList.remove('vitec-preview-header--in-header'); + body.prepend(preview); + } + } + } } export default new VitecContainerCollapse(); diff --git a/packages/vitec/ext_localconf.php b/packages/vitec/ext_localconf.php index 55e2c26..f3a15ae 100755 --- a/packages/vitec/ext_localconf.php +++ b/packages/vitec/ext_localconf.php @@ -193,4 +193,38 @@ $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1750000000] = [ ] ); + + ExtensionUtility::configurePlugin( + 'Vitec', + 'Contactform', + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ], + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ] + ); + + ExtensionUtility::configurePlugin( + 'Vitec', + 'Demoform', + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ], + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ] + ); + + ExtensionUtility::configurePlugin( + 'Vitec', + 'Helpdeskform', + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ], + [ + \Evomedien\Vitec\Controller\FormController::class => 'list' + ] + ); + })(); diff --git a/packages/vitec/ext_tables.sql b/packages/vitec/ext_tables.sql index 2828c46..bf72035 100755 --- a/packages/vitec/ext_tables.sql +++ b/packages/vitec/ext_tables.sql @@ -281,3 +281,11 @@ CREATE TABLE tx_vitec_domain_model_customer ( logo int(11) unsigned DEFAULT '0' NOT NULL, emphasize_logo smallint(5) unsigned DEFAULT '0' NOT NULL ); + +CREATE TABLE tx_vitec_form_submission ( + form_key varchar(30) DEFAULT '' NOT NULL, + payload text, + delivery_method varchar(20) DEFAULT '' NOT NULL, + delivery_status varchar(20) DEFAULT '' NOT NULL, + delivery_error text +); diff --git a/public/_frontend/.htaccess b/public/_frontend/.htaccess index 52d7f05..15135c6 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 28191e9..c173b31 100644 --- a/public/_frontend/index.html +++ b/public/_frontend/index.html @@ -21,12 +21,12 @@ - + VITEC - - + +