From 53ab6877f4f167f387ed03b5fe33341db232cba7 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Mon, 13 Jan 2025 09:57:55 +0000 Subject: [PATCH 01/30] add changelog WIP --- CHANGELOG-WIP.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG-WIP.md diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md new file mode 100644 index 0000000000..37efffc910 --- /dev/null +++ b/CHANGELOG-WIP.md @@ -0,0 +1 @@ +# Release Notes for Craft Commerce 4.8 (WIP) \ No newline at end of file From abee563d7b3a0624187dd6224b840ed3162bada2 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Mon, 13 Jan 2025 09:58:14 +0000 Subject: [PATCH 02/30] Update CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8139bf8ef..feceefd8ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: branches: - '4.x' - - '4.7' + - '4.8' pull_request: permissions: contents: read From 697cfe40447a41107dd26ca50a336612cb5f7aa1 Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 14 Jan 2025 07:50:59 +0000 Subject: [PATCH 03/30] Show archived gateways table --- CHANGELOG-WIP.md | 5 +- src/controllers/GatewaysController.php | 28 ++++++++++++ src/services/Gateways.php | 11 +++++ src/templates/settings/gateways/index.twig | 53 +++++++++++++++++++++- src/translations/en/commerce.php | 2 + 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 37efffc910..83aa03d3cf 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1 +1,4 @@ -# Release Notes for Craft Commerce 4.8 (WIP) \ No newline at end of file +# Release Notes for Craft Commerce 4.8 (WIP) + +### Extensibility +- Added `craft\commerce\services\Gateways\getAllArchivedGateways()`. \ No newline at end of file diff --git a/src/controllers/GatewaysController.php b/src/controllers/GatewaysController.php index 5c6b084fa9..e42dfd5bfb 100644 --- a/src/controllers/GatewaysController.php +++ b/src/controllers/GatewaysController.php @@ -8,12 +8,16 @@ namespace craft\commerce\controllers; use Craft; +use craft\base\MissingComponentInterface; use craft\commerce\base\Gateway; use craft\commerce\base\GatewayInterface; +use craft\commerce\db\Table; use craft\commerce\gateways\Dummy; use craft\commerce\helpers\DebugPanel; use craft\commerce\Plugin; +use craft\db\Query; use craft\errors\DeprecationException; +use craft\helpers\Html; use craft\helpers\Json; use yii\base\Exception; use yii\base\InvalidConfigException; @@ -32,9 +36,33 @@ class GatewaysController extends BaseAdminController public function actionIndex(): Response { $gateways = Plugin::getInstance()->getGateways()->getAllGateways(); + $archivedGateways = Plugin::getInstance()->getGateways()->getAllArchivedGateways(); + + if (!empty($archivedGateways)) { + $gatewayIdsWithTransactions = (new Query()) + ->select(['gatewayId']) + ->from(Table::TRANSACTIONS) + ->groupBy(['gatewayId']) + ->column(); + + foreach ($archivedGateways as &$gateway) { + $missing = $gateway instanceof MissingComponentInterface; + $gateway = [ + 'id' => $gateway->id, + 'title' => Craft::t('site', $gateway->name), + 'handle' => Html::encode($gateway->handle), + 'type' => [ + 'missing' => $missing, + 'name' => $missing ? $gateway->expectedType : $gateway->displayName(), + ], + 'hasTransactions' => in_array($gateway->id, $gatewayIdsWithTransactions), + ]; + } + } return $this->renderTemplate('commerce/settings/gateways/index', [ 'gateways' => $gateways, + 'archivedGateways' => array_values($archivedGateways), ]); } diff --git a/src/services/Gateways.php b/src/services/Gateways.php index e0cdac9cb4..d49dce97c9 100644 --- a/src/services/Gateways.php +++ b/src/services/Gateways.php @@ -138,6 +138,17 @@ public function getAllGateways(): array return ArrayHelper::where($this->_getAllGateways(), 'isArchived', false); } + /** + * @return array + * @throws DeprecationException + * @throws InvalidConfigException + * @sine 4.8.0 + */ + public function getAllArchivedGateways(): array + { + return ArrayHelper::where($this->_getAllGateways(), 'isArchived', true); + } + /** * Archives a gateway by its ID. * diff --git a/src/templates/settings/gateways/index.twig b/src/templates/settings/gateways/index.twig index ccc76a7668..f72929180f 100644 --- a/src/templates/settings/gateways/index.twig +++ b/src/templates/settings/gateways/index.twig @@ -18,8 +18,25 @@ {{ 'New gateway'|t('commerce') }} {% endblock %} -{% block content %} -
+{% block main %} +
+ +
+
+
+ {% if archivedGateways|length %} +

+ {{ 'Show archived gateways'|t('commerce') }} +

