diff --git a/composer.lock b/composer.lock
index dedc5efe..3da546ae 100644
--- a/composer.lock
+++ b/composer.lock
@@ -2272,16 +2272,16 @@
},
{
"name": "jms/serializer",
- "version": "3.32.4",
+ "version": "3.32.5",
"source": {
"type": "git",
"url": "https://github.com/schmittjoh/serializer.git",
- "reference": "f5c6227b2664d1e75fda65f1e6c5686a0c034b31"
+ "reference": "7c88b1b02ff868eecc870eeddbb3b1250e4bd89c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/f5c6227b2664d1e75fda65f1e6c5686a0c034b31",
- "reference": "f5c6227b2664d1e75fda65f1e6c5686a0c034b31",
+ "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/7c88b1b02ff868eecc870eeddbb3b1250e4bd89c",
+ "reference": "7c88b1b02ff868eecc870eeddbb3b1250e4bd89c",
"shasum": ""
},
"require": {
@@ -2358,7 +2358,7 @@
],
"support": {
"issues": "https://github.com/schmittjoh/serializer/issues",
- "source": "https://github.com/schmittjoh/serializer/tree/3.32.4"
+ "source": "https://github.com/schmittjoh/serializer/tree/3.32.5"
},
"funding": [
{
@@ -2370,7 +2370,7 @@
"type": "github"
}
],
- "time": "2025-04-06T18:42:47+00:00"
+ "time": "2025-05-26T15:55:41+00:00"
},
{
"name": "jms/serializer-bundle",
@@ -3202,16 +3202,16 @@
},
{
"name": "nelmio/api-doc-bundle",
- "version": "v5.2.0",
+ "version": "v5.3.0",
"source": {
"type": "git",
"url": "https://github.com/nelmio/NelmioApiDocBundle.git",
- "reference": "f48d9cb3930a34df209547ffd86db01254d5fce8"
+ "reference": "60429836bab244d3c92f37b1c329af89aafd786f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/f48d9cb3930a34df209547ffd86db01254d5fce8",
- "reference": "f48d9cb3930a34df209547ffd86db01254d5fce8",
+ "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/60429836bab244d3c92f37b1c329af89aafd786f",
+ "reference": "60429836bab244d3c92f37b1c329af89aafd786f",
"shasum": ""
},
"require": {
@@ -3261,6 +3261,7 @@
"symfony/serializer": "^6.4 || ^7.1",
"symfony/stopwatch": "^6.4 || ^7.1",
"symfony/templating": "^6.4 || ^7.1",
+ "symfony/translation": "^6.4 || ^7.1",
"symfony/twig-bundle": "^6.4 || ^7.1",
"symfony/uid": "^6.4 || ^7.1",
"symfony/validator": "^6.4 || ^7.1",
@@ -3312,7 +3313,7 @@
],
"support": {
"issues": "https://github.com/nelmio/NelmioApiDocBundle/issues",
- "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.2.0"
+ "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v5.3.0"
},
"funding": [
{
@@ -3320,7 +3321,7 @@
"type": "github"
}
],
- "time": "2025-05-23T21:30:13+00:00"
+ "time": "2025-05-24T13:30:55+00:00"
},
{
"name": "nelmio/cors-bundle",
@@ -3386,16 +3387,16 @@
},
{
"name": "nikic/php-parser",
- "version": "v5.4.0",
+ "version": "v5.5.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494"
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494",
- "reference": "447a020a1f875a434d62f2a401f53b82a396e494",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
"shasum": ""
},
"require": {
@@ -3438,22 +3439,22 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
},
- "time": "2024-12-30T11:07:19+00:00"
+ "time": "2025-05-31T08:24:38+00:00"
},
{
"name": "onelogin/php-saml",
- "version": "4.2.0",
+ "version": "4.3.0",
"source": {
"type": "git",
"url": "https://github.com/SAML-Toolkits/php-saml.git",
- "reference": "d3b5172f137db2f412239432d77253ceaaa1e939"
+ "reference": "bf5efce9f2df5d489d05e78c27003a0fc8bc50f0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/SAML-Toolkits/php-saml/zipball/d3b5172f137db2f412239432d77253ceaaa1e939",
- "reference": "d3b5172f137db2f412239432d77253ceaaa1e939",
+ "url": "https://api.github.com/repos/SAML-Toolkits/php-saml/zipball/bf5efce9f2df5d489d05e78c27003a0fc8bc50f0",
+ "reference": "bf5efce9f2df5d489d05e78c27003a0fc8bc50f0",
"shasum": ""
},
"require": {
@@ -3504,7 +3505,7 @@
"type": "github"
}
],
- "time": "2024-05-30T15:10:40+00:00"
+ "time": "2025-05-25T14:28:00+00:00"
},
{
"name": "openspout/openspout",
@@ -5299,16 +5300,16 @@
},
{
"name": "symfony/cache-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/cache-contracts.git",
- "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b"
+ "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b",
- "reference": "15a4f8e5cd3bce9aeafc882b1acab39ec8de2c1b",
+ "url": "https://api.github.com/repos/symfony/cache-contracts/zipball/5d68a57d66910405e5c0b63d6f0af941e66fc868",
+ "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868",
"shasum": ""
},
"require": {
@@ -5322,7 +5323,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -5355,7 +5356,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/cache-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -5371,7 +5372,7 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2025-03-13T15:25:07+00:00"
},
{
"name": "symfony/clock",
@@ -5449,16 +5450,16 @@
},
{
"name": "symfony/config",
- "version": "v6.4.14",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
- "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef"
+ "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/4e55e7e4ffddd343671ea972216d4509f46c22ef",
- "reference": "4e55e7e4ffddd343671ea972216d4509f46c22ef",
+ "url": "https://api.github.com/repos/symfony/config/zipball/af5917a3b1571f54689e56677a3f06440d2fe4c7",
+ "reference": "af5917a3b1571f54689e56677a3f06440d2fe4c7",
"shasum": ""
},
"require": {
@@ -5504,7 +5505,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/config/tree/v6.4.14"
+ "source": "https://github.com/symfony/config/tree/v6.4.22"
},
"funding": [
{
@@ -5520,20 +5521,20 @@
"type": "tidelift"
}
],
- "time": "2024-11-04T11:33:53+00:00"
+ "time": "2025-05-14T06:00:01+00:00"
},
{
"name": "symfony/console",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
- "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719"
+ "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/a3011c7b7adb58d89f6c0d822abb641d7a5f9719",
- "reference": "a3011c7b7adb58d89f6c0d822abb641d7a5f9719",
+ "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3",
+ "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3",
"shasum": ""
},
"require": {
@@ -5598,7 +5599,7 @@
"terminal"
],
"support": {
- "source": "https://github.com/symfony/console/tree/v6.4.21"
+ "source": "https://github.com/symfony/console/tree/v6.4.22"
},
"funding": [
{
@@ -5614,7 +5615,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-07T15:42:41+00:00"
+ "time": "2025-05-07T07:05:04+00:00"
},
{
"name": "symfony/css-selector",
@@ -5683,16 +5684,16 @@
},
{
"name": "symfony/dependency-injection",
- "version": "v6.4.20",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/dependency-injection.git",
- "reference": "c49796a9184a532843e78e50df9e55708b92543a"
+ "reference": "8cb11f833d1f5bfbb2df97dfc23c92b4d42c18d9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c49796a9184a532843e78e50df9e55708b92543a",
- "reference": "c49796a9184a532843e78e50df9e55708b92543a",
+ "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8cb11f833d1f5bfbb2df97dfc23c92b4d42c18d9",
+ "reference": "8cb11f833d1f5bfbb2df97dfc23c92b4d42c18d9",
"shasum": ""
},
"require": {
@@ -5744,7 +5745,7 @@
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dependency-injection/tree/v6.4.20"
+ "source": "https://github.com/symfony/dependency-injection/tree/v6.4.22"
},
"funding": [
{
@@ -5760,20 +5761,20 @@
"type": "tidelift"
}
],
- "time": "2025-03-13T09:55:08+00:00"
+ "time": "2025-05-17T07:35:26+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6"
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
- "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62",
+ "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62",
"shasum": ""
},
"require": {
@@ -5786,7 +5787,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -5811,7 +5812,7 @@
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -5827,20 +5828,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/doctrine-bridge",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/doctrine-bridge.git",
- "reference": "fcce66ede41ca56100b91fd4a00131ba6cf89aba"
+ "reference": "b880cebd0689a466cc4afef30009a54162c003c4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/fcce66ede41ca56100b91fd4a00131ba6cf89aba",
- "reference": "fcce66ede41ca56100b91fd4a00131ba6cf89aba",
+ "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/b880cebd0689a466cc4afef30009a54162c003c4",
+ "reference": "b880cebd0689a466cc4afef30009a54162c003c4",
"shasum": ""
},
"require": {
@@ -5919,7 +5920,7 @@
"description": "Provides integration for Doctrine with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.21"
+ "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.22"
},
"funding": [
{
@@ -5935,7 +5936,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T15:22:02+00:00"
+ "time": "2025-05-09T14:04:38+00:00"
},
{
"name": "symfony/dotenv",
@@ -6013,16 +6014,16 @@
},
{
"name": "symfony/error-handler",
- "version": "v6.4.20",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/error-handler.git",
- "reference": "aa3bcf4f7674719df078e61cc8062e5b7f752031"
+ "reference": "ce765a2d28b3cce61de1fb916e207767a73171d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/error-handler/zipball/aa3bcf4f7674719df078e61cc8062e5b7f752031",
- "reference": "aa3bcf4f7674719df078e61cc8062e5b7f752031",
+ "url": "https://api.github.com/repos/symfony/error-handler/zipball/ce765a2d28b3cce61de1fb916e207767a73171d1",
+ "reference": "ce765a2d28b3cce61de1fb916e207767a73171d1",
"shasum": ""
},
"require": {
@@ -6068,7 +6069,7 @@
"description": "Provides tools to manage errors and ease debugging PHP code",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/error-handler/tree/v6.4.20"
+ "source": "https://github.com/symfony/error-handler/tree/v6.4.22"
},
"funding": [
{
@@ -6084,7 +6085,7 @@
"type": "tidelift"
}
],
- "time": "2025-03-01T13:00:38+00:00"
+ "time": "2025-05-28T12:00:15+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -6168,16 +6169,16 @@
},
{
"name": "symfony/event-dispatcher-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
- "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f"
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f",
- "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586",
+ "reference": "59eb412e93815df44f05f342958efa9f46b1e586",
"shasum": ""
},
"require": {
@@ -6191,7 +6192,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -6224,7 +6225,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -6240,7 +6241,7 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-25T14:21:43+00:00"
},
{
"name": "symfony/expression-language",
@@ -6438,16 +6439,16 @@
},
{
"name": "symfony/flex",
- "version": "v2.7.0",
+ "version": "v2.7.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/flex.git",
- "reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc"
+ "reference": "4ae50d368415a06820739e54d38a4a29d6df9155"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/flex/zipball/5d743b3b78fabe9f3146586d77b0a1f9292851fc",
- "reference": "5d743b3b78fabe9f3146586d77b0a1f9292851fc",
+ "url": "https://api.github.com/repos/symfony/flex/zipball/4ae50d368415a06820739e54d38a4a29d6df9155",
+ "reference": "4ae50d368415a06820739e54d38a4a29d6df9155",
"shasum": ""
},
"require": {
@@ -6486,7 +6487,7 @@
"description": "Composer plugin for Symfony",
"support": {
"issues": "https://github.com/symfony/flex/issues",
- "source": "https://github.com/symfony/flex/tree/v2.7.0"
+ "source": "https://github.com/symfony/flex/tree/v2.7.1"
},
"funding": [
{
@@ -6502,20 +6503,20 @@
"type": "tidelift"
}
],
- "time": "2025-05-23T11:41:40+00:00"
+ "time": "2025-05-28T14:22:54+00:00"
},
{
"name": "symfony/form",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
- "reference": "44a0e253c16a3187299f07b8f80e23ecb000d360"
+ "reference": "2e2e32b7cc9e543a3f739b4968611a44ef17a2de"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/form/zipball/44a0e253c16a3187299f07b8f80e23ecb000d360",
- "reference": "44a0e253c16a3187299f07b8f80e23ecb000d360",
+ "url": "https://api.github.com/repos/symfony/form/zipball/2e2e32b7cc9e543a3f739b4968611a44ef17a2de",
+ "reference": "2e2e32b7cc9e543a3f739b4968611a44ef17a2de",
"shasum": ""
},
"require": {
@@ -6583,7 +6584,7 @@
"description": "Allows to easily create, process and reuse HTML forms",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/form/tree/v6.4.21"
+ "source": "https://github.com/symfony/form/tree/v6.4.22"
},
"funding": [
{
@@ -6599,20 +6600,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T15:22:02+00:00"
+ "time": "2025-05-04T06:52:29+00:00"
},
{
"name": "symfony/framework-bundle",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/framework-bundle.git",
- "reference": "d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a"
+ "reference": "b1de19b2083484d0ce945977f6c6484e9e493a2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a",
- "reference": "d0b06133b00e4dd3df7f47a3188fb7baabcc6b2a",
+ "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/b1de19b2083484d0ce945977f6c6484e9e493a2e",
+ "reference": "b1de19b2083484d0ce945977f6c6484e9e493a2e",
"shasum": ""
},
"require": {
@@ -6732,7 +6733,7 @@
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/framework-bundle/tree/v6.4.21"
+ "source": "https://github.com/symfony/framework-bundle/tree/v6.4.22"
},
"funding": [
{
@@ -6748,7 +6749,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-05-14T07:14:36+00:00"
},
{
"name": "symfony/http-client",
@@ -6845,16 +6846,16 @@
},
{
"name": "symfony/http-client-contracts",
- "version": "v3.5.2",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client-contracts.git",
- "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645"
+ "reference": "75d7043853a42837e68111812f4d964b01e5101c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/ee8d807ab20fcb51267fdace50fbe3494c31e645",
- "reference": "ee8d807ab20fcb51267fdace50fbe3494c31e645",
+ "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/75d7043853a42837e68111812f4d964b01e5101c",
+ "reference": "75d7043853a42837e68111812f4d964b01e5101c",
"shasum": ""
},
"require": {
@@ -6867,7 +6868,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -6903,7 +6904,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/http-client-contracts/tree/v3.5.2"
+ "source": "https://github.com/symfony/http-client-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -6919,20 +6920,20 @@
"type": "tidelift"
}
],
- "time": "2024-12-07T08:49:48+00:00"
+ "time": "2025-04-29T11:18:49+00:00"
},
{
"name": "symfony/http-foundation",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
- "reference": "3f0c7ea41db479383b81d436b836d37168fd5b99"
+ "reference": "6b7c97fe1ddac8df3cc9ba6410c8abc683e148ae"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-foundation/zipball/3f0c7ea41db479383b81d436b836d37168fd5b99",
- "reference": "3f0c7ea41db479383b81d436b836d37168fd5b99",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/6b7c97fe1ddac8df3cc9ba6410c8abc683e148ae",
+ "reference": "6b7c97fe1ddac8df3cc9ba6410c8abc683e148ae",
"shasum": ""
},
"require": {
@@ -6980,7 +6981,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-foundation/tree/v6.4.21"
+ "source": "https://github.com/symfony/http-foundation/tree/v6.4.22"
},
"funding": [
{
@@ -6996,20 +6997,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-05-11T15:36:20+00:00"
},
{
"name": "symfony/http-kernel",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
- "reference": "983ca05eec6623920d24ec0f1005f487d3734a0c"
+ "reference": "15c105b839a7cfa1bc0989c091bfb6477f23b673"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/http-kernel/zipball/983ca05eec6623920d24ec0f1005f487d3734a0c",
- "reference": "983ca05eec6623920d24ec0f1005f487d3734a0c",
+ "url": "https://api.github.com/repos/symfony/http-kernel/zipball/15c105b839a7cfa1bc0989c091bfb6477f23b673",
+ "reference": "15c105b839a7cfa1bc0989c091bfb6477f23b673",
"shasum": ""
},
"require": {
@@ -7094,7 +7095,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/http-kernel/tree/v6.4.21"
+ "source": "https://github.com/symfony/http-kernel/tree/v6.4.22"
},
"funding": [
{
@@ -7110,20 +7111,20 @@
"type": "tidelift"
}
],
- "time": "2025-05-02T08:46:38+00:00"
+ "time": "2025-05-29T07:23:40+00:00"
},
{
"name": "symfony/intl",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
- "reference": "b248d227fa10fd6345efd4c1c74efaa1c1de6f76"
+ "reference": "aaecb52f18a6f95766a239ca0a6cc0df983d92cc"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/intl/zipball/b248d227fa10fd6345efd4c1c74efaa1c1de6f76",
- "reference": "b248d227fa10fd6345efd4c1c74efaa1c1de6f76",
+ "url": "https://api.github.com/repos/symfony/intl/zipball/aaecb52f18a6f95766a239ca0a6cc0df983d92cc",
+ "reference": "aaecb52f18a6f95766a239ca0a6cc0df983d92cc",
"shasum": ""
},
"require": {
@@ -7177,7 +7178,7 @@
"localization"
],
"support": {
- "source": "https://github.com/symfony/intl/tree/v6.4.21"
+ "source": "https://github.com/symfony/intl/tree/v6.4.22"
},
"funding": [
{
@@ -7193,7 +7194,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-07T19:02:30+00:00"
+ "time": "2025-05-04T12:02:38+00:00"
},
{
"name": "symfony/mailer",
@@ -8108,16 +8109,16 @@
},
{
"name": "symfony/routing",
- "version": "v6.4.18",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
- "reference": "e9bfc94953019089acdfb9be51c1b9142c4afa68"
+ "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/routing/zipball/e9bfc94953019089acdfb9be51c1b9142c4afa68",
- "reference": "e9bfc94953019089acdfb9be51c1b9142c4afa68",
+ "url": "https://api.github.com/repos/symfony/routing/zipball/1f5234e8457164a3a0038a4c0a4ba27876a9c670",
+ "reference": "1f5234e8457164a3a0038a4c0a4ba27876a9c670",
"shasum": ""
},
"require": {
@@ -8171,7 +8172,7 @@
"url"
],
"support": {
- "source": "https://github.com/symfony/routing/tree/v6.4.18"
+ "source": "https://github.com/symfony/routing/tree/v6.4.22"
},
"funding": [
{
@@ -8187,20 +8188,20 @@
"type": "tidelift"
}
],
- "time": "2025-01-09T08:51:02+00:00"
+ "time": "2025-04-27T16:08:38+00:00"
},
{
"name": "symfony/runtime",
- "version": "v6.4.14",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/runtime.git",
- "reference": "4facd4174f45cd37c65860403412b67c7381136a"
+ "reference": "832c3ce3b810509815050434ccb7ead68d06395b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/runtime/zipball/4facd4174f45cd37c65860403412b67c7381136a",
- "reference": "4facd4174f45cd37c65860403412b67c7381136a",
+ "url": "https://api.github.com/repos/symfony/runtime/zipball/832c3ce3b810509815050434ccb7ead68d06395b",
+ "reference": "832c3ce3b810509815050434ccb7ead68d06395b",
"shasum": ""
},
"require": {
@@ -8250,7 +8251,7 @@
"runtime"
],
"support": {
- "source": "https://github.com/symfony/runtime/tree/v6.4.14"
+ "source": "https://github.com/symfony/runtime/tree/v6.4.22"
},
"funding": [
{
@@ -8266,20 +8267,20 @@
"type": "tidelift"
}
],
- "time": "2024-11-05T16:39:55+00:00"
+ "time": "2025-05-07T21:15:03+00:00"
},
{
"name": "symfony/security-bundle",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-bundle.git",
- "reference": "99b656ff6046ef217d4e3f852940de7e22489849"
+ "reference": "671ab5339a1e53923bfb8069bf984a47a412f612"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-bundle/zipball/99b656ff6046ef217d4e3f852940de7e22489849",
- "reference": "99b656ff6046ef217d4e3f852940de7e22489849",
+ "url": "https://api.github.com/repos/symfony/security-bundle/zipball/671ab5339a1e53923bfb8069bf984a47a412f612",
+ "reference": "671ab5339a1e53923bfb8069bf984a47a412f612",
"shasum": ""
},
"require": {
@@ -8362,7 +8363,7 @@
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-bundle/tree/v6.4.21"
+ "source": "https://github.com/symfony/security-bundle/tree/v6.4.22"
},
"funding": [
{
@@ -8378,20 +8379,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-05-09T21:27:20+00:00"
},
{
"name": "symfony/security-core",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-core.git",
- "reference": "c6e70da38436a9a49ed39d9cbead1ecf760f0fbd"
+ "reference": "110483f4e0106cf4bb63ed0479f6a5d09ab24a9e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-core/zipball/c6e70da38436a9a49ed39d9cbead1ecf760f0fbd",
- "reference": "c6e70da38436a9a49ed39d9cbead1ecf760f0fbd",
+ "url": "https://api.github.com/repos/symfony/security-core/zipball/110483f4e0106cf4bb63ed0479f6a5d09ab24a9e",
+ "reference": "110483f4e0106cf4bb63ed0479f6a5d09ab24a9e",
"shasum": ""
},
"require": {
@@ -8448,7 +8449,7 @@
"description": "Symfony Security Component - Core Library",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-core/tree/v6.4.21"
+ "source": "https://github.com/symfony/security-core/tree/v6.4.22"
},
"funding": [
{
@@ -8464,7 +8465,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-17T07:43:34+00:00"
+ "time": "2025-05-20T14:15:13+00:00"
},
{
"name": "symfony/security-csrf",
@@ -8536,16 +8537,16 @@
},
{
"name": "symfony/security-http",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/security-http.git",
- "reference": "67d0edaf6702c3192f27ad483df9a875c9a1f1a2"
+ "reference": "786c8eeee44b07419264ede2a795e8f490113dc2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/security-http/zipball/67d0edaf6702c3192f27ad483df9a875c9a1f1a2",
- "reference": "67d0edaf6702c3192f27ad483df9a875c9a1f1a2",
+ "url": "https://api.github.com/repos/symfony/security-http/zipball/786c8eeee44b07419264ede2a795e8f490113dc2",
+ "reference": "786c8eeee44b07419264ede2a795e8f490113dc2",
"shasum": ""
},
"require": {
@@ -8604,7 +8605,7 @@
"description": "Symfony Security Component - HTTP Integration",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/security-http/tree/v6.4.21"
+ "source": "https://github.com/symfony/security-http/tree/v6.4.22"
},
"funding": [
{
@@ -8620,20 +8621,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:58:34+00:00"
+ "time": "2025-05-09T07:11:47+00:00"
},
{
"name": "symfony/serializer",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
- "reference": "c45f8f7763afb11e85772c0c1debb8f272c17f51"
+ "reference": "b836df93e9ea07d1d3ada58a679ef205d54b64d1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/serializer/zipball/c45f8f7763afb11e85772c0c1debb8f272c17f51",
- "reference": "c45f8f7763afb11e85772c0c1debb8f272c17f51",
+ "url": "https://api.github.com/repos/symfony/serializer/zipball/b836df93e9ea07d1d3ada58a679ef205d54b64d1",
+ "reference": "b836df93e9ea07d1d3ada58a679ef205d54b64d1",
"shasum": ""
},
"require": {
@@ -8702,7 +8703,7 @@
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/serializer/tree/v6.4.21"
+ "source": "https://github.com/symfony/serializer/tree/v6.4.22"
},
"funding": [
{
@@ -8718,20 +8719,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-05-12T08:02:50+00:00"
},
{
"name": "symfony/service-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/service-contracts.git",
- "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0"
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
- "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0",
+ "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
+ "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4",
"shasum": ""
},
"require": {
@@ -8749,7 +8750,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -8785,7 +8786,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/service-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/service-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -8801,7 +8802,7 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2025-04-25T09:37:31+00:00"
},
{
"name": "symfony/stopwatch",
@@ -8953,16 +8954,16 @@
},
{
"name": "symfony/translation",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
- "reference": "bb92ea5588396b319ba43283a5a3087a034cb29c"
+ "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation/zipball/bb92ea5588396b319ba43283a5a3087a034cb29c",
- "reference": "bb92ea5588396b319ba43283a5a3087a034cb29c",
+ "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad",
+ "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad",
"shasum": ""
},
"require": {
@@ -9028,7 +9029,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/translation/tree/v6.4.21"
+ "source": "https://github.com/symfony/translation/tree/v6.4.22"
},
"funding": [
{
@@ -9044,20 +9045,20 @@
"type": "tidelift"
}
],
- "time": "2025-04-07T19:02:30+00:00"
+ "time": "2025-05-29T07:06:44+00:00"
},
{
"name": "symfony/translation-contracts",
- "version": "v3.5.1",
+ "version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation-contracts.git",
- "reference": "4667ff3bd513750603a09c8dedbea942487fb07c"
+ "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c",
- "reference": "4667ff3bd513750603a09c8dedbea942487fb07c",
+ "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
+ "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d",
"shasum": ""
},
"require": {
@@ -9070,7 +9071,7 @@
"name": "symfony/contracts"
},
"branch-alias": {
- "dev-main": "3.5-dev"
+ "dev-main": "3.6-dev"
}
},
"autoload": {
@@ -9106,7 +9107,7 @@
"standards"
],
"support": {
- "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1"
+ "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0"
},
"funding": [
{
@@ -9122,20 +9123,20 @@
"type": "tidelift"
}
],
- "time": "2024-09-25T14:20:29+00:00"
+ "time": "2024-09-27T08:32:26+00:00"
},
{
"name": "symfony/twig-bridge",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
- "reference": "0457b7944bf1cc9c846c98d4923b5379ec6afc09"
+ "reference": "04ab306a2f2c9dbd46f4363383812954f704af9d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/0457b7944bf1cc9c846c98d4923b5379ec6afc09",
- "reference": "0457b7944bf1cc9c846c98d4923b5379ec6afc09",
+ "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/04ab306a2f2c9dbd46f4363383812954f704af9d",
+ "reference": "04ab306a2f2c9dbd46f4363383812954f704af9d",
"shasum": ""
},
"require": {
@@ -9215,7 +9216,7 @@
"description": "Provides integration for Twig with various Symfony components",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/twig-bridge/tree/v6.4.21"
+ "source": "https://github.com/symfony/twig-bridge/tree/v6.4.22"
},
"funding": [
{
@@ -9231,7 +9232,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T13:27:38+00:00"
+ "time": "2025-05-16T08:23:44+00:00"
},
{
"name": "symfony/twig-bundle",
@@ -9319,16 +9320,16 @@
},
{
"name": "symfony/validator",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/validator.git",
- "reference": "47610116f476595b90c368ff2a22514050712785"
+ "reference": "4c5fbccb2d8f64017c8dada6473701a5c8539716"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/validator/zipball/47610116f476595b90c368ff2a22514050712785",
- "reference": "47610116f476595b90c368ff2a22514050712785",
+ "url": "https://api.github.com/repos/symfony/validator/zipball/4c5fbccb2d8f64017c8dada6473701a5c8539716",
+ "reference": "4c5fbccb2d8f64017c8dada6473701a5c8539716",
"shasum": ""
},
"require": {
@@ -9396,7 +9397,7 @@
"description": "Provides tools to validate values",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/validator/tree/v6.4.21"
+ "source": "https://github.com/symfony/validator/tree/v6.4.22"
},
"funding": [
{
@@ -9412,7 +9413,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-30T18:50:04+00:00"
+ "time": "2025-05-29T07:03:46+00:00"
},
{
"name": "symfony/var-dumper",
@@ -9501,16 +9502,16 @@
},
{
"name": "symfony/var-exporter",
- "version": "v6.4.21",
+ "version": "v6.4.22",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-exporter.git",
- "reference": "717e7544aa99752c54ecba5c0e17459c48317472"
+ "reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/var-exporter/zipball/717e7544aa99752c54ecba5c0e17459c48317472",
- "reference": "717e7544aa99752c54ecba5c0e17459c48317472",
+ "url": "https://api.github.com/repos/symfony/var-exporter/zipball/f28cf841f5654955c9f88ceaf4b9dc29571988a9",
+ "reference": "f28cf841f5654955c9f88ceaf4b9dc29571988a9",
"shasum": ""
},
"require": {
@@ -9558,7 +9559,7 @@
"serialize"
],
"support": {
- "source": "https://github.com/symfony/var-exporter/tree/v6.4.21"
+ "source": "https://github.com/symfony/var-exporter/tree/v6.4.22"
},
"funding": [
{
@@ -9574,7 +9575,7 @@
"type": "tidelift"
}
],
- "time": "2025-04-27T21:06:26+00:00"
+ "time": "2025-05-14T13:00:13+00:00"
},
{
"name": "symfony/webpack-encore-bundle",
diff --git a/src/API/Serializer/ValidationFailedExceptionErrorHandler.php b/src/API/Serializer/ValidationFailedExceptionErrorHandler.php
index d2c9868b..1ace17aa 100644
--- a/src/API/Serializer/ValidationFailedExceptionErrorHandler.php
+++ b/src/API/Serializer/ValidationFailedExceptionErrorHandler.php
@@ -63,24 +63,6 @@ final class ValidationFailedExceptionErrorHandler implements SubscribingHandlerI
}
public function serializeValidationExceptionToJson(JsonSerializationVisitor $visitor, ValidationFailedException $exception, array $type, Context $context)
- {
- $errors = [];
-
- /** @var ConstraintViolationInterface $error */
- foreach (iterator_to_array($exception->getViolations()) as $error) {
- $errors[$error->getPropertyPath()]['errors'][] = $this->getErrorMessage($error);
- }
-
- return [
- 'code' => '400',
- 'message' => $this->translator->trans($exception->getMessage(), [], 'validators'),
- 'errors' => [
- 'children' => $errors
- ],
- ];
- }
-
- private function getErrorMessage(ConstraintViolationInterface $error): string
{
$locale = \Locale::getDefault();
/** @var User $user */
@@ -90,6 +72,24 @@ final class ValidationFailedExceptionErrorHandler implements SubscribingHandlerI
$locale = $user->getLanguage();
}
+ $errors = [];
+
+ /** @var ConstraintViolationInterface $error */
+ foreach (iterator_to_array($exception->getViolations()) as $error) {
+ $errors[$error->getPropertyPath()]['errors'][] = $this->getErrorMessage($error, $locale);
+ }
+
+ return [
+ 'code' => '400',
+ 'message' => $this->translator->trans($exception->getMessage(), [], 'validators', $locale),
+ 'errors' => [
+ 'children' => $errors
+ ],
+ ];
+ }
+
+ private function getErrorMessage(ConstraintViolationInterface $error, string $locale): string
+ {
if (null !== $error->getPlural()) {
return $this->translator->trans($error->getMessageTemplate(), ['%count%' => $error->getPlural()] + $error->getParameters(), 'validators', $locale);
}
diff --git a/src/API/TimesheetController.php b/src/API/TimesheetController.php
index cf058df6..c317a45f 100644
--- a/src/API/TimesheetController.php
+++ b/src/API/TimesheetController.php
@@ -315,7 +315,7 @@ final class TimesheetController extends BaseApiController
if ($form->isValid()) {
try {
- $this->service->saveNewTimesheet($timesheet);
+ $this->service->saveTimesheet($timesheet);
$view = new View($timesheet, 200);
@@ -372,7 +372,7 @@ final class TimesheetController extends BaseApiController
return $this->viewHandler->handle($view);
}
- $this->service->updateTimesheet($timesheet);
+ $this->service->saveTimesheet($timesheet);
$view = new View($timesheet, Response::HTTP_OK);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -547,7 +547,7 @@ final class TimesheetController extends BaseApiController
$copyTimesheet = clone $timesheet;
$this->dispatcher->dispatch(new TimesheetDuplicatePreEvent($copyTimesheet, $timesheet));
- $this->service->saveNewTimesheet($copyTimesheet);
+ $this->service->saveTimesheet($copyTimesheet);
$this->dispatcher->dispatch(new TimesheetDuplicatePostEvent($copyTimesheet, $timesheet));
$view = new View($copyTimesheet, 200);
@@ -571,7 +571,7 @@ final class TimesheetController extends BaseApiController
$timesheet->setExported(!$timesheet->isExported());
- $this->service->updateTimesheet($timesheet);
+ $this->service->saveTimesheet($timesheet);
$view = new View($timesheet, 200);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
@@ -601,7 +601,7 @@ final class TimesheetController extends BaseApiController
$meta->setValue($paramFetcher->get('value'));
- $this->service->updateTimesheet($timesheet);
+ $this->service->saveTimesheet($timesheet);
$view = new View($timesheet, 200);
$view->getContext()->setGroups(self::GROUPS_ENTITY);
diff --git a/src/Activity/ActivityService.php b/src/Activity/ActivityService.php
index 3a429e7f..f862d900 100644
--- a/src/Activity/ActivityService.php
+++ b/src/Activity/ActivityService.php
@@ -97,7 +97,7 @@ class ActivityService
$errors = $this->validator->validate($activity, null, $groups);
if ($errors->count() > 0) {
- throw new ValidationFailedException($errors, 'Validation Failed');
+ throw new ValidationFailedException($errors);
}
}
diff --git a/src/Constants.php b/src/Constants.php
index ec7c800a..698f2ec2 100644
--- a/src/Constants.php
+++ b/src/Constants.php
@@ -17,11 +17,11 @@ final class Constants
/**
* The current release version
*/
- public const VERSION = '2.35.1';
+ public const VERSION = '2.36.0';
/**
* The current release: major * 10000 + minor * 100 + patch
*/
- public const VERSION_ID = 23501;
+ public const VERSION_ID = 23600;
/**
* The software name
*/
diff --git a/src/Controller/TimesheetAbstractController.php b/src/Controller/TimesheetAbstractController.php
index 0568c0bc..4f7e58de 100644
--- a/src/Controller/TimesheetAbstractController.php
+++ b/src/Controller/TimesheetAbstractController.php
@@ -152,7 +152,7 @@ abstract class TimesheetAbstractController extends AbstractController
if ($editForm->isSubmitted() && $editForm->isValid()) {
try {
- $this->service->updateTimesheet($entry);
+ $this->service->saveTimesheet($entry);
$this->flashSuccess('action.update.success');
return $this->redirectToRoute($this->getTimesheetRoute());
@@ -184,7 +184,7 @@ abstract class TimesheetAbstractController extends AbstractController
if ($createForm->isSubmitted() && $createForm->isValid()) {
try {
- $this->service->saveNewTimesheet($entry);
+ $this->service->saveTimesheet($entry);
$this->flashSuccess('action.update.success');
return $this->redirectToRoute($this->getTimesheetRoute());
@@ -216,7 +216,7 @@ abstract class TimesheetAbstractController extends AbstractController
if ($form->isSubmitted() && $form->isValid()) {
try {
$this->dispatcher->dispatch(new TimesheetDuplicatePreEvent($copyTimesheet, $timesheet));
- $this->service->saveNewTimesheet($copyTimesheet);
+ $this->service->saveTimesheet($copyTimesheet);
$this->dispatcher->dispatch(new TimesheetDuplicatePostEvent($copyTimesheet, $timesheet));
$this->flashSuccess('action.update.success');
diff --git a/src/Controller/TimesheetTeamController.php b/src/Controller/TimesheetTeamController.php
index 63ad00c0..22e768ad 100644
--- a/src/Controller/TimesheetTeamController.php
+++ b/src/Controller/TimesheetTeamController.php
@@ -126,7 +126,7 @@ final class TimesheetTeamController extends TimesheetAbstractController
}
foreach ($newTimesheets as $newTimesheet) {
- $this->service->saveNewTimesheet($newTimesheet);
+ $this->service->saveTimesheet($newTimesheet);
}
$this->flashSuccess('action.update.success');
diff --git a/src/Customer/CustomerService.php b/src/Customer/CustomerService.php
index 51d0b762..c59150e5 100644
--- a/src/Customer/CustomerService.php
+++ b/src/Customer/CustomerService.php
@@ -101,7 +101,7 @@ final class CustomerService
$errors = $this->validator->validate($customer, null, $groups);
if ($errors->count() > 0) {
- throw new ValidationFailedException($errors, 'Validation Failed');
+ throw new ValidationFailedException($errors);
}
}
diff --git a/src/Form/TimesheetEditForm.php b/src/Form/TimesheetEditForm.php
index ac6dfe3b..67c85acd 100644
--- a/src/Form/TimesheetEditForm.php
+++ b/src/Form/TimesheetEditForm.php
@@ -59,8 +59,7 @@ class TimesheetEditForm extends AbstractType
$timezone = $options['timezone'];
$isNew = true;
- if (isset($options['data'])) {
- /** @var Timesheet $entry */
+ if (isset($options['data']) && $options['data'] instanceof Timesheet) {
$entry = $options['data'];
$activity = $entry->getActivity();
@@ -338,7 +337,7 @@ class TimesheetEditForm extends AbstractType
function (FormEvent $event) {
/** @var Timesheet|null $timesheet */
$timesheet = $event->getData();
- if (null === $timesheet || $timesheet->isRunning()) {
+ if (null === $timesheet || ($timesheet instanceof Timesheet && $timesheet->isRunning())) {
$event->getForm()->get('duration')->setData(null);
}
}
diff --git a/src/Invoice/InvoiceModel.php b/src/Invoice/InvoiceModel.php
index 625dbf4b..94108949 100644
--- a/src/Invoice/InvoiceModel.php
+++ b/src/Invoice/InvoiceModel.php
@@ -218,9 +218,7 @@ final class InvoiceModel
}
/**
- * Returns the user who is currently creating the invoice.
- *
- * @return User|null
+ * Returns the user currently creating the invoice.
*/
public function getUser(): ?User
{
diff --git a/src/Project/ProjectService.php b/src/Project/ProjectService.php
index 83130af1..bd991f0b 100644
--- a/src/Project/ProjectService.php
+++ b/src/Project/ProjectService.php
@@ -105,7 +105,7 @@ final class ProjectService
$errors = $this->validator->validate($project, null, $groups);
if ($errors->count() > 0) {
- throw new ValidationFailedException($errors, 'Validation Failed');
+ throw new ValidationFailedException($errors);
}
}
diff --git a/src/Saml/SamlAuthenticator.php b/src/Saml/SamlAuthenticator.php
index 45bb7674..a41e8673 100644
--- a/src/Saml/SamlAuthenticator.php
+++ b/src/Saml/SamlAuthenticator.php
@@ -12,6 +12,7 @@ namespace App\Saml;
use App\Configuration\SamlConfigurationInterface;
use App\Saml\Security\SamlAuthenticationFailureHandler;
use App\Saml\Security\SamlAuthenticationSuccessHandler;
+use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
@@ -40,7 +41,8 @@ class SamlAuthenticator extends AbstractAuthenticator
private readonly SamlAuthenticationFailureHandler $failureHandler,
private readonly SamlAuthFactory $samlAuthFactory,
private readonly SamlProvider $samlProvider,
- private readonly SamlConfigurationInterface $configuration
+ private readonly SamlConfigurationInterface $configuration,
+ private readonly LoggerInterface $logger
) {
}
@@ -86,6 +88,7 @@ class SamlAuthenticator extends AbstractAuthenticator
// file_put_contents(__DIR__ . '/../../var/log/saml.xml', $oneLoginAuth->getLastResponseXML());
if (\count($oneLoginAuth->getErrors()) > 0) {
+ $this->logger->critical('SAML login failed: ' . $oneLoginAuth->getLastErrorReason());
throw new AuthenticationException($oneLoginAuth->getLastErrorReason());
}
@@ -102,7 +105,9 @@ class SamlAuthenticator extends AbstractAuthenticator
if (isset($this->options['username_attribute'])) {
if (!\array_key_exists($this->options['username_attribute'], $attributes)) {
- throw new \Exception(\sprintf("Attribute '%s' not found in SAML data", $this->options['username_attribute']));
+ $errorMessage = \sprintf("Attribute '%s' not found in SAML data", $this->options['username_attribute']);
+ $this->logger->critical($errorMessage);
+ throw new \Exception($errorMessage);
}
$username = $attributes[$this->options['username_attribute']][0];
diff --git a/src/Timesheet/TimesheetService.php b/src/Timesheet/TimesheetService.php
index 6a8ef9ec..ae0707cc 100644
--- a/src/Timesheet/TimesheetService.php
+++ b/src/Timesheet/TimesheetService.php
@@ -45,12 +45,12 @@ final class TimesheetService
private array $doNotValidateCodes = [];
public function __construct(
- private SystemConfiguration $configuration,
- private TimesheetRepository $repository,
- private TrackingModeService $trackingModeService,
- private EventDispatcherInterface $dispatcher,
- private AuthorizationCheckerInterface $auth,
- private ValidatorInterface $validator
+ private readonly SystemConfiguration $configuration,
+ private readonly TimesheetRepository $repository,
+ private readonly TrackingModeService $trackingModeService,
+ private readonly EventDispatcherInterface $dispatcher,
+ private readonly AuthorizationCheckerInterface $auth,
+ private readonly ValidatorInterface $validator
) {
}
@@ -101,7 +101,7 @@ final class TimesheetService
public function restartTimesheet(Timesheet $timesheet, Timesheet $copyFrom): Timesheet
{
$this->dispatcher->dispatch(new TimesheetRestartPreEvent($timesheet, $copyFrom));
- $this->saveNewTimesheet($timesheet);
+ $this->saveNewTimesheet($timesheet); // @phpstan-ignore method.deprecated
$this->dispatcher->dispatch(new TimesheetRestartPostEvent($timesheet, $copyFrom));
return $timesheet;
@@ -111,6 +111,7 @@ final class TimesheetService
* @throws ValidationFailedException for invalid timesheets or running timesheets that should be stopped
* @throws InvalidArgumentException for already persisted timesheets
* @throws AccessDeniedException if user is not allowed to start timesheet
+ * @deprecated since 2.36.0 - use saveTimesheet() instead
*/
public function saveNewTimesheet(Timesheet $timesheet): Timesheet
{
@@ -150,12 +151,19 @@ final class TimesheetService
return $timesheet;
}
+ public function saveTimesheet(Timesheet $timesheet): Timesheet
+ {
+ if ($timesheet->getId() === null) {
+ return $this->saveNewTimesheet($timesheet); // @phpstan-ignore method.deprecated
+ } else {
+ return $this->updateTimesheet($timesheet); // @phpstan-ignore method.deprecated
+ }
+ }
+
/**
- * Does NOT validate the given timesheet!
+ * Does NOT validate the given timesheet.
*
- * @param Timesheet $timesheet
- * @return Timesheet
- * @throws \Exception
+ * @deprecated since 2.36.0 - use saveTimesheet() instead
*/
public function updateTimesheet(Timesheet $timesheet): Timesheet
{
@@ -245,7 +253,7 @@ final class TimesheetService
continue;
}
- throw new ValidationFailedException($errors, 'Validation Failed');
+ throw new ValidationFailedException($errors);
}
}
}
diff --git a/src/User/UserService.php b/src/User/UserService.php
index 1852a240..baa8aedf 100644
--- a/src/User/UserService.php
+++ b/src/User/UserService.php
@@ -112,7 +112,7 @@ class UserService
$errors = $this->validator->validate($user, null, $groups);
if ($errors->count() > 0) {
- throw new ValidationFailedException($errors, 'Validation Failed');
+ throw new ValidationFailedException($errors);
}
}
diff --git a/src/Validator/Constraints/TimesheetNegativeDuration.php b/src/Validator/Constraints/TimesheetNegativeDuration.php
new file mode 100644
index 00000000..2e884ee1
--- /dev/null
+++ b/src/Validator/Constraints/TimesheetNegativeDuration.php
@@ -0,0 +1,26 @@
+ 'Duration cannot be negative.',
+ ];
+
+ public string $message = 'Duration cannot be negative.';
+
+ public function getTargets(): string
+ {
+ return self::CLASS_CONSTRAINT;
+ }
+}
diff --git a/src/Validator/Constraints/TimesheetNegativeDurationValidator.php b/src/Validator/Constraints/TimesheetNegativeDurationValidator.php
new file mode 100644
index 00000000..72bc8f50
--- /dev/null
+++ b/src/Validator/Constraints/TimesheetNegativeDurationValidator.php
@@ -0,0 +1,43 @@
+isRunning()) {
+ return;
+ }
+
+ $duration = $value->getCalculatedDuration();
+
+ if ($duration !== null && $duration < 0) {
+ $this->context->buildViolation($constraint->message)
+ ->atPath('duration')
+ ->setTranslationDomain('validators')
+ ->setCode(TimesheetNegativeDuration::NEGATIVE_DURATION_ERROR)
+ ->addViolation();
+ }
+ }
+}
diff --git a/src/Validator/Constraints/TimesheetZeroDurationValidator.php b/src/Validator/Constraints/TimesheetZeroDurationValidator.php
index 347ff439..bfdd6bad 100644
--- a/src/Validator/Constraints/TimesheetZeroDurationValidator.php
+++ b/src/Validator/Constraints/TimesheetZeroDurationValidator.php
@@ -44,7 +44,7 @@ final class TimesheetZeroDurationValidator extends ConstraintValidator
$duration = $value->getCalculatedDuration();
}
- if ($duration <= 0) {
+ if ($duration === 0) {
$this->context->buildViolation($constraint->message)
->atPath('duration')
->setTranslationDomain('validators')
diff --git a/src/Validator/ValidationException.php b/src/Validator/ValidationException.php
index 1dd242ba..4c34d3dd 100644
--- a/src/Validator/ValidationException.php
+++ b/src/Validator/ValidationException.php
@@ -14,7 +14,7 @@ final class ValidationException extends \RuntimeException
public function __construct(string $message = null)
{
if ($message === null) {
- $message = 'Validation failed';
+ $message = 'Validation Failed';
}
parent::__construct($message, 400);
}
diff --git a/src/Validator/ValidationFailedException.php b/src/Validator/ValidationFailedException.php
index 5150ebac..24940b22 100644
--- a/src/Validator/ValidationFailedException.php
+++ b/src/Validator/ValidationFailedException.php
@@ -13,10 +13,10 @@ use Symfony\Component\Validator\ConstraintViolationListInterface;
final class ValidationFailedException extends \RuntimeException
{
- public function __construct(private ConstraintViolationListInterface $violations, ?string $message = null)
+ public function __construct(private readonly ConstraintViolationListInterface $violations, ?string $message = null)
{
if ($message === null) {
- $message = 'Validation failed';
+ $message = 'Validation Failed';
}
parent::__construct($message, 400);
}
diff --git a/tests/Entity/TimesheetValidationTest.php b/tests/Entity/TimesheetValidationTest.php
index 16e724ee..12402162 100644
--- a/tests/Entity/TimesheetValidationTest.php
+++ b/tests/Entity/TimesheetValidationTest.php
@@ -251,7 +251,7 @@ class TimesheetValidationTest extends KernelTestCase
$entity->setBegin($begin);
$entity->setEnd($end);
- $this->assertHasViolationForField($entity, 'end_date');
+ $this->assertHasViolationForField($entity, ['end_date', 'duration']);
// allow same begin and end
$entity = $this->getEntity();
diff --git a/tests/Timesheet/TimesheetServiceTest.php b/tests/Timesheet/TimesheetServiceTest.php
index c9784bbe..b5d773ba 100644
--- a/tests/Timesheet/TimesheetServiceTest.php
+++ b/tests/Timesheet/TimesheetServiceTest.php
@@ -65,6 +65,9 @@ class TimesheetServiceTest extends TestCase
return $service;
}
+ /**
+ * @group legacy
+ */
public function testCannotSavePersistedTimesheetAsNew(): void
{
$timesheet = $this->createMock(Timesheet::class);
@@ -75,7 +78,7 @@ class TimesheetServiceTest extends TestCase
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Cannot create timesheet, already persisted');
- $sut->saveNewTimesheet($timesheet);
+ $sut->saveNewTimesheet($timesheet); // @phpstan-ignore method.deprecated
}
public function testCannotStartTimesheet(): void
@@ -88,7 +91,7 @@ class TimesheetServiceTest extends TestCase
$this->expectException(AccessDeniedHttpException::class);
$this->expectExceptionMessage('You are not allowed to start this timesheet record');
- $sut->saveNewTimesheet(new Timesheet());
+ $sut->saveTimesheet(new Timesheet());
}
public function testSaveNewTimesheetHasValidationError(): void
@@ -107,7 +110,7 @@ class TimesheetServiceTest extends TestCase
$this->expectException(ValidationFailedException::class);
$this->expectExceptionMessage('Validation Failed');
- $sut->saveNewTimesheet(new Timesheet());
+ $sut->saveTimesheet(new Timesheet());
}
public function testSaveNewTimesheetStopsActiveRecords(): void
@@ -134,7 +137,7 @@ class TimesheetServiceTest extends TestCase
$sut = $this->getSut($authorizationChecker, null, null, $repository);
- $sut->saveNewTimesheet($newTimesheet);
+ $sut->saveTimesheet($newTimesheet);
}
public function testSaveNewTimesheetFixesTimezone(): void
@@ -155,11 +158,14 @@ class TimesheetServiceTest extends TestCase
$authorizationChecker->expects($this->once())->method('isGranted')->willReturn(true);
$sut = $this->getSut($authorizationChecker);
- $sut->saveNewTimesheet($timesheet);
+ $sut->saveTimesheet($timesheet);
self::assertEquals('Europe/Paris', $timesheet->getTimezone());
}
+ /**
+ * @group legacy
+ */
public function testUpdateTimesheetFixesTimezone(): void
{
$user = new User();
@@ -176,7 +182,7 @@ class TimesheetServiceTest extends TestCase
$sut = $this->getSut();
- $sut->updateTimesheet($timesheet);
+ $sut->updateTimesheet($timesheet); // @phpstan-ignore method.deprecated
self::assertEquals('Europe/Paris', $timesheet->getTimezone());
}
diff --git a/tests/Validator/Constraints/TimesheetNegativeDurationTest.php b/tests/Validator/Constraints/TimesheetNegativeDurationTest.php
new file mode 100644
index 00000000..242b44b9
--- /dev/null
+++ b/tests/Validator/Constraints/TimesheetNegativeDurationTest.php
@@ -0,0 +1,25 @@
+
+ */
+class TimesheetNegativeDurationValidatorTest extends ConstraintValidatorTestCase
+{
+ protected function createValidator(): TimesheetNegativeDurationValidator
+ {
+ return new TimesheetNegativeDurationValidator();
+ }
+
+ public function testConstraintIsInvalid(): void
+ {
+ $this->expectException(UnexpectedTypeException::class);
+
+ $this->validator->validate(new Timesheet(), new NotBlank());
+ }
+
+ public function testInvalidValueThrowsException(): void
+ {
+ $this->expectException(UnexpectedTypeException::class);
+
+ $this->validator->validate(new NotBlank(), new TimesheetNegativeDuration(['message' => 'Duration cannot be negative.']));
+ }
+
+ public function testNegativeDurationIsNotAllowed(): void
+ {
+ $begin = new \DateTime();
+ $timesheet = new Timesheet();
+ $timesheet->setBegin(clone $begin);
+ $timesheet->setEnd(clone $begin);
+ $timesheet->setBreak(3600);
+
+ $this->validator->validate($timesheet, new TimesheetNegativeDuration(['message' => 'Duration cannot be negative.']));
+
+ $this->buildViolation('Duration cannot be negative.')
+ ->atPath('property.path.duration')
+ ->setCode(TimesheetNegativeDuration::NEGATIVE_DURATION_ERROR)
+ ->assertRaised();
+ }
+
+ public function testZeroDurationIsAllowed(): void
+ {
+ $begin = new \DateTime();
+ $timesheet = new Timesheet();
+ $timesheet->setBegin(clone $begin);
+ $timesheet->setEnd(clone $begin);
+
+ $this->validator = $this->createValidator();
+ $this->validator->initialize($this->context);
+
+ $this->validator->validate($timesheet, new TimesheetNegativeDuration(['message' => 'Duration cannot be negative.']));
+
+ $this->assertNoViolation();
+ }
+
+ public function testDoesNotTriggerOnRunningTimesheet(): void
+ {
+ $begin = new \DateTime();
+ $timesheet = new Timesheet();
+ $timesheet->setBegin(clone $begin);
+ $timesheet->setBreak(3600);
+
+ $this->validator = $this->createValidator();
+ $this->validator->initialize($this->context);
+
+ $this->validator->validate($timesheet, new TimesheetNegativeDuration(['message' => 'Duration cannot be negative.']));
+
+ $this->assertNoViolation();
+ }
+}
diff --git a/tests/Validator/ValidationExceptionTest.php b/tests/Validator/ValidationExceptionTest.php
index 9d960aa5..d0dd60a3 100644
--- a/tests/Validator/ValidationExceptionTest.php
+++ b/tests/Validator/ValidationExceptionTest.php
@@ -21,7 +21,7 @@ class ValidationExceptionTest extends TestCase
{
$sut = new ValidationException();
self::assertEquals(400, $sut->getCode());
- self::assertEquals('Validation failed', $sut->getMessage());
+ self::assertEquals('Validation Failed', $sut->getMessage());
}
public function testConstruct(): void
diff --git a/tests/Validator/ValidationFailedExceptionTest.php b/tests/Validator/ValidationFailedExceptionTest.php
index 67c96d2d..2cc9f4e8 100644
--- a/tests/Validator/ValidationFailedExceptionTest.php
+++ b/tests/Validator/ValidationFailedExceptionTest.php
@@ -23,7 +23,7 @@ class ValidationFailedExceptionTest extends TestCase
$list = new ConstraintViolationList();
$sut = new ValidationFailedException($list);
self::assertEquals(400, $sut->getCode());
- self::assertEquals('Validation failed', $sut->getMessage());
+ self::assertEquals('Validation Failed', $sut->getMessage());
self::assertSame($list, $sut->getViolations());
}
diff --git a/translations/validators.de.xlf b/translations/validators.de.xlf
index 370e6723..3544ac33 100644
--- a/translations/validators.de.xlf
+++ b/translations/validators.de.xlf
@@ -162,6 +162,10 @@
Selected period cannot be locked: unconfirmed absence requests are pending.
Ausgewählter Zeitraum kann nicht gesperrt werden: unbestätigte Abwesenheitsanträge stehen an.
+
+ Duration cannot be negative.
+ Die Dauer kann nicht negativ sein.
+