Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3221008f90 | ||
|
|
b27639c7fa | ||
|
|
ca1ab17595 | ||
|
|
c5685e6d7c | ||
|
|
0a5602fd41 | ||
|
|
f3745f918e | ||
|
|
65c1a7bd12 |
No files matched your search
Generated
+164
-164
@@ -4938,16 +4938,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache",
|
"name": "symfony/cache",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/cache.git",
|
"url": "https://github.com/symfony/cache.git",
|
||||||
"reference": "e7e604eab58639503495a46f5c194cc69a9c80a8"
|
"reference": "e037fd41e9f8ec9ac270e2a4c76a1992d18d808c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/cache/zipball/e7e604eab58639503495a46f5c194cc69a9c80a8",
|
"url": "https://api.github.com/repos/symfony/cache/zipball/e037fd41e9f8ec9ac270e2a4c76a1992d18d808c",
|
||||||
"reference": "e7e604eab58639503495a46f5c194cc69a9c80a8",
|
"reference": "e037fd41e9f8ec9ac270e2a4c76a1992d18d808c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5017,7 +5017,7 @@
|
|||||||
"psr6"
|
"psr6"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/cache/tree/v7.4.20"
|
"source": "https://github.com/symfony/cache/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5037,7 +5037,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T12:35:16+00:00"
|
"time": "2026-09-08T13:28:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/cache-contracts",
|
"name": "symfony/cache-contracts",
|
||||||
@@ -5278,16 +5278,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
"reference": "d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d"
|
"reference": "3a1973458b153f566a8d7499bc06ece323d60063"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/console/zipball/d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d",
|
"url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063",
|
||||||
"reference": "d7bca2fe05241dc7732d0c8b6d551b879f5d0a7d",
|
"reference": "3a1973458b153f566a8d7499bc06ece323d60063",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5352,7 +5352,7 @@
|
|||||||
"terminal"
|
"terminal"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/console/tree/v7.4.20"
|
"source": "https://github.com/symfony/console/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5372,20 +5372,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-18T15:55:00+00:00"
|
"time": "2026-09-13T10:38:41+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/dependency-injection",
|
"name": "symfony/dependency-injection",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/dependency-injection.git",
|
"url": "https://github.com/symfony/dependency-injection.git",
|
||||||
"reference": "a7faee783a87b55f27460fc571f8c639bf9d93a2"
|
"reference": "f318ac9da5aba0be2cf43ecdd562c05a33bc11c0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/a7faee783a87b55f27460fc571f8c639bf9d93a2",
|
"url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f318ac9da5aba0be2cf43ecdd562c05a33bc11c0",
|
||||||
"reference": "a7faee783a87b55f27460fc571f8c639bf9d93a2",
|
"reference": "f318ac9da5aba0be2cf43ecdd562c05a33bc11c0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5436,7 +5436,7 @@
|
|||||||
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
"description": "Allows you to standardize and centralize the way objects are constructed in your application",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/dependency-injection/tree/v7.4.20"
|
"source": "https://github.com/symfony/dependency-injection/tree/v7.4.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5456,7 +5456,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T12:35:16+00:00"
|
"time": "2026-08-21T17:40:08+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
@@ -5531,16 +5531,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/doctrine-bridge",
|
"name": "symfony/doctrine-bridge",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/doctrine-bridge.git",
|
"url": "https://github.com/symfony/doctrine-bridge.git",
|
||||||
"reference": "03d285cfe0f633a856e7acb68cecf5964f8494a7"
|
"reference": "d89437a59232a7e9b294652faea2469f2af66b63"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/03d285cfe0f633a856e7acb68cecf5964f8494a7",
|
"url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/d89437a59232a7e9b294652faea2469f2af66b63",
|
||||||
"reference": "03d285cfe0f633a856e7acb68cecf5964f8494a7",
|
"reference": "d89437a59232a7e9b294652faea2469f2af66b63",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5620,7 +5620,7 @@
|
|||||||
"description": "Provides integration for Doctrine with various Symfony components",
|
"description": "Provides integration for Doctrine with various Symfony components",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/doctrine-bridge/tree/v7.4.20"
|
"source": "https://github.com/symfony/doctrine-bridge/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5640,20 +5640,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T19:09:50+00:00"
|
"time": "2026-09-11T14:46:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/doctrine-messenger",
|
"name": "symfony/doctrine-messenger",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/doctrine-messenger.git",
|
"url": "https://github.com/symfony/doctrine-messenger.git",
|
||||||
"reference": "a34e55dce43e1229bb47555b57ba3c95910988c7"
|
"reference": "edee21e6307105f0aca6b82a1dc201e6789c5ed1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/a34e55dce43e1229bb47555b57ba3c95910988c7",
|
"url": "https://api.github.com/repos/symfony/doctrine-messenger/zipball/edee21e6307105f0aca6b82a1dc201e6789c5ed1",
|
||||||
"reference": "a34e55dce43e1229bb47555b57ba3c95910988c7",
|
"reference": "edee21e6307105f0aca6b82a1dc201e6789c5ed1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5666,7 +5666,7 @@
|
|||||||
"doctrine/persistence": "<1.3"
|
"doctrine/persistence": "<1.3"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/persistence": "^1.3|^2|^3|^4",
|
"doctrine/persistence": "^1.3|^2|^3",
|
||||||
"symfony/property-access": "^6.4|^7.0|^8.0",
|
"symfony/property-access": "^6.4|^7.0|^8.0",
|
||||||
"symfony/serializer": "^6.4|^7.0|^8.0"
|
"symfony/serializer": "^6.4|^7.0|^8.0"
|
||||||
},
|
},
|
||||||
@@ -5696,7 +5696,7 @@
|
|||||||
"description": "Symfony Doctrine Messenger Bridge",
|
"description": "Symfony Doctrine Messenger Bridge",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/doctrine-messenger/tree/v7.4.20"
|
"source": "https://github.com/symfony/doctrine-messenger/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5716,7 +5716,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-24T08:15:08+00:00"
|
"time": "2026-09-13T08:27:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/dom-crawler",
|
"name": "symfony/dom-crawler",
|
||||||
@@ -5870,16 +5870,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/error-handler",
|
"name": "symfony/error-handler",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/error-handler.git",
|
"url": "https://github.com/symfony/error-handler.git",
|
||||||
"reference": "82239fe00f7cdc9f17c4166fff4ebdc22bd26ec0"
|
"reference": "8373921e231e190a88e2ad526951bbaa791576fa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/error-handler/zipball/82239fe00f7cdc9f17c4166fff4ebdc22bd26ec0",
|
"url": "https://api.github.com/repos/symfony/error-handler/zipball/8373921e231e190a88e2ad526951bbaa791576fa",
|
||||||
"reference": "82239fe00f7cdc9f17c4166fff4ebdc22bd26ec0",
|
"reference": "8373921e231e190a88e2ad526951bbaa791576fa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5928,7 +5928,7 @@
|
|||||||
"description": "Provides tools to manage errors and ease debugging PHP code",
|
"description": "Provides tools to manage errors and ease debugging PHP code",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/error-handler/tree/v7.4.20"
|
"source": "https://github.com/symfony/error-handler/tree/v7.4.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5948,7 +5948,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-22T18:56:40+00:00"
|
"time": "2026-08-21T17:40:08+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
@@ -6255,16 +6255,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/finder",
|
"name": "symfony/finder",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/finder.git",
|
"url": "https://github.com/symfony/finder.git",
|
||||||
"reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e"
|
"reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/finder/zipball/2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e",
|
"url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe",
|
||||||
"reference": "2dd31e5208db7a0fda7bfc998ad727b79c2e4d1e",
|
"reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6299,7 +6299,7 @@
|
|||||||
"description": "Finds files and directories via an intuitive fluent interface",
|
"description": "Finds files and directories via an intuitive fluent interface",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/finder/tree/v7.4.20"
|
"source": "https://github.com/symfony/finder/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6319,7 +6319,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-26T06:31:10+00:00"
|
"time": "2026-09-10T19:14:10+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/flex",
|
"name": "symfony/flex",
|
||||||
@@ -6396,16 +6396,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/form",
|
"name": "symfony/form",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/form.git",
|
"url": "https://github.com/symfony/form.git",
|
||||||
"reference": "a36c175f54bbe0320e096a857d67780053a28f44"
|
"reference": "ededc326d2e5f271019b37d54c978e67cbc53ae3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/form/zipball/a36c175f54bbe0320e096a857d67780053a28f44",
|
"url": "https://api.github.com/repos/symfony/form/zipball/ededc326d2e5f271019b37d54c978e67cbc53ae3",
|
||||||
"reference": "a36c175f54bbe0320e096a857d67780053a28f44",
|
"reference": "ededc326d2e5f271019b37d54c978e67cbc53ae3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6475,7 +6475,7 @@
|
|||||||
"description": "Allows to easily create, process and reuse HTML forms",
|
"description": "Allows to easily create, process and reuse HTML forms",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/form/tree/v7.4.20"
|
"source": "https://github.com/symfony/form/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6495,20 +6495,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T12:35:16+00:00"
|
"time": "2026-09-15T06:01:15+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/framework-bundle",
|
"name": "symfony/framework-bundle",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/framework-bundle.git",
|
"url": "https://github.com/symfony/framework-bundle.git",
|
||||||
"reference": "99786f9338c24770ce4de6a95d049675aa75b129"
|
"reference": "666aece282b9c6f4664c5d81149b17ac5a0f4732"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/99786f9338c24770ce4de6a95d049675aa75b129",
|
"url": "https://api.github.com/repos/symfony/framework-bundle/zipball/666aece282b9c6f4664c5d81149b17ac5a0f4732",
|
||||||
"reference": "99786f9338c24770ce4de6a95d049675aa75b129",
|
"reference": "666aece282b9c6f4664c5d81149b17ac5a0f4732",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6562,6 +6562,7 @@
|
|||||||
"symfony/workflow": "<7.4"
|
"symfony/workflow": "<7.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
"doctrine/persistence": "^1.3|^2|^3",
|
||||||
"dragonmantank/cron-expression": "^3.1",
|
"dragonmantank/cron-expression": "^3.1",
|
||||||
"phpdocumentor/reflection-docblock": "^5.2|^6.0",
|
"phpdocumentor/reflection-docblock": "^5.2|^6.0",
|
||||||
"seld/jsonlint": "^1.10",
|
"seld/jsonlint": "^1.10",
|
||||||
@@ -6632,7 +6633,7 @@
|
|||||||
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
|
"description": "Provides a tight integration between Symfony components and the Symfony full-stack framework",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/framework-bundle/tree/v7.4.20"
|
"source": "https://github.com/symfony/framework-bundle/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6652,20 +6653,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T19:09:50+00:00"
|
"time": "2026-09-14T09:18:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/html-sanitizer",
|
"name": "symfony/html-sanitizer",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/html-sanitizer.git",
|
"url": "https://github.com/symfony/html-sanitizer.git",
|
||||||
"reference": "0b7605debddc90c7c9d4e9c1b6eb520a248f26dc"
|
"reference": "d49c351e24d38604466e5a51ab20cbc99f9493c7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/0b7605debddc90c7c9d4e9c1b6eb520a248f26dc",
|
"url": "https://api.github.com/repos/symfony/html-sanitizer/zipball/d49c351e24d38604466e5a51ab20cbc99f9493c7",
|
||||||
"reference": "0b7605debddc90c7c9d4e9c1b6eb520a248f26dc",
|
"reference": "d49c351e24d38604466e5a51ab20cbc99f9493c7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6706,7 +6707,7 @@
|
|||||||
"sanitizer"
|
"sanitizer"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/html-sanitizer/tree/v7.4.20"
|
"source": "https://github.com/symfony/html-sanitizer/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6726,20 +6727,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T05:21:23+00:00"
|
"time": "2026-09-04T10:11:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-client",
|
"name": "symfony/http-client",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-client.git",
|
"url": "https://github.com/symfony/http-client.git",
|
||||||
"reference": "750a420b8dd0af3d6e1c6c1412802abf2cdd2b6c"
|
"reference": "3a523f38dc399337ec45b2ab9628e8c37ceb23c7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-client/zipball/750a420b8dd0af3d6e1c6c1412802abf2cdd2b6c",
|
"url": "https://api.github.com/repos/symfony/http-client/zipball/3a523f38dc399337ec45b2ab9628e8c37ceb23c7",
|
||||||
"reference": "750a420b8dd0af3d6e1c6c1412802abf2cdd2b6c",
|
"reference": "3a523f38dc399337ec45b2ab9628e8c37ceb23c7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6807,7 +6808,7 @@
|
|||||||
"http"
|
"http"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-client/tree/v7.4.20"
|
"source": "https://github.com/symfony/http-client/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6827,7 +6828,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-24T11:02:13+00:00"
|
"time": "2026-09-08T13:28:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-client-contracts",
|
"name": "symfony/http-client-contracts",
|
||||||
@@ -6913,16 +6914,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-foundation",
|
"name": "symfony/http-foundation",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-foundation.git",
|
"url": "https://github.com/symfony/http-foundation.git",
|
||||||
"reference": "9d0761c0da6bd5b801b401666834901a9a1ffc33"
|
"reference": "f7b953be7801974a5a0685e1228bbc3b12b35936"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/9d0761c0da6bd5b801b401666834901a9a1ffc33",
|
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/f7b953be7801974a5a0685e1228bbc3b12b35936",
|
||||||
"reference": "9d0761c0da6bd5b801b401666834901a9a1ffc33",
|
"reference": "f7b953be7801974a5a0685e1228bbc3b12b35936",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -6971,7 +6972,7 @@
|
|||||||
"description": "Defines an object-oriented layer for the HTTP specification",
|
"description": "Defines an object-oriented layer for the HTTP specification",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-foundation/tree/v7.4.20"
|
"source": "https://github.com/symfony/http-foundation/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -6991,20 +6992,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-24T07:12:44+00:00"
|
"time": "2026-09-14T17:41:17+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/http-kernel",
|
"name": "symfony/http-kernel",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/http-kernel.git",
|
"url": "https://github.com/symfony/http-kernel.git",
|
||||||
"reference": "392784b5d7b959b8d872542a2530c50c540a788f"
|
"reference": "683507424cc84ca85ac1551adb609b6b98216667"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/392784b5d7b959b8d872542a2530c50c540a788f",
|
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/683507424cc84ca85ac1551adb609b6b98216667",
|
||||||
"reference": "392784b5d7b959b8d872542a2530c50c540a788f",
|
"reference": "683507424cc84ca85ac1551adb609b6b98216667",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -7090,7 +7091,7 @@
|
|||||||
"description": "Provides a structured process for converting a Request into a Response",
|
"description": "Provides a structured process for converting a Request into a Response",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/http-kernel/tree/v7.4.20"
|
"source": "https://github.com/symfony/http-kernel/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -7110,7 +7111,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T14:05:16+00:00"
|
"time": "2026-09-15T07:09:35+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/intl",
|
"name": "symfony/intl",
|
||||||
@@ -7204,16 +7205,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/lock",
|
"name": "symfony/lock",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/lock.git",
|
"url": "https://github.com/symfony/lock.git",
|
||||||
"reference": "80a1a0f5ca166ddec7c68bc5f1b39cea19f333f0"
|
"reference": "4073e583b1cb048799706d7ba3245328858cfea6"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/lock/zipball/80a1a0f5ca166ddec7c68bc5f1b39cea19f333f0",
|
"url": "https://api.github.com/repos/symfony/lock/zipball/4073e583b1cb048799706d7ba3245328858cfea6",
|
||||||
"reference": "80a1a0f5ca166ddec7c68bc5f1b39cea19f333f0",
|
"reference": "4073e583b1cb048799706d7ba3245328858cfea6",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -7263,7 +7264,7 @@
|
|||||||
"semaphore"
|
"semaphore"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/lock/tree/v7.4.20"
|
"source": "https://github.com/symfony/lock/tree/v7.4.18"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -7283,7 +7284,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-18T15:55:00+00:00"
|
"time": "2026-08-30T20:10:52+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/mailer",
|
"name": "symfony/mailer",
|
||||||
@@ -7371,16 +7372,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/messenger",
|
"name": "symfony/messenger",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/messenger.git",
|
"url": "https://github.com/symfony/messenger.git",
|
||||||
"reference": "4cf94f12c9e899a87e6b6bf800dffcb709585410"
|
"reference": "4070504211a16c4237247f7b2a91143c3a1f4e51"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/messenger/zipball/4cf94f12c9e899a87e6b6bf800dffcb709585410",
|
"url": "https://api.github.com/repos/symfony/messenger/zipball/4070504211a16c4237247f7b2a91143c3a1f4e51",
|
||||||
"reference": "4cf94f12c9e899a87e6b6bf800dffcb709585410",
|
"reference": "4070504211a16c4237247f7b2a91143c3a1f4e51",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -7441,7 +7442,7 @@
|
|||||||
"description": "Helps applications send and receive messages to/from other applications or via message queues",
|
"description": "Helps applications send and receive messages to/from other applications or via message queues",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/messenger/tree/v7.4.20"
|
"source": "https://github.com/symfony/messenger/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -7461,7 +7462,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T06:38:53+00:00"
|
"time": "2026-09-14T15:23:30+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/mime",
|
"name": "symfony/mime",
|
||||||
@@ -8925,16 +8926,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/property-info",
|
"name": "symfony/property-info",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/property-info.git",
|
"url": "https://github.com/symfony/property-info.git",
|
||||||
"reference": "d24c4c810b1285c5d8d77c6fd7b25e714479bd48"
|
"reference": "547d2acbe865d511809b96ab653304fd632abbaa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/property-info/zipball/d24c4c810b1285c5d8d77c6fd7b25e714479bd48",
|
"url": "https://api.github.com/repos/symfony/property-info/zipball/547d2acbe865d511809b96ab653304fd632abbaa",
|
||||||
"reference": "d24c4c810b1285c5d8d77c6fd7b25e714479bd48",
|
"reference": "547d2acbe865d511809b96ab653304fd632abbaa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -8991,7 +8992,7 @@
|
|||||||
"validator"
|
"validator"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/property-info/tree/v7.4.20"
|
"source": "https://github.com/symfony/property-info/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9011,7 +9012,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T09:47:41+00:00"
|
"time": "2026-09-04T10:11:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/psr-http-message-bridge",
|
"name": "symfony/psr-http-message-bridge",
|
||||||
@@ -9103,16 +9104,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/routing",
|
"name": "symfony/routing",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/routing.git",
|
"url": "https://github.com/symfony/routing.git",
|
||||||
"reference": "2bacb72be5eadeaf9874d5accb75bfb33d21c74b"
|
"reference": "ddd558991e98f693ae6bf5063cc1b0362c6bbec3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/routing/zipball/2bacb72be5eadeaf9874d5accb75bfb33d21c74b",
|
"url": "https://api.github.com/repos/symfony/routing/zipball/ddd558991e98f693ae6bf5063cc1b0362c6bbec3",
|
||||||
"reference": "2bacb72be5eadeaf9874d5accb75bfb33d21c74b",
|
"reference": "ddd558991e98f693ae6bf5063cc1b0362c6bbec3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9164,7 +9165,7 @@
|
|||||||
"url"
|
"url"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/routing/tree/v7.4.20"
|
"source": "https://github.com/symfony/routing/tree/v7.4.18"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9184,7 +9185,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T19:09:50+00:00"
|
"time": "2026-08-17T13:12:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/runtime",
|
"name": "symfony/runtime",
|
||||||
@@ -9273,16 +9274,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/security-bundle",
|
"name": "symfony/security-bundle",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/security-bundle.git",
|
"url": "https://github.com/symfony/security-bundle.git",
|
||||||
"reference": "a2a8dc7ab9d5d90d5937963a4ef1b9c8e3e7258c"
|
"reference": "caaa8b92f2657a6b99057f4da36ee89f3baab506"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/security-bundle/zipball/a2a8dc7ab9d5d90d5937963a4ef1b9c8e3e7258c",
|
"url": "https://api.github.com/repos/symfony/security-bundle/zipball/caaa8b92f2657a6b99057f4da36ee89f3baab506",
|
||||||
"reference": "a2a8dc7ab9d5d90d5937963a4ef1b9c8e3e7258c",
|
"reference": "caaa8b92f2657a6b99057f4da36ee89f3baab506",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9323,7 +9324,6 @@
|
|||||||
"symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0",
|
"symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0",
|
||||||
"symfony/http-client": "^6.4|^7.0|^8.0",
|
"symfony/http-client": "^6.4|^7.0|^8.0",
|
||||||
"symfony/ldap": "^6.4|^7.0|^8.0",
|
"symfony/ldap": "^6.4|^7.0|^8.0",
|
||||||
"symfony/lock": "^6.4|^7.0|^8.0",
|
|
||||||
"symfony/process": "^6.4|^7.0|^8.0",
|
"symfony/process": "^6.4|^7.0|^8.0",
|
||||||
"symfony/rate-limiter": "^6.4|^7.0|^8.0",
|
"symfony/rate-limiter": "^6.4|^7.0|^8.0",
|
||||||
"symfony/runtime": "^6.4.13|^7.1.6|^8.0",
|
"symfony/runtime": "^6.4.13|^7.1.6|^8.0",
|
||||||
@@ -9362,7 +9362,7 @@
|
|||||||
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
|
"description": "Provides a tight integration of the Security component into the Symfony full-stack framework",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/security-bundle/tree/v7.4.20"
|
"source": "https://github.com/symfony/security-bundle/tree/v7.4.18"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9382,20 +9382,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-24T11:02:13+00:00"
|
"time": "2026-08-30T17:23:54+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/security-core",
|
"name": "symfony/security-core",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/security-core.git",
|
"url": "https://github.com/symfony/security-core.git",
|
||||||
"reference": "4e5c01656aa79e6d463fde45117041ebccbaf829"
|
"reference": "4fac226354298b8d66f680275b0daef1d3f580d2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/security-core/zipball/4e5c01656aa79e6d463fde45117041ebccbaf829",
|
"url": "https://api.github.com/repos/symfony/security-core/zipball/4fac226354298b8d66f680275b0daef1d3f580d2",
|
||||||
"reference": "4e5c01656aa79e6d463fde45117041ebccbaf829",
|
"reference": "4fac226354298b8d66f680275b0daef1d3f580d2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9454,7 +9454,7 @@
|
|||||||
"description": "Symfony Security Component - Core Library",
|
"description": "Symfony Security Component - Core Library",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/security-core/tree/v7.4.20"
|
"source": "https://github.com/symfony/security-core/tree/v7.4.18"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9474,7 +9474,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T16:00:27+00:00"
|
"time": "2026-08-30T00:47:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/security-csrf",
|
"name": "symfony/security-csrf",
|
||||||
@@ -9552,16 +9552,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/security-http",
|
"name": "symfony/security-http",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/security-http.git",
|
"url": "https://github.com/symfony/security-http.git",
|
||||||
"reference": "5fa566b8bfbb398cded6988f059c3d97506ed692"
|
"reference": "c13182ec07b8a0c5316505a4c9c51948f87d87bb"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/security-http/zipball/5fa566b8bfbb398cded6988f059c3d97506ed692",
|
"url": "https://api.github.com/repos/symfony/security-http/zipball/c13182ec07b8a0c5316505a4c9c51948f87d87bb",
|
||||||
"reference": "5fa566b8bfbb398cded6988f059c3d97506ed692",
|
"reference": "c13182ec07b8a0c5316505a4c9c51948f87d87bb",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9620,7 +9620,7 @@
|
|||||||
"description": "Symfony Security Component - HTTP Integration",
|
"description": "Symfony Security Component - HTTP Integration",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/security-http/tree/v7.4.20"
|
"source": "https://github.com/symfony/security-http/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9640,20 +9640,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T18:33:58+00:00"
|
"time": "2026-09-15T06:01:15+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/serializer",
|
"name": "symfony/serializer",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/serializer.git",
|
"url": "https://github.com/symfony/serializer.git",
|
||||||
"reference": "67c5b0b2ef39863835f43b9c34dcb3af80b892cf"
|
"reference": "d631b82e0db971444651c4e6a076040d20878caa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/serializer/zipball/67c5b0b2ef39863835f43b9c34dcb3af80b892cf",
|
"url": "https://api.github.com/repos/symfony/serializer/zipball/d631b82e0db971444651c4e6a076040d20878caa",
|
||||||
"reference": "67c5b0b2ef39863835f43b9c34dcb3af80b892cf",
|
"reference": "d631b82e0db971444651c4e6a076040d20878caa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -9724,7 +9724,7 @@
|
|||||||
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
|
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/serializer/tree/v7.4.20"
|
"source": "https://github.com/symfony/serializer/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -9744,7 +9744,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T09:39:38+00:00"
|
"time": "2026-09-08T13:28:43+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/service-contracts",
|
"name": "symfony/service-contracts",
|
||||||
@@ -10452,16 +10452,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/type-info",
|
"name": "symfony/type-info",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/type-info.git",
|
"url": "https://github.com/symfony/type-info.git",
|
||||||
"reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2"
|
"reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/type-info/zipball/bbaaf3366400f5c85f66800b955c77b46e8abad2",
|
"url": "https://api.github.com/repos/symfony/type-info/zipball/62d0ad6995630a4f7ea8fd270aadb3e2545c8e31",
|
||||||
"reference": "bbaaf3366400f5c85f66800b955c77b46e8abad2",
|
"reference": "62d0ad6995630a4f7ea8fd270aadb3e2545c8e31",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10511,7 +10511,7 @@
|
|||||||
"type"
|
"type"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/type-info/tree/v7.4.20"
|
"source": "https://github.com/symfony/type-info/tree/v7.4.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10531,20 +10531,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T12:19:37+00:00"
|
"time": "2026-08-21T15:52:48+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/uid",
|
"name": "symfony/uid",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/uid.git",
|
"url": "https://github.com/symfony/uid.git",
|
||||||
"reference": "a96bbdb02a04ff3b5592cee6741b03edc78b56aa"
|
"reference": "69d732355a139c6f8881337d28515aa01f12b8be"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/uid/zipball/a96bbdb02a04ff3b5592cee6741b03edc78b56aa",
|
"url": "https://api.github.com/repos/symfony/uid/zipball/69d732355a139c6f8881337d28515aa01f12b8be",
|
||||||
"reference": "a96bbdb02a04ff3b5592cee6741b03edc78b56aa",
|
"reference": "69d732355a139c6f8881337d28515aa01f12b8be",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10589,7 +10589,7 @@
|
|||||||
"uuid"
|
"uuid"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/uid/tree/v7.4.20"
|
"source": "https://github.com/symfony/uid/tree/v7.4.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10609,7 +10609,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-23T16:18:28+00:00"
|
"time": "2026-08-11T07:38:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/ux-twig-component",
|
"name": "symfony/ux-twig-component",
|
||||||
@@ -10702,16 +10702,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/validator",
|
"name": "symfony/validator",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/validator.git",
|
"url": "https://github.com/symfony/validator.git",
|
||||||
"reference": "75a30e116016554477f662e106fd8e337f0c4dac"
|
"reference": "a6d275d96d107fb2000dae04ab23b2e0c29ceb66"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/validator/zipball/75a30e116016554477f662e106fd8e337f0c4dac",
|
"url": "https://api.github.com/repos/symfony/validator/zipball/a6d275d96d107fb2000dae04ab23b2e0c29ceb66",
|
||||||
"reference": "75a30e116016554477f662e106fd8e337f0c4dac",
|
"reference": "a6d275d96d107fb2000dae04ab23b2e0c29ceb66",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10782,7 +10782,7 @@
|
|||||||
"description": "Provides tools to validate values",
|
"description": "Provides tools to validate values",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/validator/tree/v7.4.20"
|
"source": "https://github.com/symfony/validator/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10802,7 +10802,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T12:35:16+00:00"
|
"time": "2026-09-09T19:50:33+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/var-dumper",
|
"name": "symfony/var-dumper",
|
||||||
@@ -11137,16 +11137,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/yaml.git",
|
"url": "https://github.com/symfony/yaml.git",
|
||||||
"reference": "0222bf4dfaa1e3ceb8fff579635d655f2950d5a7"
|
"reference": "4cef939e55b8a21c5780418de0d98ab00d0736e1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/0222bf4dfaa1e3ceb8fff579635d655f2950d5a7",
|
"url": "https://api.github.com/repos/symfony/yaml/zipball/4cef939e55b8a21c5780418de0d98ab00d0736e1",
|
||||||
"reference": "0222bf4dfaa1e3ceb8fff579635d655f2950d5a7",
|
"reference": "4cef939e55b8a21c5780418de0d98ab00d0736e1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11189,7 +11189,7 @@
|
|||||||
"description": "Loads and dumps YAML files",
|
"description": "Loads and dumps YAML files",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/yaml/tree/v7.4.20"
|
"source": "https://github.com/symfony/yaml/tree/v7.4.18"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11209,7 +11209,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-26T06:31:10+00:00"
|
"time": "2026-08-30T00:47:26+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "twig/extra-bundle",
|
"name": "twig/extra-bundle",
|
||||||
@@ -13055,16 +13055,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "12.5.37",
|
"version": "12.5.36",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "684340c1590e5f3f3b859f5e80619725d88b63ba"
|
"reference": "ddb775588a0ea447d1c9b190c0f29c174ce81d65"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/684340c1590e5f3f3b859f5e80619725d88b63ba",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ddb775588a0ea447d1c9b190c0f29c174ce81d65",
|
||||||
"reference": "684340c1590e5f3f3b859f5e80619725d88b63ba",
|
"reference": "ddb775588a0ea447d1c9b190c0f29c174ce81d65",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -13133,7 +13133,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.37"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.36"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -13141,7 +13141,7 @@
|
|||||||
"type": "other"
|
"type": "other"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-29T12:08:54+00:00"
|
"time": "2026-09-25T08:47:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "react/cache",
|
"name": "react/cache",
|
||||||
@@ -15103,16 +15103,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/web-profiler-bundle",
|
"name": "symfony/web-profiler-bundle",
|
||||||
"version": "v7.4.20",
|
"version": "v7.4.19",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/web-profiler-bundle.git",
|
"url": "https://github.com/symfony/web-profiler-bundle.git",
|
||||||
"reference": "916c2a4edf51270a5173a57d8d3936c02a4543a1"
|
"reference": "f9c5fed39f307b23c47e8a11c431d848390f8a67"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/916c2a4edf51270a5173a57d8d3936c02a4543a1",
|
"url": "https://api.github.com/repos/symfony/web-profiler-bundle/zipball/f9c5fed39f307b23c47e8a11c431d848390f8a67",
|
||||||
"reference": "916c2a4edf51270a5173a57d8d3936c02a4543a1",
|
"reference": "f9c5fed39f307b23c47e8a11c431d848390f8a67",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -15169,7 +15169,7 @@
|
|||||||
"dev"
|
"dev"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.20"
|
"source": "https://github.com/symfony/web-profiler-bundle/tree/v7.4.19"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -15189,7 +15189,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-09-28T19:09:50+00:00"
|
"time": "2026-09-04T10:11:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
|
|||||||
@@ -1,199 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Command;
|
|
||||||
|
|
||||||
use App\Entity\User;
|
|
||||||
use App\Repository\UserRepository;
|
|
||||||
use App\Security\Role;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
use Symfony\Component\Console\Attribute\AsCommand;
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
|
||||||
use Symfony\Component\Console\Input\InputOption;
|
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
|
||||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes local accounts that carry nothing worth keeping.
|
|
||||||
*
|
|
||||||
* Nothing here destroys customer data: BusPro owns the person, the bookings and the roles, and
|
|
||||||
* BpnAuthenticator rebuilds the local row on the next login (`findOneBy(email) ?? new User()`),
|
|
||||||
* re-deriving the roles, the hotel codes and the profile completeness from the CRM as it goes.
|
|
||||||
* An account is a cache of what BusPro says, so dropping one costs its owner nothing.
|
|
||||||
*
|
|
||||||
* One thing is not rebuilt, and it is the reason every removal gets a log line of its own: the
|
|
||||||
* uuid, minted in the constructor and exported as the OAuth2 `sub` claim (see UserinfoController).
|
|
||||||
* A returning account gets a fresh one, so the old subject cannot be tied to the new row by
|
|
||||||
* anything except that log entry. The SSO clients that gate on a role — E&P Team, E&P Hub — never
|
|
||||||
* see these accounts anyway, since the roles they require are exactly the ones excluded below.
|
|
||||||
*
|
|
||||||
* An account is removed when all of the following hold:
|
|
||||||
*
|
|
||||||
* - it holds no effective role beyond ROLE_CUSTOMER, that role being the fallback for an account
|
|
||||||
* with no privileges at all (see Role::withCustomerFallback()). An account left with no roles
|
|
||||||
* at all is the same state, spelled the way it was before the fallback landed;
|
|
||||||
* - it carries no pending nomination, which would otherwise disappear from /admin/user before
|
|
||||||
* anybody had ruled on it;
|
|
||||||
* - it holds no booking edit draft, and nothing in the Groups subsystem names it.
|
|
||||||
*
|
|
||||||
* --inactive-for adds a "has not signed in for N months" condition on top. It is off by default:
|
|
||||||
* a returning customer is recreated on sight, so age says nothing about whether a row may go.
|
|
||||||
*
|
|
||||||
* Deleting requires --force. The removal cannot be undone and the first run clears thousands of
|
|
||||||
* rows, so a bare invocation reports what it would do and stops. The guard is a flag rather than
|
|
||||||
* an $io->confirm() because the scheduler hands the command a StringInput, which reports itself
|
|
||||||
* as interactive — a confirmation would sit waiting on a STDIN nobody is attached to. A flag
|
|
||||||
* behaves the same way whether a person or cron is running it, and it makes the schedule entry
|
|
||||||
* say out loud that the task destroys data.
|
|
||||||
*/
|
|
||||||
#[AsCommand(
|
|
||||||
name: 'app:cleanup:users',
|
|
||||||
description: 'Removes accounts that hold no role, no draft and no group booking reference',
|
|
||||||
)]
|
|
||||||
class CleanupUsersCommand extends Command
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* How many accounts are removed between two flushes. The unit of work holds every scheduled
|
|
||||||
* removal until then, and the first run has thousands of rows to get through, so it is a
|
|
||||||
* memory bound rather than a policy one.
|
|
||||||
*/
|
|
||||||
private const BATCH_SIZE = 500;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
private readonly UserRepository $userRepository,
|
|
||||||
private readonly EntityManagerInterface $entityManager,
|
|
||||||
private readonly LoggerInterface $logger,
|
|
||||||
) {
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function configure(): void
|
|
||||||
{
|
|
||||||
$this
|
|
||||||
->addOption(
|
|
||||||
'inactive-for',
|
|
||||||
null,
|
|
||||||
InputOption::VALUE_REQUIRED,
|
|
||||||
'Only consider accounts without a login for this many months (default: no such limit)',
|
|
||||||
)
|
|
||||||
->addOption(
|
|
||||||
'limit',
|
|
||||||
null,
|
|
||||||
InputOption::VALUE_REQUIRED,
|
|
||||||
'Maximum number of accounts to delete in one run (default: no limit)',
|
|
||||||
)
|
|
||||||
->addOption('dry-run', null, InputOption::VALUE_NONE, 'Show what would be deleted without actually deleting')
|
|
||||||
->addOption('force', null, InputOption::VALUE_NONE, 'Actually delete; without it the command only reports what it would do')
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
|
||||||
{
|
|
||||||
$io = new SymfonyStyle($input, $output);
|
|
||||||
|
|
||||||
$months = null !== $input->getOption('inactive-for') ? (int) $input->getOption('inactive-for') : null;
|
|
||||||
$limit = null !== $input->getOption('limit') ? (int) $input->getOption('limit') : null;
|
|
||||||
$dryRun = (bool) $input->getOption('dry-run');
|
|
||||||
$force = (bool) $input->getOption('force');
|
|
||||||
|
|
||||||
if ((null !== $months && $months < 1) || (null !== $limit && $limit < 1)) {
|
|
||||||
$io->error('--inactive-for and --limit have to be at least 1 when given.');
|
|
||||||
|
|
||||||
return Command::INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
$inactiveSince = null !== $months ? new \DateTimeImmutable(sprintf('-%d months', $months)) : null;
|
|
||||||
|
|
||||||
$candidates = array_values(array_filter(
|
|
||||||
$this->userRepository->findCleanupCandidates($inactiveSince),
|
|
||||||
static fn (User $user): bool => self::isDisposable($user),
|
|
||||||
));
|
|
||||||
|
|
||||||
// After the role filter, never before it: --limit caps the accounts actually deleted, and
|
|
||||||
// the candidate query cannot tell which ones those are.
|
|
||||||
if (null !== $limit) {
|
|
||||||
$candidates = \array_slice($candidates, 0, $limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([] === $candidates) {
|
|
||||||
$io->success('No accounts to remove.');
|
|
||||||
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true === $dryRun || false === $force) {
|
|
||||||
$io->table(
|
|
||||||
['E-Mail', 'Letzter Login', 'Rollen'],
|
|
||||||
array_map(static fn (User $user): array => [
|
|
||||||
$user->getEmail(),
|
|
||||||
$user->getLastLoginAt()?->format('Y-m-d') ?? '-',
|
|
||||||
implode(', ', Role::assignedOnly($user->getRoles())) ?: '-',
|
|
||||||
], $candidates),
|
|
||||||
);
|
|
||||||
|
|
||||||
$io->note(sprintf('Would delete %d account(s).', \count($candidates)));
|
|
||||||
|
|
||||||
if (true === $dryRun) {
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a dry run, just an invocation that forgot to say so. Reported as a failure so a
|
|
||||||
// schedule entry missing --force turns into a mail rather than a task that quietly
|
|
||||||
// does nothing every week.
|
|
||||||
$io->error('Refusing to delete without --force. Pass --dry-run to preview, or --force to delete.');
|
|
||||||
|
|
||||||
return Command::INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($candidates as $index => $user) {
|
|
||||||
// One line per account, before the removal rather than after: once the row is gone
|
|
||||||
// this is the only place left that records which account the uuid belonged to. Worth
|
|
||||||
// a log_entry row per deletion, including the several thousand the first run writes —
|
|
||||||
// they age out with the six-month log retention.
|
|
||||||
$this->logger->info('Deleting account without roles', [
|
|
||||||
'uuid' => $user->getUuid(),
|
|
||||||
'email' => $user->getEmail(),
|
|
||||||
'lastLoginAt' => $user->getLastLoginAt()?->format(\DateTimeInterface::ATOM),
|
|
||||||
'roles' => Role::assignedOnly($user->getRoles()),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->entityManager->remove($user);
|
|
||||||
|
|
||||||
if (0 === ($index + 1) % self::BATCH_SIZE) {
|
|
||||||
$this->entityManager->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->entityManager->flush();
|
|
||||||
|
|
||||||
$count = \count($candidates);
|
|
||||||
|
|
||||||
$this->logger->info('Deleted accounts without roles', [
|
|
||||||
'count' => $count,
|
|
||||||
'inactiveSince' => $inactiveSince?->format('Y-m-d'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$io->success(sprintf('Deleted %d account(s).', $count));
|
|
||||||
|
|
||||||
return Command::SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the account's role set says it may go. ROLE_CUSTOMER is the fallback rather than a
|
|
||||||
* baseline, so "customer only" and "nothing at all" are the same state — the second being the
|
|
||||||
* spelling accounts kept that have not signed in since the fallback landed.
|
|
||||||
*
|
|
||||||
* Pending markers grant nothing, but they are a nomination waiting for an administrator, so an
|
|
||||||
* account carrying one stays until somebody has ruled on it.
|
|
||||||
*/
|
|
||||||
private static function isDisposable(User $user): bool
|
|
||||||
{
|
|
||||||
$roles = $user->getRoles();
|
|
||||||
|
|
||||||
return [] === Role::pendingOnly($roles)
|
|
||||||
&& [] === array_diff(Role::effectiveOnly($roles), [Role::CUSTOMER]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,12 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Repository;
|
namespace App\Repository;
|
||||||
|
|
||||||
use App\Entity\BookingEditDraft;
|
|
||||||
use App\Entity\Groups\Accommodation;
|
|
||||||
use App\Entity\Groups\AccommodationBooking;
|
use App\Entity\Groups\AccommodationBooking;
|
||||||
use App\Entity\Groups\AccommodationPrice;
|
|
||||||
use App\Entity\Groups\AdditionalService;
|
|
||||||
use App\Entity\Groups\BoardService;
|
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Form\Model\Filter\UserFilterDto;
|
use App\Form\Model\Filter\UserFilterDto;
|
||||||
use App\Repository\Filter\AppliesListFiltersTrait;
|
use App\Repository\Filter\AppliesListFiltersTrait;
|
||||||
@@ -127,87 +122,4 @@ class UserRepository extends ServiceEntityRepository
|
|||||||
|
|
||||||
return $users;
|
return $users;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* The entities that point at a User and would silently lose that pointer if the account were
|
|
||||||
* deleted. Every one of these join columns is ON DELETE SET NULL, so the database does not
|
|
||||||
* stand in the way — it just erases who created, last touched or looks after a group booking.
|
|
||||||
* The cleanup therefore has to ask, rather than let the constraint decide.
|
|
||||||
*
|
|
||||||
* @var array<class-string, string[]>
|
|
||||||
*/
|
|
||||||
private const USER_REFERENCES = [
|
|
||||||
Accommodation::class => ['createdBy', 'updatedBy'],
|
|
||||||
AccommodationPrice::class => ['createdBy', 'updatedBy'],
|
|
||||||
AccommodationBooking::class => ['createdBy', 'updatedBy', 'managedBy'],
|
|
||||||
AdditionalService::class => ['createdBy', 'updatedBy'],
|
|
||||||
BoardService::class => ['createdBy', 'updatedBy'],
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accounts the cleanup may consider: holding no booking edit draft and named by nothing in the
|
|
||||||
* Groups subsystem. $inactiveSince narrows that to accounts dormant since then, and is the
|
|
||||||
* caller's choice rather than this method's default — BpnAuthenticator recreates an account on
|
|
||||||
* sight, so how long ago somebody last signed in says nothing about whether the row may go.
|
|
||||||
*
|
|
||||||
* Deliberately unbounded: the caller still has to drop everything whose roles disqualify it,
|
|
||||||
* so a LIMIT here would cap the rows examined rather than the rows acted on, and a caller
|
|
||||||
* asking for ten deletions would get however many of the first ten candidates survived the
|
|
||||||
* role filter. Ordered oldest login first so a caller that does cap the result takes the
|
|
||||||
* least recently seen accounts.
|
|
||||||
*
|
|
||||||
* The roles are deliberately not part of this query. They live in a JSON array column with no
|
|
||||||
* JSON_CONTAINS registered, and the column holds more than one spelling of the same set — rows
|
|
||||||
* written by different Doctrine versions differ in whitespace — so a LIKE or an equality test
|
|
||||||
* on the raw JSON would quietly miss accounts or, far worse, match the wrong ones. The caller
|
|
||||||
* filters the shortlist through Role::effectiveOnly(), which is the only thing that knows what
|
|
||||||
* a role set actually means.
|
|
||||||
*
|
|
||||||
* @return User[]
|
|
||||||
*/
|
|
||||||
public function findCleanupCandidates(?\DateTimeImmutable $inactiveSince = null): array
|
|
||||||
{
|
|
||||||
$qb = $this->createQueryBuilder('u');
|
|
||||||
|
|
||||||
if (null !== $inactiveSince) {
|
|
||||||
$qb
|
|
||||||
->andWhere('u.lastLoginAt IS NOT NULL')
|
|
||||||
->andWhere('u.lastLoginAt < :inactiveSince')
|
|
||||||
->setParameter('inactiveSince', $inactiveSince)
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
$qb->andWhere($qb->expr()->not($qb->expr()->exists(
|
|
||||||
$this->getEntityManager()->createQueryBuilder()
|
|
||||||
->select('draft.id')
|
|
||||||
->from(BookingEditDraft::class, 'draft')
|
|
||||||
->andWhere('draft.user = u')
|
|
||||||
->getDQL()
|
|
||||||
)));
|
|
||||||
|
|
||||||
$index = 0;
|
|
||||||
|
|
||||||
foreach (self::USER_REFERENCES as $class => $fields) {
|
|
||||||
foreach ($fields as $field) {
|
|
||||||
$alias = sprintf('ref%d%s', $index++, $field);
|
|
||||||
|
|
||||||
$qb->andWhere($qb->expr()->not($qb->expr()->exists(
|
|
||||||
$this->getEntityManager()->createQueryBuilder()
|
|
||||||
->select($alias.'.id')
|
|
||||||
->from($class, $alias)
|
|
||||||
->andWhere(sprintf('%s.%s = u', $alias, $field))
|
|
||||||
->getDQL()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var User[] $users */
|
|
||||||
$users = $qb
|
|
||||||
->orderBy('u.lastLoginAt', 'ASC')
|
|
||||||
->getQuery()
|
|
||||||
->getResult()
|
|
||||||
;
|
|
||||||
|
|
||||||
return $users;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Tests\Command;
|
|
||||||
|
|
||||||
use App\Command\CleanupUsersCommand;
|
|
||||||
use App\Entity\User;
|
|
||||||
use App\Repository\UserRepository;
|
|
||||||
use App\Security\Role;
|
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
|
||||||
use PHPUnit\Framework\Attributes\DataProvider;
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
use Symfony\Component\Console\Command\Command;
|
|
||||||
use Symfony\Component\Console\Tester\CommandTester;
|
|
||||||
|
|
||||||
class CleanupUsersCommandTest extends TestCase
|
|
||||||
{
|
|
||||||
public function testDeletesAccountsWithoutAnEffectiveRole(): void
|
|
||||||
{
|
|
||||||
$customer = $this->user('[email protected]', [Role::CUSTOMER]);
|
|
||||||
$roleless = $this->user('[email protected]', []);
|
|
||||||
|
|
||||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
||||||
$removed = [];
|
|
||||||
$entityManager->expects(self::exactly(2))
|
|
||||||
->method('remove')
|
|
||||||
->willReturnCallback(static function (User $user) use (&$removed): void {
|
|
||||||
$removed[] = $user->getEmail();
|
|
||||||
});
|
|
||||||
$entityManager->expects(self::once())->method('flush');
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->command([$customer, $roleless], $entityManager));
|
|
||||||
$tester->execute(['--force' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
self::assertSame(['[email protected]', '[email protected]'], $removed);
|
|
||||||
self::assertStringContainsString('Deleted 2 account(s).', $tester->getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The candidate query cannot read the roles, so everything the role policy protects has to
|
|
||||||
* survive this filter — including an account whose only effective role sits beside the
|
|
||||||
* ROLE_CUSTOMER fallback that older rows still carry.
|
|
||||||
*
|
|
||||||
* @param string[] $roles
|
|
||||||
*/
|
|
||||||
#[DataProvider('protectedRoleSets')]
|
|
||||||
public function testKeepsAccountsHoldingAnythingBeyondTheCustomerFallback(array $roles): void
|
|
||||||
{
|
|
||||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
||||||
$entityManager->expects(self::never())->method('remove');
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->command([$this->user('[email protected]', $roles)], $entityManager));
|
|
||||||
$tester->execute(['--force' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
self::assertStringContainsString('No accounts to remove.', $tester->getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return iterable<string, array{string[]}>
|
|
||||||
*/
|
|
||||||
public static function protectedRoleSets(): iterable
|
|
||||||
{
|
|
||||||
yield 'teamer' => [[Role::TEAMER]];
|
|
||||||
yield 'employee' => [[Role::EMPLOYEE]];
|
|
||||||
yield 'approved administrative role' => [[Role::TEAMER, Role::MANAGER]];
|
|
||||||
yield 'house manager' => [[Role::HOUSE_MANAGER]];
|
|
||||||
yield 'pending nomination awaiting approval' => [[Role::CUSTOMER, Role::pending(Role::ADMIN)]];
|
|
||||||
yield 'stale fallback beside a real role' => [[Role::CUSTOMER, Role::TEAMER]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The whole point of the guard: a bare invocation is a preview, not a deletion. Symfony's
|
|
||||||
* VALUE_REQUIRED only says an option needs a value when it is given, so nothing in the
|
|
||||||
* definition ever forces the caller to state an intent — this does.
|
|
||||||
*/
|
|
||||||
public function testRefusesToDeleteWithoutForce(): void
|
|
||||||
{
|
|
||||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
||||||
$entityManager->expects(self::never())->method('remove');
|
|
||||||
$entityManager->expects(self::never())->method('flush');
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->command([$this->user('[email protected]', [Role::CUSTOMER])], $entityManager));
|
|
||||||
$tester->execute([]);
|
|
||||||
|
|
||||||
self::assertSame(Command::INVALID, $tester->getStatusCode());
|
|
||||||
self::assertStringContainsString('Would delete 1 account(s).', $tester->getDisplay());
|
|
||||||
self::assertStringContainsString('Refusing to delete without --force', $tester->getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDryRunDeletesNothing(): void
|
|
||||||
{
|
|
||||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
||||||
$entityManager->expects(self::never())->method('remove');
|
|
||||||
$entityManager->expects(self::never())->method('flush');
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->command([$this->user('[email protected]', [Role::CUSTOMER])], $entityManager));
|
|
||||||
$tester->execute(['--dry-run' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
self::assertStringContainsString('Would delete 1 account(s).', $tester->getDisplay());
|
|
||||||
self::assertStringNotContainsString('Refusing to delete', $tester->getDisplay());
|
|
||||||
self::assertStringContainsString('[email protected]', $tester->getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* --limit caps the deletions, not the candidates examined, so a run that finds one deletable
|
|
||||||
* account among protected ones still deletes it rather than spending the budget on rows it
|
|
||||||
* then discards.
|
|
||||||
*/
|
|
||||||
public function testLimitCountsDeletionsRatherThanCandidates(): void
|
|
||||||
{
|
|
||||||
$users = [
|
|
||||||
$this->user('[email protected]', [Role::TEAMER]),
|
|
||||||
$this->user('[email protected]', [Role::CUSTOMER]),
|
|
||||||
];
|
|
||||||
|
|
||||||
$entityManager = $this->createMock(EntityManagerInterface::class);
|
|
||||||
$entityManager->expects(self::once())
|
|
||||||
->method('remove')
|
|
||||||
->with(self::callback(static fn (User $user): bool => '[email protected]' === $user->getEmail()));
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->command($users, $entityManager));
|
|
||||||
$tester->execute(['--limit' => 1, '--force' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
self::assertStringContainsString('Deleted 1 account(s).', $tester->getDisplay());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInactivityWindowIsOffUnlessAskedFor(): void
|
|
||||||
{
|
|
||||||
$repository = $this->createMock(UserRepository::class);
|
|
||||||
$repository->expects(self::once())
|
|
||||||
->method('findCleanupCandidates')
|
|
||||||
->with(null)
|
|
||||||
->willReturn([]);
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->commandWith($repository, $this->createStub(EntityManagerInterface::class)));
|
|
||||||
$tester->execute(['--force' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testInactivityWindowIsPassedOnAsADate(): void
|
|
||||||
{
|
|
||||||
$repository = $this->createMock(UserRepository::class);
|
|
||||||
$repository->expects(self::once())
|
|
||||||
->method('findCleanupCandidates')
|
|
||||||
->with(self::callback(static function (?\DateTimeImmutable $since): bool {
|
|
||||||
$expected = new \DateTimeImmutable('-6 months');
|
|
||||||
|
|
||||||
return null !== $since && $since->format('Y-m-d') === $expected->format('Y-m-d');
|
|
||||||
}))
|
|
||||||
->willReturn([]);
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->commandWith($repository, $this->createStub(EntityManagerInterface::class)));
|
|
||||||
$tester->execute(['--inactive-for' => 6, '--force' => true]);
|
|
||||||
|
|
||||||
self::assertSame(Command::SUCCESS, $tester->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRejectsANonPositiveWindow(): void
|
|
||||||
{
|
|
||||||
$repository = $this->createMock(UserRepository::class);
|
|
||||||
$repository->expects(self::never())->method('findCleanupCandidates');
|
|
||||||
|
|
||||||
$tester = new CommandTester($this->commandWith($repository, $this->createStub(EntityManagerInterface::class)));
|
|
||||||
$tester->execute(['--inactive-for' => 0]);
|
|
||||||
|
|
||||||
self::assertSame(Command::INVALID, $tester->getStatusCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string[] $roles
|
|
||||||
*/
|
|
||||||
private function user(string $email, array $roles): User
|
|
||||||
{
|
|
||||||
return (new User($email))
|
|
||||||
->setRoles($roles)
|
|
||||||
->setLastLoginAt(new \DateTimeImmutable('-2 years'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param User[] $candidates
|
|
||||||
*/
|
|
||||||
private function command(array $candidates, EntityManagerInterface $entityManager): CleanupUsersCommand
|
|
||||||
{
|
|
||||||
$repository = $this->createStub(UserRepository::class);
|
|
||||||
$repository->method('findCleanupCandidates')->willReturn($candidates);
|
|
||||||
|
|
||||||
return $this->commandWith($repository, $entityManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function commandWith(UserRepository $repository, EntityManagerInterface $entityManager): CleanupUsersCommand
|
|
||||||
{
|
|
||||||
return new CleanupUsersCommand($repository, $entityManager, $this->createStub(LoggerInterface::class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user