+ + + {% endif %} +
{% endblock %} {% set tableData = [] %} @@ -73,4 +90,36 @@ reorderFailMessage: Craft.t('commerce', 'Couldn’t reorder gateways.'), tableData: {{ tableData|json_encode|raw }} }); + + {% if archivedGateways|length %} + new Craft.VueAdminTable({ + columns: [ + { name: 'id', title: Craft.t('commerce', 'ID') }, + { name: '__slot:title', title: Craft.t('commerce', 'Name') }, + { name: '__slot:handle', title: Craft.t('commerce', 'Handle') }, + { + name: 'type', + title: Craft.t('commerce', 'Type'), + callback: function(value) { + if (value.missing) { + return ''+value.name+''; + } + + return value.name; + } + }, + { + name: 'hasTransactions', + title: Craft.t('commerce', 'Has Transactions?'), + callback: function(value) { + if (value) { + return '
'; + } + } + }, + ], + container: '#gateways-archived-vue-admin-table', + tableData: {{ archivedGateways|json_encode|raw }} + }); + {% endif %} {% endjs %} diff --git a/src/translations/en/commerce.php b/src/translations/en/commerce.php index 3b3d97cba9..c52f4dadf3 100644 --- a/src/translations/en/commerce.php +++ b/src/translations/en/commerce.php @@ -464,6 +464,7 @@ 'Has Emails?' => 'Has Emails?', 'Has Orders' => 'Has Orders', 'Has Purchasable' => 'Has Purchasable', + 'Has Transactions?' => 'Has Transactions?', 'Has Variants?' => 'Has Variants?', 'Height ({unit})' => 'Height ({unit})', 'Height' => 'Height', @@ -894,6 +895,7 @@ 'Short Number' => 'Short Number', 'Show Chart?' => 'Show Chart?', 'Show Order Count?' => 'Show Order Count?', + 'Show archived gateways' => 'Show archived gateways', 'Show order count line on chart.' => 'Show order count line on chart.', 'Show rule details' => 'Show rule details', 'Show the Dimensions and Weight fields for products of this type' => 'Show the Dimensions and Weight fields for products of this type', From 27877db5d7344c115ec2fca7bb8b5f4e99f7029b Mon Sep 17 00:00:00 2001 From: Nathaniel Hammond Date: Tue, 14 Jan 2025 07:53:57 +0000 Subject: [PATCH 04/30] Changelog --- CHANGELOG-WIP.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 83aa03d3cf..81a0099ca5 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -1,4 +1,7 @@ # Release Notes for Craft Commerce 4.8 (WIP) +### Store Management +- It is now possible to see archived gateways listed in the control panel. ([#3839](https://github.com/craftcms/commerce/issues/3839)) + ### Extensibility - Added `craft\commerce\services\Gateways\getAllArchivedGateways()`. \ No newline at end of file From 2c676e39c1c005b1f44890b31694563d34e1f82f Mon Sep 17 00:00:00 2001 From: Luke Holder Date: Wed, 22 Jan 2025 15:34:59 +0800 Subject: [PATCH 05/30] WIP --- CHANGELOG.md | 1 + composer.lock | 1015 +++++++++-------- src/Plugin.php | 4 +- src/adjusters/Tax.php | 43 +- src/base/TaxIdValidatorInterface.php | 45 + .../ValidateOrganizationTaxIdBehavior.php | 12 +- src/controllers/TaxRatesController.php | 12 +- src/events/TaxIdValidatorsEvent.php | 25 + src/migrations/Install.php | 3 +- ...50120_080035_move_to_tax_id_validators.php | 40 + src/models/TaxRate.php | 79 +- src/records/TaxRate.php | 1 + src/services/TaxRates.php | 15 + src/services/Taxes.php | 63 +- src/services/Vat.php | 77 ++ src/taxidvalidators/EuVatIdValidator.php | 54 + src/templates/tax/taxrates/_fields.twig | 16 +- src/templates/tax/taxrates/index.twig | 7 +- src/translations/en/commerce.php | 3 +- tests/unit/taxids/UkVatIdValidatorTest.php | 62 + 20 files changed, 1035 insertions(+), 542 deletions(-) create mode 100644 src/base/TaxIdValidatorInterface.php create mode 100644 src/events/TaxIdValidatorsEvent.php create mode 100644 src/migrations/m250120_080035_move_to_tax_id_validators.php create mode 100644 src/services/Vat.php create mode 100644 src/taxidvalidators/EuVatIdValidator.php create mode 100644 tests/unit/taxids/UkVatIdValidatorTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d805883b3c..6f66f02bcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +- Improved the performance of recalculating shipping method prices. ([#3841](https://github.com/craftcms/commerce/pull/3841)) - Fixed a bug where Edit Product pages would allow duplication for users that didn’t have permission to duplicate the product. ([#3819](https://github.com/craftcms/commerce/issues/3819)) ## 4.7.2 - 2024-12-18 diff --git a/composer.lock b/composer.lock index 99813d3015..8d18c6bf98 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0d8acbb335cf17c09d954b320db50c6c", + "content-hash": "c9d04a747e32a8316ad7efcb6ea496a9", "packages": [ { "name": "cebe/markdown", @@ -136,16 +136,16 @@ }, { "name": "composer/ca-bundle", - "version": "1.5.0", + "version": "1.5.5", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99" + "reference": "08c50d5ec4c6ced7d0271d2862dec8c1033283e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/0c5ccfcfea312b5c5a190a21ac5cef93f74baf99", - "reference": "0c5ccfcfea312b5c5a190a21ac5cef93f74baf99", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/08c50d5ec4c6ced7d0271d2862dec8c1033283e6", + "reference": "08c50d5ec4c6ced7d0271d2862dec8c1033283e6", "shasum": "" }, "require": { @@ -155,8 +155,8 @@ }, "require-dev": { "phpstan/phpstan": "^1.10", - "psr/log": "^1.0", - "symfony/phpunit-bridge": "^4.2 || ^5", + "phpunit/phpunit": "^8 || ^9", + "psr/log": "^1.0 || ^2.0 || ^3.0", "symfony/process": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, "type": "library", @@ -192,7 +192,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.5.0" + "source": "https://github.com/composer/ca-bundle/tree/1.5.5" }, "funding": [ { @@ -208,20 +208,20 @@ "type": "tidelift" } ], - "time": "2024-03-15T14:00:32+00:00" + "time": "2025-01-08T16:17:16+00:00" }, { "name": "composer/class-map-generator", - "version": "1.3.2", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "acd227952154850d0bb7d65caa4f9edf9cd806a7" + "reference": "4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/acd227952154850d0bb7d65caa4f9edf9cd806a7", - "reference": "acd227952154850d0bb7d65caa4f9edf9cd806a7", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915", + "reference": "4b0a223cf5be7c9ee7e0ef1bc7db42b4a97c9915", "shasum": "" }, "require": { @@ -230,12 +230,12 @@ "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" }, "require-dev": { - "phpstan/phpstan": "^1.6", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/filesystem": "^5.4 || ^6", - "symfony/phpunit-bridge": "^5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-deprecation-rules": "^1 || ^2", + "phpstan/phpstan-phpunit": "^1 || ^2", + "phpstan/phpstan-strict-rules": "^1.1 || ^2", + "phpunit/phpunit": "^8", + "symfony/filesystem": "^5.4 || ^6" }, "type": "library", "extra": { @@ -265,7 +265,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.3.2" + "source": "https://github.com/composer/class-map-generator/tree/1.5.0" }, "funding": [ { @@ -281,28 +281,28 @@ "type": "tidelift" } ], - "time": "2024-05-31T19:45:56+00:00" + "time": "2024-11-25T16:11:06+00:00" }, { "name": "composer/composer", - "version": "2.7.6", + "version": "2.7.7", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "fabd995783b633829fd4280e272284b39b6ae702" + "reference": "291942978f39435cf904d33739f98d7d4eca7b23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/fabd995783b633829fd4280e272284b39b6ae702", - "reference": "fabd995783b633829fd4280e272284b39b6ae702", + "url": "https://api.github.com/repos/composer/composer/zipball/291942978f39435cf904d33739f98d7d4eca7b23", + "reference": "291942978f39435cf904d33739f98d7d4eca7b23", "shasum": "" }, "require": { "composer/ca-bundle": "^1.0", - "composer/class-map-generator": "^1.0", + "composer/class-map-generator": "^1.3.3", "composer/metadata-minifier": "^1.0", "composer/pcre": "^2.1 || ^3.1", - "composer/semver": "^3.2.5", + "composer/semver": "^3.3", "composer/spdx-licenses": "^1.5.7", "composer/xdebug-handler": "^2.0.2 || ^3.0.3", "justinrainbow/json-schema": "^5.2.11", @@ -321,11 +321,11 @@ "symfony/process": "^5.4 || ^6.0 || ^7" }, "require-dev": { - "phpstan/phpstan": "^1.9.3", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1", - "phpstan/phpstan-symfony": "^1.2.10", + "phpstan/phpstan": "^1.11.0", + "phpstan/phpstan-deprecation-rules": "^1.2.0", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.0", + "phpstan/phpstan-symfony": "^1.4.0", "symfony/phpunit-bridge": "^6.4.1 || ^7.0.1" }, "suggest": { @@ -338,13 +338,13 @@ ], "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.7-dev" - }, "phpstan": { "includes": [ "phpstan/rules.neon" ] + }, + "branch-alias": { + "dev-main": "2.7-dev" } }, "autoload": { @@ -379,7 +379,7 @@ "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/composer/issues", "security": "https://github.com/composer/composer/security/policy", - "source": "https://github.com/composer/composer/tree/2.7.6" + "source": "https://github.com/composer/composer/tree/2.7.7" }, "funding": [ { @@ -395,7 +395,7 @@ "type": "tidelift" } ], - "time": "2024-05-04T21:03:15+00:00" + "time": "2024-06-10T20:11:12+00:00" }, { "name": "composer/metadata-minifier", @@ -468,28 +468,36 @@ }, { "name": "composer/pcre", - "version": "3.1.4", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "04229f163664973f68f38f6f73d917799168ef24" + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", - "reference": "04229f163664973f68f38f6f73d917799168ef24", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<1.11.10" + }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { "dev-main": "3.x-dev" } @@ -519,7 +527,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.4" + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { @@ -535,28 +543,28 @@ "type": "tidelift" } ], - "time": "2024-05-27T13:40:54+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.3", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -600,7 +608,7 @@ "support": { "irc": "ircs://irc.libera.chat:6697/composer", "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.0" + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { @@ -616,7 +624,7 @@ "type": "tidelift" } ], - "time": "2023-08-31T09:50:34+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { "name": "composer/spdx-licenses", @@ -766,16 +774,16 @@ }, { "name": "craftcms/cms", - "version": "4.9.6", + "version": "4.14.0.2", "source": { "type": "git", "url": "https://github.com/craftcms/cms.git", - "reference": "240f78187a174166e5b40207dd597eadae7da9e8" + "reference": "7eff5f7106ebdfac56e9296e9ee6d9faddb1a9fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/cms/zipball/240f78187a174166e5b40207dd597eadae7da9e8", - "reference": "240f78187a174166e5b40207dd597eadae7da9e8", + "url": "https://api.github.com/repos/craftcms/cms/zipball/7eff5f7106ebdfac56e9296e9ee6d9faddb1a9fe", + "reference": "7eff5f7106ebdfac56e9296e9ee6d9faddb1a9fe", "shasum": "" }, "require": { @@ -785,7 +793,7 @@ "craftcms/server-check": "~2.1.2", "creocoder/yii2-nested-sets": "~0.9.0", "elvanto/litemoji": "~4.3.0", - "enshrined/svg-sanitize": "~0.16.0", + "enshrined/svg-sanitize": "~0.19.0", "ext-bcmath": "*", "ext-curl": "*", "ext-dom": "*", @@ -809,11 +817,11 @@ "symfony/var-dumper": "^5.0|^6.0", "symfony/yaml": "^5.2.3", "theiconic/name-parser": "^1.2", - "twig/twig": "~3.8.0", + "twig/twig": "~3.15.0", "voku/stringy": "^6.4.0", "webonyx/graphql-php": "~14.11.5", - "yiisoft/yii2": "~2.0.48.1", - "yiisoft/yii2-debug": "~2.1.22.0", + "yiisoft/yii2": "~2.0.51.0", + "yiisoft/yii2-debug": "~2.1.25.0", "yiisoft/yii2-queue": "~2.3.2", "yiisoft/yii2-symfonymailer": "^2.0.0" }, @@ -821,10 +829,10 @@ "webonyx/graphql-php": "14.11.7" }, "provide": { - "bower-asset/inputmask": "~3.2.2|~3.3.5", - "bower-asset/jquery": "3.5.*@stable|3.4.*@stable|3.3.*@stable|3.2.*@stable|3.1.*@stable|2.2.*@stable|2.1.*@stable|1.11.*@stable|1.12.*@stable", - "bower-asset/punycode": "1.3.*", - "bower-asset/yii2-pjax": "~2.0.1", + "bower-asset/inputmask": "5.0.9", + "bower-asset/jquery": "3.6.1", + "bower-asset/punycode": "2.3.1", + "bower-asset/yii2-pjax": "2.0.8", "yii2tech/ar-softdelete": "1.0.4" }, "require-dev": { @@ -878,7 +886,7 @@ "rss": "https://github.com/craftcms/cms/releases.atom", "source": "https://github.com/craftcms/cms" }, - "time": "2024-06-03T17:21:01+00:00" + "time": "2025-01-22T01:46:41+00:00" }, { "name": "craftcms/plugin-installer", @@ -935,16 +943,16 @@ }, { "name": "craftcms/server-check", - "version": "2.1.8", + "version": "2.1.9", "source": { "type": "git", "url": "https://github.com/craftcms/server-check.git", - "reference": "c86e8aeabc73333111e2bfb4483d7b5402232e48" + "reference": "a24b91625fd4c0f2815bfe17275e14f1bfb37b94" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/server-check/zipball/c86e8aeabc73333111e2bfb4483d7b5402232e48", - "reference": "c86e8aeabc73333111e2bfb4483d7b5402232e48", + "url": "https://api.github.com/repos/craftcms/server-check/zipball/a24b91625fd4c0f2815bfe17275e14f1bfb37b94", + "reference": "a24b91625fd4c0f2815bfe17275e14f1bfb37b94", "shasum": "" }, "type": "library", @@ -973,7 +981,7 @@ "rss": "https://github.com/craftcms/server-check/releases.atom", "source": "https://github.com/craftcms/server-check" }, - "time": "2023-09-25T17:28:37+00:00" + "time": "2024-09-16T15:05:30+00:00" }, { "name": "creocoder/yii2-nested-sets", @@ -1158,29 +1166,27 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" + "psr/log": "^1 || ^2 || ^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -1188,7 +1194,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1199,9 +1205,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "source": "https://github.com/doctrine/deprecations/tree/1.1.4" }, - "time": "2024-01-30T19:34:25+00:00" + "time": "2024-12-07T21:18:45+00:00" }, { "name": "doctrine/lexer", @@ -1455,22 +1461,21 @@ }, { "name": "enshrined/svg-sanitize", - "version": "0.16.0", + "version": "0.19.0", "source": { "type": "git", "url": "https://github.com/darylldoyle/svg-sanitizer.git", - "reference": "239e257605e2141265b429e40987b2ee51bba4b4" + "reference": "e95cd17be68e45f523cbfb0fe50cdd891b0cf20e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/239e257605e2141265b429e40987b2ee51bba4b4", - "reference": "239e257605e2141265b429e40987b2ee51bba4b4", + "url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/e95cd17be68e45f523cbfb0fe50cdd891b0cf20e", + "reference": "e95cd17be68e45f523cbfb0fe50cdd891b0cf20e", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", - "ezyang/htmlpurifier": "^4.16", "php": "^5.6 || ^7.0 || ^8.0" }, "require-dev": { @@ -1495,26 +1500,26 @@ "description": "An SVG sanitizer for PHP", "support": { "issues": "https://github.com/darylldoyle/svg-sanitizer/issues", - "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.16.0" + "source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.19.0" }, - "time": "2023-03-20T10:51:12+00:00" + "time": "2024-06-18T10:27:15+00:00" }, { "name": "ezyang/htmlpurifier", - "version": "v4.17.0", + "version": "v4.18.0", "source": { "type": "git", "url": "https://github.com/ezyang/htmlpurifier.git", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c" + "reference": "cb56001e54359df7ae76dc522d08845dc741621b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c", - "reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/cb56001e54359df7ae76dc522d08845dc741621b", + "reference": "cb56001e54359df7ae76dc522d08845dc741621b", "shasum": "" }, "require": { - "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0" + "php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { "cerdic/css-tidy": "^1.7 || ^2.0", @@ -1556,28 +1561,28 @@ ], "support": { "issues": "https://github.com/ezyang/htmlpurifier/issues", - "source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0" + "source": "https://github.com/ezyang/htmlpurifier/tree/v4.18.0" }, - "time": "2023-11-17T15:01:25+00:00" + "time": "2024-11-01T03:51:45+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.9.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d281ed313b989f213357e3be1a179f02196ac99b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d281ed313b989f213357e3be1a179f02196ac99b", + "reference": "d281ed313b989f213357e3be1a179f02196ac99b", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^1.5.3 || ^2.0.3", + "guzzlehttp/psr7": "^2.7.0", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.2 || ^3.0" @@ -1588,9 +1593,9 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -1668,7 +1673,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.9.2" }, "funding": [ { @@ -1684,20 +1689,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2024-07-24T11:22:20+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/f9c436286ab2892c7db7be8c8da4ef61ccf7b455", + "reference": "f9c436286ab2892c7db7be8c8da4ef61ccf7b455", "shasum": "" }, "require": { @@ -1705,7 +1710,7 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "type": "library", "extra": { @@ -1751,7 +1756,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.0.4" }, "funding": [ { @@ -1767,20 +1772,20 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2024-10-17T10:06:22+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/a70f5c95fb43bc83f07c9c948baa0dc1829bf201", + "reference": "a70f5c95fb43bc83f07c9c948baa0dc1829bf201", "shasum": "" }, "require": { @@ -1795,8 +1800,8 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.39 || ^9.6.20" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -1867,7 +1872,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.7.0" }, "funding": [ { @@ -1883,7 +1888,7 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2024-07-18T11:15:46+00:00" }, { "name": "ibericode/vat", @@ -2462,16 +2467,16 @@ }, { "name": "monolog/monolog", - "version": "2.9.3", + "version": "2.10.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215" + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/a30bfe2e142720dfa990d0a7e573997f5d884215", - "reference": "a30bfe2e142720dfa990d0a7e573997f5d884215", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5cf826f2991858b54d5c3809bee745560a1042a7", + "reference": "5cf826f2991858b54d5c3809bee745560a1042a7", "shasum": "" }, "require": { @@ -2548,7 +2553,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/2.9.3" + "source": "https://github.com/Seldaek/monolog/tree/2.10.0" }, "funding": [ { @@ -2560,7 +2565,7 @@ "type": "tidelift" } ], - "time": "2024-04-12T20:52:51+00:00" + "time": "2024-11-12T12:43:37+00:00" }, { "name": "paragonie/random_compat", @@ -2757,16 +2762,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.4.1", + "version": "5.6.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c" + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", - "reference": "9d07b3f7fdcf5efec5d1609cba3c19c5ea2bdc9c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", "shasum": "" }, "require": { @@ -2775,17 +2780,17 @@ "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.5", + "mockery/mockery": "~1.3.5 || ~1.6.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan": "^1.8", "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "vimeo/psalm": "^5.13" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -2815,29 +2820,29 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.4.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.1" }, - "time": "2024-05-21T05:55:05+00:00" + "time": "2024-12-07T09:39:29+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "version": "1.10.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", @@ -2873,36 +2878,36 @@ "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.2" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" }, - "time": "2024-02-23T11:10:43+00:00" + "time": "2024-11-09T15:12:26+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299", + "reference": "c00d78fb6b29658347f9d37ebe104bffadf36299", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" }, "require-dev": { "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", + "nikic/php-parser": "^5.3.0", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", "symfony/process": "^5.2" }, "type": "library", @@ -2920,9 +2925,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.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-10-13T11:29:49+00:00" }, { "name": "pixelandtonic/imagine", @@ -3468,24 +3473,24 @@ }, { "name": "sabberworm/php-css-parser", - "version": "v8.5.1", + "version": "v8.7.0", "source": { "type": "git", "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152" + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152", - "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152", + "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/f414ff953002a9b18e3a116f5e462c56f21237cf", + "reference": "f414ff953002a9b18e3a116f5e462c56f21237cf", "shasum": "" }, "require": { "ext-iconv": "*", - "php": ">=5.6.20" + "php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0" }, "require-dev": { - "phpunit/phpunit": "^5.7.27" + "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.40" }, "suggest": { "ext-mbstring": "for parsing UTF-8 CSS" @@ -3527,9 +3532,9 @@ ], "support": { "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues", - "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1" + "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.7.0" }, - "time": "2024-02-15T16:41:13+00:00" + "time": "2024-10-27T17:38:32+00:00" }, { "name": "samdark/yii2-psr-log-target", @@ -3650,23 +3655,23 @@ }, { "name": "seld/jsonlint", - "version": "1.10.2", + "version": "1.11.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259" + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/9bb7db07b5d66d90f6ebf542f09fc67d800e5259", - "reference": "9bb7db07b5d66d90f6ebf542f09fc67d800e5259", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.5", + "phpstan/phpstan": "^1.11", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, "bin": [ @@ -3698,7 +3703,7 @@ ], "support": { "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.10.2" + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { @@ -3710,7 +3715,7 @@ "type": "tidelift" } ], - "time": "2024-02-07T12:57:50+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { "name": "seld/phar-utils", @@ -3823,16 +3828,16 @@ }, { "name": "setasign/fpdi", - "version": "v2.6.0", + "version": "v2.6.2", "source": { "type": "git", "url": "https://github.com/Setasign/FPDI.git", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad" + "reference": "9e013b376939c0d4029f54150d2a16f3c67a5797" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Setasign/FPDI/zipball/a6db878129ec6c7e141316ee71872923e7f1b7ad", - "reference": "a6db878129ec6c7e141316ee71872923e7f1b7ad", + "url": "https://api.github.com/repos/Setasign/FPDI/zipball/9e013b376939c0d4029f54150d2a16f3c67a5797", + "reference": "9e013b376939c0d4029f54150d2a16f3c67a5797", "shasum": "" }, "require": { @@ -3883,7 +3888,7 @@ ], "support": { "issues": "https://github.com/Setasign/FPDI/issues", - "source": "https://github.com/Setasign/FPDI/tree/v2.6.0" + "source": "https://github.com/Setasign/FPDI/tree/v2.6.2" }, "funding": [ { @@ -3891,20 +3896,20 @@ "type": "tidelift" } ], - "time": "2023-12-11T16:03:32+00:00" + "time": "2024-12-10T13:12:19+00:00" }, { "name": "symfony/console", - "version": "v5.4.40", + "version": "v5.4.47", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd" + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/aa73115c0c24220b523625bfcfa655d7d73662dd", - "reference": "aa73115c0c24220b523625bfcfa655d7d73662dd", + "url": "https://api.github.com/repos/symfony/console/zipball/c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", + "reference": "c4ba980ca61a9eb18ee6bcc73f28e475852bb1ed", "shasum": "" }, "require": { @@ -3974,7 +3979,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.40" + "source": "https://github.com/symfony/console/tree/v5.4.47" }, "funding": [ { @@ -3990,7 +3995,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-11-06T11:30:55+00:00" }, { "name": "symfony/deprecation-contracts", @@ -4011,12 +4016,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4061,16 +4066,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a54e2a8a114065f31020d6a89ede83e34c3b27a4" + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a54e2a8a114065f31020d6a89ede83e34c3b27a4", - "reference": "a54e2a8a114065f31020d6a89ede83e34c3b27a4", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/72982eb416f61003e9bb6e91f8b3213600dcf9e9", + "reference": "72982eb416f61003e9bb6e91f8b3213600dcf9e9", "shasum": "" }, "require": { @@ -4126,7 +4131,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.40" + "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.45" }, "funding": [ { @@ -4142,7 +4147,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/event-dispatcher-contracts", @@ -4167,12 +4172,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4288,16 +4293,16 @@ }, { "name": "symfony/finder", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "f51cff4687547641c7d8180d74932ab40b2205ce" + "reference": "63741784cd7b9967975eec610b256eed3ede022b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/f51cff4687547641c7d8180d74932ab40b2205ce", - "reference": "f51cff4687547641c7d8180d74932ab40b2205ce", + "url": "https://api.github.com/repos/symfony/finder/zipball/63741784cd7b9967975eec610b256eed3ede022b", + "reference": "63741784cd7b9967975eec610b256eed3ede022b", "shasum": "" }, "require": { @@ -4331,7 +4336,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.40" + "source": "https://github.com/symfony/finder/tree/v5.4.45" }, "funding": [ { @@ -4347,7 +4352,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-09-28T13:32:08+00:00" }, { "name": "symfony/http-client", @@ -4455,12 +4460,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -4669,20 +4674,20 @@ }, { "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -4693,8 +4698,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4728,7 +4733,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -4744,24 +4749,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f" + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", - "reference": "cd4226d140ecd3d0f13d32ed0a4a095ffe871d2f", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/48becf00c920479ca2e910c22a5a39e5d47ca956", + "reference": "48becf00c920479ca2e910c22a5a39e5d47ca956", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-iconv": "*" @@ -4772,8 +4777,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4808,7 +4813,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.31.0" }, "funding": [ { @@ -4824,24 +4829,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -4849,8 +4854,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4886,7 +4891,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -4902,26 +4907,25 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-idn", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919" + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/a287ed7475f85bf6f61890146edbc932c0fff919", - "reference": "a287ed7475f85bf6f61890146edbc932c0fff919", + "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/c36586dcf89a12315939e00ec9b4474adcb1d773", + "reference": "c36586dcf89a12315939e00ec9b4474adcb1d773", "shasum": "" }, "require": { - "php": ">=7.1", - "symfony/polyfill-intl-normalizer": "^1.10", - "symfony/polyfill-php72": "^1.10" + "php": ">=7.2", + "symfony/polyfill-intl-normalizer": "^1.10" }, "suggest": { "ext-intl": "For best performance" @@ -4929,8 +4933,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4970,7 +4974,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.31.0" }, "funding": [ { @@ -4986,24 +4990,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -5011,8 +5015,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5051,7 +5055,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -5067,24 +5071,24 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -5095,8 +5099,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5131,7 +5135,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -5147,38 +5151,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25" + "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/861391a8da9a04cbad2d232ddd9e4893220d6e25", - "reference": "861391a8da9a04cbad2d232ddd9e4893220d6e25", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", + "reference": "fa2ae56c44f03bed91a39bfc9822e31e7c5c38ce", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, - "type": "library", + "type": "metapackage", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "notification-url": "https://packagist.org/downloads/", @@ -5204,7 +5200,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.31.0" }, "funding": [ { @@ -5220,30 +5216,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2" + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/21bd091060673a1177ae842c0ef8fe30893114d2", - "reference": "21bd091060673a1177ae842c0ef8fe30893114d2", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5280,7 +5276,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" }, "funding": [ { @@ -5296,30 +5292,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", - "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5360,7 +5356,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -5376,30 +5372,30 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.29.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d" + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/c565ad1e63f30e7477fc40738343c62b40bc672d", - "reference": "c565ad1e63f30e7477fc40738343c62b40bc672d", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -5436,7 +5432,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -5452,7 +5448,7 @@ "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/process", @@ -5541,12 +5537,12 @@ }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.0-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5772,16 +5768,16 @@ }, { "name": "symfony/yaml", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83" + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/81cad0ceab3d61fe14fe941ff18a230ac9c80f83", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a454d47278cc16a5db371fe73ae66a78a633371e", + "reference": "a454d47278cc16a5db371fe73ae66a78a633371e", "shasum": "" }, "require": { @@ -5827,7 +5823,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.40" + "source": "https://github.com/symfony/yaml/tree/v5.4.45" }, "funding": [ { @@ -5843,24 +5839,25 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "tecnickcom/tcpdf", - "version": "6.7.5", + "version": "6.8.0", "source": { "type": "git", "url": "https://github.com/tecnickcom/TCPDF.git", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36" + "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", - "reference": "951eabf0338ec2522bd0d5d9c79b08a3a3d36b36", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/14ffa0e308f5634aa2489568b4b90b24073b6731", + "reference": "14ffa0e308f5634aa2489568b4b90b24073b6731", "shasum": "" }, "require": { - "php": ">=5.5.0" + "ext-curl": "*", + "php": ">=7.1.0" }, "type": "library", "autoload": { @@ -5907,7 +5904,7 @@ ], "support": { "issues": "https://github.com/tecnickcom/TCPDF/issues", - "source": "https://github.com/tecnickcom/TCPDF/tree/6.7.5" + "source": "https://github.com/tecnickcom/TCPDF/tree/6.8.0" }, "funding": [ { @@ -5915,7 +5912,7 @@ "type": "custom" } ], - "time": "2024-04-20T17:25:10+00:00" + "time": "2024-12-23T13:34:57+00:00" }, { "name": "theiconic/name-parser", @@ -5967,30 +5964,37 @@ }, { "name": "twig/twig", - "version": "v3.8.0", + "version": "v3.15.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d" + "reference": "2d5b3964cc21d0188633d7ddce732dc8e874db02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", - "reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/2d5b3964cc21d0188633d7ddce732dc8e874db02", + "reference": "2d5b3964cc21d0188633d7ddce732dc8e874db02", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22" + "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", - "symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -6023,7 +6027,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.8.0" + "source": "https://github.com/twigphp/Twig/tree/v3.15.0" }, "funding": [ { @@ -6035,7 +6039,7 @@ "type": "tidelift" } ], - "time": "2023-11-21T18:54:41+00:00" + "time": "2024-11-17T15:59:19+00:00" }, { "name": "voku/anti-xss", @@ -6281,16 +6285,16 @@ }, { "name": "voku/portable-ascii", - "version": "2.0.1", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/voku/portable-ascii.git", - "reference": "b56450eed252f6801410d810c8e1727224ae0743" + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b56450eed252f6801410d810c8e1727224ae0743", - "reference": "b56450eed252f6801410d810c8e1727224ae0743", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { @@ -6315,7 +6319,7 @@ "authors": [ { "name": "Lars Moelleken", - "homepage": "http://www.moelleken.org/" + "homepage": "https://www.moelleken.org/" } ], "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", @@ -6327,7 +6331,7 @@ ], "support": { "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.1" + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { @@ -6351,7 +6355,7 @@ "type": "tidelift" } ], - "time": "2022-03-08T17:03:00+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { "name": "voku/portable-utf8", @@ -6805,30 +6809,30 @@ }, { "name": "yiisoft/yii2", - "version": "2.0.48.1", + "version": "2.0.51", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "de92f154eefe322fc1b1b2a52cce46677441ced4" + "reference": "ea1f112f4dc9a9824e77b788019e2d53325d823c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/de92f154eefe322fc1b1b2a52cce46677441ced4", - "reference": "de92f154eefe322fc1b1b2a52cce46677441ced4", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/ea1f112f4dc9a9824e77b788019e2d53325d823c", + "reference": "ea1f112f4dc9a9824e77b788019e2d53325d823c", "shasum": "" }, "require": { - "bower-asset/inputmask": "~3.2.2 | ~3.3.5", - "bower-asset/jquery": "3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", - "bower-asset/punycode": "1.3.*", + "bower-asset/inputmask": "^5.0.8 ", + "bower-asset/jquery": "3.7.*@stable | 3.6.*@stable | 3.5.*@stable | 3.4.*@stable | 3.3.*@stable | 3.2.*@stable | 3.1.*@stable | 2.2.*@stable | 2.1.*@stable | 1.11.*@stable | 1.12.*@stable", + "bower-asset/punycode": "^2.2", "bower-asset/yii2-pjax": "~2.0.1", "cebe/markdown": "~1.0.0 | ~1.1.0 | ~1.2.0", "ext-ctype": "*", "ext-mbstring": "*", - "ezyang/htmlpurifier": "^4.6", + "ezyang/htmlpurifier": "^4.17", "lib-pcre": "*", "paragonie/random_compat": ">=1", - "php": ">=5.4.0", + "php": ">=7.3.0", "yiisoft/yii2-composer": "~2.0.4" }, "bin": [ @@ -6923,7 +6927,7 @@ "type": "tidelift" } ], - "time": "2023-05-24T19:04:02+00:00" + "time": "2024-07-18T19:50:00+00:00" }, { "name": "yiisoft/yii2-composer", @@ -7003,16 +7007,16 @@ }, { "name": "yiisoft/yii2-debug", - "version": "2.1.22", + "version": "2.1.25", "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-debug.git", - "reference": "c0fa388c56b64edfb92987fdcc37d7a0243170d7" + "reference": "4d011b9bfc83bde71cde43c9f6837f5a74685ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-debug/zipball/c0fa388c56b64edfb92987fdcc37d7a0243170d7", - "reference": "c0fa388c56b64edfb92987fdcc37d7a0243170d7", + "url": "https://api.github.com/repos/yiisoft/yii2-debug/zipball/4d011b9bfc83bde71cde43c9f6837f5a74685ea7", + "reference": "4d011b9bfc83bde71cde43c9f6837f5a74685ea7", "shasum": "" }, "require": { @@ -7028,20 +7032,20 @@ }, "type": "yii2-extension", "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - }, - "composer-exit-on-patch-failure": true, "patches": { - "phpunit/phpunit-mock-objects": { - "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" - }, "phpunit/phpunit": { "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch", "Fix PHP 8.1 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php81.patch" + }, + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" } - } + }, + "branch-alias": { + "dev-master": "2.0.x-dev" + }, + "composer-exit-on-patch-failure": true }, "autoload": { "psr-4": { @@ -7089,7 +7093,7 @@ "type": "tidelift" } ], - "time": "2022-11-18T17:29:27+00:00" + "time": "2023-09-26T15:50:00+00:00" }, { "name": "yiisoft/yii2-queue", @@ -7135,19 +7139,19 @@ }, "type": "yii2-extension", "extra": { - "branch-alias": { - "dev-master": "2.x-dev" - }, - "composer-exit-on-patch-failure": true, "patches": { - "phpunit/phpunit-mock-objects": { - "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" - }, "phpunit/phpunit": { "Fix PHP 7 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php7.patch", "Fix PHP 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_php8.patch" + }, + "phpunit/phpunit-mock-objects": { + "Fix PHP 7 and 8 compatibility": "https://yiisoft.github.io/phpunit-patches/phpunit_mock_objects.patch" } - } + }, + "branch-alias": { + "dev-master": "2.x-dev" + }, + "composer-exit-on-patch-failure": true }, "autoload": { "psr-4": { @@ -7287,25 +7291,25 @@ "packages-dev": [ { "name": "behat/gherkin", - "version": "v4.9.0", + "version": "v4.10.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", + "reference": "cbb83c4c435dd8d05a161f2a5ae322e61b2f4db6", "shasum": "" }, "require": { "php": "~7.2|~8.0" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", + "cucumber/cucumber": "dev-gherkin-24.1.0", "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" + "symfony/yaml": "~3|~4|~5|~6|~7" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -7344,9 +7348,9 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.10.0" }, - "time": "2021-10-12T13:05:09+00:00" + "time": "2024-10-19T14:46:06+00:00" }, { "name": "codeception/codeception", @@ -7926,16 +7930,16 @@ }, { "name": "craftcms/ckeditor", - "version": "3.8.3", + "version": "3.10.0", "source": { "type": "git", "url": "https://github.com/craftcms/ckeditor.git", - "reference": "2dada986452c8a403c3ca652e87fe3a440fe9bf5" + "reference": "852bcfbd5aa982fa23e1209e3f2ae6c8292639a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/ckeditor/zipball/2dada986452c8a403c3ca652e87fe3a440fe9bf5", - "reference": "2dada986452c8a403c3ca652e87fe3a440fe9bf5", + "url": "https://api.github.com/repos/craftcms/ckeditor/zipball/852bcfbd5aa982fa23e1209e3f2ae6c8292639a7", + "reference": "852bcfbd5aa982fa23e1209e3f2ae6c8292639a7", "shasum": "" }, "require": { @@ -7986,7 +7990,7 @@ "rss": "https://github.com/craftcms/ckeditor/commits/master.atom", "source": "https://github.com/craftcms/ckeditor" }, - "time": "2024-03-28T19:52:16+00:00" + "time": "2024-10-19T14:26:49+00:00" }, { "name": "craftcms/ecs", @@ -7994,12 +7998,12 @@ "source": { "type": "git", "url": "https://github.com/craftcms/ecs.git", - "reference": "b4ef13140cd808feed5bfb857b3083d6c44ca2b4" + "reference": "3823f989668e12a85ba681f8c7f3fd8488e23066" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/ecs/zipball/b4ef13140cd808feed5bfb857b3083d6c44ca2b4", - "reference": "b4ef13140cd808feed5bfb857b3083d6c44ca2b4", + "url": "https://api.github.com/repos/craftcms/ecs/zipball/3823f989668e12a85ba681f8c7f3fd8488e23066", + "reference": "3823f989668e12a85ba681f8c7f3fd8488e23066", "shasum": "" }, "require": { @@ -8019,20 +8023,20 @@ "issues": "https://github.com/craftcms/ecs/issues", "source": "https://github.com/craftcms/ecs/tree/main" }, - "time": "2022-06-30T16:27:12+00:00" + "time": "2024-08-07T21:54:45+00:00" }, { "name": "craftcms/html-field", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/craftcms/html-field.git", - "reference": "80c232ef92960748553afd27c87f2c0ef4bd4d93" + "reference": "1737b962a6bc3604263ce2e588b333b49d3fec52" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/html-field/zipball/80c232ef92960748553afd27c87f2c0ef4bd4d93", - "reference": "80c232ef92960748553afd27c87f2c0ef4bd4d93", + "url": "https://api.github.com/repos/craftcms/html-field/zipball/1737b962a6bc3604263ce2e588b333b49d3fec52", + "reference": "1737b962a6bc3604263ce2e588b333b49d3fec52", "shasum": "" }, "require": { @@ -8068,7 +8072,7 @@ "rss": "https://github.com/craftcms/html-field/commits/main.atom", "source": "https://github.com/craftcms/html-field" }, - "time": "2024-03-06T18:13:29+00:00" + "time": "2024-10-15T20:00:51+00:00" }, { "name": "craftcms/phpstan", @@ -8138,16 +8142,16 @@ }, { "name": "craftcms/redactor", - "version": "3.0.4", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/craftcms/redactor.git", - "reference": "3e71faf0f129025d8814207e496870ce6deeef01" + "reference": "b12a0fbd1b0b605e2c64032152c99080b50d62a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/craftcms/redactor/zipball/3e71faf0f129025d8814207e496870ce6deeef01", - "reference": "3e71faf0f129025d8814207e496870ce6deeef01", + "url": "https://api.github.com/repos/craftcms/redactor/zipball/b12a0fbd1b0b605e2c64032152c99080b50d62a4", + "reference": "b12a0fbd1b0b605e2c64032152c99080b50d62a4", "shasum": "" }, "require": { @@ -8197,7 +8201,7 @@ "source": "https://github.com/craftcms/redactor" }, "abandoned": "craftcms/ckeditor", - "time": "2023-03-16T23:00:48+00:00" + "time": "2024-06-12T16:48:11+00:00" }, { "name": "doctrine/instantiator", @@ -8271,16 +8275,16 @@ }, { "name": "fakerphp/faker", - "version": "v1.23.1", + "version": "v1.24.1", "source": { "type": "git", "url": "https://github.com/FakerPHP/Faker.git", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b" + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/bfb4fe148adbf78eff521199619b93a52ae3554b", - "reference": "bfb4fe148adbf78eff521199619b93a52ae3554b", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { @@ -8328,9 +8332,9 @@ ], "support": { "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.23.1" + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "time": "2024-01-02T13:46:09+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { "name": "league/factory-muffin", @@ -8477,16 +8481,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.12.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845", + "reference": "123267b2c49fbf30d78a7b2d333f6be754b94845", "shasum": "" }, "require": { @@ -8494,11 +8498,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "doctrine/common": "<2.13.3 || >=3 <3.2.2" }, "require-dev": { "doctrine/collections": "^1.6.8", "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, "type": "library", @@ -8524,7 +8529,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.12.1" }, "funding": [ { @@ -8532,20 +8537,20 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2024-11-08T17:47:46+00:00" }, { "name": "nikic/php-parser", - "version": "v5.0.2", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -8556,7 +8561,7 @@ }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -8588,22 +8593,22 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "nystudio107/craft-code-editor", - "version": "1.0.19", + "version": "1.0.22", "source": { "type": "git", "url": "https://github.com/nystudio107/craft-code-editor.git", - "reference": "a793406e62cd6c7d8e25ac5e0fb2208b4206815b" + "reference": "170edf71355b659e1db9ede12980b17c20eb3d1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nystudio107/craft-code-editor/zipball/a793406e62cd6c7d8e25ac5e0fb2208b4206815b", - "reference": "a793406e62cd6c7d8e25ac5e0fb2208b4206815b", + "url": "https://api.github.com/repos/nystudio107/craft-code-editor/zipball/170edf71355b659e1db9ede12980b17c20eb3d1f", + "reference": "170edf71355b659e1db9ede12980b17c20eb3d1f", "shasum": "" }, "require": { @@ -8658,7 +8663,7 @@ "type": "github" } ], - "time": "2024-04-15T16:35:48+00:00" + "time": "2024-09-23T17:20:25+00:00" }, { "name": "phar-io/manifest", @@ -8780,16 +8785,16 @@ }, { "name": "phpoption/phpoption", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-option.git", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820" + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/80735db690fe4fc5c76dfa7f9b770634285fa820", - "reference": "80735db690fe4fc5c76dfa7f9b770634285fa820", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/e3fac8b24f56113f7cb96af14958c0dd16330f54", + "reference": "e3fac8b24f56113f7cb96af14958c0dd16330f54", "shasum": "" }, "require": { @@ -8797,13 +8802,13 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.34 || ^9.6.13 || ^10.4.2" + "phpunit/phpunit": "^8.5.39 || ^9.6.20 || ^10.5.28" }, "type": "library", "extra": { "bamarni-bin": { "bin-links": true, - "forward-command": true + "forward-command": false }, "branch-alias": { "dev-master": "1.9-dev" @@ -8839,7 +8844,7 @@ ], "support": { "issues": "https://github.com/schmittjoh/php-option/issues", - "source": "https://github.com/schmittjoh/php-option/tree/1.9.2" + "source": "https://github.com/schmittjoh/php-option/tree/1.9.3" }, "funding": [ { @@ -8851,20 +8856,20 @@ "type": "tidelift" } ], - "time": "2023-11-12T21:59:55+00:00" + "time": "2024-07-20T21:41:07+00:00" }, { "name": "phpstan/phpstan", - "version": "1.11.3", + "version": "1.12.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5" + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e64220a05c1209fc856d58e789c3b7a32c0bb9a5", - "reference": "e64220a05c1209fc856d58e789c3b7a32c0bb9a5", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e0bb5cb78545aae631220735aa706eac633a6be9", + "reference": "e0bb5cb78545aae631220735aa706eac633a6be9", "shasum": "" }, "require": { @@ -8909,39 +8914,39 @@ "type": "github" } ], - "time": "2024-05-31T13:53:37+00:00" + "time": "2025-01-21T14:50:05+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.31", + "version": "9.2.32", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965" + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965", - "reference": "48c34b5d8d983006bd2adc2d0de92963b9155965", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5", + "reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", + "nikic/php-parser": "^4.19.1 || ^5.1.0", "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "phpunit/php-file-iterator": "^3.0.6", + "phpunit/php-text-template": "^2.0.4", + "sebastian/code-unit-reverse-lookup": "^2.0.3", + "sebastian/complexity": "^2.0.3", + "sebastian/environment": "^5.1.5", + "sebastian/lines-of-code": "^1.0.4", + "sebastian/version": "^3.0.2", + "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^9.6" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -8950,7 +8955,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "9.2.x-dev" } }, "autoload": { @@ -8979,7 +8984,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.31" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32" }, "funding": [ { @@ -8987,7 +8992,7 @@ "type": "github" } ], - "time": "2024-03-02T06:37:42+00:00" + "time": "2024-08-22T04:23:01+00:00" }, { "name": "phpunit/php-file-iterator", @@ -9232,45 +9237,45 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.19", + "version": "9.6.22", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8" + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1a54a473501ef4cdeaae4e06891674114d79db8", - "reference": "a1a54a473501ef4cdeaae4e06891674114d79db8", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f80235cb4d3caa59ae09be3adf1ded27521d1a9c", + "reference": "f80235cb4d3caa59ae09be3adf1ded27521d1a9c", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", + "doctrine/instantiator": "^1.5.0 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.1", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", + "phpunit/php-code-coverage": "^9.2.32", + "phpunit/php-file-iterator": "^3.0.6", "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", + "phpunit/php-text-template": "^2.0.4", + "phpunit/php-timer": "^5.0.3", + "sebastian/cli-parser": "^1.0.2", + "sebastian/code-unit": "^1.0.8", "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", + "sebastian/diff": "^4.0.6", + "sebastian/environment": "^5.1.5", + "sebastian/exporter": "^4.0.6", + "sebastian/global-state": "^5.0.7", + "sebastian/object-enumerator": "^4.0.4", + "sebastian/resource-operations": "^3.0.4", + "sebastian/type": "^3.2.1", "sebastian/version": "^3.0.2" }, "suggest": { @@ -9315,7 +9320,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.19" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.22" }, "funding": [ { @@ -9331,25 +9336,25 @@ "type": "tidelift" } ], - "time": "2024-04-05T04:35:58+00:00" + "time": "2024-12-05T13:48:26+00:00" }, { "name": "rector/rector", - "version": "1.1.0", + "version": "1.2.10", "source": { "type": "git", "url": "https://github.com/rectorphp/rector.git", - "reference": "556509e2dcf527369892b7d411379c4a02f31859" + "reference": "40f9cf38c05296bd32f444121336a521a293fa61" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/556509e2dcf527369892b7d411379c4a02f31859", - "reference": "556509e2dcf527369892b7d411379c4a02f31859", + "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", + "reference": "40f9cf38c05296bd32f444121336a521a293fa61", "shasum": "" }, "require": { "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.11" + "phpstan/phpstan": "^1.12.5" }, "conflict": { "rector/rector-doctrine": "*", @@ -9382,7 +9387,7 @@ ], "support": { "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.1.0" + "source": "https://github.com/rectorphp/rector/tree/1.2.10" }, "funding": [ { @@ -9390,7 +9395,7 @@ "type": "github" } ], - "time": "2024-05-18T09:40:27+00:00" + "time": "2024-11-08T13:59:10+00:00" }, { "name": "sebastian/cli-parser", @@ -10422,16 +10427,16 @@ }, { "name": "symfony/browser-kit", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "92c8ba1e5ee12d07120744c90898516132b4e58b" + "reference": "03cce39764429e07fbab9b989a1182a24578341d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/92c8ba1e5ee12d07120744c90898516132b4e58b", - "reference": "92c8ba1e5ee12d07120744c90898516132b4e58b", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/03cce39764429e07fbab9b989a1182a24578341d", + "reference": "03cce39764429e07fbab9b989a1182a24578341d", "shasum": "" }, "require": { @@ -10474,7 +10479,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.4.40" + "source": "https://github.com/symfony/browser-kit/tree/v5.4.45" }, "funding": [ { @@ -10490,20 +10495,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-10-22T13:05:35+00:00" }, { "name": "symfony/css-selector", - "version": "v5.4.40", + "version": "v5.4.45", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "ea43887e9afd2029509662d4f95e8b5ef6fc9bbb" + "reference": "4f7f3c35fba88146b56d0025d20ace3f3901f097" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/ea43887e9afd2029509662d4f95e8b5ef6fc9bbb", - "reference": "ea43887e9afd2029509662d4f95e8b5ef6fc9bbb", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/4f7f3c35fba88146b56d0025d20ace3f3901f097", + "reference": "4f7f3c35fba88146b56d0025d20ace3f3901f097", "shasum": "" }, "require": { @@ -10540,7 +10545,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.4.40" + "source": "https://github.com/symfony/css-selector/tree/v5.4.45" }, "funding": [ { @@ -10556,20 +10561,20 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-09-25T14:11:13+00:00" }, { "name": "symfony/dom-crawler", - "version": "v5.4.40", + "version": "v5.4.48", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "2ad469c3e07fdba677b278d0e266071a51aa0dac" + "reference": "b57df76f4757a9a8dfbb57ba48d7780cc20776c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2ad469c3e07fdba677b278d0e266071a51aa0dac", - "reference": "2ad469c3e07fdba677b278d0e266071a51aa0dac", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/b57df76f4757a9a8dfbb57ba48d7780cc20776c6", + "reference": "b57df76f4757a9a8dfbb57ba48d7780cc20776c6", "shasum": "" }, "require": { @@ -10615,7 +10620,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.4.40" + "source": "https://github.com/symfony/dom-crawler/tree/v5.4.48" }, "funding": [ { @@ -10631,7 +10636,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-11-13T14:36:38+00:00" }, { "name": "symplify/easy-coding-standard", @@ -10828,7 +10833,7 @@ "platform": { "php": "^8.0.2" }, - "platform-dev": [], + "platform-dev": {}, "platform-overrides": { "php": "8.0.2" }, diff --git a/src/Plugin.php b/src/Plugin.php index be450fd4f7..aab7cb9368 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -80,6 +80,7 @@ use craft\commerce\services\TaxZones; use craft\commerce\services\Transactions; use craft\commerce\services\Variants as VariantsService; +use craft\commerce\services\Vat; use craft\commerce\services\Webhooks; use craft\commerce\web\twig\CraftVariableBehavior; use craft\commerce\web\twig\Extension; @@ -195,6 +196,7 @@ public static function config(): array 'transactions' => ['class' => Transactions::class], 'customers' => ['class' => Customers::class], 'variants' => ['class' => VariantsService::class], + 'vat' => ['class' => Vat::class], 'webhooks' => ['class' => Webhooks::class], ], ]; @@ -210,7 +212,7 @@ public static function editions(): array /** * @inheritDoc */ - public string $schemaVersion = '4.7.0.1'; + public string $schemaVersion = '4.8.0.0'; /** * @inheritdoc diff --git a/src/adjusters/Tax.php b/src/adjusters/Tax.php index 43331970cc..1bc86f6fb9 100644 --- a/src/adjusters/Tax.php +++ b/src/adjusters/Tax.php @@ -10,6 +10,7 @@ use Craft; use craft\base\Component; use craft\commerce\base\AdjusterInterface; +use craft\commerce\base\TaxIdValidatorInterface; use craft\commerce\elements\Order; use craft\commerce\helpers\Currency; use craft\commerce\models\OrderAdjustment; @@ -17,6 +18,8 @@ use craft\commerce\models\TaxRate; use craft\commerce\Plugin; use craft\commerce\records\TaxRate as TaxRateRecord; +use craft\commerce\services\Taxes; +use craft\commerce\taxidvalidators\EuVatIdValidator; use craft\elements\Address; use DvK\Vat\Validator; use Exception; @@ -118,17 +121,17 @@ private function _adjustInternal(): array private function _getAdjustments(TaxRate $taxRate): array { $adjustments = []; - $hasValidEuVatId = false; + $hasValidTaxId = false; $zoneMatches = $taxRate->getIsEverywhere() || ($taxRate->getTaxZone() && $this->_matchAddress($taxRate->getTaxZone())); - if ($zoneMatches && $taxRate->isVat) { - $hasValidEuVatId = $this->organizationTaxId(); + if ($zoneMatches && $taxRate->hasTaxIdValidators()) { + $hasValidTaxId = $this->organizationTaxIdIsValidTaxId($taxRate->getSelectedEnabledTaxIdValidators()); } $removeIncluded = (!$zoneMatches && $taxRate->removeIncluded); - $removeDueToVat = ($zoneMatches && $hasValidEuVatId && $taxRate->removeVatIncluded); - if ($removeIncluded || $removeDueToVat) { + $removeDueToVatId = ($zoneMatches && $hasValidTaxId && $taxRate->removeVatIncluded); + if ($removeIncluded || $removeDueToVatId) { // Is this an order level tax rate? if (in_array($taxRate->taxable, TaxRateRecord::ORDER_TAXABALES, false)) { @@ -195,7 +198,7 @@ private function _getAdjustments(TaxRate $taxRate): array return $adjustments; } - if (!$zoneMatches || ($taxRate->isVat && $hasValidEuVatId)) { + if (!$zoneMatches || ($taxRate->hasTaxIdValidators() && $hasValidTaxId)) { return []; } @@ -323,7 +326,7 @@ private function _matchAddress(TaxAddressZone $zone): bool /** * @return bool */ - private function organizationTaxId(): bool + private function organizationTaxIdIsValidTaxId(array $validators): bool { if (!$this->_address) { return false; @@ -336,11 +339,11 @@ private function organizationTaxId(): bool return false; } - $validOrganizationTaxId = Craft::$app->getCache()->exists('commerce:validVatId:' . $this->_address->organizationTaxId); + $validOrganizationTaxId = false;//Craft::$app->getCache()->exists('commerce:validVatId:' . $this->_address->organizationTaxId); // If we do not have a valid VAT ID in cache, see if we can get one from the API if (!$validOrganizationTaxId) { - $validOrganizationTaxId = $this->validateVatNumber($this->_address->organizationTaxId); + $validOrganizationTaxId = $this->validateTaxIdNumber($this->_address->organizationTaxId, $validators); } if ($validOrganizationTaxId) { @@ -355,19 +358,37 @@ private function organizationTaxId(): bool /** * @param string $businessVatId * @return bool + * @deprecated in 4.8.0. Use `validateEuVatNumber()` instead, passing the validators you want to check the ID with. */ protected function validateVatNumber(string $businessVatId): bool + { + $oldValidator = [new EuVatIdValidator()]; + return $this->validateTaxIdNumber($businessVatId, $oldValidator); + } + + /** + * @param string $organizationTaxId + * @param TaxIdValidatorInterface[] $validators + * @return bool + */ + protected function validateTaxIdNumber(string $organizationTaxId, array $validators = []): bool { try { - return $this->_getVatValidator()->validate($businessVatId); + foreach ($validators as $validator) { + if ($validator->validate($organizationTaxId)) { + return true; + } + } } catch (Exception $e) { Craft::error('Communication with VAT API failed: ' . $e->getMessage(), __METHOD__); return false; } + + return false; } - private function _getVatValidator(): Validator + private function _getEuVatValidator(): Validator { if ($this->_vatValidator === null) { $this->_vatValidator = new Validator(); diff --git a/src/base/TaxIdValidatorInterface.php b/src/base/TaxIdValidatorInterface.php new file mode 100644 index 0000000000..ff869987ad --- /dev/null +++ b/src/base/TaxIdValidatorInterface.php @@ -0,0 +1,45 @@ +_getVatValidator()->validate($businessVatId); + $validators = Plugin::getInstance()->getTaxes()->getEngine()->getValidators(); + $valid = false; + foreach ($validators as $validator) { + if ($validator->validateExistence($businessVatId)) { + $valid = true; + break; + } + } + return $valid; } catch (Exception $e) { - Craft::error('Communication with VAT API failed: ' . $e->getMessage(), __METHOD__); + Craft::error('Communication with Tax ID validators failed: ' . $e->getMessage(), __METHOD__); return false; } diff --git a/src/controllers/TaxRatesController.php b/src/controllers/TaxRatesController.php index 7579ed0232..31ccdc2fc0 100644 --- a/src/controllers/TaxRatesController.php +++ b/src/controllers/TaxRatesController.php @@ -156,6 +156,13 @@ public function actionEdit(int $id = null, TaxRate $taxRate = null): Response ); $variables['newTaxCategoryJs'] = $view->clearJsBuffer(false); + $taxIdValidators = Plugin::getInstance()->getTaxes()->getTaxIdValidators(); + foreach ($taxIdValidators as $validator) { + if ($validator::isEnabled()) { + $variables['taxIdValidators'][] = $validator; + } + } + return $this->renderTemplate('commerce/tax/taxrates/_edit', $variables); } @@ -181,12 +188,15 @@ public function actionSave(): void $taxRate->include = (bool)$this->request->getBodyParam('include'); $taxRate->removeIncluded = (bool)$this->request->getBodyParam('removeIncluded'); $taxRate->removeVatIncluded = (bool)$this->request->getBodyParam('removeVatIncluded'); - $taxRate->isVat = (bool)$this->request->getBodyParam('isVat'); $taxRate->taxable = $this->request->getBodyParam('taxable'); $taxRate->taxCategoryId = (int)$this->request->getBodyParam('taxCategoryId') ?: null; $taxRate->taxZoneId = (int)$this->request->getBodyParam('taxZoneId') ?: null; $taxRate->rate = Localization::normalizePercentage($this->request->getBodyParam('rate')); + // data comes in as className => bool, we want just the class names that are true + $validators = collect($this->request->getBodyParam('taxIdValidators'))->filter(fn($enabled) => (bool)$enabled)->keys(); + $taxRate->taxIdValidators = $validators->toArray(); + // Save it if (Plugin::getInstance()->getTaxRates()->saveTaxRate($taxRate)) { $this->setSuccessFlash(Craft::t('commerce', 'Tax rate saved.')); diff --git a/src/events/TaxIdValidatorsEvent.php b/src/events/TaxIdValidatorsEvent.php new file mode 100644 index 0000000000..5f1caf98fe --- /dev/null +++ b/src/events/TaxIdValidatorsEvent.php @@ -0,0 +1,25 @@ + + * @since 4.8.0 + */ +class TaxIdValidatorsEvent extends Event +{ + /** + * @var TaxIdValidatorInterface[] Holds the registered tax ID validators. + */ + public array $validators = []; +} diff --git a/src/migrations/Install.php b/src/migrations/Install.php index 4083ee432e..a6f943819d 100644 --- a/src/migrations/Install.php +++ b/src/migrations/Install.php @@ -741,7 +741,8 @@ public function createTables(): void 'code' => $this->string(), 'rate' => $this->decimal(14, 10)->notNull(), 'include' => $this->boolean()->notNull()->defaultValue(false), - 'isVat' => $this->boolean()->notNull()->defaultValue(false), // TODO rename to isEuVat #COM-45 + 'isVat' => $this->boolean()->notNull()->defaultValue(false), // Remove in Commerce 6 + 'taxIdValidators' => $this->text(), 'removeIncluded' => $this->boolean()->notNull()->defaultValue(false), 'removeVatIncluded' => $this->boolean()->notNull()->defaultValue(false), 'taxable' => $this->enum('taxable', ['purchasable', 'price', 'shipping', 'price_shipping', 'order_total_shipping', 'order_total_price'])->notNull(), diff --git a/src/migrations/m250120_080035_move_to_tax_id_validators.php b/src/migrations/m250120_080035_move_to_tax_id_validators.php new file mode 100644 index 0000000000..55366b03f5 --- /dev/null +++ b/src/migrations/m250120_080035_move_to_tax_id_validators.php @@ -0,0 +1,40 @@ +addColumn('{{%commerce_taxrates}}', 'taxIdValidators', $this->text()->after('isVat')); + + $taxRates = (new \craft\db\Query()) + ->select(['id', 'isVat']) + ->from(['{{%commerce_taxrates}}']) + ->all(); + + foreach ($taxRates as $taxRate) { + $taxIdValidators = $taxRate['isVat'] ? ['craft\commerce\taxidvalidators\EuVatIdValidator'] : []; + $this->update('{{%commerce_taxrates}}', ['taxIdValidators' => json_encode($taxIdValidators)], ['id' => $taxRate['id']]); + } + + return true; + } + + /** + * @inheritdoc + */ + public function safeDown(): bool + { + echo "m250120_080035_move_to_tax_id_validators cannot be reverted.\n"; + return false; + } +} diff --git a/src/models/TaxRate.php b/src/models/TaxRate.php index 126346800c..52510379ac 100644 --- a/src/models/TaxRate.php +++ b/src/models/TaxRate.php @@ -9,6 +9,7 @@ use Craft; use craft\commerce\base\Model; +use craft\commerce\base\TaxIdValidatorInterface; use craft\commerce\Plugin; use craft\commerce\records\TaxRate as TaxRateRecord; use craft\errors\DeprecationException; @@ -63,16 +64,11 @@ class TaxRate extends Model public bool $removeIncluded = false; /** - * @var bool Whether an included VAT tax amount should be removed from VAT-disqualified subject prices + * @var bool Whether an included VAT ID tax amount should be removed from VAT-disqualified subject prices * @since 3.4 */ public bool $removeVatIncluded = false; - /** - * @var bool Whether this tax rate represents VAT - */ - public bool $isVat = false; - /** * @var string The subject to which `$rate` should be applied. Options: * - `price` – line item price @@ -94,6 +90,11 @@ class TaxRate extends Model */ public ?int $taxZoneId = null; + /** + * @var array|null Tax ID Validators + */ + public array $taxIdValidators = []; + /** * @var DateTime|null * @since 3.4 @@ -116,6 +117,16 @@ class TaxRate extends Model */ private ?TaxAddressZone $_taxZone = null; + /** + * @inheritdoc + */ + public function attributes(): array + { + $names = parent::attributes(); + $names[] = 'isVat'; // TODO remove in Commerce 6.x + return $names; + } + /** * @inheritdoc */ @@ -208,6 +219,62 @@ public function getIsEverywhere(): bool return !$this->getTaxZone(); } + /** + * @return bool + * @deprecated in 4.8.0. + */ + public function getIsVat(): bool + { + Craft::$app->getDeprecator()->log(__METHOD__, 'TaxRate::setIsVat() is deprecated.'); + + return $this->hasTaxIdValidators(); + } + + /** + * @return bool + * @deprecated in 4.8.0. + */ + public function setIsVat(): void + { + Craft::$app->getDeprecator()->log(__METHOD__, 'TaxRate::setIsVat() is deprecated.'); + } + + /** + * @return bool + * @since 4.8.0 + */ + public function hasTaxIdValidators(): bool + { + return count($this->taxIdValidators) > 0; + } + + /** + * @param string $className + * @return bool + * @since 4.8.0 + */ + public function hasTaxIdValidator(string $className): bool + { + return in_array($className, $this->taxIdValidators, true); + } + + /** + * @return TaxIdValidatorInterface[] + * @throws InvalidConfigException + */ + public function getSelectedEnabledTaxIdValidators(): array + { + $selectedValidators = $this->taxIdValidators; + $validators = Plugin::getInstance()->getTaxes()->getTaxIdValidators(); + $activeValidators = []; + foreach ($validators as $validator) { + if ($validator::isEnabled() & in_array($validator::class, $selectedValidators)) { + $activeValidators[] = $validator; + } + } + return $activeValidators; + } + /** * @return bool * @throws DeprecationException diff --git a/src/records/TaxRate.php b/src/records/TaxRate.php index 993ce40820..486447e33f 100644 --- a/src/records/TaxRate.php +++ b/src/records/TaxRate.php @@ -27,6 +27,7 @@ * @property int $taxCategoryId * @property TaxZone $taxZone * @property bool $isEverywhere + * @property array $taxIdValidators * @property int $taxZoneId * @author Pixel & Tonic, Inc. * @since 2.0 diff --git a/src/services/TaxRates.php b/src/services/TaxRates.php index 32c7295ae4..d842048dd6 100644 --- a/src/services/TaxRates.php +++ b/src/services/TaxRates.php @@ -15,6 +15,7 @@ use craft\commerce\records\TaxRate as TaxRateRecord; use craft\db\Query; use craft\helpers\ArrayHelper; +use DvK\Vat\Validator; use Throwable; use yii\base\Component; use yii\base\Exception; @@ -129,6 +130,7 @@ public function saveTaxRate(TaxRate $model, bool $runValidation = true): bool $record->taxCategoryId = $model->taxCategoryId; $record->taxZoneId = $model->taxZoneId ?: null; $record->isEverywhere = $model->getIsEverywhere(); + $record->taxIdValidators = $model->taxIdValidators; if (!$record->isEverywhere && $record->taxZoneId && empty($record->getErrors('taxZoneId'))) { $taxZone = Plugin::getInstance()->getTaxZones()->getTaxZoneById($record->taxZoneId); @@ -237,6 +239,19 @@ private function _createTaxRatesQuery(): Query ->orderBy(['include' => SORT_DESC, 'isVat' => SORT_DESC]) ->from([Table::TAXRATES]); + // add taxIdValidators select + if (Craft::$app->getDb()->columnExists(Table::TAXRATES, 'taxIdValidators')) { + $query->addSelect(['taxIdValidators']); + } + return $query; } + + public function getOrganizationTaxIdValidators() + { + return [ + 'vat' => new Vat(), + 'euVat' => new Validator(), + ]; + } } diff --git a/src/services/Taxes.php b/src/services/Taxes.php index 82aa990d8f..b6ff1e6c63 100644 --- a/src/services/Taxes.php +++ b/src/services/Taxes.php @@ -9,8 +9,11 @@ use craft\base\Component; use craft\commerce\base\TaxEngineInterface; +use craft\commerce\base\TaxIdValidatorInterface; use craft\commerce\engines\Tax; use craft\commerce\events\TaxEngineEvent; +use craft\commerce\events\TaxIdValidatorsEvent; +use craft\commerce\taxidvalidators\EuVatIdValidator; use yii\base\InvalidConfigException; /** @@ -21,6 +24,27 @@ */ class Taxes extends Component implements TaxEngineInterface { + /** + * @event TaxIdValidatorsEvent The event that is raised when tax ID validators are registered. + * + * Any validator added must be a TaxIdValidatorInterface. + * + * ```php + * use craft\commerce\events\TaxIdValidatorsEvent; + * use craft\commerce\services\Taxes; + * use yii\base\Event; + * + * Event::on( + * Taxes::class, + * Taxes::EVENT_REGISTER_TAX_ID_VALIDATORS, + * function(TaxIdValidatorsEvent $event) { + * $event->validators[] = new MyTaxIdValidator(); + * } + * ); + * ``` + */ + public const EVENT_REGISTER_TAX_ID_VALIDATORS = 'registerTaxIdValidators'; + /** * @event TaxEngineEvent The event that is triggered when determining the tax engine. * @since 3.1 @@ -47,11 +71,46 @@ class Taxes extends Component implements TaxEngineInterface */ public const EVENT_REGISTER_TAX_ENGINE = 'registerTaxEngine'; + /** + * @var ?TaxEngineInterface $engine The tax engine + */ + private ?TaxEngineInterface $_taxEngine = null; + + /** + * @return TaxIdValidatorInterface[] + * @throws InvalidConfigException + */ + public function getTaxIdValidators(): array + { + $validators = []; + $validators[] = new EuVatIdValidator(); + + $event = new TaxIdValidatorsEvent([ + 'validators' => $validators, + ]); + + if ($this->hasEventHandlers(self::EVENT_REGISTER_TAX_ID_VALIDATORS)) { + $this->trigger(self::EVENT_REGISTER_TAX_ID_VALIDATORS, $event); + } + + foreach ($event->validators as $validator) { + if (!$validator instanceof TaxIdValidatorInterface) { + throw new InvalidConfigException('Tax ID validator must implement TaxIdValidatorInterface'); + } + } + + return $event->validators; + } + /** * Get the current tax engine. */ public function getEngine(): TaxEngineInterface { + if ($this->_taxEngine !== null) { + return $this->_taxEngine; + } + $event = new TaxEngineEvent(['engine' => new Tax()]); // Only allow third party tax engines for PRO edition @@ -64,7 +123,9 @@ public function getEngine(): TaxEngineInterface throw new InvalidConfigException('No tax engine has been registered.'); } - return $event->engine; + $this->_taxEngine = $event->engine; + + return $this->_taxEngine; } /** diff --git a/src/services/Vat.php b/src/services/Vat.php new file mode 100644 index 0000000000..ebdfe8d325 --- /dev/null +++ b/src/services/Vat.php @@ -0,0 +1,77 @@ + + * @since 4.8 + * + * @property-read \DvK\Vat\Validator $vatValidator + */ +class Vat extends Component +{ + /** + * @var string + */ + protected string $cacheKeyPrefix = 'commerce:validVatId:'; + + /** + * @var mixed Allows for the possibility of a custom validator + */ + protected mixed $validator; + + /** + * @param string $vatId + * @return bool + * @deprecated Use (new EuVatValidator)->validateExistence() instead + */ + public function isValidVatId(string $vatId): bool + { + // Do we have a valid VAT ID in our cache? + $validOrganizationTaxId = Craft::$app->getCache()->exists($this->cacheKeyPrefix . $vatId); + + // If we do not have a valid VAT ID in cache, see if we can get one from the API + if (!$validOrganizationTaxId) { + try { + $validOrganizationTaxId = $this->getVatValidator()->validate($vatId); + } catch (Exception $e) { + Craft::error('Communication with VAT API failed: ' . $e->getMessage(), __METHOD__); + + $validOrganizationTaxId = false; + } + } + + if (!$validOrganizationTaxId) { + // Clean up if the API returned false and the item was still in cache + Craft::$app->getCache()->delete($this->cacheKeyPrefix . $vatId); + return false; + } + + Craft::$app->getCache()->set($this->cacheKeyPrefix . $vatId, '1'); + return true; + } + + /** + * @return Validator + */ + protected function getVatValidator(): Validator + { + if (!isset($this->validator)) { + $this->validator = new Validator(); + } + + return $this->validator; + } +} diff --git a/src/taxidvalidators/EuVatIdValidator.php b/src/taxidvalidators/EuVatIdValidator.php new file mode 100644 index 0000000000..747f85b812 --- /dev/null +++ b/src/taxidvalidators/EuVatIdValidator.php @@ -0,0 +1,54 @@ +_vatValidator = new Validator(); + } + + public static function displayName(): string + { + return \Craft::t('commerce', 'EU VAT ID'); + } + + public function validateFormat(string $idNumber): bool + { + return $this->_vatValidator->validateFormat($idNumber); + } + + public function validateExistence(string $idNumber): bool + { + return $this->_vatValidator->validateExistence($idNumber); + } + + /** + * @inheritdoc + */ + public static function isEnabled(): bool + { + return true; + } + + public function validate(string $idNumber): bool + { + try { + return $this->_vatValidator->validate($idNumber); + } catch (\Exception $e) { + \Craft::error('Error validating EU VAT ID: ' . $e->getMessage()); + return false; + } + } +} diff --git a/src/templates/tax/taxrates/_fields.twig b/src/templates/tax/taxrates/_fields.twig index 776395409e..5b3d11cd45 100644 --- a/src/templates/tax/taxrates/_fields.twig +++ b/src/templates/tax/taxrates/_fields.twig @@ -174,13 +174,15 @@ ) }} {% set isVatInput %} + {% for taxIdValidator in taxIdValidators %} {{ forms.checkboxField({ - label: "EU VAT ID"|t('commerce'), - name: 'isVat', - checked: taxRate.isVat, - errors: taxRate.getErrors('isVat'), - toggle: '#isVatContainer' + label: taxIdValidator.displayName(), + name: 'taxIdValidators[' ~ className(taxIdValidator) ~ ']', + checked: taxRate.hasTaxIdValidator(className(taxIdValidator)), + errors: taxRate.getErrors('taxIdValidators'), + toggle: '#isTaxIdContainer' }) }} + {% endfor %} {% endset %} {{ forms.field({ @@ -258,11 +260,11 @@ }) }} -