From dee90bb15e98ddaa7e3d4889288dc0671fb2f8ac Mon Sep 17 00:00:00 2001 From: Kevin Papst Date: Sun, 10 Mar 2024 15:35:59 +0100 Subject: [PATCH] Release 2.13 (#4659) --- .github/release-drafter.yml | 2 +- .github/workflows/testing.yaml | 2 +- README.md | 4 +- UPGRADING.md | 2 +- composer.lock | 812 ++++++++---------- phpstan.neon | 60 -- src/API/ActivityController.php | 9 +- src/API/CustomerController.php | 9 +- src/API/ProjectController.php | 11 +- src/API/TimesheetController.php | 11 +- src/Constants.php | 4 +- src/Controller/CustomerController.php | 10 +- src/Controller/PluginController.php | 31 +- .../Security/PasswordResetController.php | 10 +- src/EventSubscriber/MenuSubscriber.php | 19 +- src/Form/Toolbar/CustomerToolbarForm.php | 34 + src/Form/Type/TagsType.php | 4 +- .../ConfigurableNumberGenerator.php | 14 +- src/Pdf/MPdfConverter.php | 2 +- .../CustomerMonthlyProjectsRepository.php | 8 +- src/Repository/CustomerRepository.php | 16 +- src/Repository/Query/CustomerQuery.php | 13 + src/Saml/SamlAuthFactory.php | 4 +- src/Saml/SamlAuthenticator.php | 12 +- src/Saml/SamlBadge.php | 2 +- src/Saml/SamlLogoutSubscriber.php | 4 +- src/Saml/SamlProvider.php | 8 +- src/Security/RolePermissionManager.php | 22 +- src/Timesheet/TimesheetStatisticService.php | 30 +- src/Twig/LocaleFormatExtensions.php | 2 + src/Twig/PaginationExtension.php | 2 +- .../QuickEntryTimesheetValidator.php | 6 +- .../Constraints/TimesheetBasicValidator.php | 10 +- .../TimesheetBudgetUsedValidator.php | 70 +- .../TimesheetExportedValidator.php | 18 +- .../TimesheetFutureTimesValidator.php | 6 +- .../TimesheetLockdownValidator.php | 19 +- .../TimesheetLongRunningValidator.php | 16 +- .../TimesheetOverlappingValidator.php | 21 +- .../Constraints/TimesheetRestartValidator.php | 19 +- .../Constraints/TimesheetValidator.php | 12 +- .../TimesheetZeroDurationValidator.php | 18 +- src/Voter/ActivityVoter.php | 24 +- src/Voter/CustomerVoter.php | 22 +- src/Voter/EntityMultiRoleVoter.php | 13 +- src/Voter/ProjectVoter.php | 22 +- src/Voter/QuickEntryVoter.php | 12 +- src/Voter/ReportingVoter.php | 14 +- src/Voter/RolePermissionVoter.php | 20 +- src/Voter/TeamVoter.php | 22 +- src/Voter/TimesheetVoter.php | 26 +- src/Voter/UserVoter.php | 22 +- .../DailyWorkingTimeChartProvider.php | 5 +- src/WorkingTime/WorkingTimeService.php | 6 +- symfony.lock | 12 - templates/activity/details.html.twig | 2 +- templates/customer/details.html.twig | 2 +- templates/form/blocks.html.twig | 2 +- templates/macros/webloader.html.twig | 1 - templates/project/details.html.twig | 2 +- tests/API/ActivityControllerTest.php | 42 +- tests/API/CustomerControllerTest.php | 44 +- tests/API/ProjectControllerTest.php | 65 +- tests/API/RateControllerTestTrait.php | 35 +- tests/API/TimesheetControllerTest.php | 49 +- tests/Entity/EntityValidationTestTrait.php | 4 +- tests/Entity/TimesheetValidationTest.php | 2 +- .../ConfigurableNumberGeneratorTest.php | 42 +- tests/Twig/LocaleFormatExtensionsTest.php | 3 + .../TimesheetBudgetUsedValidatorTest.php | 2 +- .../TimesheetExportedValidatorTest.php | 2 +- .../TimesheetFutureTimesValidatorTest.php | 2 +- .../TimesheetLockdownValidatorTest.php | 2 +- .../TimesheetLongRunningValidatorTest.php | 2 +- .../TimesheetOverlappingValidatorTest.php | 2 +- .../TimesheetRestartValidatorTest.php | 2 +- .../Constraints/TimesheetValidatorTest.php | 2 +- .../TimesheetZeroDurationValidatorTest.php | 2 +- tests/phpstan.neon | 30 - 79 files changed, 1019 insertions(+), 932 deletions(-) diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml index 487180a5..f6f08e08 100644 --- a/.github/release-drafter.yml +++ b/.github/release-drafter.yml @@ -21,7 +21,7 @@ version-resolver: - 'translation' default: patch template: | - [Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://tobybatch.github.io/kimai2/) + [Upgrade Kimai](https://www.kimai.org/documentation/updates.html) - [Install Kimai](https://www.kimai.org/documentation/installation.html) - [Docker](https://www.kimai.org/documentation/docker.html) **Compatible with PHP 8.1 to 8.3** diff --git a/.github/workflows/testing.yaml b/.github/workflows/testing.yaml index 06b55bf6..7fcc28ff 100644 --- a/.github/workflows/testing.yaml +++ b/.github/workflows/testing.yaml @@ -106,7 +106,7 @@ jobs: - name: Upload code coverage if: matrix.php == '8.2' - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.xml diff --git a/README.md b/README.md index b96dab2d..f3a8377a 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ Support for PHP 8.2 and 8.3. ## Installation -- [Recommended setup](https://www.kimai.org/documentation/installation.html#recommended-setup) — with Git and Composer -- [Docker](https://hub.docker.com/r/kimai/kimai2) — containerized by [@tobybatch](https://github.com/tobybatch/kimai2) +- [Native setup](https://www.kimai.org/documentation/installation.html) — with Git and Composer +- [Docker](https://hub.docker.com/r/kimai/kimai2) — containerized by @tobybatch There are also documentations for: - [developer setups](https://www.kimai.org/documentation/developers.html) — on your local machine diff --git a/UPGRADING.md b/UPGRADING.md index 034e2d0b..a71a15e8 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -10,7 +10,7 @@ Perform EACH version specific task between your version and the new one, otherwi ## [2.0.30](https://github.com/kimai/kimai/releases/tag/2.0.30) -The `DATABASE_URL` in your environment settings ([.env](https://github.com/kimai/kimai/issues/4246) or in your [Docker](https://github.com/tobybatch/kimai2/issues/531) or webserver config) +The `DATABASE_URL` in your environment settings (e.g. [.env](https://github.com/kimai/kimai/issues/4246), [docker-compose.yaml](https://github.com/tobybatch/kimai2/issues/531) or webserver config) now requires the `charset` and `serverVersion` params, e.g.: `DATABASE_URL=mysql://user:password@127.0.0.1:3306/database?charset=utf8mb4&serverVersion=10.5.8-MariaDB` (examples in `.env`). ## [2.0](https://github.com/kimai/kimai/releases/tag/2.0) diff --git a/composer.lock b/composer.lock index 5bc550f5..9298cf50 100644 --- a/composer.lock +++ b/composer.lock @@ -313,82 +313,6 @@ }, "time": "2023-08-25T16:18:39+00:00" }, - { - "name": "doctrine/annotations", - "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^2 || ^3", - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^2.0", - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.8.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6", - "vimeo/psalm": "^4.10" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.1" - }, - "time": "2023-02-02T22:02:53+00:00" - }, { "name": "doctrine/cache", "version": "2.2.0", @@ -484,16 +408,16 @@ }, { "name": "doctrine/collections", - "version": "2.1.4", + "version": "2.2.1", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "72328a11443a0de79967104ad36ba7b30bded134" + "reference": "420480fc085bc65f3c956af13abe8e7546f94813" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/72328a11443a0de79967104ad36ba7b30bded134", - "reference": "72328a11443a0de79967104ad36ba7b30bded134", + "url": "https://api.github.com/repos/doctrine/collections/zipball/420480fc085bc65f3c956af13abe8e7546f94813", + "reference": "420480fc085bc65f3c956af13abe8e7546f94813", "shasum": "" }, "require": { @@ -505,7 +429,7 @@ "ext-json": "*", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^9.5", + "phpunit/phpunit": "^10.5", "vimeo/psalm": "^5.11" }, "type": "library", @@ -550,7 +474,7 @@ ], "support": { "issues": "https://github.com/doctrine/collections/issues", - "source": "https://github.com/doctrine/collections/tree/2.1.4" + "source": "https://github.com/doctrine/collections/tree/2.2.1" }, "funding": [ { @@ -566,7 +490,7 @@ "type": "tidelift" } ], - "time": "2023-10-03T09:22:33+00:00" + "time": "2024-03-05T22:28:45+00:00" }, { "name": "doctrine/common", @@ -661,16 +585,16 @@ }, { "name": "doctrine/dbal", - "version": "3.8.2", + "version": "3.8.3", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb" + "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/a19a1d05ca211f41089dffcc387733a6875196cb", - "reference": "a19a1d05ca211f41089dffcc387733a6875196cb", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/db922ba9436b7b18a23d1653a0b41ff2369ca41c", + "reference": "db922ba9436b7b18a23d1653a0b41ff2369ca41c", "shasum": "" }, "require": { @@ -686,12 +610,12 @@ "doctrine/coding-standard": "12.0.0", "fig/log-test": "^1", "jetbrains/phpstorm-stubs": "2023.1", - "phpstan/phpstan": "1.10.57", + "phpstan/phpstan": "1.10.58", "phpstan/phpstan-strict-rules": "^1.5", "phpunit/phpunit": "9.6.16", "psalm/plugin-phpunit": "0.18.4", "slevomat/coding-standard": "8.13.1", - "squizlabs/php_codesniffer": "3.8.1", + "squizlabs/php_codesniffer": "3.9.0", "symfony/cache": "^5.4|^6.0|^7.0", "symfony/console": "^4.4|^5.4|^6.0|^7.0", "vimeo/psalm": "4.30.0" @@ -754,7 +678,7 @@ ], "support": { "issues": "https://github.com/doctrine/dbal/issues", - "source": "https://github.com/doctrine/dbal/tree/3.8.2" + "source": "https://github.com/doctrine/dbal/tree/3.8.3" }, "funding": [ { @@ -770,7 +694,7 @@ "type": "tidelift" } ], - "time": "2024-02-12T18:36:36+00:00" + "time": "2024-03-03T15:55:06+00:00" }, { "name": "doctrine/deprecations", @@ -1360,16 +1284,16 @@ }, { "name": "doctrine/migrations", - "version": "3.7.2", + "version": "3.7.4", "source": { "type": "git", "url": "https://github.com/doctrine/migrations.git", - "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a" + "reference": "954e0a314c2f0eb9fb418210445111747de254a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/migrations/zipball/47af29eef49f29ebee545947e8b2a4b3be318c8a", - "reference": "47af29eef49f29ebee545947e8b2a4b3be318c8a", + "url": "https://api.github.com/repos/doctrine/migrations/zipball/954e0a314c2f0eb9fb418210445111747de254a6", + "reference": "954e0a314c2f0eb9fb418210445111747de254a6", "shasum": "" }, "require": { @@ -1442,7 +1366,7 @@ ], "support": { "issues": "https://github.com/doctrine/migrations/issues", - "source": "https://github.com/doctrine/migrations/tree/3.7.2" + "source": "https://github.com/doctrine/migrations/tree/3.7.4" }, "funding": [ { @@ -1458,20 +1382,20 @@ "type": "tidelift" } ], - "time": "2023-12-05T11:35:05+00:00" + "time": "2024-03-06T13:41:11+00:00" }, { "name": "doctrine/orm", - "version": "2.18.0", + "version": "2.19.0", "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "f2176a9ce56cafdfd1624d54bfdb076819083d5b" + "reference": "a809a71aa6a233a6c82e68ebaaf8954adc4998dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/f2176a9ce56cafdfd1624d54bfdb076819083d5b", - "reference": "f2176a9ce56cafdfd1624d54bfdb076819083d5b", + "url": "https://api.github.com/repos/doctrine/orm/zipball/a809a71aa6a233a6c82e68ebaaf8954adc4998dc", + "reference": "a809a71aa6a233a6c82e68ebaaf8954adc4998dc", "shasum": "" }, "require": { @@ -1500,14 +1424,14 @@ "doctrine/annotations": "^1.13 || ^2", "doctrine/coding-standard": "^9.0.2 || ^12.0", "phpbench/phpbench": "^0.16.10 || ^1.0", - "phpstan/phpstan": "~1.4.10 || 1.10.35", + "phpstan/phpstan": "~1.4.10 || 1.10.59", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6", "psr/log": "^1 || ^2 || ^3", "squizlabs/php_codesniffer": "3.7.2", "symfony/cache": "^4.4 || ^5.4 || ^6.4 || ^7.0", "symfony/var-exporter": "^4.4 || ^5.4 || ^6.2 || ^7.0", "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "vimeo/psalm": "4.30.0 || 5.16.0" + "vimeo/psalm": "4.30.0 || 5.22.2" }, "suggest": { "ext-dom": "Provides support for XSD validation for XML mapping files", @@ -1557,22 +1481,22 @@ ], "support": { "issues": "https://github.com/doctrine/orm/issues", - "source": "https://github.com/doctrine/orm/tree/2.18.0" + "source": "https://github.com/doctrine/orm/tree/2.19.0" }, - "time": "2024-01-31T15:53:12+00:00" + "time": "2024-03-03T17:43:41+00:00" }, { "name": "doctrine/persistence", - "version": "3.2.0", + "version": "3.3.1", "source": { "type": "git", "url": "https://github.com/doctrine/persistence.git", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603" + "reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/persistence/zipball/63fee8c33bef740db6730eb2a750cd3da6495603", - "reference": "63fee8c33bef740db6730eb2a750cd3da6495603", + "url": "https://api.github.com/repos/doctrine/persistence/zipball/b6fd1f126b13c1f7e7321f7338b14a19116b5de4", + "reference": "b6fd1f126b13c1f7e7321f7338b14a19116b5de4", "shasum": "" }, "require": { @@ -1641,7 +1565,7 @@ ], "support": { "issues": "https://github.com/doctrine/persistence/issues", - "source": "https://github.com/doctrine/persistence/tree/3.2.0" + "source": "https://github.com/doctrine/persistence/tree/3.3.1" }, "funding": [ { @@ -1657,20 +1581,20 @@ "type": "tidelift" } ], - "time": "2023-05-17T18:32:04+00:00" + "time": "2024-03-01T19:53:13+00:00" }, { "name": "doctrine/sql-formatter", - "version": "1.1.3", + "version": "1.2.0", "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5" + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/25a06c7bf4c6b8218f47928654252863ffc890a5", - "reference": "25a06c7bf4c6b8218f47928654252863ffc890a5", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/a321d114e0a18e6497f8a2cd6f890e000cc17ecc", + "reference": "a321d114e0a18e6497f8a2cd6f890e000cc17ecc", "shasum": "" }, "require": { @@ -1707,9 +1631,9 @@ ], "support": { "issues": "https://github.com/doctrine/sql-formatter/issues", - "source": "https://github.com/doctrine/sql-formatter/tree/1.1.3" + "source": "https://github.com/doctrine/sql-formatter/tree/1.2.0" }, - "time": "2022-05-23T21:33:49+00:00" + "time": "2023-08-16T21:49:04+00:00" }, { "name": "egulias/email-validator", @@ -2263,27 +2187,27 @@ }, { "name": "jms/serializer", - "version": "3.29.1", + "version": "3.30.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "111451f43abb448ce297361a8ab96a9591e848cd" + "reference": "bf1105358123d7c02ee6cad08ea33ab535a09d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/111451f43abb448ce297361a8ab96a9591e848cd", - "reference": "111451f43abb448ce297361a8ab96a9591e848cd", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/bf1105358123d7c02ee6cad08ea33ab535a09d5e", + "reference": "bf1105358123d7c02ee6cad08ea33ab535a09d5e", "shasum": "" }, "require": { - "doctrine/annotations": "^1.14 || ^2.0", "doctrine/instantiator": "^1.3.1 || ^2.0", "doctrine/lexer": "^2.0 || ^3.0", "jms/metadata": "^2.6", - "php": "^7.2 || ^8.0", + "php": "^7.4 || ^8.0", "phpstan/phpdoc-parser": "^1.20" }, "require-dev": { + "doctrine/annotations": "^1.14 || ^2.0", "doctrine/coding-standard": "^12.0", "doctrine/orm": "^2.14 || ^3.0", "doctrine/persistence": "^2.5.2 || ^3.0", @@ -2293,16 +2217,17 @@ "ocramius/proxy-manager": "^1.0 || ^2.0", "phpbench/phpbench": "^1.0", "phpstan/phpstan": "^1.0.2", - "phpunit/phpunit": "^8.5.21 || ^9.0 || ^10.0", + "phpunit/phpunit": "^9.0 || ^10.0", "psr/container": "^1.0 || ^2.0", - "symfony/dependency-injection": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/expression-language": "^3.2 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/filesystem": "^4.2 || ^5.0 || ^6.0 || ^7.0", - "symfony/form": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/translation": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/uid": "^5.1 || ^6.0 || ^7.0", - "symfony/validator": "^3.1.9 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/yaml": "^3.4 || ^4.0 || ^5.0 || ^6.0 || ^7.0", + "rector/rector": "^0.19.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", + "symfony/expression-language": "^5.4 || ^6.0 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", + "symfony/form": "^5.4 || ^6.0 || ^7.0", + "symfony/translation": "^5.4 || ^6.0 || ^7.0", + "symfony/uid": "^5.4 || ^6.0 || ^7.0", + "symfony/validator": "^5.4 || ^6.0 || ^7.0", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0", "twig/twig": "^1.34 || ^2.4 || ^3.0" }, "suggest": { @@ -2347,7 +2272,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/serializer/issues", - "source": "https://github.com/schmittjoh/serializer/tree/3.29.1" + "source": "https://github.com/schmittjoh/serializer/tree/3.30.0" }, "funding": [ { @@ -2355,7 +2280,7 @@ "type": "github" } ], - "time": "2023-12-14T15:25:09+00:00" + "time": "2024-02-24T14:12:14+00:00" }, { "name": "jms/serializer-bundle", @@ -3183,16 +3108,16 @@ }, { "name": "nelmio/api-doc-bundle", - "version": "v4.20.0", + "version": "v4.23.1", "source": { "type": "git", "url": "https://github.com/nelmio/NelmioApiDocBundle.git", - "reference": "61c08f139e104db21b12832173b08fa37c22e619" + "reference": "a15b5923602c669007ea53a1a87991e9e147daab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/61c08f139e104db21b12832173b08fa37c22e619", - "reference": "61c08f139e104db21b12832173b08fa37c22e619", + "url": "https://api.github.com/repos/nelmio/NelmioApiDocBundle/zipball/a15b5923602c669007ea53a1a87991e9e147daab", + "reference": "a15b5923602c669007ea53a1a87991e9e147daab", "shasum": "" }, "require": { @@ -3262,11 +3187,8 @@ }, "autoload": { "psr-4": { - "Nelmio\\ApiDocBundle\\": "" - }, - "exclude-from-classmap": [ - "Tests/" - ] + "Nelmio\\ApiDocBundle\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3287,9 +3209,9 @@ ], "support": { "issues": "https://github.com/nelmio/NelmioApiDocBundle/issues", - "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.20.0" + "source": "https://github.com/nelmio/NelmioApiDocBundle/tree/v4.23.1" }, - "time": "2024-02-13T14:15:29+00:00" + "time": "2024-03-07T09:21:17+00:00" }, { "name": "nelmio/cors-bundle", @@ -3741,21 +3663,21 @@ }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353" + "reference": "153ae662783729388a584b4361f2545e4d841e3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", - "reference": "bc3dc91a5e9b14aa06d1d9e90647c5c5a2cc5353", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", + "reference": "153ae662783729388a584b4361f2545e4d841e3c", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", + "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpstan/phpdoc-parser": "^1.13" }, @@ -3793,9 +3715,9 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.1" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" }, - "time": "2024-01-18T19:15:27+00:00" + "time": "2024-02-23T11:10:43+00:00" }, { "name": "phpoffice/math", @@ -4066,16 +3988,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.25.0", + "version": "1.26.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240" + "reference": "231e3186624c03d7e7c890ec662b81e6b0405227" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240", - "reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227", + "reference": "231e3186624c03d7e7c890ec662b81e6b0405227", "shasum": "" }, "require": { @@ -4107,9 +4029,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0" }, - "time": "2024-01-04T17:06:16+00:00" + "time": "2024-02-23T16:05:55+00:00" }, { "name": "psr/cache", @@ -5005,16 +4927,16 @@ }, { "name": "symfony/cache", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "49f8cdee544a621a621cd21b6cda32a38926d310" + "reference": "0ef36534694c572ff526d91c7181f3edede176e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/49f8cdee544a621a621cd21b6cda32a38926d310", - "reference": "49f8cdee544a621a621cd21b6cda32a38926d310", + "url": "https://api.github.com/repos/symfony/cache/zipball/0ef36534694c572ff526d91c7181f3edede176e7", + "reference": "0ef36534694c572ff526d91c7181f3edede176e7", "shasum": "" }, "require": { @@ -5081,7 +5003,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v6.4.3" + "source": "https://github.com/symfony/cache/tree/v6.4.4" }, "funding": [ { @@ -5097,7 +5019,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/cache-contracts", @@ -5177,16 +5099,16 @@ }, { "name": "symfony/clock", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/clock.git", - "reference": "f48770105c544001da00b8d745873a628e0de198" + "reference": "ecba44be4def12cd71e0460b956ab7e51a2c980e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/clock/zipball/f48770105c544001da00b8d745873a628e0de198", - "reference": "f48770105c544001da00b8d745873a628e0de198", + "url": "https://api.github.com/repos/symfony/clock/zipball/ecba44be4def12cd71e0460b956ab7e51a2c980e", + "reference": "ecba44be4def12cd71e0460b956ab7e51a2c980e", "shasum": "" }, "require": { @@ -5231,7 +5153,7 @@ "time" ], "support": { - "source": "https://github.com/symfony/clock/tree/v6.4.3" + "source": "https://github.com/symfony/clock/tree/v6.4.5" }, "funding": [ { @@ -5247,20 +5169,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-03-01T14:02:27+00:00" }, { "name": "symfony/config", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "206482ff3ed450495b1d5b7bad1bc3a852def96f" + "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/206482ff3ed450495b1d5b7bad1bc3a852def96f", - "reference": "206482ff3ed450495b1d5b7bad1bc3a852def96f", + "url": "https://api.github.com/repos/symfony/config/zipball/6ea4affc27f2086c9d16b92ab5429ce1e3c38047", + "reference": "6ea4affc27f2086c9d16b92ab5429ce1e3c38047", "shasum": "" }, "require": { @@ -5306,7 +5228,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.3" + "source": "https://github.com/symfony/config/tree/v6.4.4" }, "funding": [ { @@ -5322,20 +5244,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T13:26:27+00:00" + "time": "2024-02-26T07:52:26+00:00" }, { "name": "symfony/console", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e" + "reference": "0d9e4eb5ad413075624378f474c4167ea202de78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e", - "reference": "2aaf83b4de5b9d43b93e4aec6f2f8b676f7c567e", + "url": "https://api.github.com/repos/symfony/console/zipball/0d9e4eb5ad413075624378f474c4167ea202de78", + "reference": "0d9e4eb5ad413075624378f474c4167ea202de78", "shasum": "" }, "require": { @@ -5400,7 +5322,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.3" + "source": "https://github.com/symfony/console/tree/v6.4.4" }, "funding": [ { @@ -5416,7 +5338,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/css-selector", @@ -5485,16 +5407,16 @@ }, { "name": "symfony/dependency-injection", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "6871811c5a5c5e180244ddb689746446db02c05b" + "reference": "6236e5e843cb763e9d0f74245678b994afea5363" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6871811c5a5c5e180244ddb689746446db02c05b", - "reference": "6871811c5a5c5e180244ddb689746446db02c05b", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/6236e5e843cb763e9d0f74245678b994afea5363", + "reference": "6236e5e843cb763e9d0f74245678b994afea5363", "shasum": "" }, "require": { @@ -5546,7 +5468,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.3" + "source": "https://github.com/symfony/dependency-injection/tree/v6.4.4" }, "funding": [ { @@ -5562,7 +5484,7 @@ "type": "tidelift" } ], - "time": "2024-01-30T08:32:12+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/deprecation-contracts", @@ -5633,16 +5555,16 @@ }, { "name": "symfony/doctrine-bridge", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "9c9a44bb06337dadeb9db1a8b202f15cca804353" + "reference": "fb868f29461c8a9ffc5c729ac03d08bf49e0139b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/9c9a44bb06337dadeb9db1a8b202f15cca804353", - "reference": "9c9a44bb06337dadeb9db1a8b202f15cca804353", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/fb868f29461c8a9ffc5c729ac03d08bf49e0139b", + "reference": "fb868f29461c8a9ffc5c729ac03d08bf49e0139b", "shasum": "" }, "require": { @@ -5721,7 +5643,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.3" + "source": "https://github.com/symfony/doctrine-bridge/tree/v6.4.5" }, "funding": [ { @@ -5737,20 +5659,20 @@ "type": "tidelift" } ], - "time": "2024-01-30T11:24:52+00:00" + "time": "2024-02-27T12:33:30+00:00" }, { "name": "symfony/dotenv", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dotenv.git", - "reference": "3cb7ca997124760ed1389d5341806247670f4ef8" + "reference": "f6f0a3dd102915b4c5bfdf4f4e3139a8cbf477a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dotenv/zipball/3cb7ca997124760ed1389d5341806247670f4ef8", - "reference": "3cb7ca997124760ed1389d5341806247670f4ef8", + "url": "https://api.github.com/repos/symfony/dotenv/zipball/f6f0a3dd102915b4c5bfdf4f4e3139a8cbf477a0", + "reference": "f6f0a3dd102915b4c5bfdf4f4e3139a8cbf477a0", "shasum": "" }, "require": { @@ -5795,7 +5717,7 @@ "environment" ], "support": { - "source": "https://github.com/symfony/dotenv/tree/v6.4.3" + "source": "https://github.com/symfony/dotenv/tree/v6.4.4" }, "funding": [ { @@ -5811,20 +5733,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-08T17:53:17+00:00" }, { "name": "symfony/error-handler", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "6dc3c76a278b77f01d864a6005d640822c6f26a6" + "reference": "c725219bdf2afc59423c32793d5019d2a904e13a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/6dc3c76a278b77f01d864a6005d640822c6f26a6", - "reference": "6dc3c76a278b77f01d864a6005d640822c6f26a6", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/c725219bdf2afc59423c32793d5019d2a904e13a", + "reference": "c725219bdf2afc59423c32793d5019d2a904e13a", "shasum": "" }, "require": { @@ -5870,7 +5792,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.3" + "source": "https://github.com/symfony/error-handler/tree/v6.4.4" }, "funding": [ { @@ -5886,7 +5808,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:40:36+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/event-dispatcher", @@ -6237,16 +6159,16 @@ }, { "name": "symfony/flex", - "version": "v2.4.4", + "version": "v2.4.5", "source": { "type": "git", "url": "https://github.com/symfony/flex.git", - "reference": "bec213c39511eda66663baa2ee7440c65f89c695" + "reference": "b0a405f40614c9f584b489d54f91091817b0e26e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/flex/zipball/bec213c39511eda66663baa2ee7440c65f89c695", - "reference": "bec213c39511eda66663baa2ee7440c65f89c695", + "url": "https://api.github.com/repos/symfony/flex/zipball/b0a405f40614c9f584b489d54f91091817b0e26e", + "reference": "b0a405f40614c9f584b489d54f91091817b0e26e", "shasum": "" }, "require": { @@ -6282,7 +6204,7 @@ "description": "Composer plugin for Symfony", "support": { "issues": "https://github.com/symfony/flex/issues", - "source": "https://github.com/symfony/flex/tree/v2.4.4" + "source": "https://github.com/symfony/flex/tree/v2.4.5" }, "funding": [ { @@ -6298,20 +6220,20 @@ "type": "tidelift" } ], - "time": "2024-02-05T18:04:53+00:00" + "time": "2024-03-02T08:16:47+00:00" }, { "name": "symfony/form", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/form.git", - "reference": "dabf7e9375550aada8916ced1736d01c2e3debff" + "reference": "c72cf9aab0d6c6db64358f9dd0ab391c2cc6014a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/form/zipball/dabf7e9375550aada8916ced1736d01c2e3debff", - "reference": "dabf7e9375550aada8916ced1736d01c2e3debff", + "url": "https://api.github.com/repos/symfony/form/zipball/c72cf9aab0d6c6db64358f9dd0ab391c2cc6014a", + "reference": "c72cf9aab0d6c6db64358f9dd0ab391c2cc6014a", "shasum": "" }, "require": { @@ -6379,7 +6301,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.3" + "source": "https://github.com/symfony/form/tree/v6.4.4" }, "funding": [ { @@ -6395,20 +6317,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-12T11:14:32+00:00" }, { "name": "symfony/framework-bundle", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "fb413ac4483803954411966a39f3a9204835848e" + "reference": "c76d3881596860ead95f5444a5ce4414447f0067" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/fb413ac4483803954411966a39f3a9204835848e", - "reference": "fb413ac4483803954411966a39f3a9204835848e", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/c76d3881596860ead95f5444a5ce4414447f0067", + "reference": "c76d3881596860ead95f5444a5ce4414447f0067", "shasum": "" }, "require": { @@ -6447,7 +6369,7 @@ "symfony/mime": "<6.4", "symfony/property-access": "<5.4", "symfony/property-info": "<5.4", - "symfony/scheduler": "<6.4.3|>=7.0.0,<7.0.3", + "symfony/scheduler": "<6.4.4|>=7.0.0,<7.0.4", "symfony/security-core": "<5.4", "symfony/security-csrf": "<5.4", "symfony/serializer": "<6.4", @@ -6486,7 +6408,7 @@ "symfony/process": "^5.4|^6.0|^7.0", "symfony/property-info": "^5.4|^6.0|^7.0", "symfony/rate-limiter": "^5.4|^6.0|^7.0", - "symfony/scheduler": "^6.4.3|^7.0.3", + "symfony/scheduler": "^6.4.4|^7.0.4", "symfony/security-bundle": "^5.4|^6.0|^7.0", "symfony/semaphore": "^5.4|^6.0|^7.0", "symfony/serializer": "^6.4|^7.0", @@ -6499,7 +6421,7 @@ "symfony/web-link": "^5.4|^6.0|^7.0", "symfony/workflow": "^6.4|^7.0", "symfony/yaml": "^5.4|^6.0|^7.0", - "twig/twig": "^2.10|^3.0" + "twig/twig": "^2.10|^3.0.4" }, "type": "symfony-bundle", "autoload": { @@ -6527,7 +6449,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.3" + "source": "https://github.com/symfony/framework-bundle/tree/v6.4.4" }, "funding": [ { @@ -6543,20 +6465,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:02:55+00:00" + "time": "2024-02-22T22:50:59+00:00" }, { "name": "symfony/http-client", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86" + "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/a9034bc119fab8238f76cf49c770f3135f3ead86", - "reference": "a9034bc119fab8238f76cf49c770f3135f3ead86", + "url": "https://api.github.com/repos/symfony/http-client/zipball/f3c86a60a3615f466333a11fd42010d4382a82c7", + "reference": "f3c86a60a3615f466333a11fd42010d4382a82c7", "shasum": "" }, "require": { @@ -6620,7 +6542,7 @@ "http" ], "support": { - "source": "https://github.com/symfony/http-client/tree/v6.4.3" + "source": "https://github.com/symfony/http-client/tree/v6.4.5" }, "funding": [ { @@ -6636,7 +6558,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:01:07+00:00" + "time": "2024-03-02T12:45:30+00:00" }, { "name": "symfony/http-client-contracts", @@ -6718,16 +6640,16 @@ }, { "name": "symfony/http-foundation", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "5677bdf7cade4619cb17fc9e1e7b31ec392244a9" + "reference": "ebc713bc6e6f4b53f46539fc158be85dfcd77304" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/5677bdf7cade4619cb17fc9e1e7b31ec392244a9", - "reference": "5677bdf7cade4619cb17fc9e1e7b31ec392244a9", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/ebc713bc6e6f4b53f46539fc158be85dfcd77304", + "reference": "ebc713bc6e6f4b53f46539fc158be85dfcd77304", "shasum": "" }, "require": { @@ -6775,7 +6697,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.3" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.4" }, "funding": [ { @@ -6791,20 +6713,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-08T15:01:18+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "9c6ec4e543044f7568a53a76ab1484ecd30637a2" + "reference": "f6947cb939d8efee137797382cb4db1af653ef75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/9c6ec4e543044f7568a53a76ab1484ecd30637a2", - "reference": "9c6ec4e543044f7568a53a76ab1484ecd30637a2", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f6947cb939d8efee137797382cb4db1af653ef75", + "reference": "f6947cb939d8efee137797382cb4db1af653ef75", "shasum": "" }, "require": { @@ -6853,7 +6775,7 @@ "symfony/process": "^5.4|^6.0|^7.0", "symfony/property-access": "^5.4.5|^6.0.5|^7.0", "symfony/routing": "^5.4|^6.0|^7.0", - "symfony/serializer": "^6.3|^7.0", + "symfony/serializer": "^6.4.4|^7.0.4", "symfony/stopwatch": "^5.4|^6.0|^7.0", "symfony/translation": "^5.4|^6.0|^7.0", "symfony/translation-contracts": "^2.5|^3", @@ -6888,7 +6810,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.3" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.5" }, "funding": [ { @@ -6904,7 +6826,7 @@ "type": "tidelift" } ], - "time": "2024-01-31T07:21:29+00:00" + "time": "2024-03-04T21:00:47+00:00" }, { "name": "symfony/intl", @@ -6990,16 +6912,16 @@ }, { "name": "symfony/mailer", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/mailer.git", - "reference": "74412c62f88a85a41b61f0b71ab0afcaad6f03ee" + "reference": "791c5d31a8204cf3db0c66faab70282307f4376b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mailer/zipball/74412c62f88a85a41b61f0b71ab0afcaad6f03ee", - "reference": "74412c62f88a85a41b61f0b71ab0afcaad6f03ee", + "url": "https://api.github.com/repos/symfony/mailer/zipball/791c5d31a8204cf3db0c66faab70282307f4376b", + "reference": "791c5d31a8204cf3db0c66faab70282307f4376b", "shasum": "" }, "require": { @@ -7050,7 +6972,7 @@ "description": "Helps sending emails", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/mailer/tree/v6.4.3" + "source": "https://github.com/symfony/mailer/tree/v6.4.4" }, "funding": [ { @@ -7066,7 +6988,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:01:07+00:00" + "time": "2024-02-03T21:33:47+00:00" }, { "name": "symfony/mime", @@ -7154,16 +7076,16 @@ }, { "name": "symfony/monolog-bridge", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/monolog-bridge.git", - "reference": "1e1ec293f15dcc815146637ee9ee8a7f43642fa1" + "reference": "db7468152b27242f1a4d10fabe278a2cfaa4eac0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/1e1ec293f15dcc815146637ee9ee8a7f43642fa1", - "reference": "1e1ec293f15dcc815146637ee9ee8a7f43642fa1", + "url": "https://api.github.com/repos/symfony/monolog-bridge/zipball/db7468152b27242f1a4d10fabe278a2cfaa4eac0", + "reference": "db7468152b27242f1a4d10fabe278a2cfaa4eac0", "shasum": "" }, "require": { @@ -7176,7 +7098,7 @@ "conflict": { "symfony/console": "<5.4", "symfony/http-foundation": "<5.4", - "symfony/security-core": "<6.0" + "symfony/security-core": "<5.4" }, "require-dev": { "symfony/console": "^5.4|^6.0|^7.0", @@ -7184,7 +7106,7 @@ "symfony/mailer": "^5.4|^6.0|^7.0", "symfony/messenger": "^5.4|^6.0|^7.0", "symfony/mime": "^5.4|^6.0|^7.0", - "symfony/security-core": "^6.0|^7.0", + "symfony/security-core": "^5.4|^6.0|^7.0", "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "symfony-bridge", @@ -7213,7 +7135,7 @@ "description": "Provides integration for Monolog with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/monolog-bridge/tree/v6.4.3" + "source": "https://github.com/symfony/monolog-bridge/tree/v6.4.4" }, "funding": [ { @@ -7229,7 +7151,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:01:07+00:00" + "time": "2024-02-01T11:49:25+00:00" }, { "name": "symfony/monolog-bundle", @@ -7381,16 +7303,16 @@ }, { "name": "symfony/password-hasher", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/password-hasher.git", - "reference": "5189cdfe89a9acb56cc6d6d7a5233bfb180c7268" + "reference": "114788555e6d768d25fffdbae618cee48cbcd112" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/password-hasher/zipball/5189cdfe89a9acb56cc6d6d7a5233bfb180c7268", - "reference": "5189cdfe89a9acb56cc6d6d7a5233bfb180c7268", + "url": "https://api.github.com/repos/symfony/password-hasher/zipball/114788555e6d768d25fffdbae618cee48cbcd112", + "reference": "114788555e6d768d25fffdbae618cee48cbcd112", "shasum": "" }, "require": { @@ -7433,7 +7355,7 @@ "password" ], "support": { - "source": "https://github.com/symfony/password-hasher/tree/v6.4.3" + "source": "https://github.com/symfony/password-hasher/tree/v6.4.4" }, "funding": [ { @@ -7449,7 +7371,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-12T11:14:32+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -7857,16 +7779,16 @@ }, { "name": "symfony/property-access", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/property-access.git", - "reference": "dd22c9247a16c712bfa128b3c90ebdd505102778" + "reference": "c0664db266024013e31446dd690b6bfcf218ad93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/dd22c9247a16c712bfa128b3c90ebdd505102778", - "reference": "dd22c9247a16c712bfa128b3c90ebdd505102778", + "url": "https://api.github.com/repos/symfony/property-access/zipball/c0664db266024013e31446dd690b6bfcf218ad93", + "reference": "c0664db266024013e31446dd690b6bfcf218ad93", "shasum": "" }, "require": { @@ -7914,7 +7836,7 @@ "reflection" ], "support": { - "source": "https://github.com/symfony/property-access/tree/v6.4.3" + "source": "https://github.com/symfony/property-access/tree/v6.4.4" }, "funding": [ { @@ -7930,7 +7852,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-16T13:31:43+00:00" }, { "name": "symfony/property-info", @@ -8088,16 +8010,16 @@ }, { "name": "symfony/routing", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/routing.git", - "reference": "3b2957ad54902f0f544df83e3d58b38d7e8e5842" + "reference": "7fe30068e207d9c31c0138501ab40358eb2d49a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/routing/zipball/3b2957ad54902f0f544df83e3d58b38d7e8e5842", - "reference": "3b2957ad54902f0f544df83e3d58b38d7e8e5842", + "url": "https://api.github.com/repos/symfony/routing/zipball/7fe30068e207d9c31c0138501ab40358eb2d49a4", + "reference": "7fe30068e207d9c31c0138501ab40358eb2d49a4", "shasum": "" }, "require": { @@ -8151,7 +8073,7 @@ "url" ], "support": { - "source": "https://github.com/symfony/routing/tree/v6.4.3" + "source": "https://github.com/symfony/routing/tree/v6.4.5" }, "funding": [ { @@ -8167,7 +8089,7 @@ "type": "tidelift" } ], - "time": "2024-01-30T13:55:02+00:00" + "time": "2024-02-27T12:33:30+00:00" }, { "name": "symfony/runtime", @@ -8250,16 +8172,16 @@ }, { "name": "symfony/security-bundle", - "version": "v6.4.3", + "version": "v6.4.5", "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "a53a9e1f6695447ce613aa8c9c698cfd012bd2aa" + "reference": "b7825ec970f51fcc4982397856405728544df9ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/a53a9e1f6695447ce613aa8c9c698cfd012bd2aa", - "reference": "a53a9e1f6695447ce613aa8c9c698cfd012bd2aa", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/b7825ec970f51fcc4982397856405728544df9ce", + "reference": "b7825ec970f51fcc4982397856405728544df9ce", "shasum": "" }, "require": { @@ -8342,7 +8264,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.3" + "source": "https://github.com/symfony/security-bundle/tree/v6.4.5" }, "funding": [ { @@ -8358,7 +8280,7 @@ "type": "tidelift" } ], - "time": "2024-01-28T15:49:46+00:00" + "time": "2024-03-02T12:45:30+00:00" }, { "name": "symfony/security-core", @@ -8516,16 +8438,16 @@ }, { "name": "symfony/security-http", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "d1962d08e02d620dccbaa28192498642500b5043" + "reference": "bf7548976c19ce751c95a3d012d0dcd27409e506" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/d1962d08e02d620dccbaa28192498642500b5043", - "reference": "d1962d08e02d620dccbaa28192498642500b5043", + "url": "https://api.github.com/repos/symfony/security-http/zipball/bf7548976c19ce751c95a3d012d0dcd27409e506", + "reference": "bf7548976c19ce751c95a3d012d0dcd27409e506", "shasum": "" }, "require": { @@ -8584,7 +8506,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v6.4.3" + "source": "https://github.com/symfony/security-http/tree/v6.4.4" }, "funding": [ { @@ -8600,20 +8522,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-26T07:52:26+00:00" }, { "name": "symfony/serializer", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/serializer.git", - "reference": "51a06ee93c4d5ab5b9edaa0635d8b83953e3c14d" + "reference": "88da7f8fe03c5f4c2a69da907f1de03fab2e6872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/serializer/zipball/51a06ee93c4d5ab5b9edaa0635d8b83953e3c14d", - "reference": "51a06ee93c4d5ab5b9edaa0635d8b83953e3c14d", + "url": "https://api.github.com/repos/symfony/serializer/zipball/88da7f8fe03c5f4c2a69da907f1de03fab2e6872", + "reference": "88da7f8fe03c5f4c2a69da907f1de03fab2e6872", "shasum": "" }, "require": { @@ -8682,7 +8604,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.3" + "source": "https://github.com/symfony/serializer/tree/v6.4.4" }, "funding": [ { @@ -8698,7 +8620,7 @@ "type": "tidelift" } ], - "time": "2024-01-30T08:32:12+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/service-contracts", @@ -8846,16 +8768,16 @@ }, { "name": "symfony/string", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "7a14736fb179876575464e4658fce0c304e8c15b" + "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/7a14736fb179876575464e4658fce0c304e8c15b", - "reference": "7a14736fb179876575464e4658fce0c304e8c15b", + "url": "https://api.github.com/repos/symfony/string/zipball/4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", + "reference": "4e465a95bdc32f49cf4c7f07f751b843bbd6dcd9", "shasum": "" }, "require": { @@ -8912,7 +8834,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.3" + "source": "https://github.com/symfony/string/tree/v6.4.4" }, "funding": [ { @@ -8928,20 +8850,20 @@ "type": "tidelift" } ], - "time": "2024-01-25T09:26:29+00:00" + "time": "2024-02-01T13:16:41+00:00" }, { "name": "symfony/translation", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "637c51191b6b184184bbf98937702bcf554f7d04" + "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/637c51191b6b184184bbf98937702bcf554f7d04", - "reference": "637c51191b6b184184bbf98937702bcf554f7d04", + "url": "https://api.github.com/repos/symfony/translation/zipball/bce6a5a78e94566641b2594d17e48b0da3184a8e", + "reference": "bce6a5a78e94566641b2594d17e48b0da3184a8e", "shasum": "" }, "require": { @@ -9007,7 +8929,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.3" + "source": "https://github.com/symfony/translation/tree/v6.4.4" }, "funding": [ { @@ -9023,7 +8945,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T13:11:52+00:00" + "time": "2024-02-20T13:16:58+00:00" }, { "name": "symfony/translation-contracts", @@ -9105,16 +9027,16 @@ }, { "name": "symfony/twig-bridge", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "bf6b411a5d9a0ce6ea43cca0fcf5f05f5196a957" + "reference": "256f330026d1c97187b61aa5c29e529499877f13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/bf6b411a5d9a0ce6ea43cca0fcf5f05f5196a957", - "reference": "bf6b411a5d9a0ce6ea43cca0fcf5f05f5196a957", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/256f330026d1c97187b61aa5c29e529499877f13", + "reference": "256f330026d1c97187b61aa5c29e529499877f13", "shasum": "" }, "require": { @@ -9194,7 +9116,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.3" + "source": "https://github.com/symfony/twig-bridge/tree/v6.4.4" }, "funding": [ { @@ -9210,20 +9132,20 @@ "type": "tidelift" } ], - "time": "2024-01-30T08:32:12+00:00" + "time": "2024-02-15T11:26:02+00:00" }, { "name": "symfony/twig-bundle", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/twig-bundle.git", - "reference": "2e63e50de2ade430191af0b5d21bfd6526fe3709" + "reference": "f60ba43a09d88395d05797af982588b57331ff4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/2e63e50de2ade430191af0b5d21bfd6526fe3709", - "reference": "2e63e50de2ade430191af0b5d21bfd6526fe3709", + "url": "https://api.github.com/repos/symfony/twig-bundle/zipball/f60ba43a09d88395d05797af982588b57331ff4d", + "reference": "f60ba43a09d88395d05797af982588b57331ff4d", "shasum": "" }, "require": { @@ -9278,7 +9200,7 @@ "description": "Provides a tight integration of Twig into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/twig-bundle/tree/v6.4.3" + "source": "https://github.com/symfony/twig-bundle/tree/v6.4.4" }, "funding": [ { @@ -9294,20 +9216,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-15T11:23:52+00:00" }, { "name": "symfony/validator", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "9c1d8bb4edce5304fcefca7923741085f1ca5b60" + "reference": "1cf92edc9a94d16275efef949fa6748d11cc8f47" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/9c1d8bb4edce5304fcefca7923741085f1ca5b60", - "reference": "9c1d8bb4edce5304fcefca7923741085f1ca5b60", + "url": "https://api.github.com/repos/symfony/validator/zipball/1cf92edc9a94d16275efef949fa6748d11cc8f47", + "reference": "1cf92edc9a94d16275efef949fa6748d11cc8f47", "shasum": "" }, "require": { @@ -9374,7 +9296,7 @@ "description": "Provides tools to validate values", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/validator/tree/v6.4.3" + "source": "https://github.com/symfony/validator/tree/v6.4.4" }, "funding": [ { @@ -9390,20 +9312,20 @@ "type": "tidelift" } ], - "time": "2024-01-29T15:01:07+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "symfony/var-dumper", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "0435a08f69125535336177c29d56af3abc1f69da" + "reference": "b439823f04c98b84d4366c79507e9da6230944b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0435a08f69125535336177c29d56af3abc1f69da", - "reference": "0435a08f69125535336177c29d56af3abc1f69da", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b439823f04c98b84d4366c79507e9da6230944b1", + "reference": "b439823f04c98b84d4366c79507e9da6230944b1", "shasum": "" }, "require": { @@ -9459,7 +9381,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v6.4.3" + "source": "https://github.com/symfony/var-dumper/tree/v6.4.4" }, "funding": [ { @@ -9475,20 +9397,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:53:30+00:00" + "time": "2024-02-15T11:23:52+00:00" }, { "name": "symfony/var-exporter", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "a8c12b5448a5ac685347f5eeb2abf6a571ec16b8" + "reference": "0bd342e24aef49fc82a21bd4eedd3e665d177e5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/a8c12b5448a5ac685347f5eeb2abf6a571ec16b8", - "reference": "a8c12b5448a5ac685347f5eeb2abf6a571ec16b8", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/0bd342e24aef49fc82a21bd4eedd3e665d177e5b", + "reference": "0bd342e24aef49fc82a21bd4eedd3e665d177e5b", "shasum": "" }, "require": { @@ -9534,7 +9456,7 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v6.4.3" + "source": "https://github.com/symfony/var-exporter/tree/v6.4.4" }, "funding": [ { @@ -9550,7 +9472,7 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-26T08:37:45+00:00" }, { "name": "symfony/webpack-encore-bundle", @@ -10398,16 +10320,16 @@ "packages-dev": [ { "name": "composer/pcre", - "version": "3.1.1", + "version": "3.1.2", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "url": "https://api.github.com/repos/composer/pcre/zipball/4775f35b2d70865807c89d32c8e7385b86eb0ace", + "reference": "4775f35b2d70865807c89d32c8e7385b86eb0ace", "shasum": "" }, "require": { @@ -10449,7 +10371,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.1" + "source": "https://github.com/composer/pcre/tree/3.1.2" }, "funding": [ { @@ -10465,7 +10387,7 @@ "type": "tidelift" } ], - "time": "2023-10-11T07:11:09+00:00" + "time": "2024-03-07T15:38:35+00:00" }, { "name": "composer/xdebug-handler", @@ -10837,16 +10759,16 @@ }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.49.0", + "version": "v3.51.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2" + "reference": "127fa74f010da99053e3f5b62672615b72dd6efd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/8742f7aa6f72a399688b65e4f58992c2d4681fc2", - "reference": "8742f7aa6f72a399688b65e4f58992c2d4681fc2", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/127fa74f010da99053e3f5b62672615b72dd6efd", + "reference": "127fa74f010da99053e3f5b62672615b72dd6efd", "shasum": "" }, "require": { @@ -10856,7 +10778,7 @@ "ext-json": "*", "ext-tokenizer": "*", "php": "^7.4 || ^8.0", - "sebastian/diff": "^4.0 || ^5.0", + "sebastian/diff": "^4.0 || ^5.0 || ^6.0", "symfony/console": "^5.4 || ^6.0 || ^7.0", "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", @@ -10877,7 +10799,8 @@ "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", - "phpunit/phpunit": "^9.6 || ^10.5.5", + "phpunit/phpunit": "^9.6 || ^10.5.5 || ^11.0.2", + "symfony/var-dumper": "^5.4 || ^6.0 || ^7.0", "symfony/yaml": "^5.4 || ^6.0 || ^7.0" }, "suggest": { @@ -10916,7 +10839,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.49.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.51.0" }, "funding": [ { @@ -10924,7 +10847,7 @@ "type": "github" } ], - "time": "2024-02-02T00:41:40+00:00" + "time": "2024-02-28T19:50:06+00:00" }, { "name": "masterminds/html5", @@ -10995,16 +10918,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.0.1", + "version": "v5.0.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69" + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69", - "reference": "2218c2252c874a4624ab2f613d86ac32d227bc69", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", + "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", "shasum": "" }, "require": { @@ -11047,26 +10970,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" }, - "time": "2024-02-21T19:24:10+00:00" + "time": "2024-03-05T20:51:40+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, "require": { "ext-dom": "*", + "ext-libxml": "*", "ext-phar": "*", "ext-xmlwriter": "*", "phar-io/version": "^3.0.1", @@ -11107,9 +11031,15 @@ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", "support": { "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -11164,16 +11094,16 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.59", + "version": "1.10.60", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e607609388d3a6d418a50a49f7940e8086798281" + "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e607609388d3a6d418a50a49f7940e8086798281", - "reference": "e607609388d3a6d418a50a49f7940e8086798281", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/95dcea7d6c628a3f2f56d091d8a0219485a86bbe", + "reference": "95dcea7d6c628a3f2f56d091d8a0219485a86bbe", "shasum": "" }, "require": { @@ -11222,7 +11152,7 @@ "type": "tidelift" } ], - "time": "2024-02-20T13:59:13+00:00" + "time": "2024-03-07T13:30:19+00:00" }, { "name": "phpstan/phpstan-doctrine", @@ -11298,16 +11228,16 @@ }, { "name": "phpstan/phpstan-phpunit", - "version": "1.3.15", + "version": "1.3.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a" + "reference": "d5242a59d035e46774f2e634b374bc39ff62cb95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/d5242a59d035e46774f2e634b374bc39ff62cb95", + "reference": "d5242a59d035e46774f2e634b374bc39ff62cb95", "shasum": "" }, "require": { @@ -11344,9 +11274,9 @@ "description": "PHPUnit extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.16" }, - "time": "2023-10-09T18:58:39+00:00" + "time": "2024-02-23T09:51:20+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -11399,16 +11329,16 @@ }, { "name": "phpstan/phpstan-symfony", - "version": "1.3.7", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "ef7db637be9b85fa00278fc3477ac66abe8eb7d1" + "reference": "d8a0bc03a68d95288b6471c37d435647fbdaff1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/ef7db637be9b85fa00278fc3477ac66abe8eb7d1", - "reference": "ef7db637be9b85fa00278fc3477ac66abe8eb7d1", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/d8a0bc03a68d95288b6471c37d435647fbdaff1a", + "reference": "d8a0bc03a68d95288b6471c37d435647fbdaff1a", "shasum": "" }, "require": { @@ -11465,22 +11395,22 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/1.3.7" + "source": "https://github.com/phpstan/phpstan-symfony/tree/1.3.8" }, - "time": "2024-01-10T21:54:42+00:00" + "time": "2024-03-05T16:33:08+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.30", + "version": "9.2.31", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", + "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", "shasum": "" }, "require": { @@ -11537,7 +11467,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31" }, "funding": [ { @@ -11545,7 +11475,7 @@ "type": "github" } ], - "time": "2023-12-22T06:47:57+00:00" + "time": "2024-03-02T06:37:42+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11892,16 +11822,16 @@ }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/2b56bea83a09de3ac06bb18b92f068e60cc6f50b", + "reference": "2b56bea83a09de3ac06bb18b92f068e60cc6f50b", "shasum": "" }, "require": { @@ -11936,7 +11866,7 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.2" }, "funding": [ { @@ -11944,7 +11874,7 @@ "type": "github" } ], - "time": "2020-09-28T06:08:49+00:00" + "time": "2024-03-02T06:27:43+00:00" }, { "name": "sebastian/code-unit", @@ -12190,16 +12120,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc", + "reference": "ba01945089c3a293b01ba9badc29ad55b106b0bc", "shasum": "" }, "require": { @@ -12244,7 +12174,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.6" }, "funding": [ { @@ -12252,7 +12182,7 @@ "type": "github" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2024-03-02T06:30:58+00:00" }, { "name": "sebastian/environment", @@ -12319,16 +12249,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "4.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/78c00df8f170e02473b682df15bfcdacc3d32d72", + "reference": "78c00df8f170e02473b682df15bfcdacc3d32d72", "shasum": "" }, "require": { @@ -12384,7 +12314,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.6" }, "funding": [ { @@ -12392,20 +12322,20 @@ "type": "github" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2024-03-02T06:33:00+00:00" }, { "name": "sebastian/global-state", - "version": "5.0.6", + "version": "5.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", + "reference": "bca7df1f32ee6fe93b4d4a9abbf69e13a4ada2c9", "shasum": "" }, "require": { @@ -12448,7 +12378,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.7" }, "funding": [ { @@ -12456,7 +12386,7 @@ "type": "github" } ], - "time": "2023-08-02T09:26:13+00:00" + "time": "2024-03-02T06:35:11+00:00" }, { "name": "sebastian/lines-of-code", @@ -12998,16 +12928,16 @@ }, { "name": "symfony/dom-crawler", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "6db31849011fefe091e94d0bb10cba26f7919894" + "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/6db31849011fefe091e94d0bb10cba26f7919894", - "reference": "6db31849011fefe091e94d0bb10cba26f7919894", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531", + "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531", "shasum": "" }, "require": { @@ -13045,7 +12975,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v6.4.3" + "source": "https://github.com/symfony/dom-crawler/tree/v6.4.4" }, "funding": [ { @@ -13061,20 +12991,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-07T09:17:57+00:00" }, { "name": "symfony/phpunit-bridge", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/phpunit-bridge.git", - "reference": "d49b4f6dc4690cf2c194311bb498abf0cf4f7485" + "reference": "16ed5bdfd18e14fc7de347c8688e8ac479284222" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/d49b4f6dc4690cf2c194311bb498abf0cf4f7485", - "reference": "d49b4f6dc4690cf2c194311bb498abf0cf4f7485", + "url": "https://api.github.com/repos/symfony/phpunit-bridge/zipball/16ed5bdfd18e14fc7de347c8688e8ac479284222", + "reference": "16ed5bdfd18e14fc7de347c8688e8ac479284222", "shasum": "" }, "require": { @@ -13126,7 +13056,7 @@ "description": "Provides utilities for PHPUnit, especially user deprecation notices management", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.3" + "source": "https://github.com/symfony/phpunit-bridge/tree/v6.4.4" }, "funding": [ { @@ -13142,20 +13072,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-08T14:08:19+00:00" }, { "name": "symfony/process", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "31642b0818bfcff85930344ef93193f8c607e0a3" + "reference": "710e27879e9be3395de2b98da3f52a946039f297" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/31642b0818bfcff85930344ef93193f8c607e0a3", - "reference": "31642b0818bfcff85930344ef93193f8c607e0a3", + "url": "https://api.github.com/repos/symfony/process/zipball/710e27879e9be3395de2b98da3f52a946039f297", + "reference": "710e27879e9be3395de2b98da3f52a946039f297", "shasum": "" }, "require": { @@ -13187,7 +13117,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.3" + "source": "https://github.com/symfony/process/tree/v6.4.4" }, "funding": [ { @@ -13203,20 +13133,20 @@ "type": "tidelift" } ], - "time": "2024-01-23T14:51:35+00:00" + "time": "2024-02-20T12:31:00+00:00" }, { "name": "symfony/web-profiler-bundle", - "version": "v6.4.3", + "version": "v6.4.4", "source": { "type": "git", "url": "https://github.com/symfony/web-profiler-bundle.git", - "reference": "e78f98da7b4f842bab89368d53c962f5b2f9e49c" + "reference": "a69d7124bfb2e15638ba0a1be94f0845d8d05ee4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/e78f98da7b4f842bab89368d53c962f5b2f9e49c", - "reference": "e78f98da7b4f842bab89368d53c962f5b2f9e49c", + "url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/a69d7124bfb2e15638ba0a1be94f0845d8d05ee4", + "reference": "a69d7124bfb2e15638ba0a1be94f0845d8d05ee4", "shasum": "" }, "require": { @@ -13269,7 +13199,7 @@ "dev" ], "support": { - "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.3" + "source": "https://github.com/symfony/web-profiler-bundle/tree/v6.4.4" }, "funding": [ { @@ -13285,20 +13215,20 @@ "type": "tidelift" } ], - "time": "2024-01-28T15:49:46+00:00" + "time": "2024-02-22T20:27:10+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "1.2.3", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "shasum": "" }, "require": { @@ -13327,7 +13257,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/1.2.3" }, "funding": [ { @@ -13335,7 +13265,7 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2024-03-03T12:36:25+00:00" } ], "aliases": [], diff --git a/phpstan.neon b/phpstan.neon index 061d6b43..8c1c9e9a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1107,11 +1107,6 @@ parameters: count: 1 path: src/Controller/Security/PasswordResetController.php - - - message: "#^Parameter \\#1 \\$usernameOrEmail of method App\\\\User\\\\UserService\\:\\:findUserByUsernameOrEmail\\(\\) expects string, bool\\|float\\|int\\|string\\|null given\\.$#" - count: 1 - path: src/Controller/Security/PasswordResetController.php - - message: "#^Method App\\\\Controller\\\\Security\\\\SelfRegistrationController\\:\\:getTargetUrlFromSession\\(\\) should return string\\|null but returns mixed\\.$#" count: 1 @@ -4812,11 +4807,6 @@ parameters: count: 1 path: src/Saml/SamlLoginAttributes.php - - - message: "#^Method App\\\\Saml\\\\SamlLogoutSubscriber\\:\\:logout\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Saml/SamlLogoutSubscriber.php - - message: "#^Parameter \\#4 \\$sessionIndex of method OneLogin\\\\Saml2\\\\Auth\\:\\:logout\\(\\) expects string\\|null, mixed given\\.$#" count: 1 @@ -4827,11 +4817,6 @@ parameters: count: 1 path: src/Saml/SamlProvider.php - - - message: "#^Method App\\\\Saml\\\\SamlProvider\\:\\:getPropertyValue\\(\\) has no return type specified\\.$#" - count: 1 - path: src/Saml/SamlProvider.php - - message: "#^Method App\\\\Saml\\\\SamlProvider\\:\\:getPropertyValue\\(\\) has parameter \\$attribute with no type specified\\.$#" count: 1 @@ -5557,11 +5542,6 @@ parameters: count: 1 path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetBudgetUsedValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php - - message: "#^Parameter \\#4 \\$duration of method App\\\\Validator\\\\Constraints\\\\TimesheetBudgetUsedValidator\\:\\:checkBudgets\\(\\) expects int, float\\|int\\|null given\\.$#" count: 3 @@ -5572,31 +5552,11 @@ parameters: count: 2 path: src/Validator/Constraints/TimesheetBudgetUsedValidator.php - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetExportedValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetExportedValidator.php - - message: "#^Cannot call method getTimezone\\(\\) on DateTime\\|null\\.$#" count: 1 path: src/Validator/Constraints/TimesheetFutureTimesValidator.php - - - message: "#^Parameter \\#1 \\$value \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetFutureTimesValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetFutureTimesValidator.php - - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetLockdownValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetLockdownValidator.php - - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetLongRunningValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetLongRunningValidator.php - - message: "#^Parameter \\#2 \\$value of method Symfony\\\\Component\\\\Validator\\\\Violation\\\\ConstraintViolationBuilderInterface\\:\\:setParameter\\(\\) expects string, string\\|null given\\.$#" count: 1 @@ -5607,26 +5567,6 @@ parameters: count: 1 path: src/Validator/Constraints/TimesheetMultiUpdateValidator.php - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetOverlappingValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetOverlappingValidator.php - - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetRestartValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetRestartValidator.php - - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetValidator.php - - - - message: "#^Parameter \\#1 \\$timesheet \\(App\\\\Entity\\\\Timesheet\\) of method App\\\\Validator\\\\Constraints\\\\TimesheetZeroDurationValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" - count: 2 - path: src/Validator/Constraints/TimesheetZeroDurationValidator.php - - message: "#^Parameter \\#1 \\$value \\(App\\\\Entity\\\\User\\) of method App\\\\Validator\\\\Constraints\\\\UserValidator\\:\\:validate\\(\\) should be contravariant with parameter \\$value \\(mixed\\) of method Symfony\\\\Component\\\\Validator\\\\ConstraintValidatorInterface\\:\\:validate\\(\\)$#" count: 2 diff --git a/src/API/ActivityController.php b/src/API/ActivityController.php index b1b652ff..518c8fe5 100644 --- a/src/API/ActivityController.php +++ b/src/API/ActivityController.php @@ -44,10 +44,10 @@ final class ActivityController extends BaseApiController public const GROUPS_RATE = ['Default', 'Entity', 'Activity_Rate']; public function __construct( - private ViewHandlerInterface $viewHandler, - private ActivityRepository $repository, - private EventDispatcherInterface $dispatcher, - private ActivityRateRepository $activityRateRepository + private readonly ViewHandlerInterface $viewHandler, + private readonly ActivityRepository $repository, + private readonly EventDispatcherInterface $dispatcher, + private readonly ActivityRateRepository $activityRateRepository ) { } @@ -128,6 +128,7 @@ final class ActivityController extends BaseApiController #[Route(methods: ['GET'], path: '/{id}', name: 'get_activity', requirements: ['id' => '\d+'])] #[ApiSecurity(name: 'apiUser')] #[ApiSecurity(name: 'apiToken')] + #[IsGranted('view', 'activity')] public function getAction(Activity $activity): Response { $view = new View($activity, 200); diff --git a/src/API/CustomerController.php b/src/API/CustomerController.php index 6daf608e..3bdede78 100644 --- a/src/API/CustomerController.php +++ b/src/API/CustomerController.php @@ -44,10 +44,10 @@ final class CustomerController extends BaseApiController public const GROUPS_RATE = ['Default', 'Entity', 'Customer_Rate']; public function __construct( - private ViewHandlerInterface $viewHandler, - private CustomerRepository $repository, - private EventDispatcherInterface $dispatcher, - private CustomerRateRepository $customerRateRepository + private readonly ViewHandlerInterface $viewHandler, + private readonly CustomerRepository $repository, + private readonly EventDispatcherInterface $dispatcher, + private readonly CustomerRateRepository $customerRateRepository ) { } @@ -105,6 +105,7 @@ final class CustomerController extends BaseApiController #[Route(methods: ['GET'], path: '/{id}', name: 'get_customer', requirements: ['id' => '\d+'])] #[ApiSecurity(name: 'apiUser')] #[ApiSecurity(name: 'apiToken')] + #[IsGranted('view', 'customer')] public function getAction(Customer $customer): Response { $view = new View($customer, 200); diff --git a/src/API/ProjectController.php b/src/API/ProjectController.php index ef57aa05..cd0f7458 100644 --- a/src/API/ProjectController.php +++ b/src/API/ProjectController.php @@ -46,11 +46,11 @@ final class ProjectController extends BaseApiController public const GROUPS_RATE = ['Default', 'Entity', 'Project_Rate']; public function __construct( - private ViewHandlerInterface $viewHandler, - private ProjectRepository $repository, - private EventDispatcherInterface $dispatcher, - private ProjectRateRepository $projectRateRepository, - private ProjectService $projectService + private readonly ViewHandlerInterface $viewHandler, + private readonly ProjectRepository $repository, + private readonly EventDispatcherInterface $dispatcher, + private readonly ProjectRateRepository $projectRateRepository, + private readonly ProjectService $projectService ) { } @@ -159,6 +159,7 @@ final class ProjectController extends BaseApiController #[Route(methods: ['GET'], path: '/{id}', name: 'get_project', requirements: ['id' => '\d+'])] #[ApiSecurity(name: 'apiUser')] #[ApiSecurity(name: 'apiToken')] + #[IsGranted('view', 'project')] public function getAction(Project $project): Response { $view = new View($project, 200); diff --git a/src/API/TimesheetController.php b/src/API/TimesheetController.php index 9952a64a..7f39e473 100644 --- a/src/API/TimesheetController.php +++ b/src/API/TimesheetController.php @@ -54,11 +54,11 @@ final class TimesheetController extends BaseApiController public const GROUPS_COLLECTION_FULL = ['Default', 'Collection', 'Timesheet', 'Expanded']; public function __construct( - private ViewHandlerInterface $viewHandler, - private TimesheetRepository $repository, - private TagRepository $tagRepository, - private EventDispatcherInterface $dispatcher, - private TimesheetService $service + private readonly ViewHandlerInterface $viewHandler, + private readonly TimesheetRepository $repository, + private readonly TagRepository $tagRepository, + private readonly EventDispatcherInterface $dispatcher, + private readonly TimesheetService $service ) { } @@ -99,6 +99,7 @@ final class TimesheetController extends BaseApiController public function cgetAction(ParamFetcherInterface $paramFetcher, CustomerRepository $customerRepository, ProjectRepository $projectRepository, ActivityRepository $activityRepository, UserRepository $userRepository): Response { $query = new TimesheetQuery(false); + $query->setCurrentUser($this->getUser()); $seeAll = false; if ($this->isGranted('view_other_timesheet')) { diff --git a/src/Constants.php b/src/Constants.php index bbeab3a5..ddce22e2 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -17,11 +17,11 @@ class Constants /** * The current release version */ - public const VERSION = '2.12.0'; + public const VERSION = '2.13.0'; /** * The current release: major * 10000 + minor * 100 + patch */ - public const VERSION_ID = 21200; + public const VERSION_ID = 21300; /** * The software name */ diff --git a/src/Controller/CustomerController.php b/src/Controller/CustomerController.php index 89537dd3..456bd50b 100644 --- a/src/Controller/CustomerController.php +++ b/src/Controller/CustomerController.php @@ -67,7 +67,7 @@ final class CustomerController extends AbstractController $query->setCurrentUser($this->getUser()); $query->setPage($page); - $form = $this->getToolbarForm($query); + $form = $this->getToolbarForm($query, $request); if ($this->handleSearch($form, $request)) { return $this->redirectToRoute('admin_customer'); } @@ -473,7 +473,7 @@ final class CustomerController extends AbstractController $query = new CustomerQuery(); $query->setCurrentUser($this->getUser()); - $form = $this->getToolbarForm($query); + $form = $this->getToolbarForm($query, $request); $form->setData($query); $form->submit($request->query->all(), false); @@ -526,12 +526,12 @@ final class CustomerController extends AbstractController } /** - * @param CustomerQuery $query * @return FormInterface */ - private function getToolbarForm(CustomerQuery $query): FormInterface + private function getToolbarForm(CustomerQuery $query, Request $request): FormInterface { return $this->createSearchForm(CustomerToolbarForm::class, $query, [ + 'locale' => $request->getLocale(), 'action' => $this->generateUrl('admin_customer', [ 'page' => $query->getPage(), ]) @@ -539,7 +539,6 @@ final class CustomerController extends AbstractController } /** - * @param CustomerComment $comment * @return FormInterface */ private function getCommentForm(CustomerComment $comment): FormInterface @@ -555,7 +554,6 @@ final class CustomerController extends AbstractController } /** - * @param Customer $customer * @return FormInterface */ private function createEditForm(Customer $customer): FormInterface diff --git a/src/Controller/PluginController.php b/src/Controller/PluginController.php index 92806b6d..5cbe10f8 100644 --- a/src/Controller/PluginController.php +++ b/src/Controller/PluginController.php @@ -45,21 +45,28 @@ final class PluginController extends AbstractController private function getPluginInformation(HttpClientInterface $client, CacheInterface $cache): array { return $cache->get('kimai.marketplace_extensions', function (ItemInterface $item) use ($client) { - $response = $client->request('GET', 'https://www.kimai.org/plugins.json'); + try { + $response = $client->request('GET', 'https://www.kimai.org/plugins.json'); - if ($response->getStatusCode() !== 200) { - return []; + if ($response->getStatusCode() !== 200) { + return []; + } + + $json = json_decode($response->getContent(), true); + + if ($json === null) { + return []; + } + + $item->expiresAfter(86400); // one day + + return $response->toArray(); + } catch (\Exception $exception) { + $this->logException($exception); + $this->flashError('Could not download plugin information'); } - $json = json_decode($response->getContent(), true); - - if ($json === null) { - return []; - } - - $item->expiresAfter(86400); // one day - - return $response->toArray(); + return []; }); } } diff --git a/src/Controller/Security/PasswordResetController.php b/src/Controller/Security/PasswordResetController.php index 00a5feb6..2da89feb 100644 --- a/src/Controller/Security/PasswordResetController.php +++ b/src/Controller/Security/PasswordResetController.php @@ -32,9 +32,9 @@ use Symfony\Contracts\Translation\TranslatorInterface; final class PasswordResetController extends AbstractController { public function __construct( - private EventDispatcherInterface $eventDispatcher, - private UserService $userService, - private SystemConfiguration $configuration + private readonly EventDispatcherInterface $eventDispatcher, + private readonly UserService $userService, + private readonly SystemConfiguration $configuration ) { } @@ -62,6 +62,10 @@ final class PasswordResetController extends AbstractController } $username = $request->request->get('username'); + if (!\is_string($username) || trim($username) === '') { + throw $this->createAccessDeniedException('Username cannot be empty'); + } + $user = $this->userService->findUserByUsernameOrEmail($username); if (!$user->isPasswordRequestNonExpired($this->configuration->getPasswordResetRetryLifetime())) { diff --git a/src/EventSubscriber/MenuSubscriber.php b/src/EventSubscriber/MenuSubscriber.php index 1269fc5c..3344c467 100644 --- a/src/EventSubscriber/MenuSubscriber.php +++ b/src/EventSubscriber/MenuSubscriber.php @@ -18,7 +18,10 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class MenuSubscriber implements EventSubscriberInterface { - public function __construct(private Security $security, private ContextHelper $helper) + public function __construct( + private readonly Security $security, + private readonly ContextHelper $helper + ) { } @@ -134,19 +137,19 @@ final class MenuSubscriber implements EventSubscriberInterface $menu = $event->getAdminMenu(); if ($auth->isGranted('view_customer') || $auth->isGranted('view_teamlead_customer') || $auth->isGranted('view_team_customer')) { - $customers = new MenuItemModel('customer_admin', 'customers', 'admin_customer', [], 'customer'); + $customers = new MenuItemModel('customers', 'customers', 'admin_customer', [], 'customer'); $customers->setChildRoutes(['admin_customer_create', 'admin_customer_permissions', 'customer_details', 'admin_customer_edit', 'admin_customer_delete']); $menu->addChild($customers); } if ($auth->isGranted('view_project') || $auth->isGranted('view_teamlead_project') || $auth->isGranted('view_team_project')) { - $projects = new MenuItemModel('project_admin', 'projects', 'admin_project', [], 'project'); + $projects = new MenuItemModel('projects', 'projects', 'admin_project', [], 'project'); $projects->setChildRoutes(['admin_project_permissions', 'admin_project_create', 'project_details', 'admin_project_edit', 'admin_project_delete']); $menu->addChild($projects); } if ($auth->isGranted('view_activity') || $auth->isGranted('view_teamlead_activity') || $auth->isGranted('view_team_activity')) { - $activities = new MenuItemModel('activity_admin', 'activities', 'admin_activity', [], 'activity'); + $activities = new MenuItemModel('activities', 'activities', 'admin_activity', [], 'activity'); $activities->setChildRoutes(['admin_activity_create', 'activity_details', 'admin_activity_edit', 'admin_activity_delete']); $menu->addChild($activities); } @@ -163,18 +166,18 @@ final class MenuSubscriber implements EventSubscriberInterface $menu = $event->getSystemMenu(); if ($auth->isGranted('view_user')) { - $users = new MenuItemModel('user_admin', 'users', 'admin_user', [], 'users'); + $users = new MenuItemModel('users', 'users', 'admin_user', [], 'users'); $users->setChildRoutes(['admin_user_create', 'admin_user_delete', 'user_profile', 'user_profile_edit', 'user_profile_password', 'user_profile_api_token', 'user_profile_roles', 'user_profile_teams', 'user_profile_preferences', 'user_profile_2fa']); $menu->addChild($users); } if ($auth->isGranted('role_permissions')) { - $users = new MenuItemModel('admin_user_permissions', 'profile.roles', 'admin_user_permissions', [], 'permissions'); + $users = new MenuItemModel('roles', 'profile.roles', 'admin_user_permissions', [], 'permissions'); $menu->addChild($users); } if ($auth->isGranted('view_team')) { - $teams = new MenuItemModel('user_team', 'teams', 'admin_team', [], 'team'); + $teams = new MenuItemModel('teams', 'teams', 'admin_team', [], 'team'); $teams->setChildRoutes(['admin_team_create', 'admin_team_edit']); $menu->addChild($teams); } @@ -190,7 +193,7 @@ final class MenuSubscriber implements EventSubscriberInterface } if ($auth->isGranted('system_configuration')) { - $systemConfig = new MenuItemModel('system_configuration', 'menu.system_configuration', 'system_configuration', [], 'configuration'); + $systemConfig = new MenuItemModel('configurations', 'menu.system_configuration', 'system_configuration', [], 'configuration'); $systemConfig->setChildRoutes(['system_configuration_update', 'system_configuration_section']); $menu->addChild($systemConfig); } diff --git a/src/Form/Toolbar/CustomerToolbarForm.php b/src/Form/Toolbar/CustomerToolbarForm.php index 597e9e39..520b7a8d 100644 --- a/src/Form/Toolbar/CustomerToolbarForm.php +++ b/src/Form/Toolbar/CustomerToolbarForm.php @@ -9,9 +9,12 @@ namespace App\Form\Toolbar; +use App\Repository\CustomerRepository; use App\Repository\Query\CustomerQuery; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Intl\Countries; use Symfony\Component\OptionsResolver\OptionsResolver; /** @@ -22,9 +25,38 @@ final class CustomerToolbarForm extends AbstractType { use ToolbarFormTrait; + public function __construct(private readonly CustomerRepository $customerRepository) + { + } + public function buildForm(FormBuilderInterface $builder, array $options): void { $this->addSearchTermInputField($builder); + + // fetch countries + $qb = $this->customerRepository->createQueryBuilder('c'); + $qb + ->select('c.country') + ->distinct(true); + + $countries = $qb->getQuery()->getSingleColumnResult(); + $choices = []; + foreach ($countries as $country) { + if (\is_string($country) && \is_string($options['locale'])) { + $choices[$country] = Countries::getName($country, $options['locale']); + } + } + + if (\count($choices) > 0) { + $choices = array_flip($choices); + ksort($choices); + $builder->add('country', ChoiceType::class, [ + 'label' => 'country', + 'choices' => $choices, + 'required' => false, + ]); + } + $this->addVisibilityChoice($builder); $this->addPageSizeChoice($builder); $this->addHiddenPagination($builder); @@ -37,6 +69,8 @@ final class CustomerToolbarForm extends AbstractType $resolver->setDefaults([ 'data_class' => CustomerQuery::class, 'csrf_protection' => false, + 'locale' => locale_get_default(), ]); + $resolver->setAllowedTypes('locale', ['string']); } } diff --git a/src/Form/Type/TagsType.php b/src/Form/Type/TagsType.php index 71ea30e4..5619bcdd 100644 --- a/src/Form/Type/TagsType.php +++ b/src/Form/Type/TagsType.php @@ -19,8 +19,8 @@ final class TagsType extends AbstractType private ?int $count = null; public function __construct( - private AuthorizationCheckerInterface $auth, - private TagRepository $repository + private readonly AuthorizationCheckerInterface $auth, + private readonly TagRepository $repository ) { } diff --git a/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php b/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php index b7934bbd..c3ee8682 100644 --- a/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php +++ b/src/Invoice/NumberGenerator/ConfigurableNumberGenerator.php @@ -19,7 +19,10 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface { private ?InvoiceModel $model = null; - public function __construct(private InvoiceRepository $repository, private SystemConfiguration $configuration) + public function __construct( + private readonly InvoiceRepository $repository, + private readonly SystemConfiguration $configuration + ) { } @@ -33,9 +36,6 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface $this->model = $model; } - /** - * @return string - */ public function getInvoiceNumber(): string { $format = $this->configuration->find('invoice.number_format'); @@ -68,6 +68,10 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface 'm' => $invoiceDate->format('n'), 'D' => $invoiceDate->format('d'), 'd' => $invoiceDate->format('j'), + 'YY' => (int) $invoiceDate->format('Y') + $increaseBy, + 'yy' => (int) $invoiceDate->format('y') + $increaseBy, + 'MM' => (int) $invoiceDate->format('m') + $increaseBy, + 'DD' => (int) $invoiceDate->format('d') + $increaseBy, 'date' => $invoiceDate->format('ymd'), 'cc' => $this->repository->getCounterForCustomerAllTime($this->model->getCustomer()) + $increaseBy, 'ccy' => $this->repository->getCounterForYear($invoiceDate, $this->model->getCustomer()) + $increaseBy, @@ -92,7 +96,7 @@ final class ConfigurableNumberGenerator implements NumberGeneratorInterface do { $result = $numberGenerator->getNumber($increaseBy); $increaseBy++; - } while ($this->repository->hasInvoice($result) && $loops++ < 99); + } while ((int) $result < 0 || ($this->repository->hasInvoice($result) && $loops++ < 99)); return $result; } diff --git a/src/Pdf/MPdfConverter.php b/src/Pdf/MPdfConverter.php index d940a2f4..6e2ef7a2 100644 --- a/src/Pdf/MPdfConverter.php +++ b/src/Pdf/MPdfConverter.php @@ -130,7 +130,7 @@ final class MPdfConverter implements HtmlToPdfConverter // lowercase all font names, otherwise they cannot be loaded // see https://github.com/kimai/www.kimai.org/issues/280 - if (\array_key_exists('fonts', $options)) { + if (\array_key_exists('fonts', $options) && \is_array($options['fonts'])) { $fonts = []; foreach ($options['fonts'] as $name => $values) { $fonts[strtolower($name)] = $values; diff --git a/src/Reporting/CustomerMonthlyProjects/CustomerMonthlyProjectsRepository.php b/src/Reporting/CustomerMonthlyProjects/CustomerMonthlyProjectsRepository.php index 9de34152..8284c926 100644 --- a/src/Reporting/CustomerMonthlyProjects/CustomerMonthlyProjectsRepository.php +++ b/src/Reporting/CustomerMonthlyProjects/CustomerMonthlyProjectsRepository.php @@ -20,15 +20,15 @@ use Doctrine\ORM\Query\Expr\Join; final class CustomerMonthlyProjectsRepository { - public function __construct(private TimesheetRepository $repository, private EntityManagerInterface $entityManager) + public function __construct( + private readonly TimesheetRepository $repository, + private readonly EntityManagerInterface $entityManager + ) { } /** - * @param DateTime $begin - * @param DateTime $end * @param User[] $users - * @param Customer|null $customer * @return array * @internal */ diff --git a/src/Repository/CustomerRepository.php b/src/Repository/CustomerRepository.php index bb1a40a8..3a3c4bf2 100644 --- a/src/Repository/CustomerRepository.php +++ b/src/Repository/CustomerRepository.php @@ -55,10 +55,6 @@ class CustomerRepository extends EntityRepository return $customers; } - /** - * @param Customer $customer - * @throws ORMException - */ public function saveCustomer(Customer $customer): void { $entityManager = $this->getEntityManager(); @@ -75,7 +71,7 @@ class CustomerRepository extends EntityRepository return $this->count([]); } - private function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): void + public function addPermissionCriteria(QueryBuilder $qb, ?User $user = null, array $teams = []): void { $permissions = $this->getPermissionCriteria($qb, $user, $teams); if ($permissions->count() > 0) { @@ -124,9 +120,6 @@ class CustomerRepository extends EntityRepository /** * Returns a query builder that is used for CustomerType and your own 'query_builder' option. - * - * @param CustomerFormTypeQuery $query - * @return QueryBuilder */ public function getQueryBuilderForFormType(CustomerFormTypeQuery $query): QueryBuilder { @@ -177,6 +170,13 @@ class CustomerRepository extends EntityRepository ->from(Customer::class, 'c') ; + if ($query->getCountry() !== null) { + $qb + ->andWhere($qb->expr()->eq('c.country', ':country')) + ->setParameter('country', $query->getCountry()) + ; + } + foreach ($query->getOrderGroups() as $orderBy => $order) { switch ($orderBy) { case 'vat_id': diff --git a/src/Repository/Query/CustomerQuery.php b/src/Repository/Query/CustomerQuery.php index d6779578..3c84ad2e 100644 --- a/src/Repository/Query/CustomerQuery.php +++ b/src/Repository/Query/CustomerQuery.php @@ -18,11 +18,24 @@ class CustomerQuery extends BaseQuery implements VisibilityInterface 'phone', 'currency', 'address', 'contact', 'company', 'vat_id', 'budget', 'timeBudget', 'visible' ]; + private ?string $country = null; + public function __construct() { $this->setDefaults([ 'orderBy' => 'name', 'visibility' => VisibilityInterface::SHOW_VISIBLE, + 'country' => null, ]); } + + public function getCountry(): ?string + { + return $this->country; + } + + public function setCountry(?string $country): void + { + $this->country = $country; + } } diff --git a/src/Saml/SamlAuthFactory.php b/src/Saml/SamlAuthFactory.php index a0de1b5b..cedb4d60 100644 --- a/src/Saml/SamlAuthFactory.php +++ b/src/Saml/SamlAuthFactory.php @@ -20,8 +20,8 @@ use Symfony\Component\HttpFoundation\RequestStack; class SamlAuthFactory { public function __construct( - private RequestStack $request, - private SamlConfigurationInterface $configuration + private readonly RequestStack $request, + private readonly SamlConfigurationInterface $configuration ) { } diff --git a/src/Saml/SamlAuthenticator.php b/src/Saml/SamlAuthenticator.php index 599d824c..f6e8329c 100644 --- a/src/Saml/SamlAuthenticator.php +++ b/src/Saml/SamlAuthenticator.php @@ -35,12 +35,12 @@ class SamlAuthenticator extends AbstractAuthenticator ]; public function __construct( - private HttpUtils $httpUtils, - private SamlAuthenticationSuccessHandler $successHandler, - private SamlAuthenticationFailureHandler $failureHandler, - private SamlAuthFactory $samlAuthFactory, - private SamlProvider $samlProvider, - private SamlConfigurationInterface $configuration + private readonly HttpUtils $httpUtils, + private readonly SamlAuthenticationSuccessHandler $successHandler, + private readonly SamlAuthenticationFailureHandler $failureHandler, + private readonly SamlAuthFactory $samlAuthFactory, + private readonly SamlProvider $samlProvider, + private readonly SamlConfigurationInterface $configuration ) { } diff --git a/src/Saml/SamlBadge.php b/src/Saml/SamlBadge.php index 5d1b9bdf..9e0ed590 100644 --- a/src/Saml/SamlBadge.php +++ b/src/Saml/SamlBadge.php @@ -13,7 +13,7 @@ use Symfony\Component\Security\Http\Authenticator\Passport\Badge\BadgeInterface; final class SamlBadge implements BadgeInterface { - public function __construct(private SamlLoginAttributes $samlToken) + public function __construct(private readonly SamlLoginAttributes $samlToken) { } diff --git a/src/Saml/SamlLogoutSubscriber.php b/src/Saml/SamlLogoutSubscriber.php index 1a4424fd..8577f6e2 100644 --- a/src/Saml/SamlLogoutSubscriber.php +++ b/src/Saml/SamlLogoutSubscriber.php @@ -15,7 +15,7 @@ use Symfony\Component\Security\Http\Event\LogoutEvent; final class SamlLogoutSubscriber implements EventSubscriberInterface { - public function __construct(private SamlAuthFactory $samlAuth) + public function __construct(private readonly SamlAuthFactory $samlAuth) { } @@ -26,7 +26,7 @@ final class SamlLogoutSubscriber implements EventSubscriberInterface ]; } - public function logout(LogoutEvent $event) + public function logout(LogoutEvent $event): void { $token = $event->getToken(); diff --git a/src/Saml/SamlProvider.php b/src/Saml/SamlProvider.php index 230c3d83..7909fe98 100644 --- a/src/Saml/SamlProvider.php +++ b/src/Saml/SamlProvider.php @@ -22,9 +22,9 @@ final class SamlProvider * @param UserProviderInterface $userProvider */ public function __construct( - private UserRepository $repository, - private UserProviderInterface $userProvider, - private SamlConfigurationInterface $configuration + private readonly UserRepository $repository, + private readonly UserProviderInterface $userProvider, + private readonly SamlConfigurationInterface $configuration ) { } @@ -123,7 +123,7 @@ final class SamlProvider $user->setAuth(User::AUTH_SAML); } - private function getPropertyValue(SamlLoginAttributes $token, $attribute) + private function getPropertyValue(SamlLoginAttributes $token, $attribute): string { $results = []; $attributes = $token->getAttributes(); diff --git a/src/Security/RolePermissionManager.php b/src/Security/RolePermissionManager.php index 448c57c4..3aac4b8d 100644 --- a/src/Security/RolePermissionManager.php +++ b/src/Security/RolePermissionManager.php @@ -29,11 +29,14 @@ final class RolePermissionManager private bool $isInitialized = false; /** - * @param PermissionService $service * @param array> $permissions as defined in kimai.yaml * @param array $permissionNames as defined in kimai.yaml */ - public function __construct(private PermissionService $service, private array $permissions, private array $permissionNames) + public function __construct( + private readonly PermissionService $service, + private array $permissions, + private readonly array $permissionNames + ) { } @@ -47,11 +50,6 @@ final class RolePermissionManager $perm = (string) $item['permission']; $role = (string) $item['role']; - // these permissions may not be revoked at any time, because super admin would lose the ability to reactivate any permission - if ($role === User::ROLE_SUPER_ADMIN && \array_key_exists($perm, self::SUPER_ADMIN_PERMISSIONS)) { - continue; - } - if (!\array_key_exists($role, $this->permissions)) { $this->permissions[$role] = []; } @@ -59,6 +57,7 @@ final class RolePermissionManager $this->permissions[$role][$perm] = (bool) $item['allowed']; } + // these permissions may not be revoked at any time, because super admin would lose the ability to reactivate any permission foreach (self::SUPER_ADMIN_PERMISSIONS as $perm => $value) { $this->permissions[User::ROLE_SUPER_ADMIN][$perm] = $value; } @@ -68,14 +67,9 @@ final class RolePermissionManager /** * Only permissions which were registered through the Symfony configuration stack will be acknowledged here. - * - * @param string $permission - * @return bool */ public function isRegisteredPermission(string $permission): bool { - $this->init(); - return \array_key_exists($permission, $this->permissionNames); } @@ -89,7 +83,7 @@ final class RolePermissionManager return false; } - return \array_key_exists($permission, $this->permissions[$role]) ? $this->permissions[$role][$permission] : false; + return \array_key_exists($permission, $this->permissions[$role]) && $this->permissions[$role][$permission]; } public function hasRolePermission(User $user, string $permission): bool @@ -112,8 +106,6 @@ final class RolePermissionManager */ public function getPermissions(): array { - $this->init(); - return array_keys($this->permissionNames); } } diff --git a/src/Timesheet/TimesheetStatisticService.php b/src/Timesheet/TimesheetStatisticService.php index a4e28797..f62bc1a0 100644 --- a/src/Timesheet/TimesheetStatisticService.php +++ b/src/Timesheet/TimesheetStatisticService.php @@ -18,7 +18,7 @@ use DateTimeInterface; final class TimesheetStatisticService { - public function __construct(private TimesheetRepository $repository) + public function __construct(private readonly TimesheetRepository $repository) { } @@ -53,10 +53,10 @@ final class TimesheetStatisticService ->addSelect('MONTH(t.date) as month') ->addSelect('YEAR(t.date) as year') ->where($qb->expr()->isNotNull('t.end')) - ->andWhere($qb->expr()->between('t.begin', ':begin', ':end')) + ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->in('t.user', ':user')) - ->setParameter('begin', $begin) - ->setParameter('end', $end) + ->setParameter('begin', $begin->format('Y-m-d')) + ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $users) ->groupBy('year') ->addGroupBy('month') @@ -125,10 +125,10 @@ final class TimesheetStatisticService ->addSelect('IDENTITY(t.activity) as activity') ->addSelect('DATE(t.date) as date') ->where($qb->expr()->isNotNull('t.end')) - ->andWhere($qb->expr()->between('t.begin', ':begin', ':end')) + ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->in('t.user', ':user')) - ->setParameter('begin', $begin) - ->setParameter('end', $end) + ->setParameter('begin', $begin->format('Y-m-d')) + ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $users) ->groupBy('date') ->addGroupBy('project') @@ -173,8 +173,6 @@ final class TimesheetStatisticService /** * @internal only for core development - * @param DateTimeInterface $begin - * @param DateTimeInterface $end * @param User[] $users * @return array */ @@ -203,10 +201,10 @@ final class TimesheetStatisticService ->addSelect('YEAR(t.date) as year') ->addSelect('MONTH(t.date) as month') ->where($qb->expr()->isNotNull('t.end')) - ->andWhere($qb->expr()->between('t.begin', ':begin', ':end')) + ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->in('t.user', ':user')) - ->setParameter('begin', $begin) - ->setParameter('end', $end) + ->setParameter('begin', $begin->format('Y-m-d')) + ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $users) ->groupBy('year') ->addGroupBy('month') @@ -253,7 +251,7 @@ final class TimesheetStatisticService public function findFirstRecordDate(User $user): ?\DateTimeImmutable { $result = $this->repository->createQueryBuilder('t') - ->select('MIN(t.begin)') + ->select('MIN(t.date)') ->where('t.user = :user') ->setParameter('user', $user) ->getQuery() @@ -295,10 +293,10 @@ final class TimesheetStatisticService ->addSelect('YEAR(t.date) as year') ->addSelect('IDENTITY(t.user) as user') ->where($qb->expr()->isNotNull('t.end')) - ->andWhere($qb->expr()->between('t.begin', ':begin', ':end')) + ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->in('t.user', ':user')) - ->setParameter('begin', $begin) - ->setParameter('end', $end) + ->setParameter('begin', $begin->format('Y-m-d')) + ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $users) ->groupBy('year') ->addGroupBy('month') diff --git a/src/Twig/LocaleFormatExtensions.php b/src/Twig/LocaleFormatExtensions.php index 9ffa9907..98ad9c6a 100644 --- a/src/Twig/LocaleFormatExtensions.php +++ b/src/Twig/LocaleFormatExtensions.php @@ -192,6 +192,8 @@ final class LocaleFormatExtensions extends AbstractExtension implements LocaleAw public function getJavascriptConfiguration(User $user): array { return [ + 'locale' => $this->locale, + 'language' => $user->getLanguage(), 'formatDuration' => $this->localeService->getDurationFormat($this->locale), 'formatDate' => $this->localeService->getDateFormat($this->locale), 'defaultColor' => Constants::DEFAULT_COLOR, diff --git a/src/Twig/PaginationExtension.php b/src/Twig/PaginationExtension.php index ef607d9b..8317303c 100644 --- a/src/Twig/PaginationExtension.php +++ b/src/Twig/PaginationExtension.php @@ -76,7 +76,7 @@ final class PaginationExtension extends AbstractExtension $propertyAccessor = PropertyAccess::createPropertyAccessor(); $propertyAccessor->setValue($routeParams, $pagePropertyPath, $page); - return $router->generate($routeName, $routeParams); + return $router->generate($routeName, $routeParams); // @phpstan-ignore-line }; } } diff --git a/src/Validator/Constraints/QuickEntryTimesheetValidator.php b/src/Validator/Constraints/QuickEntryTimesheetValidator.php index b7771cf3..86c6dd22 100644 --- a/src/Validator/Constraints/QuickEntryTimesheetValidator.php +++ b/src/Validator/Constraints/QuickEntryTimesheetValidator.php @@ -38,9 +38,7 @@ final class QuickEntryTimesheetValidator extends ConstraintValidator throw new UnexpectedTypeException($value, TimesheetEntity::class); } - $timesheet = $value; - - if ($timesheet->getId() === null && $timesheet->getDuration(false) === null) { + if ($value->getId() === null && $value->getDuration(false) === null) { return; } @@ -49,7 +47,7 @@ final class QuickEntryTimesheetValidator extends ConstraintValidator ->getValidator() ->inContext($this->context) ->atPath('duration') - ->validate($timesheet, $innerConstraint, [Constraint::DEFAULT_GROUP]); + ->validate($value, $innerConstraint, [Constraint::DEFAULT_GROUP]); } } } diff --git a/src/Validator/Constraints/TimesheetBasicValidator.php b/src/Validator/Constraints/TimesheetBasicValidator.php index 2e92feb8..d55f87f4 100644 --- a/src/Validator/Constraints/TimesheetBasicValidator.php +++ b/src/Validator/Constraints/TimesheetBasicValidator.php @@ -18,7 +18,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetBasicValidator extends ConstraintValidator { - public function __construct(private SystemConfiguration $systemConfiguration) + public function __construct(private readonly SystemConfiguration $systemConfiguration) { } @@ -36,10 +36,6 @@ final class TimesheetBasicValidator extends ConstraintValidator $this->validateActivityAndProject($value, $this->context); } - /** - * @param TimesheetEntity $timesheet - * @param ExecutionContextInterface $context - */ protected function validateBeginAndEnd(TimesheetEntity $timesheet, ExecutionContextInterface $context): void { $begin = $timesheet->getBegin(); @@ -64,10 +60,6 @@ final class TimesheetBasicValidator extends ConstraintValidator } } - /** - * @param TimesheetEntity $timesheet - * @param ExecutionContextInterface $context - */ protected function validateActivityAndProject(TimesheetEntity $timesheet, ExecutionContextInterface $context): void { $activity = $timesheet->getActivity(); diff --git a/src/Validator/Constraints/TimesheetBudgetUsedValidator.php b/src/Validator/Constraints/TimesheetBudgetUsedValidator.php index cc87246f..44b8e249 100644 --- a/src/Validator/Constraints/TimesheetBudgetUsedValidator.php +++ b/src/Validator/Constraints/TimesheetBudgetUsedValidator.php @@ -13,7 +13,7 @@ use App\Activity\ActivityStatisticService; use App\Configuration\LocaleService; use App\Configuration\SystemConfiguration; use App\Customer\CustomerStatisticService; -use App\Entity\Timesheet; +use App\Entity\Timesheet as TimesheetEntity; use App\Model\BudgetStatisticModel; use App\Project\ProjectStatisticService; use App\Repository\TimesheetRepository; @@ -29,53 +29,49 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetBudgetUsedValidator extends ConstraintValidator { public function __construct( - private SystemConfiguration $configuration, - private CustomerStatisticService $customerStatisticService, - private ProjectStatisticService $projectStatisticService, - private ActivityStatisticService $activityStatisticService, - private TimesheetRepository $timesheetRepository, - private RateServiceInterface $rateService, - private AuthorizationCheckerInterface $security, - private LocaleService $localeService + private readonly SystemConfiguration $configuration, + private readonly CustomerStatisticService $customerStatisticService, + private readonly ProjectStatisticService $projectStatisticService, + private readonly ActivityStatisticService $activityStatisticService, + private readonly TimesheetRepository $timesheetRepository, + private readonly RateServiceInterface $rateService, + private readonly AuthorizationCheckerInterface $security, + private readonly LocaleService $localeService ) { } - /** - * @param Timesheet $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetBudgetUsed)) { throw new UnexpectedTypeException($constraint, TimesheetBudgetUsed::class); } - if (!\is_object($timesheet) || !($timesheet instanceof Timesheet)) { - throw new UnexpectedTypeException($timesheet, Timesheet::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } if ($this->configuration->isTimesheetAllowOverbookingBudget()) { return; } - $begin = $timesheet->getBegin(); + $begin = $value->getBegin(); // we can only work with stopped entries - if ($begin === null || $timesheet->getEnd() === null || $timesheet->getUser() === null) { + if ($begin === null || $value->getEnd() === null || $value->getUser() === null) { return; } // budgets need only be calculated for billable records - if (!$timesheet->isBillable()) { + if (!$value->isBillable()) { return; } // this validator needs a project to calculate the rates - if ($timesheet->getProject() === null) { + if ($value->getProject() === null) { return; } - $id = $timesheet->getId(); + $id = $value->getId(); // when changing the date via the calendar and/or the API, the duration will not be reset by the // duration calculator (which runs after validation!) so we manually reset the duration before @@ -92,9 +88,9 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator // $timesheet->setDuration(null); // $duration = $timesheet->getDuration(); - $duration = $timesheet->getCalculatedDuration(); + $duration = $value->getCalculatedDuration(); - $timeRate = $this->rateService->calculate($timesheet); + $timeRate = $this->rateService->calculate($value); $rate = $timeRate->getRate(); $activityDuration = $duration ?? 0; @@ -116,10 +112,10 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator // this could for example happen when export flag is changed OR if "prevent overbooking" config was recently activated and this is an old entry if ($duration === $rawData['duration'] && $rate === $rawData['rate'] && - $timesheet->isBillable() === $rawData['billable'] && + $value->isBillable() === $rawData['billable'] && $begin->format('Y.m.d') === $rawData['begin']->format('Y.m.d') && - $timesheet->getProject()->getId() === $projectId && - ($timesheet->getActivity() === null || $timesheet->getActivity()->getId() === $activityId) + $value->getProject()->getId() === $projectId && + ($value->getActivity() === null || $value->getActivity()->getId() === $activityId) ) { return; } @@ -129,18 +125,18 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator // only subtract the previously logged data in case the record was billable // if it wasn't billable, then its values are not included in the statistic models used later on if ($rawData['billable']) { - if (null !== $timesheet->getActivity() && $activityId === $timesheet->getActivity()->getId()) { + if (null !== $value->getActivity() && $activityId === $value->getActivity()->getId()) { $activityDuration -= $rawData['duration']; $activityRate -= $rawData['rate']; } - if (null !== $timesheet->getProject()) { - if ($projectId === $timesheet->getProject()->getId()) { + if (null !== $value->getProject()) { + if ($projectId === $value->getProject()->getId()) { $projectDuration -= $rawData['duration']; $projectRate -= $rawData['rate']; } - if ($customerId === $timesheet->getProject()->getCustomer()->getId()) { + if ($customerId === $value->getProject()->getCustomer()->getId()) { $customerDuration -= $rawData['duration']; $customerRate -= $rawData['rate']; } @@ -153,23 +149,23 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator $now = new DateTime('now', $begin->getTimezone()); $recordDate = $begin; - if (null !== ($activity = $timesheet->getActivity()) && $activity->hasBudgets()) { + if (null !== ($activity = $value->getActivity()) && $activity->hasBudgets()) { $dateTime = $activity->isMonthlyBudget() ? $recordDate : $now; if ($activity->isMonthlyBudget() && $monthWasChanged) { $activityDuration = $duration; } $stat = $this->activityStatisticService->getBudgetStatisticModel($activity, $dateTime); - $this->checkBudgets($constraint, $stat, $timesheet, $activityDuration, $activityRate, 'activity'); + $this->checkBudgets($constraint, $stat, $value, $activityDuration, $activityRate, 'activity'); } - if (null !== ($project = $timesheet->getProject())) { + if (null !== ($project = $value->getProject())) { if ($project->hasBudgets()) { $dateTime = $project->isMonthlyBudget() ? $recordDate : $now; if ($project->isMonthlyBudget() && $monthWasChanged) { $projectDuration = $duration; } $stat = $this->projectStatisticService->getBudgetStatisticModel($project, $dateTime); - $this->checkBudgets($constraint, $stat, $timesheet, $projectDuration, $projectRate, 'project'); + $this->checkBudgets($constraint, $stat, $value, $projectDuration, $projectRate, 'project'); } if (null !== ($customer = $project->getCustomer()) && $customer->hasBudgets()) { $dateTime = $customer->isMonthlyBudget() ? $recordDate : $now; @@ -177,12 +173,12 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator $customerDuration = $duration; } $stat = $this->customerStatisticService->getBudgetStatisticModel($customer, $dateTime); - $this->checkBudgets($constraint, $stat, $timesheet, $customerDuration, $customerRate, 'customer'); + $this->checkBudgets($constraint, $stat, $value, $customerDuration, $customerRate, 'customer'); } } } - private function checkBudgets(TimesheetBudgetUsed $constraint, BudgetStatisticModel $stat, Timesheet $timesheet, int $duration, float $rate, string $field): bool + private function checkBudgets(TimesheetBudgetUsed $constraint, BudgetStatisticModel $stat, TimesheetEntity $timesheet, int $duration, float $rate, string $field): bool { $fullRate = ($stat->getBudgetSpent() + $rate); @@ -203,7 +199,7 @@ final class TimesheetBudgetUsedValidator extends ConstraintValidator return false; } - private function addBudgetViolation(TimesheetBudgetUsed $constraint, Timesheet $timesheet, string $field, float $budget, float $rate): void + private function addBudgetViolation(TimesheetBudgetUsed $constraint, TimesheetEntity $timesheet, string $field, float $budget, float $rate): void { // using the locale of the assigned user is not the best solution, but allows to be independent of the request stack $helper = new LocaleFormatter($this->localeService, $timesheet->getUser()?->getLocale() ?? 'en'); diff --git a/src/Validator/Constraints/TimesheetExportedValidator.php b/src/Validator/Constraints/TimesheetExportedValidator.php index 179daec4..0e750065 100644 --- a/src/Validator/Constraints/TimesheetExportedValidator.php +++ b/src/Validator/Constraints/TimesheetExportedValidator.php @@ -17,29 +17,25 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetExportedValidator extends ConstraintValidator { - public function __construct(private Security $security) + public function __construct(private readonly Security $security) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetExported)) { throw new UnexpectedTypeException($constraint, TimesheetExported::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } - if ($timesheet->getId() === null) { + if ($value->getId() === null) { return; } - if (!$timesheet->isExported()) { + if (!$value->isExported()) { return; } @@ -47,7 +43,7 @@ final class TimesheetExportedValidator extends ConstraintValidator // can trigger is right when the "export" flag ist set from the "edit form". // most teamleads should not have "edit_exported_timesheet" but only "edit_export_other_timesheet" - if (null !== $this->security->getUser() && $this->security->isGranted('edit_export', $timesheet)) { + if (null !== $this->security->getUser() && $this->security->isGranted('edit_export', $value)) { return; } diff --git a/src/Validator/Constraints/TimesheetFutureTimesValidator.php b/src/Validator/Constraints/TimesheetFutureTimesValidator.php index cdd686d1..c7efc784 100644 --- a/src/Validator/Constraints/TimesheetFutureTimesValidator.php +++ b/src/Validator/Constraints/TimesheetFutureTimesValidator.php @@ -17,14 +17,10 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetFutureTimesValidator extends ConstraintValidator { - public function __construct(private SystemConfiguration $configuration) + public function __construct(private readonly SystemConfiguration $configuration) { } - /** - * @param TimesheetEntity $value - * @param Constraint $constraint - */ public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetFutureTimes)) { diff --git a/src/Validator/Constraints/TimesheetLockdownValidator.php b/src/Validator/Constraints/TimesheetLockdownValidator.php index e40a6523..68c875be 100644 --- a/src/Validator/Constraints/TimesheetLockdownValidator.php +++ b/src/Validator/Constraints/TimesheetLockdownValidator.php @@ -18,29 +18,28 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetLockdownValidator extends ConstraintValidator { - public function __construct(private Security $security, private LockdownService $lockdownService) + public function __construct( + private readonly Security $security, + private readonly LockdownService $lockdownService + ) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetLockdown)) { throw new UnexpectedTypeException($constraint, TimesheetLockdown::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } if (!$this->lockdownService->isLockdownActive()) { return; } - if (null === ($timesheetStart = $timesheet->getBegin())) { + if (null === ($timesheetStart = $value->getBegin())) { return; } @@ -67,7 +66,7 @@ final class TimesheetLockdownValidator extends ConstraintValidator $allowEditInGracePeriod = true; } - if ($this->lockdownService->isEditable($timesheet, $now, $allowEditInGracePeriod)) { + if ($this->lockdownService->isEditable($value, $now, $allowEditInGracePeriod)) { return; } diff --git a/src/Validator/Constraints/TimesheetLongRunningValidator.php b/src/Validator/Constraints/TimesheetLongRunningValidator.php index 51ce6f99..97cf5acf 100644 --- a/src/Validator/Constraints/TimesheetLongRunningValidator.php +++ b/src/Validator/Constraints/TimesheetLongRunningValidator.php @@ -17,30 +17,26 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetLongRunningValidator extends ConstraintValidator { - public function __construct(private SystemConfiguration $systemConfiguration) + public function __construct(private readonly SystemConfiguration $systemConfiguration) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetLongRunning)) { throw new UnexpectedTypeException($constraint, TimesheetLongRunning::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } - if ($timesheet->isRunning()) { + if ($value->isRunning()) { return; } /** @var int $duration */ - $duration = $timesheet->getCalculatedDuration(); + $duration = $value->getCalculatedDuration(); // one year is currently the maximum that can be logged (which is already not logically) // the database column could hold more data, but let's limit it here diff --git a/src/Validator/Constraints/TimesheetOverlappingValidator.php b/src/Validator/Constraints/TimesheetOverlappingValidator.php index 067ef2e6..85e91654 100644 --- a/src/Validator/Constraints/TimesheetOverlappingValidator.php +++ b/src/Validator/Constraints/TimesheetOverlappingValidator.php @@ -18,26 +18,25 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetOverlappingValidator extends ConstraintValidator { - public function __construct(private SystemConfiguration $configuration, private TimesheetRepository $repository) + public function __construct( + private readonly SystemConfiguration $configuration, + private readonly TimesheetRepository $repository + ) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetOverlapping)) { throw new UnexpectedTypeException($constraint, TimesheetOverlapping::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } - $begin = $timesheet->getBegin(); - $end = $timesheet->getEnd(); + $begin = $value->getBegin(); + $end = $value->getEnd(); // this case is handled in TimesheetValidator and should not raise a second validation if ($begin !== null && $end !== null && $begin > $end) { @@ -48,7 +47,7 @@ final class TimesheetOverlappingValidator extends ConstraintValidator return; } - if (!$this->repository->hasRecordForTime($timesheet)) { + if (!$this->repository->hasRecordForTime($value)) { return; } diff --git a/src/Validator/Constraints/TimesheetRestartValidator.php b/src/Validator/Constraints/TimesheetRestartValidator.php index 4333bcba..5cefca44 100644 --- a/src/Validator/Constraints/TimesheetRestartValidator.php +++ b/src/Validator/Constraints/TimesheetRestartValidator.php @@ -18,28 +18,27 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetRestartValidator extends ConstraintValidator { - public function __construct(private Security $security, private TrackingModeService $trackingModeService) + public function __construct( + private readonly Security $security, + private readonly TrackingModeService $trackingModeService + ) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetRestart)) { throw new UnexpectedTypeException($constraint, TimesheetRestart::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } // special case that would otherwise need to be validated in several controllers: // an entry is edited and the end date is removed (or duration deleted) would restart the record, // which might be disallowed for the current user - if (null !== $timesheet->getEnd()) { + if (null !== $value->getEnd()) { return; } @@ -47,7 +46,7 @@ final class TimesheetRestartValidator extends ConstraintValidator return; } - if (null !== $this->security->getUser() && $this->security->isGranted('start', $timesheet)) { + if (null !== $this->security->getUser() && $this->security->isGranted('start', $value)) { return; } diff --git a/src/Validator/Constraints/TimesheetValidator.php b/src/Validator/Constraints/TimesheetValidator.php index 1d43a789..b9652215 100644 --- a/src/Validator/Constraints/TimesheetValidator.php +++ b/src/Validator/Constraints/TimesheetValidator.php @@ -28,18 +28,14 @@ final class TimesheetValidator extends ConstraintValidator { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetEntityConstraint)) { throw new UnexpectedTypeException($constraint, TimesheetEntityConstraint::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } $groups = [Constraint::DEFAULT_GROUP]; @@ -51,7 +47,7 @@ final class TimesheetValidator extends ConstraintValidator $this->context ->getValidator() ->inContext($this->context) - ->validate($timesheet, $innerConstraint, $groups); + ->validate($value, $innerConstraint, $groups); } } } diff --git a/src/Validator/Constraints/TimesheetZeroDurationValidator.php b/src/Validator/Constraints/TimesheetZeroDurationValidator.php index 659d564e..347ff439 100644 --- a/src/Validator/Constraints/TimesheetZeroDurationValidator.php +++ b/src/Validator/Constraints/TimesheetZeroDurationValidator.php @@ -17,35 +17,31 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; final class TimesheetZeroDurationValidator extends ConstraintValidator { - public function __construct(private SystemConfiguration $configuration) + public function __construct(private readonly SystemConfiguration $configuration) { } - /** - * @param TimesheetEntity $timesheet - * @param Constraint $constraint - */ - public function validate(mixed $timesheet, Constraint $constraint): void + public function validate(mixed $value, Constraint $constraint): void { if (!($constraint instanceof TimesheetZeroDuration)) { throw new UnexpectedTypeException($constraint, TimesheetZeroDuration::class); } - if (!\is_object($timesheet) || !($timesheet instanceof TimesheetEntity)) { - throw new UnexpectedTypeException($timesheet, TimesheetEntity::class); + if (!\is_object($value) || !($value instanceof TimesheetEntity)) { + throw new UnexpectedTypeException($value, TimesheetEntity::class); } if ($this->configuration->isTimesheetAllowZeroDuration()) { return; } - if ($timesheet->isRunning()) { + if ($value->isRunning()) { return; } $duration = 0; - if ($timesheet->getEnd() !== null && $timesheet->getBegin() !== null) { - $duration = $timesheet->getCalculatedDuration(); + if ($value->getEnd() !== null && $value->getBegin() !== null) { + $duration = $value->getCalculatedDuration(); } if ($duration <= 0) { diff --git a/src/Voter/ActivityVoter.php b/src/Voter/ActivityVoter.php index b0e42ebd..c6fc1a61 100644 --- a/src/Voter/ActivityVoter.php +++ b/src/Voter/ActivityVoter.php @@ -35,21 +35,23 @@ final class ActivityVoter extends Voter 'permissions', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, Activity::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof Activity)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof Activity && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool @@ -65,7 +67,7 @@ final class ActivityVoter extends Voter } // those cannot be assigned to teams - if (\in_array($attribute, ['create', 'delete'])) { + if (\in_array($attribute, ['create', 'delete'], true)) { return false; } diff --git a/src/Voter/CustomerVoter.php b/src/Voter/CustomerVoter.php index fd435bad..28b6c6d9 100644 --- a/src/Voter/CustomerVoter.php +++ b/src/Voter/CustomerVoter.php @@ -39,21 +39,23 @@ final class CustomerVoter extends Voter 'access', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, Customer::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof Customer)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof Customer && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/EntityMultiRoleVoter.php b/src/Voter/EntityMultiRoleVoter.php index 7ec55b49..979815cc 100644 --- a/src/Voter/EntityMultiRoleVoter.php +++ b/src/Voter/EntityMultiRoleVoter.php @@ -38,17 +38,22 @@ final class EntityMultiRoleVoter extends Voter 'activity', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { + if (!$this->supportsAttribute($attribute)) { return false; } - if (\is_string($subject) && \in_array($subject, self::ALLOWED_SUBJECTS)) { + if (\is_string($subject) && \in_array($subject, self::ALLOWED_SUBJECTS, true)) { return true; } @@ -69,7 +74,7 @@ final class EntityMultiRoleVoter extends Voter $suffix = null; - if (\is_string($subject) && \in_array($subject, self::ALLOWED_SUBJECTS)) { + if (\is_string($subject) && \in_array($subject, self::ALLOWED_SUBJECTS, true)) { $suffix = $subject; } elseif ($subject instanceof Activity) { $suffix = 'activity'; diff --git a/src/Voter/ProjectVoter.php b/src/Voter/ProjectVoter.php index 8b92fc26..5aa53c84 100644 --- a/src/Voter/ProjectVoter.php +++ b/src/Voter/ProjectVoter.php @@ -37,21 +37,23 @@ final class ProjectVoter extends Voter 'details', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, Project::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof Project)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof Project && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/QuickEntryVoter.php b/src/Voter/QuickEntryVoter.php index 4de2dade..3e75afbf 100644 --- a/src/Voter/QuickEntryVoter.php +++ b/src/Voter/QuickEntryVoter.php @@ -20,13 +20,21 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter; */ final class QuickEntryVoter extends Voter { - public function __construct(private RolePermissionManager $permissionManager, private TrackingModeService $trackingModeService) + public function __construct( + private readonly RolePermissionManager $permissionManager, + private readonly TrackingModeService $trackingModeService + ) { } + public function supportsAttribute(string $attribute): bool + { + return 'quick-entry' === $attribute; + } + protected function supports(string $attribute, mixed $subject): bool { - return 'quick-entry' === $attribute; + return $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/ReportingVoter.php b/src/Voter/ReportingVoter.php index 8146a010..88346a24 100644 --- a/src/Voter/ReportingVoter.php +++ b/src/Voter/ReportingVoter.php @@ -26,13 +26,23 @@ final class ReportingVoter extends Voter 'report:user', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return $subjectType === 'null'; + } + protected function supports(string $attribute, mixed $subject): bool { - return $subject === null && \in_array($attribute, self::ALLOWED_ATTRIBUTES); + return $subject === null && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/RolePermissionVoter.php b/src/Voter/RolePermissionVoter.php index 57cf832a..932039e1 100644 --- a/src/Voter/RolePermissionVoter.php +++ b/src/Voter/RolePermissionVoter.php @@ -21,18 +21,24 @@ use Symfony\Component\Security\Core\Authorization\Voter\Voter; */ final class RolePermissionVoter extends Voter { - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return $this->permissionManager->isRegisteredPermission($attribute); + } + + public function supportsType(string $subjectType): bool + { + // we only work on single strings that have no subject + return $subjectType === 'null'; + } + protected function supports(string $attribute, mixed $subject): bool { - // we only work on single strings that have no subject - if (null !== $subject) { - return false; - } - - return $this->permissionManager->isRegisteredPermission($attribute); + return $subject === null && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/TeamVoter.php b/src/Voter/TeamVoter.php index f219fac1..ee1f1366 100644 --- a/src/Voter/TeamVoter.php +++ b/src/Voter/TeamVoter.php @@ -29,21 +29,23 @@ final class TeamVoter extends Voter 'delete', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, Team::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof Team)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof Team && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/TimesheetVoter.php b/src/Voter/TimesheetVoter.php index aba0b661..06cb8229 100644 --- a/src/Voter/TimesheetVoter.php +++ b/src/Voter/TimesheetVoter.php @@ -11,6 +11,7 @@ namespace App\Voter; use App\Entity\Timesheet; use App\Entity\User; +use App\Form\Model\MultiUserTimesheet; use App\Security\RolePermissionManager; use App\Timesheet\LockdownService; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -55,21 +56,26 @@ final class TimesheetVoter extends Voter private ?bool $editExported = null; private ?\DateTime $now = null; - public function __construct(private RolePermissionManager $permissionManager, private LockdownService $lockdownService) + public function __construct( + private readonly RolePermissionManager $permissionManager, + private readonly LockdownService $lockdownService + ) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, Timesheet::class) || str_contains($subjectType, MultiUserTimesheet::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof Timesheet)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof Timesheet && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Voter/UserVoter.php b/src/Voter/UserVoter.php index 1f335a0b..d372ccfe 100644 --- a/src/Voter/UserVoter.php +++ b/src/Voter/UserVoter.php @@ -38,21 +38,23 @@ final class UserVoter extends Voter 'supervisor', ]; - public function __construct(private RolePermissionManager $permissionManager) + public function __construct(private readonly RolePermissionManager $permissionManager) { } + public function supportsAttribute(string $attribute): bool + { + return \in_array($attribute, self::ALLOWED_ATTRIBUTES, true); + } + + public function supportsType(string $subjectType): bool + { + return str_contains($subjectType, User::class); + } + protected function supports(string $attribute, mixed $subject): bool { - if (!($subject instanceof User)) { - return false; - } - - if (!\in_array($attribute, self::ALLOWED_ATTRIBUTES)) { - return false; - } - - return true; + return $subject instanceof User && $this->supportsAttribute($attribute); } protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool diff --git a/src/Widget/DataProvider/DailyWorkingTimeChartProvider.php b/src/Widget/DataProvider/DailyWorkingTimeChartProvider.php index 30fb35d5..8db972dd 100644 --- a/src/Widget/DataProvider/DailyWorkingTimeChartProvider.php +++ b/src/Widget/DataProvider/DailyWorkingTimeChartProvider.php @@ -25,7 +25,7 @@ use DateTimeInterface; */ class DailyWorkingTimeChartProvider { - public function __construct(private TimesheetRepository $repository) + public function __construct(private readonly TimesheetRepository $repository) { } @@ -33,9 +33,6 @@ class DailyWorkingTimeChartProvider * In case this method is called with one timezone and the results are from another timezone, * it might return rows outside the time-range. * - * @param DateTimeInterface $begin - * @param DateTimeInterface $end - * @param User|null $user * @return array */ protected function getDailyData(DateTimeInterface $begin, DateTimeInterface $end, ?User $user = null): array diff --git a/src/WorkingTime/WorkingTimeService.php b/src/WorkingTime/WorkingTimeService.php index bfd96d30..0cc0133c 100644 --- a/src/WorkingTime/WorkingTimeService.php +++ b/src/WorkingTime/WorkingTimeService.php @@ -144,10 +144,10 @@ final class WorkingTimeService ->select('COALESCE(SUM(t.duration), 0) as duration') ->addSelect('DATE(t.date) as day') ->where($qb->expr()->isNotNull('t.end')) - ->andWhere($qb->expr()->between('t.begin', ':begin', ':end')) + ->andWhere($qb->expr()->between('t.date', ':begin', ':end')) ->andWhere($qb->expr()->eq('t.user', ':user')) - ->setParameter('begin', $begin) - ->setParameter('end', $end) + ->setParameter('begin', $begin->format('Y-m-d')) + ->setParameter('end', $end->format('Y-m-d')) ->setParameter('user', $user->getId()) ->addGroupBy('day') ; diff --git a/symfony.lock b/symfony.lock index 5201f681..0ca2efbf 100644 --- a/symfony.lock +++ b/symfony.lock @@ -20,18 +20,6 @@ "ref": "56eaa387b5e48ebcc7c95a893b47dfa1ad51449c" } }, - "doctrine/annotations": { - "version": "1.13", - "recipe": { - "repo": "github.com/symfony/recipes", - "branch": "master", - "version": "1.0", - "ref": "a2759dd6123694c8d901d0ec80006e044c2e6457" - }, - "files": [ - "config/routes/annotations.yaml" - ] - }, "doctrine/cache": { "version": "v1.7.1" }, diff --git a/templates/activity/details.html.twig b/templates/activity/details.html.twig index 1e2adf4f..6e7daf58 100644 --- a/templates/activity/details.html.twig +++ b/templates/activity/details.html.twig @@ -14,7 +14,7 @@ {% import "project/actions.html.twig" as projectActions %} {% block box_attributes %}id="activity_details_box"{% endblock %} {% block box_title %} - {{ widgets.label_name(activity.name, activity.color) }} + {{ widgets.label_dot(activity.name, activity.color) }} {% endblock %} {% block box_tools %} {% if can_edit %} diff --git a/templates/customer/details.html.twig b/templates/customer/details.html.twig index 50006c25..5e5b4193 100644 --- a/templates/customer/details.html.twig +++ b/templates/customer/details.html.twig @@ -8,7 +8,7 @@ {% import "macros/widgets.html.twig" as widgets %} {% block box_attributes %}id="customer_details_box"{% endblock %} {% block box_title %} - {{ widgets.label_customer(customer) }} + {{ widgets.label_dot(customer.name, customer.color) }} {% if customer.company is not empty %} – {{ customer.company }}{% endif %} {% endblock %} {% block box_tools %} diff --git a/templates/form/blocks.html.twig b/templates/form/blocks.html.twig index f9da47ae..8cdb6f59 100644 --- a/templates/form/blocks.html.twig +++ b/templates/form/blocks.html.twig @@ -176,7 +176,6 @@ {% set format = locale_format('date')|js_format %} {% set attr = attr|merge({'data-daterangepicker': 'on', 'autocomplete': 'off', 'data-format': format, 'placeholder': format ~ attr['data-separator'] ~ format}) -%}
- {{ block('form_widget_simple') }} {% if ranges is defined %} {% endif %} + {{ block('form_widget_simple') }}
{% endblock daterange_widget %} diff --git a/templates/macros/webloader.html.twig b/templates/macros/webloader.html.twig index df3bd6b4..8bbc8087 100644 --- a/templates/macros/webloader.html.twig +++ b/templates/macros/webloader.html.twig @@ -1,7 +1,6 @@ {% macro init_frontend_loader() %} {% set configurations = javascript_configurations(app.user)|merge({ login: path('login'), - locale: app.request.locale, direction: tabler_bundle.rightToLeft ? 'rtl' : 'ltr', first_dow_iso: iso_day_by_name(app.user.firstDayOfWeek), }) %} diff --git a/templates/project/details.html.twig b/templates/project/details.html.twig index 61df994e..6066b43a 100644 --- a/templates/project/details.html.twig +++ b/templates/project/details.html.twig @@ -14,7 +14,7 @@ {% endif %} {% endblock %} {% block box_title %} - {{ widgets.label_name(project.name, project.color) }} + {{ widgets.label_dot(project.name, project.color) }} {% endblock %} {% block box_body_class %}p-0{% endblock %} {% block box_body %} diff --git a/tests/API/ActivityControllerTest.php b/tests/API/ActivityControllerTest.php index b4b2117e..a8217767 100644 --- a/tests/API/ActivityControllerTest.php +++ b/tests/API/ActivityControllerTest.php @@ -168,7 +168,10 @@ class ActivityControllerTest extends APIControllerBaseTest } $this->assertAccessIsGranted($client, $url, 'GET', $parameters); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertNotEmpty($result); @@ -208,7 +211,10 @@ class ActivityControllerTest extends APIControllerBaseTest $query = ['order' => 'ASC', 'orderBy' => 'project']; $this->assertAccessIsGranted($client, '/api/activities', 'GET', $query); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertNotEmpty($result); @@ -219,11 +225,19 @@ class ActivityControllerTest extends APIControllerBaseTest $this->assertEquals($imports[1]->getId(), $result[2]['project']); } + public function testGetEntityIsSecure(): void + { + $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/activities/1'); + } + public function testGetEntity(): void { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $this->assertAccessIsGranted($client, '/api/activities/1'); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); @@ -247,7 +261,10 @@ class ActivityControllerTest extends APIControllerBaseTest $this->request($client, '/api/activities', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); $this->assertNotEmpty($result['id']); @@ -262,7 +279,10 @@ class ActivityControllerTest extends APIControllerBaseTest $this->request($client, '/api/activities', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); $this->assertNotEmpty($result['id']); @@ -308,7 +328,10 @@ class ActivityControllerTest extends APIControllerBaseTest $this->request($client, '/api/activities/1', 'PATCH', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); $this->assertNotEmpty($result['id']); @@ -330,7 +353,10 @@ class ActivityControllerTest extends APIControllerBaseTest $this->request($client, '/api/activities/' . $imports[2]->getId(), 'PATCH', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ActivityEntity', $result); $this->assertNotEmpty($result['id']); diff --git a/tests/API/CustomerControllerTest.php b/tests/API/CustomerControllerTest.php index 1e4f3d44..fab2703c 100644 --- a/tests/API/CustomerControllerTest.php +++ b/tests/API/CustomerControllerTest.php @@ -96,7 +96,10 @@ class CustomerControllerTest extends APIControllerBaseTest { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $this->assertAccessIsGranted($client, '/api/customers'); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertNotEmpty($result); @@ -109,7 +112,10 @@ class CustomerControllerTest extends APIControllerBaseTest $query = ['order' => 'ASC', 'orderBy' => 'name', 'visible' => 3, 'term' => 'test']; $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $this->assertAccessIsGranted($client, '/api/customers', 'GET', $query); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertNotEmpty($result); @@ -117,11 +123,19 @@ class CustomerControllerTest extends APIControllerBaseTest self::assertApiResponseTypeStructure('CustomerCollection', $result[0]); } + public function testGetEntityIsSecure(): void + { + $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/customers/1'); + } + public function testGetEntity(): void { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $this->assertAccessIsGranted($client, '/api/customers/1'); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); @@ -129,7 +143,7 @@ class CustomerControllerTest extends APIControllerBaseTest public function testGetEntityWithFullResponse(): void { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $em = $this->getEntityManager(); @@ -165,7 +179,10 @@ class CustomerControllerTest extends APIControllerBaseTest $em->flush(); $this->assertAccessIsGranted($client, '/api/customers/1'); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); @@ -191,7 +208,10 @@ class CustomerControllerTest extends APIControllerBaseTest $this->request($client, '/api/customers', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); $this->assertNotEmpty($result['id']); @@ -209,7 +229,10 @@ class CustomerControllerTest extends APIControllerBaseTest $this->request($client, '/api/customers', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); $this->assertNotEmpty($result['id']); @@ -262,7 +285,10 @@ class CustomerControllerTest extends APIControllerBaseTest $this->request($client, '/api/customers/1', 'PATCH', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('CustomerEntity', $result); $this->assertNotEmpty($result['id']); diff --git a/tests/API/ProjectControllerTest.php b/tests/API/ProjectControllerTest.php index 246b1598..47e2970b 100644 --- a/tests/API/ProjectControllerTest.php +++ b/tests/API/ProjectControllerTest.php @@ -97,7 +97,10 @@ class ProjectControllerTest extends APIControllerBaseTest { $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); $this->assertAccessIsGranted($client, '/api/projects'); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertNotEmpty($result); @@ -206,7 +209,10 @@ class ProjectControllerTest extends APIControllerBaseTest } $this->assertAccessIsGranted($client, $url, 'GET', $parameters); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); $this->assertEquals(\count($expected), \count($result), 'Found wrong amount of projects'); @@ -242,9 +248,27 @@ class ProjectControllerTest extends APIControllerBaseTest yield ['/api/projects', 1, ['customers' => ['2', '2'], 'visible' => VisibilityInterface::SHOW_HIDDEN, 'start' => '2010-12-11', 'end' => '2030-12-11'], []]; } + public function testGetEntityIsSecure(): void + { + $this->assertUrlIsSecuredForRole(User::ROLE_USER, '/api/projects/1'); + } + public function testGetEntity(): void { - $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); + $this->assertAccessIsGranted($client, '/api/projects/1'); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + + $this->assertIsArray($result); + self::assertApiResponseTypeStructure('ProjectEntity', $result); + } + + public function testGetEntityComplex(): void + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); $em = $this->getEntityManager(); $customer = new Customer('first one'); @@ -268,7 +292,10 @@ class ProjectControllerTest extends APIControllerBaseTest $em->flush(); $this->assertAccessIsGranted($client, '/api/projects/' . $project->getId()); - $result = json_decode($client->getResponse()->getContent(), true); + + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); @@ -318,7 +345,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); @@ -344,7 +374,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); @@ -367,7 +400,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); @@ -390,7 +426,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); @@ -410,7 +449,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects', 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); @@ -461,7 +503,10 @@ class ProjectControllerTest extends APIControllerBaseTest $this->request($client, '/api/projects/1', 'PATCH', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); self::assertApiResponseTypeStructure('ProjectEntity', $result); $this->assertNotEmpty($result['id']); diff --git a/tests/API/RateControllerTestTrait.php b/tests/API/RateControllerTestTrait.php index d173be15..d7e2c92e 100644 --- a/tests/API/RateControllerTestTrait.php +++ b/tests/API/RateControllerTestTrait.php @@ -20,20 +20,14 @@ use App\Entity\User; */ trait RateControllerTestTrait { - /** - * @param string|int $id - * @param string|int|null $rateId - * @return string - */ - abstract protected function getRateUrl($id = '1', $rateId = null): string; + abstract protected function getRateUrl(string|int $id = '1', string|int|null $rateId = null): string; abstract protected function getRateUrlByRate(RateInterface $rate, bool $isCollection): string; /** - * @param string|int $id * @return RateInterface[] */ - abstract protected function importTestRates($id): array; + abstract protected function importTestRates(string|int $id): array; public function testAddRateMissingEntityAction(): void { @@ -89,7 +83,10 @@ trait RateControllerTestTrait $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); $this->assertRateStructure($result, null); $this->assertNotEmpty($result['id']); @@ -110,7 +107,10 @@ trait RateControllerTestTrait $this->request($client, $this->getRateUrl(), 'POST', [], json_encode($data)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); $this->assertRateStructure($result, 1); $this->assertNotEmpty($result['id']); @@ -125,7 +125,10 @@ trait RateControllerTestTrait $this->request($client, $this->getRateUrl(1)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); $this->assertEmpty($result); } @@ -138,7 +141,10 @@ trait RateControllerTestTrait $this->request($client, $this->getRateUrl(1)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); $this->assertNotEmpty($result); $this->assertEquals(\count($expectedRates), \count($result)); @@ -171,7 +177,10 @@ trait RateControllerTestTrait $this->request($client, $this->getRateUrlByRate($expectedRates[0], true)); $this->assertTrue($client->getResponse()->isSuccessful()); - $result = json_decode($client->getResponse()->getContent(), true); + $content = $client->getResponse()->getContent(); + $this->assertIsString($content); + $result = json_decode($content, true); + $this->assertIsArray($result); $this->assertEquals(\count($expectedRates) - 1, \count($result)); } diff --git a/tests/API/TimesheetControllerTest.php b/tests/API/TimesheetControllerTest.php index 74eca529..fda791bd 100644 --- a/tests/API/TimesheetControllerTest.php +++ b/tests/API/TimesheetControllerTest.php @@ -118,7 +118,7 @@ class TimesheetControllerTest extends APIControllerBaseTest self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]); } - public function testGetCollectionForAllUser(): void + public function testGetCollectionForAllUserIsSecure(): void { $client = $this->getClientForAuthenticatedUser(User::ROLE_TEAMLEAD); $this->importFixtureForUser(User::ROLE_USER); @@ -136,6 +136,53 @@ class TimesheetControllerTest extends APIControllerBaseTest self::assertIsString($content); $result = json_decode($content, true); + self::assertIsArray($result); + self::assertEmpty($result); + self::assertEquals(0, \count($result)); + } + + public function testGetCollectionForAllUserIsSecureForUser(): void + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_USER); + $this->importFixtureForUser(User::ROLE_USER); + + $fixture = new TimesheetFixtures($this->getUserByRole(User::ROLE_ADMIN), 7); + $fixture->setFixedRate(true); + $fixture->setHourlyRate(true); + $fixture->setStartDate(new \DateTime('-10 days')); + $this->importFixture($fixture); + + $query = ['user' => 'all']; + $this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query); + + $content = $client->getResponse()->getContent(); + self::assertIsString($content); + $result = json_decode($content, true); + + self::assertIsArray($result); + self::assertNotEmpty($result); + self::assertEquals(10, \count($result)); + self::assertApiResponseTypeStructure('TimesheetCollection', $result[0]); + } + + public function testGetCollectionForAllUser(): void + { + $client = $this->getClientForAuthenticatedUser(User::ROLE_ADMIN); + $this->importFixtureForUser(User::ROLE_USER); + + $fixture = new TimesheetFixtures($this->getUserByRole(User::ROLE_SUPER_ADMIN), 7); + $fixture->setFixedRate(true); + $fixture->setHourlyRate(true); + $fixture->setStartDate(new \DateTime('-10 days')); + $this->importFixture($fixture); + + $query = ['user' => 'all']; + $this->assertAccessIsGranted($client, '/api/timesheets', 'GET', $query); + + $content = $client->getResponse()->getContent(); + self::assertIsString($content); + $result = json_decode($content, true); + self::assertIsArray($result); self::assertNotEmpty($result); self::assertEquals(17, \count($result)); diff --git a/tests/Entity/EntityValidationTestTrait.php b/tests/Entity/EntityValidationTestTrait.php index 060c1f62..083cab4b 100644 --- a/tests/Entity/EntityValidationTestTrait.php +++ b/tests/Entity/EntityValidationTestTrait.php @@ -19,9 +19,9 @@ trait EntityValidationTestTrait { /** * @param object $entity - * @param array|string $fieldNames + * @param array|string $fieldNames */ - public function assertHasViolationForField(object $entity, $fieldNames, $groups = null): void + public function assertHasViolationForField(object $entity, array|string $fieldNames, $groups = null): void { self::bootKernel(); /** @var ValidatorInterface $validator */ diff --git a/tests/Entity/TimesheetValidationTest.php b/tests/Entity/TimesheetValidationTest.php index 49d5f10d..16e724ee 100644 --- a/tests/Entity/TimesheetValidationTest.php +++ b/tests/Entity/TimesheetValidationTest.php @@ -24,7 +24,7 @@ class TimesheetValidationTest extends KernelTestCase { use EntityValidationTestTrait; - protected function getEntity() + protected function getEntity(): Timesheet { $customer = new Customer('Test Customer'); diff --git a/tests/Invoice/NumberGenerator/ConfigurableNumberGeneratorTest.php b/tests/Invoice/NumberGenerator/ConfigurableNumberGeneratorTest.php index 2ab24dd3..7925fe2a 100644 --- a/tests/Invoice/NumberGenerator/ConfigurableNumberGeneratorTest.php +++ b/tests/Invoice/NumberGenerator/ConfigurableNumberGeneratorTest.php @@ -61,6 +61,8 @@ class ConfigurableNumberGeneratorTest extends TestCase public function getTestData(): array { $invoiceDate = new \DateTime(); + $invoiceDateFixed = new \DateTime('2021-03-07'); + $invoiceDateFixed1 = new \DateTime('2021-03-27'); return [ // simple tests for single calls @@ -109,6 +111,7 @@ class ConfigurableNumberGeneratorTest extends TestCase ['{c+13,3}', '014', $invoiceDate], ['{c+13,2}', '14', $invoiceDate], ['{c+13}', '14', $invoiceDate], + ['{c-20}', '0', $invoiceDate], ['{cd+111}', '112', $invoiceDate], ['{cd+111,5}', '00112', $invoiceDate], ['{cd+111,2}', '113', $invoiceDate, 2], @@ -128,7 +131,7 @@ class ConfigurableNumberGeneratorTest extends TestCase ['{Y,5}/{cy,5}', '0' . $invoiceDate->format('Y') . '/00002', $invoiceDate], // with decrementing counter ['{c-1,2}', '00', $invoiceDate], - ['{c-2,2}', '-1', $invoiceDate], + ['{c-2,2}', '00', $invoiceDate], // with incrementing and decrementing counter ['{c-5+13,1}', '9', $invoiceDate], ['{c+13-5,2}', '09', $invoiceDate], @@ -142,6 +145,43 @@ class ConfigurableNumberGeneratorTest extends TestCase ['{cm+22+2+2-1-13-4}', '9', $invoiceDate], ['{cm+22+2-2-22}', '6', $invoiceDate, 5], ['{cm-21+22+2-2}', '6', $invoiceDate, 5], + // increment dates + ['{YY}', '2022', $invoiceDateFixed], + ['{YY+1}', '2022', $invoiceDateFixed], + ['{YY+2}', '2023', $invoiceDateFixed], + ['{YY+3}', '2024', $invoiceDateFixed], + ['{YY-1}', '2020', $invoiceDateFixed], + ['{YY-2}', '2019', $invoiceDateFixed], + ['{YY-3}', '2018', $invoiceDateFixed], + ['{yy}', '22', $invoiceDateFixed], + ['{yy+1}', '22', $invoiceDateFixed], + ['{yy+2}', '23', $invoiceDateFixed], + ['{yy+3}', '24', $invoiceDateFixed], + ['{yy-1}', '20', $invoiceDateFixed], + ['{yy-2}', '19', $invoiceDateFixed], + ['{yy-3}', '18', $invoiceDateFixed], + ['{MM}', '4', $invoiceDateFixed], // cast to int removes leading zero + ['{MM+1}', '4', $invoiceDateFixed], // cast to int removes leading zero + ['{MM+2}', '5', $invoiceDateFixed], // cast to int removes leading zero + ['{MM+3}', '6', $invoiceDateFixed], // cast to int removes leading zero + ['{MM-1}', '2', $invoiceDateFixed], // cast to int removes leading zero + ['{MM-2}', '1', $invoiceDateFixed], // cast to int removes leading zero + ['{MM-3}', '0', $invoiceDateFixed], // cast to int removes leading zero + ['{MM-4}', '0', $invoiceDateFixed], // cast to int removes leading zero + ['{DD}', '8', $invoiceDateFixed], // cast to int removes leading zero + ['{DD+1}', '8', $invoiceDateFixed], // cast to int removes leading zero + ['{DD+2}', '9', $invoiceDateFixed], // cast to int removes leading zero + ['{DD+3}', '10', $invoiceDateFixed], // cast to int removes leading zero + ['{DD-1}', '6', $invoiceDateFixed], // cast to int removes leading zero + ['{DD-2}', '5', $invoiceDateFixed], // cast to int removes leading zero + ['{DD-3}', '4', $invoiceDateFixed], // cast to int removes leading zero + ['{DD}', '28', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD+1}', '28', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD+2}', '29', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD+3}', '30', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD-1}', '26', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD-2}', '25', $invoiceDateFixed1], // cast to int removes leading zero + ['{DD-3}', '24', $invoiceDateFixed1], // cast to int removes leading zero ]; } diff --git a/tests/Twig/LocaleFormatExtensionsTest.php b/tests/Twig/LocaleFormatExtensionsTest.php index 6463c198..281877c2 100644 --- a/tests/Twig/LocaleFormatExtensionsTest.php +++ b/tests/Twig/LocaleFormatExtensionsTest.php @@ -463,6 +463,8 @@ class LocaleFormatExtensionsTest extends TestCase 'twentyFourHours' => false, 'updateBrowserTitle' => false, 'timezone' => 'America/Edmonton', + 'locale' => 'en', + 'language' => 'de', 'user' => [ 'id' => 1, 'name' => null, @@ -473,6 +475,7 @@ class LocaleFormatExtensionsTest extends TestCase $user = $this->createMock(User::class); $user->method('getId')->willReturn(1); $user->method('getName')->willReturn('Testing'); + $user->method('getLanguage')->willReturn('de'); $user->method('isAdmin')->willReturn(false); $user->method('isSuperAdmin')->willReturn(false); $user->method('getTimezone')->willReturn('America/Edmonton'); diff --git a/tests/Validator/Constraints/TimesheetBudgetUsedValidatorTest.php b/tests/Validator/Constraints/TimesheetBudgetUsedValidatorTest.php index df7f7fef..a751b1a2 100644 --- a/tests/Validator/Constraints/TimesheetBudgetUsedValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetBudgetUsedValidatorTest.php @@ -121,7 +121,7 @@ class TimesheetBudgetUsedValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate('foo', new TimesheetBudgetUsed()); // @phpstan-ignore-line + $this->validator->validate('foo', new TimesheetBudgetUsed()); } public function testWithMissingEnd(): void diff --git a/tests/Validator/Constraints/TimesheetExportedValidatorTest.php b/tests/Validator/Constraints/TimesheetExportedValidatorTest.php index 5f0bf4c0..9af5f9a0 100644 --- a/tests/Validator/Constraints/TimesheetExportedValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetExportedValidatorTest.php @@ -59,7 +59,7 @@ class TimesheetExportedValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetExported(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetExported(['message' => 'myMessage'])); } public function testTriggersOnMissingPermission(): void diff --git a/tests/Validator/Constraints/TimesheetFutureTimesValidatorTest.php b/tests/Validator/Constraints/TimesheetFutureTimesValidatorTest.php index 766b5f1b..7afcc765 100644 --- a/tests/Validator/Constraints/TimesheetFutureTimesValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetFutureTimesValidatorTest.php @@ -60,7 +60,7 @@ class TimesheetFutureTimesValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetFutureTimes(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetFutureTimes(['message' => 'myMessage'])); } public function testFutureBeginIsDisallowed(): void diff --git a/tests/Validator/Constraints/TimesheetLockdownValidatorTest.php b/tests/Validator/Constraints/TimesheetLockdownValidatorTest.php index 221c603d..70fa7032 100644 --- a/tests/Validator/Constraints/TimesheetLockdownValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetLockdownValidatorTest.php @@ -75,7 +75,7 @@ class TimesheetLockdownValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetLockdown(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetLockdown(['message' => 'myMessage'])); } public function testValidatorWithoutNowConstraint(): void diff --git a/tests/Validator/Constraints/TimesheetLongRunningValidatorTest.php b/tests/Validator/Constraints/TimesheetLongRunningValidatorTest.php index 044f00d6..db2742fc 100644 --- a/tests/Validator/Constraints/TimesheetLongRunningValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetLongRunningValidatorTest.php @@ -55,7 +55,7 @@ class TimesheetLongRunningValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetLongRunning(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetLongRunning(['message' => 'myMessage'])); } public function testLongRunningTriggers(): void diff --git a/tests/Validator/Constraints/TimesheetOverlappingValidatorTest.php b/tests/Validator/Constraints/TimesheetOverlappingValidatorTest.php index 0b467128..f0b275c7 100644 --- a/tests/Validator/Constraints/TimesheetOverlappingValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetOverlappingValidatorTest.php @@ -58,7 +58,7 @@ class TimesheetOverlappingValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetOverlapping(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetOverlapping(['message' => 'myMessage'])); } public function testOverlappingDisallowedWithRecords(): void diff --git a/tests/Validator/Constraints/TimesheetRestartValidatorTest.php b/tests/Validator/Constraints/TimesheetRestartValidatorTest.php index 42d6ab1a..e8de122f 100644 --- a/tests/Validator/Constraints/TimesheetRestartValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetRestartValidatorTest.php @@ -57,7 +57,7 @@ class TimesheetRestartValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetOverlapping(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetOverlapping(['message' => 'myMessage'])); } /** diff --git a/tests/Validator/Constraints/TimesheetValidatorTest.php b/tests/Validator/Constraints/TimesheetValidatorTest.php index acd6c88d..8c85c2ef 100644 --- a/tests/Validator/Constraints/TimesheetValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetValidatorTest.php @@ -44,6 +44,6 @@ class TimesheetValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetConstraint(['message' => 'myMessage'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetConstraint(['message' => 'myMessage'])); } } diff --git a/tests/Validator/Constraints/TimesheetZeroDurationValidatorTest.php b/tests/Validator/Constraints/TimesheetZeroDurationValidatorTest.php index 12caf35c..20d6beec 100644 --- a/tests/Validator/Constraints/TimesheetZeroDurationValidatorTest.php +++ b/tests/Validator/Constraints/TimesheetZeroDurationValidatorTest.php @@ -55,7 +55,7 @@ class TimesheetZeroDurationValidatorTest extends ConstraintValidatorTestCase { $this->expectException(UnexpectedTypeException::class); - $this->validator->validate(new NotBlank(), new TimesheetZeroDuration(['message' => 'Duration cannot be zero.'])); // @phpstan-ignore-line + $this->validator->validate(new NotBlank(), new TimesheetZeroDuration(['message' => 'Duration cannot be zero.'])); } private function prepareTimesheet() diff --git a/tests/phpstan.neon b/tests/phpstan.neon index 6f2fce64..7b904154 100644 --- a/tests/phpstan.neon +++ b/tests/phpstan.neon @@ -202,11 +202,6 @@ parameters: count: 1 path: API/ActivityControllerTest.php - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 12 - path: API/ActivityControllerTest.php - - message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTest\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#" count: 13 @@ -297,11 +292,6 @@ parameters: count: 1 path: API/CustomerControllerTest.php - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 12 - path: API/CustomerControllerTest.php - - message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTest\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#" count: 13 @@ -382,11 +372,6 @@ parameters: count: 1 path: API/ProjectControllerTest.php - - - message: "#^Parameter \\#1 \\$json of function json_decode expects string, string\\|false given\\.$#" - count: 14 - path: API/ProjectControllerTest.php - - message: "#^Parameter \\#5 \\$content of method App\\\\Tests\\\\API\\\\APIControllerBaseTest\\:\\:request\\(\\) expects string\\|null, string\\|false given\\.$#" count: 15 @@ -1657,21 +1642,11 @@ parameters: count: 1 path: Entity/TimesheetValidationTest.php - - - message: "#^Method App\\\\Tests\\\\Entity\\\\TimesheetValidationTest\\:\\:assertHasViolationForField\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#" - count: 1 - path: Entity/TimesheetValidationTest.php - - message: "#^Method App\\\\Tests\\\\Entity\\\\TimesheetValidationTest\\:\\:assertHasViolationForField\\(\\) has parameter \\$groups with no type specified\\.$#" count: 1 path: Entity/TimesheetValidationTest.php - - - message: "#^Method App\\\\Tests\\\\Entity\\\\TimesheetValidationTest\\:\\:getEntity\\(\\) has no return type specified\\.$#" - count: 1 - path: Entity/TimesheetValidationTest.php - - message: "#^Cannot access offset 0 on iterable\\\\.$#" count: 1 @@ -1692,11 +1667,6 @@ parameters: count: 1 path: Entity/UserValidationTest.php - - - message: "#^Method App\\\\Tests\\\\Entity\\\\UserValidationTest\\:\\:assertHasViolationForField\\(\\) has parameter \\$fieldNames with no value type specified in iterable type array\\.$#" - count: 1 - path: Entity/UserValidationTest.php - - message: "#^Method App\\\\Tests\\\\Entity\\\\UserValidationTest\\:\\:assertHasViolationForField\\(\\) has parameter \\$groups with no type specified\\.$#" count: 1