From 72640e9b58eb60a6362033cbe471ade9b7e81a9b Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:24:22 +0800 Subject: [PATCH 01/77] chore: Add symplify/config-transformer --- .idea/app-sf.iml | 2 ++ .idea/php.xml | 1 + composer.json | 1 + composer.lock | 82 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/.idea/app-sf.iml b/.idea/app-sf.iml index 82e1e39..9cb6c41 100644 --- a/.idea/app-sf.iml +++ b/.idea/app-sf.iml @@ -5,6 +5,7 @@ + @@ -199,6 +200,7 @@ + diff --git a/.idea/php.xml b/.idea/php.xml index aa2a1b7..b6046de 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -219,6 +219,7 @@ + diff --git a/composer.json b/composer.json index af12525..2ef7a93 100644 --- a/composer.json +++ b/composer.json @@ -146,6 +146,7 @@ "symfony/phpunit-bridge": "7.3.*", "symfony/stopwatch": "7.3.*", "symfony/web-profiler-bundle": "7.3.*", + "symplify/config-transformer": "^12.3", "vincentlanglet/twig-cs-fixer": "dev-main" } } diff --git a/composer.lock b/composer.lock index f65026a..3b6b795 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": "f459ad30f7f54194e3f29cb05fec14fb", + "content-hash": "07dda2568d49823e168caac24aced4cd", "packages": [ { "name": "composer/semver", @@ -904,12 +904,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "9e3ae34de52dd65590c4277d5cdde8f39f12418b" + "reference": "a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/9e3ae34de52dd65590c4277d5cdde8f39f12418b", - "reference": "9e3ae34de52dd65590c4277d5cdde8f39f12418b", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7", + "reference": "a9e64e5ea80184e14a66c262e5bcf3c2cb4a94d7", "shasum": "" }, "require": { @@ -922,8 +922,7 @@ "phpbench/phpbench": "^1.2", "phpstan/phpstan": "^1.9.4", "phpstan/phpstan-phpunit": "^1.3", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.4" + "phpunit/phpunit": "^10.5" }, "default-branch": true, "type": "library", @@ -967,7 +966,7 @@ "type": "tidelift" } ], - "time": "2024-11-25T19:21:52+00:00" + "time": "2024-12-02T21:34:17+00:00" }, { "name": "doctrine/lexer", @@ -1343,12 +1342,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/sql-formatter.git", - "reference": "b4068e8d1a5168769ce65410bab76a8362e04da0" + "reference": "579b67954ca6817a4d5a0a785c654fb7bc849b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/b4068e8d1a5168769ce65410bab76a8362e04da0", - "reference": "b4068e8d1a5168769ce65410bab76a8362e04da0", + "url": "https://api.github.com/repos/doctrine/sql-formatter/zipball/579b67954ca6817a4d5a0a785c654fb7bc849b2e", + "reference": "579b67954ca6817a4d5a0a785c654fb7bc849b2e", "shasum": "" }, "require": { @@ -1358,8 +1357,7 @@ "doctrine/coding-standard": "^12", "ergebnis/phpunit-slow-test-detector": "^2.14", "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "vimeo/psalm": "^5.24" + "phpunit/phpunit": "^10.5" }, "default-branch": true, "bin": [ @@ -1392,7 +1390,7 @@ "issues": "https://github.com/doctrine/sql-formatter/issues", "source": "https://github.com/doctrine/sql-formatter/tree/1.5.x" }, - "time": "2024-11-25T11:48:05+00:00" + "time": "2024-12-02T22:10:47+00:00" }, { "name": "easycorp/easyadmin-bundle", @@ -1400,12 +1398,12 @@ "source": { "type": "git", "url": "https://github.com/EasyCorp/EasyAdminBundle.git", - "reference": "2307da58ccfd0569f65943b9a01d49a4f3a71d7d" + "reference": "2b49b51ccd128ac3983ef1c585d74d60ee5c6efa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/2307da58ccfd0569f65943b9a01d49a4f3a71d7d", - "reference": "2307da58ccfd0569f65943b9a01d49a4f3a71d7d", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/2b49b51ccd128ac3983ef1c585d74d60ee5c6efa", + "reference": "2b49b51ccd128ac3983ef1c585d74d60ee5c6efa", "shasum": "" }, "require": { @@ -1489,7 +1487,7 @@ "type": "github" } ], - "time": "2024-11-29T18:27:19+00:00" + "time": "2024-12-02T19:53:15+00:00" }, { "name": "egulias/email-validator", @@ -10442,12 +10440,12 @@ "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "f8d27d5b81b23d9b679ca2ccac09261c461a15f4" + "reference": "90933b3f846e3711946b23da510e6a8147a06789" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/f8d27d5b81b23d9b679ca2ccac09261c461a15f4", - "reference": "f8d27d5b81b23d9b679ca2ccac09261c461a15f4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/90933b3f846e3711946b23da510e6a8147a06789", + "reference": "90933b3f846e3711946b23da510e6a8147a06789", "shasum": "" }, "require": { @@ -10493,7 +10491,7 @@ "type": "github" } ], - "time": "2024-12-02T07:51:15+00:00" + "time": "2024-12-02T20:56:16+00:00" }, { "name": "phpstan/phpstan-doctrine", @@ -13151,6 +13149,48 @@ ], "time": "2024-11-19T10:12:55+00:00" }, + { + "name": "symplify/config-transformer", + "version": "12.3.4", + "source": { + "type": "git", + "url": "https://github.com/symplify/config-transformer.git", + "reference": "9fb4f5acf7ec4261ba426bee9eb7aeed174eb600" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symplify/config-transformer/zipball/9fb4f5acf7ec4261ba426bee9eb7aeed174eb600", + "reference": "9fb4f5acf7ec4261ba426bee9eb7aeed174eb600", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "bin": [ + "bin/config-transformer" + ], + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Prefixed version of Symfony YAML/XML to PHP/YAML config converter", + "support": { + "issues": "https://github.com/symplify/config-transformer/issues", + "source": "https://github.com/symplify/config-transformer/tree/12.3.4" + }, + "funding": [ + { + "url": "https://www.paypal.me/rectorphp", + "type": "custom" + }, + { + "url": "https://github.com/tomasvotruba", + "type": "github" + } + ], + "time": "2024-01-15T21:58:26+00:00" + }, { "name": "theseer/tokenizer", "version": "1.2.3", From 7cc7453bf4fe122b908c77e26a6820d417cb8178 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:26:30 +0800 Subject: [PATCH 02/77] refactor(config): Migrate routes.yaml to PHP --- config/routes.php | 12 ++++++++++++ config/routes.yaml | 5 ----- 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 config/routes.php delete mode 100644 config/routes.yaml diff --git a/config/routes.php b/config/routes.php new file mode 100644 index 0000000..d3a5ab6 --- /dev/null +++ b/config/routes.php @@ -0,0 +1,12 @@ +import([ + 'path' => '../src/Controller/', + 'namespace' => 'App\Controller', + ], 'attribute'); +}; diff --git a/config/routes.yaml b/config/routes.yaml deleted file mode 100644 index 2d0ef99..0000000 --- a/config/routes.yaml +++ /dev/null @@ -1,5 +0,0 @@ -controllers: - resource: - path: ../src/Controller/ - namespace: App\Controller - type: attribute From 967dd5c7aff2b6f04f1a04e186656756381fedde Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:28:58 +0800 Subject: [PATCH 03/77] refactor(config): Migrate services.yaml to PHP --- config/services.php | 41 +++++++++++++++++++++++++++++++++++++++++ config/services.yaml | 41 ----------------------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 config/services.php delete mode 100644 config/services.yaml diff --git a/config/services.php b/config/services.php new file mode 100644 index 0000000..cf9fd95 --- /dev/null +++ b/config/services.php @@ -0,0 +1,41 @@ +parameters() + ->set('app.sqlrunner_url', env('SQLRUNNER_URL')) + ->set('app.redis_uri', env('REDIS_URI')) + ->set('app.openai_api_key', env('OPENAI_API_KEY')) + ->set('app.features.hint', true) + ->set('app.features.editable-profile', true) + ->set('app.features.comment', true); + + $services = $containerConfigurator->services(); + + $services->defaults() + ->autowire() + ->autoconfigure(); + + $services->load('App\\', __DIR__.'/../src/') + ->exclude([ + __DIR__.'/../src/DependencyInjection/', + __DIR__.'/../src/Entity/', + __DIR__.'/../src/Kernel.php', + __DIR__.'/../src/Service/Processes/', + __DIR__.'/../src/Service/Types/', + __DIR__.'/../src/Twig/Components/Challenge/EventConstant.php', + ]); + + $services->set(PromptService::class) + ->arg('$apiKey', '%app.openai_api_key%'); + + $services->set(SqlRunnerService::class) + ->arg('$baseUrl', '%app.sqlrunner_url%'); +}; diff --git a/config/services.yaml b/config/services.yaml deleted file mode 100644 index 0641d8b..0000000 --- a/config/services.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# This file is the entry point to configure your own services. -# Files in the packages/ subdirectory configure your dependencies. - -# Put parameters here that don't need to change on each machine where the app is deployed -# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration -parameters: - app.sqlrunner_url: "%env(SQLRUNNER_URL)%" - app.redis_uri: "%env(REDIS_URI)%" - app.openai_api_key: "%env(OPENAI_API_KEY)%" - - app.features.hint: true - app.features.editable-profile: true - app.features.comment: true - -services: - # default configuration for services in *this* file - _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. - - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name - App\: - resource: "../src/" - exclude: - - "../src/DependencyInjection/" - - "../src/Entity/" - - "../src/Kernel.php" - - "../src/Service/Processes/" - - "../src/Service/Types/" - - "../src/Twig/Components/Challenge/EventConstant.php" - - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones - App\Service\PromptService: - arguments: - $apiKey: "%app.openai_api_key%" - - App\Service\SqlRunnerService: - arguments: - $baseUrl: "%app.sqlrunner_url%" From d0782cc37c8464ebb7277dea6dbeadfbd9b4117c Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:40:53 +0800 Subject: [PATCH 04/77] feat: Include var/cache in PHPStan and IDE configuration https://symfony.com/doc/7.3/configuration.html#config-config-builder --- .idea/app-sf.iml | 1 + phpstan.dist.neon | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.idea/app-sf.iml b/.idea/app-sf.iml index 9cb6c41..0c0130b 100644 --- a/.idea/app-sf.iml +++ b/.idea/app-sf.iml @@ -7,6 +7,7 @@ + diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 2469fa7..a2deb25 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -6,5 +6,7 @@ parameters: - public/ - src/ - tests/ + scanDirectories: + - var/cache symfony: consoleApplicationLoader: tests/console-application.php From 52a99af9028c9cdca428bec06e282b599a9bc297 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:41:22 +0800 Subject: [PATCH 05/77] refactor(config): Migrate sensiolabs_typescript.yaml to PHP --- config/packages/sensiolabs_typescript.php | 9 +++++++++ config/packages/sensiolabs_typescript.yaml | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 config/packages/sensiolabs_typescript.php delete mode 100644 config/packages/sensiolabs_typescript.yaml diff --git a/config/packages/sensiolabs_typescript.php b/config/packages/sensiolabs_typescript.php new file mode 100644 index 0000000..f8ee609 --- /dev/null +++ b/config/packages/sensiolabs_typescript.php @@ -0,0 +1,9 @@ +swcBinary('node_modules/.bin/swc'); +}; diff --git a/config/packages/sensiolabs_typescript.yaml b/config/packages/sensiolabs_typescript.yaml deleted file mode 100644 index 604fcbc..0000000 --- a/config/packages/sensiolabs_typescript.yaml +++ /dev/null @@ -1,2 +0,0 @@ -sensiolabs_typescript: - swc_binary: "node_modules/.bin/swc" From 654e07952488b37907d309be95dcb348e0bce90c Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:43:53 +0800 Subject: [PATCH 06/77] refactor(config): Migrate translation.yaml to PHP --- config/packages/translation.php | 16 ++++++++++++++++ config/packages/translation.yaml | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 config/packages/translation.php delete mode 100644 config/packages/translation.yaml diff --git a/config/packages/translation.php b/config/packages/translation.php new file mode 100644 index 0000000..d9ec9b0 --- /dev/null +++ b/config/packages/translation.php @@ -0,0 +1,16 @@ +defaultLocale('zh_TW') + ->enabledLocales(['zh_TW']); + + $config + ->translator() + ->defaultPath('%kernel.project_dir%/translations') + ->fallbacks(['zh_TW']); +}; diff --git a/config/packages/translation.yaml b/config/packages/translation.yaml deleted file mode 100644 index 6fdb6e9..0000000 --- a/config/packages/translation.yaml +++ /dev/null @@ -1,8 +0,0 @@ -framework: - default_locale: zh_TW - enabled_locales: ["zh_TW"] - translator: - default_path: "%kernel.project_dir%/translations" - fallbacks: - - zh_TW - providers: From 5010b221019a23b98ec9b2a45f627a2b2a65b94a Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:50:01 +0800 Subject: [PATCH 07/77] refactor(config): Migrate routing.yaml to PHP --- config/packages/routing.php | 18 ++++++++++++++++++ config/packages/routing.yaml | 10 ---------- 2 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 config/packages/routing.php delete mode 100644 config/packages/routing.yaml diff --git a/config/packages/routing.php b/config/packages/routing.php new file mode 100644 index 0000000..71c1387 --- /dev/null +++ b/config/packages/routing.php @@ -0,0 +1,18 @@ +router(); + assert($routerConfig instanceof RouterConfig); // workaround for PHPStan support + + $routerConfig->strictRequirements(true); + + if ('prod' === $containerConfigurator->env()) { + $routerConfig->strictRequirements(null); + } +}; diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml deleted file mode 100644 index 5969ced..0000000 --- a/config/packages/routing.yaml +++ /dev/null @@ -1,10 +0,0 @@ -framework: - router: -# Configure how to generate URLs in non-HTTP contexts, such as CLI commands. -# See https://symfony.com/doc/current/routing.html#generating-urls-in-commands -#default_uri: http://localhost - -when@prod: - framework: - router: - strict_requirements: null From fedbc7058965f91225e17c756961ffadeed7abbf Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:53:24 +0800 Subject: [PATCH 08/77] refactor(config): Remove lock.yaml The default value has already matched our expectation. --- config/packages/lock.yaml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 config/packages/lock.yaml diff --git a/config/packages/lock.yaml b/config/packages/lock.yaml deleted file mode 100644 index 6c7aa58..0000000 --- a/config/packages/lock.yaml +++ /dev/null @@ -1,2 +0,0 @@ -framework: - lock: "%env(LOCK_DSN)%" From 503e7dfea3d0aab66464de6586fc325dc731bd9a Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 09:57:46 +0800 Subject: [PATCH 09/77] refactor(config): Migrate asset_mapper.yaml to PHP --- config/packages/asset_mapper.php | 15 +++++++++++++++ config/packages/asset_mapper.yaml | 13 ------------- config/packages/sensiolabs_typescript.php | 4 ++++ 3 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 config/packages/asset_mapper.php delete mode 100644 config/packages/asset_mapper.yaml diff --git a/config/packages/asset_mapper.php b/config/packages/asset_mapper.php new file mode 100644 index 0000000..04de67d --- /dev/null +++ b/config/packages/asset_mapper.php @@ -0,0 +1,15 @@ +assetMapper() + ->path('assets/', '') + ->path('vendor/twbs/bootstrap/', '') + ->excludedPatterns([ + '*/assets/styles/_*.scss', + '*/assets/styles/**/_*.scss', + ]); +}; diff --git a/config/packages/asset_mapper.yaml b/config/packages/asset_mapper.yaml deleted file mode 100644 index 03dc576..0000000 --- a/config/packages/asset_mapper.yaml +++ /dev/null @@ -1,13 +0,0 @@ -framework: - asset_mapper: - # The paths to make available to the asset mapper. - paths: - - assets/ - - vendor/twbs/bootstrap/ - excluded_patterns: - - "*/assets/styles/_*.scss" - - "*/assets/styles/**/_*.scss" -sensiolabs_typescript: - source_dir: - - "%kernel.project_dir%/assets/app" - - "%kernel.project_dir%/assets/controllers" diff --git a/config/packages/sensiolabs_typescript.php b/config/packages/sensiolabs_typescript.php index f8ee609..20830b9 100644 --- a/config/packages/sensiolabs_typescript.php +++ b/config/packages/sensiolabs_typescript.php @@ -6,4 +6,8 @@ return static function (SensiolabsTypescriptConfig $config): void { $config->swcBinary('node_modules/.bin/swc'); + $config->sourceDir([ + '%kernel.project_dir%/assets/app', + '%kernel.project_dir%/assets/controllers', + ]); }; From 56d43b49d2f288d6adc2a2cbbcfa0c5574eb2ad3 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:02:52 +0800 Subject: [PATCH 10/77] fix(phpstan): Scan dependency-injection explicitly Otherwise, PHPStan won't find the function: > Function Symfony\Component\DependencyInjection\Loader\Configurator\param not found. --- phpstan.dist.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index a2deb25..358bff4 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -8,5 +8,6 @@ parameters: - tests/ scanDirectories: - var/cache + - vendor/symfony/dependency-injection symfony: consoleApplicationLoader: tests/console-application.php From 8370d605e83216111ecaabfc421dc04e9bd08833 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:04:05 +0800 Subject: [PATCH 11/77] refactor(config): Migrate cache.yaml to PHP --- config/packages/cache.php | 14 ++++++++++++++ config/packages/cache.yaml | 23 ----------------------- 2 files changed, 14 insertions(+), 23 deletions(-) create mode 100644 config/packages/cache.php delete mode 100644 config/packages/cache.yaml diff --git a/config/packages/cache.php b/config/packages/cache.php new file mode 100644 index 0000000..e2a2091 --- /dev/null +++ b/config/packages/cache.php @@ -0,0 +1,14 @@ +cache() + ->prefixSeed('database_playground/app') + ->app('cache.adapter.redis_tag_aware') + ->defaultRedisProvider(param('app.redis_uri')); +}; diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml deleted file mode 100644 index 5236446..0000000 --- a/config/packages/cache.yaml +++ /dev/null @@ -1,23 +0,0 @@ -framework: - cache: - prefix_seed: "database_playground/app" - - app: cache.adapter.redis_tag_aware - default_redis_provider: "%app.redis_uri%" - # Unique name of your app: used to compute stable namespaces for cache keys. - #prefix_seed: your_vendor_name/app_name - - # The "app" cache stores to the filesystem by default. - # The data in this cache should persist between deploys. - # Other options include: - - # Redis - #app: cache.adapter.redis - #default_redis_provider: redis://localhost - - # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) - #app: cache.adapter.apcu - - # Namespaced pools use the above "app" backend by default - #pools: -#my.dedicated.cache: null From 83a58c16397780e0db11cbd4143914fe464a5fd7 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:05:19 +0800 Subject: [PATCH 12/77] refactor(config): Migrate csrf.yaml to PHP --- config/packages/csrf.php | 16 ++++++++++++++++ config/packages/csrf.yaml | 11 ----------- 2 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 config/packages/csrf.php delete mode 100644 config/packages/csrf.yaml diff --git a/config/packages/csrf.php b/config/packages/csrf.php new file mode 100644 index 0000000..352483f --- /dev/null +++ b/config/packages/csrf.php @@ -0,0 +1,16 @@ +form() + ->csrfProtection() + ->tokenId('submit'); + + $frameworkConfig + ->csrfProtection() + ->statelessTokenIds(['submit', 'authenticate', 'logout']); +}; diff --git a/config/packages/csrf.yaml b/config/packages/csrf.yaml deleted file mode 100644 index dd07de8..0000000 --- a/config/packages/csrf.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# Enable stateless CSRF protection for forms and logins/logouts -framework: - form: - csrf_protection: - token_id: submit - - csrf_protection: - stateless_token_ids: - - submit - - authenticate - - logout From 29994e47d522407009b0807e347160adb26395d7 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:06:36 +0800 Subject: [PATCH 13/77] refactor(config): Migrate debug.yaml to PHP --- config/packages/debug.php | 16 ++++++++++++++++ config/packages/debug.yaml | 5 ----- 2 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 config/packages/debug.php delete mode 100644 config/packages/debug.yaml diff --git a/config/packages/debug.php b/config/packages/debug.php new file mode 100644 index 0000000..457cce8 --- /dev/null +++ b/config/packages/debug.php @@ -0,0 +1,16 @@ +env()) { + // Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. + // See the "server:dump" command to start a new server. + $debugConfig->dumpDestination('tcp://'.env('VAR_DUMPER_SERVER')); + } +}; diff --git a/config/packages/debug.yaml b/config/packages/debug.yaml deleted file mode 100644 index ce519f3..0000000 --- a/config/packages/debug.yaml +++ /dev/null @@ -1,5 +0,0 @@ -when@dev: - debug: - # Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser. - # See the "server:dump" command to start a new server. - dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%" From 2cf33baf6ad663639086f103a5f4ea80f01c06c0 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:08:10 +0800 Subject: [PATCH 14/77] refactor(config): Migrate doctrine_migrations.yaml to PHP --- config/packages/doctrine_migrations.php | 15 +++++++++++++++ config/packages/doctrine_migrations.yaml | 6 ------ 2 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 config/packages/doctrine_migrations.php delete mode 100644 config/packages/doctrine_migrations.yaml diff --git a/config/packages/doctrine_migrations.php b/config/packages/doctrine_migrations.php new file mode 100644 index 0000000..1927c51 --- /dev/null +++ b/config/packages/doctrine_migrations.php @@ -0,0 +1,15 @@ +enableProfiler(false); + + $doctrineMigrationsConfig + // namespace is arbitrary but should be different from App\Migrations + // as migrations classes should NOT be autoloaded + ->migrationsPath('DoctrineMigrations', '%kernel.project_dir%/migrations'); +}; diff --git a/config/packages/doctrine_migrations.yaml b/config/packages/doctrine_migrations.yaml deleted file mode 100644 index 9300c9b..0000000 --- a/config/packages/doctrine_migrations.yaml +++ /dev/null @@ -1,6 +0,0 @@ -doctrine_migrations: - migrations_paths: - # namespace is arbitrary but should be different from App\Migrations - # as migrations classes should NOT be autoloaded - "DoctrineMigrations": "%kernel.project_dir%/migrations" - enable_profiler: false From cdd86fd0aee5b3ca5ca92109f77851c0eb250db3 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:09:22 +0800 Subject: [PATCH 15/77] refactor(config): Migrate mailer.yaml to PHP --- config/packages/mailer.php | 12 ++++++++++++ config/packages/mailer.yaml | 3 --- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 config/packages/mailer.php delete mode 100644 config/packages/mailer.yaml diff --git a/config/packages/mailer.php b/config/packages/mailer.php new file mode 100644 index 0000000..e9df49e --- /dev/null +++ b/config/packages/mailer.php @@ -0,0 +1,12 @@ +mailer() + ->dsn(env('MAILER_DSN')); +}; diff --git a/config/packages/mailer.yaml b/config/packages/mailer.yaml deleted file mode 100644 index 813f6e1..0000000 --- a/config/packages/mailer.yaml +++ /dev/null @@ -1,3 +0,0 @@ -framework: - mailer: - dsn: "%env(MAILER_DSN)%" From 6531102b85e4ea8eefd73e729b5b847122c5a4e6 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:10:42 +0800 Subject: [PATCH 16/77] refactor(config): Migrate routes to PHP --- config/routes/easyadmin.php | 9 +++++++++ config/routes/easyadmin.yaml | 3 --- config/routes/framework.php | 12 ++++++++++++ config/routes/framework.yaml | 4 ---- config/routes/security.php | 9 +++++++++ config/routes/security.yaml | 3 --- config/routes/ux_live_component.php | 10 ++++++++++ config/routes/ux_live_component.yaml | 5 ----- config/routes/web_profiler.php | 14 ++++++++++++++ config/routes/web_profiler.yaml | 8 -------- 10 files changed, 54 insertions(+), 23 deletions(-) create mode 100644 config/routes/easyadmin.php delete mode 100644 config/routes/easyadmin.yaml create mode 100644 config/routes/framework.php delete mode 100644 config/routes/framework.yaml create mode 100644 config/routes/security.php delete mode 100644 config/routes/security.yaml create mode 100644 config/routes/ux_live_component.php delete mode 100644 config/routes/ux_live_component.yaml create mode 100644 config/routes/web_profiler.php delete mode 100644 config/routes/web_profiler.yaml diff --git a/config/routes/easyadmin.php b/config/routes/easyadmin.php new file mode 100644 index 0000000..ddb8599 --- /dev/null +++ b/config/routes/easyadmin.php @@ -0,0 +1,9 @@ +import('.', 'easyadmin.routes'); +}; diff --git a/config/routes/easyadmin.yaml b/config/routes/easyadmin.yaml deleted file mode 100644 index f409de2..0000000 --- a/config/routes/easyadmin.yaml +++ /dev/null @@ -1,3 +0,0 @@ -easyadmin: - resource: . - type: easyadmin.routes diff --git a/config/routes/framework.php b/config/routes/framework.php new file mode 100644 index 0000000..455101b --- /dev/null +++ b/config/routes/framework.php @@ -0,0 +1,12 @@ +env()) { + $routingConfigurator->import('@FrameworkBundle/Resources/config/routing/errors.xml') + ->prefix('/_error'); + } +}; diff --git a/config/routes/framework.yaml b/config/routes/framework.yaml deleted file mode 100644 index cce01c1..0000000 --- a/config/routes/framework.yaml +++ /dev/null @@ -1,4 +0,0 @@ -when@dev: - _errors: - resource: "@FrameworkBundle/Resources/config/routing/errors.xml" - prefix: /_error diff --git a/config/routes/security.php b/config/routes/security.php new file mode 100644 index 0000000..cd2c44f --- /dev/null +++ b/config/routes/security.php @@ -0,0 +1,9 @@ +import('security.route_loader.logout', 'service'); +}; diff --git a/config/routes/security.yaml b/config/routes/security.yaml deleted file mode 100644 index 3cb5ef0..0000000 --- a/config/routes/security.yaml +++ /dev/null @@ -1,3 +0,0 @@ -_security_logout: - resource: security.route_loader.logout - type: service diff --git a/config/routes/ux_live_component.php b/config/routes/ux_live_component.php new file mode 100644 index 0000000..e7cb167 --- /dev/null +++ b/config/routes/ux_live_component.php @@ -0,0 +1,10 @@ +import('@LiveComponentBundle/config/routes.php') + ->prefix('/_components'); +}; diff --git a/config/routes/ux_live_component.yaml b/config/routes/ux_live_component.yaml deleted file mode 100644 index b062f61..0000000 --- a/config/routes/ux_live_component.yaml +++ /dev/null @@ -1,5 +0,0 @@ -live_component: - resource: "@LiveComponentBundle/config/routes.php" - prefix: "/_components" - # adjust prefix to add localization to your components - #prefix: '/{_locale}/_components' diff --git a/config/routes/web_profiler.php b/config/routes/web_profiler.php new file mode 100644 index 0000000..63bbf5f --- /dev/null +++ b/config/routes/web_profiler.php @@ -0,0 +1,14 @@ +env()) { + $routingConfigurator->import('@WebProfilerBundle/Resources/config/routing/wdt.xml') + ->prefix('/_wdt'); + $routingConfigurator->import('@WebProfilerBundle/Resources/config/routing/profiler.xml') + ->prefix('/_profiler'); + } +}; diff --git a/config/routes/web_profiler.yaml b/config/routes/web_profiler.yaml deleted file mode 100644 index e004603..0000000 --- a/config/routes/web_profiler.yaml +++ /dev/null @@ -1,8 +0,0 @@ -when@dev: - web_profiler_wdt: - resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" - prefix: /_wdt - - web_profiler_profiler: - resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" - prefix: /_profiler From 3d9dca98a8f6f62c45c197a68c3dd8aded0cb560 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:19:37 +0800 Subject: [PATCH 17/77] refactor(config): Migrate notifier.yaml to PHP --- config/packages/notifier.php | 15 +++++++++++++++ config/packages/notifier.yaml | 12 ------------ 2 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 config/packages/notifier.php delete mode 100644 config/packages/notifier.yaml diff --git a/config/packages/notifier.php b/config/packages/notifier.php new file mode 100644 index 0000000..e321b1f --- /dev/null +++ b/config/packages/notifier.php @@ -0,0 +1,15 @@ +notifier(); + + $notifierConfig->chatterTransport('linenotify', env('LINE_NOTIFY_DSN')); + $notifierConfig->adminRecipient()->email('dbplay@pan93.com'); +}; diff --git a/config/packages/notifier.yaml b/config/packages/notifier.yaml deleted file mode 100644 index 8d4eff7..0000000 --- a/config/packages/notifier.yaml +++ /dev/null @@ -1,12 +0,0 @@ -framework: - notifier: - chatter_transports: - linenotify: "%env(LINE_NOTIFY_DSN)%" - texter_transports: - channel_policy: - urgent: ["chat/linenotify"] - high: ["chat/linenotify"] - medium: ["chat/linenotify"] - low: ["chat/linenotify"] - admin_recipients: - - { email: dbplay@pan93.com } From a7bb8dc44b0b0a44c0c150ba1e595ea5b63dee54 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:21:04 +0800 Subject: [PATCH 18/77] refactor(config): Migrate twig_component.yaml to PHP --- config/packages/twig_component.php | 11 +++++++++++ config/packages/twig_component.yaml | 5 ----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 config/packages/twig_component.php delete mode 100644 config/packages/twig_component.yaml diff --git a/config/packages/twig_component.php b/config/packages/twig_component.php new file mode 100644 index 0000000..42605b9 --- /dev/null +++ b/config/packages/twig_component.php @@ -0,0 +1,11 @@ +anonymousTemplateDirectory('components/') + ->defaults('App\Twig\Components\\', 'components/'); +}; diff --git a/config/packages/twig_component.yaml b/config/packages/twig_component.yaml deleted file mode 100644 index 7b4b5eb..0000000 --- a/config/packages/twig_component.yaml +++ /dev/null @@ -1,5 +0,0 @@ -twig_component: - anonymous_template_directory: "components/" - defaults: - # Namespace & directory for components - App\Twig\Components\: "components/" From 0d6cc921eba4e4972ca01e1b70d805f0ef8b2fc4 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:22:53 +0800 Subject: [PATCH 19/77] refactor(config): Migrate validator.yaml to PHP --- .idea/app-sf.iml | 1 + config/packages/validator.php | 11 +++++++++++ config/packages/validator.yaml | 11 ----------- 3 files changed, 12 insertions(+), 11 deletions(-) create mode 100644 config/packages/validator.php delete mode 100644 config/packages/validator.yaml diff --git a/.idea/app-sf.iml b/.idea/app-sf.iml index 0c0130b..ec1057e 100644 --- a/.idea/app-sf.iml +++ b/.idea/app-sf.iml @@ -6,6 +6,7 @@ + diff --git a/config/packages/validator.php b/config/packages/validator.php new file mode 100644 index 0000000..c4f1adc --- /dev/null +++ b/config/packages/validator.php @@ -0,0 +1,11 @@ +validation()->notCompromisedPassword(); + $notCompromisedPassword->enabled('test' !== $containerConfigurator->env()); +}; diff --git a/config/packages/validator.yaml b/config/packages/validator.yaml deleted file mode 100644 index 7b5d7d5..0000000 --- a/config/packages/validator.yaml +++ /dev/null @@ -1,11 +0,0 @@ -framework: - validation: -# Enables validator auto-mapping support. -# For instance, basic validation constraints will be inferred from Doctrine's metadata. -#auto_mapping: -# App\Entity\: [] - -when@test: - framework: - validation: - not_compromised_password: false From e18e121eca63a55d460a5e70ca804c3e8097af4d Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:25:38 +0800 Subject: [PATCH 20/77] refactor(config): Migrate web_profiler.yaml to PHP --- config/packages/web_profiler.php | 32 +++++++++++++++++++++++++++++++ config/packages/web_profiler.yaml | 17 ---------------- 2 files changed, 32 insertions(+), 17 deletions(-) create mode 100644 config/packages/web_profiler.php delete mode 100644 config/packages/web_profiler.yaml diff --git a/config/packages/web_profiler.php b/config/packages/web_profiler.php new file mode 100644 index 0000000..d46eba3 --- /dev/null +++ b/config/packages/web_profiler.php @@ -0,0 +1,32 @@ +profiler(); + assert($frameworkProfilerConfig instanceof ProfilerConfig); + + switch ($containerConfigurator->env()) { + case 'dev': + $webProfilerConfig->toolbar(true); + $webProfilerConfig->interceptRedirects(false); + $frameworkProfilerConfig + ->onlyExceptions(false) + ->collectSerializerData(true); + break; + case 'test': + $webProfilerConfig->toolbar(false); + $webProfilerConfig->interceptRedirects(false); + $frameworkProfilerConfig->collect(false); + break; + } +}; diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml deleted file mode 100644 index a529921..0000000 --- a/config/packages/web_profiler.yaml +++ /dev/null @@ -1,17 +0,0 @@ -when@dev: - web_profiler: - toolbar: true - intercept_redirects: false - - framework: - profiler: - only_exceptions: false - collect_serializer_data: true - -when@test: - web_profiler: - toolbar: false - intercept_redirects: false - - framework: - profiler: { collect: false } From 55651cb4e5c43b7e48197fa12de19d72581b68f0 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:33:57 +0800 Subject: [PATCH 21/77] refactor(phpstan): Add container configuration --- phpstan.dist.neon | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/phpstan.dist.neon b/phpstan.dist.neon index 358bff4..f0afb3f 100644 --- a/phpstan.dist.neon +++ b/phpstan.dist.neon @@ -7,7 +7,9 @@ parameters: - src/ - tests/ scanDirectories: - - var/cache - - vendor/symfony/dependency-injection + - var/cache/dev/Symfony/Config + scanFiles: + - vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php symfony: consoleApplicationLoader: tests/console-application.php + containerXmlPath: var/cache/dev/App_KernelDevDebugContainer.xml From d2b3ff537e2eb2bb3200728002faa8ffb55d0173 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:35:51 +0800 Subject: [PATCH 22/77] refactor: Simplify getParameter determination --- src/Controller/CommentsController.php | 7 +------ src/Controller/ProfileController.php | 5 +---- src/Twig/Components/Challenge/Comments/CommentForm.php | 2 -- src/Twig/Components/Challenge/Instruction/Modal.php | 2 -- 4 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/Controller/CommentsController.php b/src/Controller/CommentsController.php index 8cee2a1..b7ddd9a 100644 --- a/src/Controller/CommentsController.php +++ b/src/Controller/CommentsController.php @@ -99,11 +99,6 @@ public function likes( private function isCommentFeatureEnabled(): bool { - $comment = $this->getParameter('app.features.comment'); - if (!\is_bool($comment)) { - throw new \RuntimeException('The "app.features.comment" parameter must be a boolean.'); - } - - return $comment; + return $this->getParameter('app.features.comment'); } } diff --git a/src/Controller/ProfileController.php b/src/Controller/ProfileController.php index ffc4b97..a24c34f 100644 --- a/src/Controller/ProfileController.php +++ b/src/Controller/ProfileController.php @@ -21,10 +21,7 @@ class ProfileController extends AbstractController { public function isProfileEditable(): bool { - $isProfileEditable = $this->getParameter('app.features.editable-profile'); - \assert(\is_bool($isProfileEditable)); - - return $isProfileEditable; + return $this->getParameter('app.features.editable-profile'); } #[Route('/profile', name: 'app_profile')] diff --git a/src/Twig/Components/Challenge/Comments/CommentForm.php b/src/Twig/Components/Challenge/Comments/CommentForm.php index 10cd494..3e98188 100644 --- a/src/Twig/Components/Challenge/Comments/CommentForm.php +++ b/src/Twig/Components/Challenge/Comments/CommentForm.php @@ -57,8 +57,6 @@ protected function instantiateForm(): FormInterface public function save(EntityManagerInterface $entityManager, ParameterBagInterface $parameterBag): void { $appFeatureComment = $parameterBag->get('app.features.comment'); - \assert(\is_bool($appFeatureComment)); - if (!$appFeatureComment) { throw new BadRequestHttpException('Comment feature is disabled.'); } diff --git a/src/Twig/Components/Challenge/Instruction/Modal.php b/src/Twig/Components/Challenge/Instruction/Modal.php index 04677b2..10d2cb6 100644 --- a/src/Twig/Components/Challenge/Instruction/Modal.php +++ b/src/Twig/Components/Challenge/Instruction/Modal.php @@ -67,8 +67,6 @@ public function instruct( ParameterBagInterface $parameterBag, ): void { $appFeatureHint = $parameterBag->get('app.features.hint'); - \assert(\is_bool($appFeatureHint)); - if (!$appFeatureHint) { throw new BadRequestHttpException('Hint feature is disabled.'); } From d854b4524679167088c381571b5d212bebfa4af8 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:36:44 +0800 Subject: [PATCH 23/77] refactor(config): Migrate framework.yaml to PHP --- config/packages/framework.php | 34 ++++++++++++++++++++++++++++++++++ config/packages/framework.yaml | 27 --------------------------- 2 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 config/packages/framework.php delete mode 100644 config/packages/framework.yaml diff --git a/config/packages/framework.php b/config/packages/framework.php new file mode 100644 index 0000000..0f2b85e --- /dev/null +++ b/config/packages/framework.php @@ -0,0 +1,34 @@ +session(); + assert($sessionConfig instanceof SessionConfig); + + $frameworkConfig->secret(env('APP_SECRET')); + + // Note that the session will be started ONLY if you read or write from it. + $sessionConfig->handlerId(env('REDIS_URI')); + + // proxy configuration for Zeabur + $frameworkConfig->trustedProxies('private_ranges'); + $frameworkConfig->trustedHeaders([ + 'x-forwarded-for', + 'x-forwarded-host', + 'x-forwarded-proto', + 'x-forwarded-port', + 'x-forwarded-prefix', + ]); + + if ('test' === $containerConfigurator->env()) { + $frameworkConfig->test(true); + $sessionConfig->storageFactoryId('session.storage.factory.mock_file'); + } +}; diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml deleted file mode 100644 index 2f9aceb..0000000 --- a/config/packages/framework.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# see https://symfony.com/doc/current/reference/configuration/framework.html -framework: - secret: "%env(APP_SECRET)%" - #csrf_protection: true - - # Note that the session will be started ONLY if you read or write from it. - session: - handler_id: "%env(REDIS_URI)%" - - #esi: true - #fragments: true - - # proxy configuration for Zeabur - trusted_proxies: "private_ranges" - trusted_headers: [ - "x-forwarded-for", - "x-forwarded-host", - "x-forwarded-proto", - "x-forwarded-port", - "x-forwarded-prefix", - ] - -when@test: - framework: - test: true - session: - storage_factory_id: session.storage.factory.mock_file From 96aec55096bfeb8e3be6ad01b029466f7303f3a3 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:38:49 +0800 Subject: [PATCH 24/77] refactor(config): Migrate http_discovery.yaml to PHP --- config/packages/http_discovery.php | 22 ++++++++++++++++++++++ config/packages/http_discovery.yaml | 10 ---------- 2 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 config/packages/http_discovery.php delete mode 100644 config/packages/http_discovery.yaml diff --git a/config/packages/http_discovery.php b/config/packages/http_discovery.php new file mode 100644 index 0000000..303701a --- /dev/null +++ b/config/packages/http_discovery.php @@ -0,0 +1,22 @@ +services() + ->alias(RequestFactoryInterface::class, Psr17Factory::class) + ->alias(ResponseFactoryInterface::class, Psr17Factory::class) + ->alias(ServerRequestFactoryInterface::class, Psr17Factory::class) + ->alias(StreamFactoryInterface::class, Psr17Factory::class) + ->alias(UploadedFileFactoryInterface::class, Psr17Factory::class) + ->alias(UriFactoryInterface::class, Psr17Factory::class); +}; diff --git a/config/packages/http_discovery.yaml b/config/packages/http_discovery.yaml deleted file mode 100644 index 3e7ef4b..0000000 --- a/config/packages/http_discovery.yaml +++ /dev/null @@ -1,10 +0,0 @@ -services: - Psr\Http\Message\RequestFactoryInterface: "@http_discovery.psr17_factory" - Psr\Http\Message\ResponseFactoryInterface: "@http_discovery.psr17_factory" - Psr\Http\Message\ServerRequestFactoryInterface: "@http_discovery.psr17_factory" - Psr\Http\Message\StreamFactoryInterface: "@http_discovery.psr17_factory" - Psr\Http\Message\UploadedFileFactoryInterface: "@http_discovery.psr17_factory" - Psr\Http\Message\UriFactoryInterface: "@http_discovery.psr17_factory" - - http_discovery.psr17_factory: - class: Http\Discovery\Psr17Factory From 6697a725740a13894023777e311454b6625d92de Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 10:46:35 +0800 Subject: [PATCH 25/77] fix(frankenphp): Dependency installation --- frankenphp/docker-entrypoint.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frankenphp/docker-entrypoint.sh b/frankenphp/docker-entrypoint.sh index 5af3f41..e8023a0 100644 --- a/frankenphp/docker-entrypoint.sh +++ b/frankenphp/docker-entrypoint.sh @@ -7,8 +7,8 @@ if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then fi if [ -z "$(ls -A 'node_modules/' 2>/dev/null)" ]; then - corepnpm prepare && pnpm install --prod --prefer-frozen-lockfile - fi + corepack prepare && pnpm install --prod --prefer-frozen-lockfile + fi if grep -q ^DATABASE_URL= .env; then echo "Waiting for database to be ready..." From 0b6f5d3038d13b75cf62daf46d1ee7138b6284f6 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 18:48:20 +0800 Subject: [PATCH 26/77] refactor(config): Migrate twig.yaml to PHP --- config/packages/twig.php | 19 +++++++++++++++++++ config/packages/twig.yaml | 13 ------------- 2 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 config/packages/twig.php delete mode 100644 config/packages/twig.yaml diff --git a/config/packages/twig.php b/config/packages/twig.php new file mode 100644 index 0000000..597a455 --- /dev/null +++ b/config/packages/twig.php @@ -0,0 +1,19 @@ +fileNamePattern('*.twig') + ->formThemes(['bootstrap_5_layout.html.twig']) + ->global('umami_domain', env('UMAMI_DOMAIN')) + ->global('umami_website_id', env('UMAMI_WEBSITE_ID')) + ->strictVariables(true) + ->global('appfeatures.hint', param('app.features.hint')) + ->global('appfeatures.comment', param('app.features.comment')); +}; diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml deleted file mode 100644 index 4af147b..0000000 --- a/config/packages/twig.yaml +++ /dev/null @@ -1,13 +0,0 @@ -twig: - file_name_pattern: "*.twig" - form_themes: ["bootstrap_5_layout.html.twig"] - globals: - umami_domain: "%env(UMAMI_DOMAIN)%" - umami_website_id: "%env(UMAMI_WEBSITE_ID)%" - appfeatures: - hint: "%app.features.hint%" - comment: "%app.features.comment%" - -when@test: - twig: - strict_variables: true From 8e720d49cd485b7a349f167ac54cc2989f5422b8 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 18:48:55 +0800 Subject: [PATCH 27/77] chore: Upgrade dependencies --- composer.lock | 26 +++++++++++++------------- devenv.lock | 4 ++-- pnpm-lock.yaml | 8 ++++---- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/composer.lock b/composer.lock index 3b6b795..419ec2a 100644 --- a/composer.lock +++ b/composer.lock @@ -1891,12 +1891,12 @@ "source": { "type": "git", "url": "https://github.com/meilisearch/meilisearch-symfony.git", - "reference": "d46b2da1865ef228aaca67eeac9e6851022dbeaf" + "reference": "345bb2b18ec0a962a8a12d0ae0e8088c2ebf0641" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/meilisearch/meilisearch-symfony/zipball/d46b2da1865ef228aaca67eeac9e6851022dbeaf", - "reference": "d46b2da1865ef228aaca67eeac9e6851022dbeaf", + "url": "https://api.github.com/repos/meilisearch/meilisearch-symfony/zipball/345bb2b18ec0a962a8a12d0ae0e8088c2ebf0641", + "reference": "345bb2b18ec0a962a8a12d0ae0e8088c2ebf0641", "shasum": "" }, "require": { @@ -1961,9 +1961,9 @@ ], "support": { "issues": "https://github.com/meilisearch/meilisearch-symfony/issues", - "source": "https://github.com/meilisearch/meilisearch-symfony/tree/main" + "source": "https://github.com/meilisearch/meilisearch-symfony/tree/v0.15.7" }, - "time": "2024-12-01T18:32:28+00:00" + "time": "2024-12-03T12:49:45+00:00" }, { "name": "monolog/monolog", @@ -3298,12 +3298,12 @@ "source": { "type": "git", "url": "https://github.com/php-runtime/frankenphp-symfony.git", - "reference": "38a5dfa1b1e40d8e0b3bbc91d84a03cf4e65fcf4" + "reference": "34b8c25e4b1043dec2a51dfebbc776260acf1921" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-runtime/frankenphp-symfony/zipball/38a5dfa1b1e40d8e0b3bbc91d84a03cf4e65fcf4", - "reference": "38a5dfa1b1e40d8e0b3bbc91d84a03cf4e65fcf4", + "url": "https://api.github.com/repos/php-runtime/frankenphp-symfony/zipball/34b8c25e4b1043dec2a51dfebbc776260acf1921", + "reference": "34b8c25e4b1043dec2a51dfebbc776260acf1921", "shasum": "" }, "require": { @@ -3343,7 +3343,7 @@ "type": "github" } ], - "time": "2024-06-14T20:56:26+00:00" + "time": "2024-12-03T16:13:06+00:00" }, { "name": "sensiolabs/typescript-bundle", @@ -10440,12 +10440,12 @@ "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "90933b3f846e3711946b23da510e6a8147a06789" + "reference": "927be0d98b92d1b0636d78e95bf9a7ba6042a5c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/90933b3f846e3711946b23da510e6a8147a06789", - "reference": "90933b3f846e3711946b23da510e6a8147a06789", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/927be0d98b92d1b0636d78e95bf9a7ba6042a5c8", + "reference": "927be0d98b92d1b0636d78e95bf9a7ba6042a5c8", "shasum": "" }, "require": { @@ -10491,7 +10491,7 @@ "type": "github" } ], - "time": "2024-12-02T20:56:16+00:00" + "time": "2024-12-03T14:24:47+00:00" }, { "name": "phpstan/phpstan-doctrine", diff --git a/devenv.lock b/devenv.lock index 6b5f771..61f5c0f 100644 --- a/devenv.lock +++ b/devenv.lock @@ -53,10 +53,10 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733064805, + "lastModified": 1733097829, "owner": "nixos", "repo": "nixpkgs", - "rev": "31d66ae40417bb13765b0ad75dd200400e98de84", + "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a", "type": "github" }, "original": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b10269e..42325b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1635,9 +1635,9 @@ packages: } engines: { node: ">= 8" } - streamx@2.20.2: + streamx@2.21.0: resolution: { - integrity: sha512-aDGDLU+j9tJcUdPGOaHmVF1u/hhI+CsGkT02V3OKlHDV7IukOI+nTWAGkiZEKCO35rWN1wIr4tS7YFr1f4qSvA==, + integrity: sha512-Qz6MsDZXJ6ur9u+b+4xCG18TluU7PGlRfXVAAjNiGsFrBUt/ioyLkxbFaKJygoPs+/kW4VyBj0bSj89Qu0IGyg==, } strip-dirs@3.0.0: @@ -2820,7 +2820,7 @@ snapshots: source-map@0.7.4: {} - streamx@2.20.2: + streamx@2.21.0: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 @@ -2852,7 +2852,7 @@ snapshots: dependencies: b4a: 1.6.7 fast-fifo: 1.3.2 - streamx: 2.20.2 + streamx: 2.21.0 text-decoder@1.2.1: {} From 047900ac67747eff95f9d47f54a5656ef4185e0d Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 19:33:47 +0800 Subject: [PATCH 28/77] refactor(config): Migrate meilisearch.yaml to PHP --- config/packages/meilisearch.php | 27 +++++++++++++++++++++++++++ config/packages/meilisearch.yaml | 17 ----------------- 2 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 config/packages/meilisearch.php delete mode 100644 config/packages/meilisearch.yaml diff --git a/config/packages/meilisearch.php b/config/packages/meilisearch.php new file mode 100644 index 0000000..df18cf2 --- /dev/null +++ b/config/packages/meilisearch.php @@ -0,0 +1,27 @@ +url(env('MEILISEARCH_URL')) + ->apiKey(env('MEILISEARCH_API_KEY')) + ->indices() + ->name('questions') + ->class(Question::class) + ->enableSerializerGroups(true) + ->settings([ + 'filterableAttributes' => [ + 'type', + 'difficulty', + ], + 'sortableAttributes' => [ + 'id', + ], + ]); +}; diff --git a/config/packages/meilisearch.yaml b/config/packages/meilisearch.yaml deleted file mode 100644 index 1b9d805..0000000 --- a/config/packages/meilisearch.yaml +++ /dev/null @@ -1,17 +0,0 @@ -meilisearch: - url: "%env(MEILISEARCH_URL)%" # URL of the Meilisearch server (mandatory) - api_key: "%env(MEILISEARCH_API_KEY)%" # API key to access the Meilisearch server (mandatory) - indices: - - name: questions - class: App\Entity\Question - enable_serializer_groups: true - settings: - filterableAttributes: - - type - - difficulty - sortableAttributes: - - id - -when@preprod: - meilisearch: - prefix: prod_ From 2dbe31e7ae824ea42e4a86f74c3649dd36fe3c41 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 20:10:57 +0800 Subject: [PATCH 29/77] refactor(config): Migrate doctrine.yaml to PHP --- config/packages/doctrine.php | 117 ++++++++++++++++++++++++++++++++++ config/packages/doctrine.yaml | 82 ------------------------ 2 files changed, 117 insertions(+), 82 deletions(-) create mode 100644 config/packages/doctrine.php delete mode 100644 config/packages/doctrine.yaml diff --git a/config/packages/doctrine.php b/config/packages/doctrine.php new file mode 100644 index 0000000..5979af8 --- /dev/null +++ b/config/packages/doctrine.php @@ -0,0 +1,117 @@ +dbal(); + $dbalConfig + ->connection('default') + ->url(env('DATABASE_URL')->resolve()) + ->profilingCollectBacktrace(param('kernel.debug')) + ->useSavepoints(true); + + $ormConfig = $doctrineConfig->orm(); + $ormConfig + ->autoGenerateProxyClasses(true) + ->enableLazyGhostObjects(true); + + $entityManager = $ormConfig->entityManager('default'); + $entityManager + ->reportFieldsWhereDeclared(true) + ->validateXmlMapping(true) + ->namingStrategy('doctrine.orm.naming_strategy.underscore_number_aware') + ->autoMapping(true) + ->mapping('App', [ + 'type' => 'attribute', + 'is_bundle' => false, + 'dir' => '%kernel.project_dir%/src/Entity', + 'prefix' => 'App\Entity', + 'alias' => 'App', + ]); + + $ormConfig + ->controllerResolver() + ->autoMapping(true); + + $entityManager->dql() + ->datetimeFunction('date', SimpleFunction::class) + ->datetimeFunction('time', SimpleFunction::class) + ->datetimeFunction('timestamp', SimpleFunction::class) + ->datetimeFunction('convert_tz', ConvertTz::class) + ->numericFunction('timestampdiff', TimestampDiff::class) + ->numericFunction('dayofyear', SimpleFunction::class) + ->numericFunction('dayofmonth', SimpleFunction::class) + ->numericFunction('dayofweek', SimpleFunction::class) + ->numericFunction('week', SimpleFunction::class) + ->numericFunction('day', SimpleFunction::class) + ->numericFunction('hour', SimpleFunction::class) + ->numericFunction('minute', SimpleFunction::class) + ->numericFunction('month', SimpleFunction::class) + ->numericFunction('quarter', SimpleFunction::class) + ->numericFunction('second', SimpleFunction::class) + ->numericFunction('year', SimpleFunction::class) + ->numericFunction('sign', Sign::class) + ->numericFunction('pow', Pow::class) + ->numericFunction('round', Round::class) + ->numericFunction('ceil', SimpleFunction::class) + ->stringFunction('md5', SimpleFunction::class) + ->stringFunction('group_concat', GroupConcat::class) + ->stringFunction('concat_ws', ConcatWs::class) + ->stringFunction('cast', Cast::class) + ->stringFunction('replace', Replace::class) + ->stringFunction('date_format', DateFormat::class); + + if ('test' === $containerConfigurator->env()) { + $dbalConfig + ->connection('default') + // "TEST_TOKEN" is typically set by ParaTest + ->dbnameSuffix('_test.%env(default::TEST_TOKEN)%'); + } + + if ('prod' === $containerConfigurator->env()) { + $systemCachePool = 'doctrine.system_cache_pool'; + $resultCachePool = 'doctrine.result_cache_pool'; + + $ormConfig + ->autoGenerateProxyClasses(false) + ->proxyDir('%kernel.build_dir%/doctrine/orm/Proxies'); + + $entityManager + ->queryCacheDriver([ + 'type' => 'pool', + 'pool' => $systemCachePool, + ]); + + $entityManager + ->resultCacheDriver([ + 'type' => 'pool', + 'pool' => $resultCachePool, + ]); + + $cache = $frameworkConfig->cache(); + $cache->pool($systemCachePool, [ + 'adapter' => 'cache.system', + ]); + $cache->pool($resultCachePool, [ + 'adapter' => 'cache.app', + ]); + } +}; diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml deleted file mode 100644 index 4c49cfc..0000000 --- a/config/packages/doctrine.yaml +++ /dev/null @@ -1,82 +0,0 @@ -doctrine: - dbal: - url: "%env(resolve:DATABASE_URL)%" - - # IMPORTANT: You MUST configure your server version, - # either here or in the DATABASE_URL env var (see .env file) - #server_version: '16' - - profiling_collect_backtrace: "%kernel.debug%" - use_savepoints: true - orm: - auto_generate_proxy_classes: true - enable_lazy_ghost_objects: true - report_fields_where_declared: true - validate_xml_mapping: true - naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware - auto_mapping: true - mappings: - App: - type: attribute - is_bundle: false - dir: "%kernel.project_dir%/src/Entity" - prefix: 'App\Entity' - alias: App - controller_resolver: - auto_mapping: false - dql: - datetime_functions: - date: Oro\ORM\Query\AST\Functions\SimpleFunction - time: Oro\ORM\Query\AST\Functions\SimpleFunction - timestamp: Oro\ORM\Query\AST\Functions\SimpleFunction - convert_tz: Oro\ORM\Query\AST\Functions\DateTime\ConvertTz - numeric_functions: - timestampdiff: Oro\ORM\Query\AST\Functions\Numeric\TimestampDiff - dayofyear: Oro\ORM\Query\AST\Functions\SimpleFunction - dayofmonth: Oro\ORM\Query\AST\Functions\SimpleFunction - dayofweek: Oro\ORM\Query\AST\Functions\SimpleFunction - week: Oro\ORM\Query\AST\Functions\SimpleFunction - day: Oro\ORM\Query\AST\Functions\SimpleFunction - hour: Oro\ORM\Query\AST\Functions\SimpleFunction - minute: Oro\ORM\Query\AST\Functions\SimpleFunction - month: Oro\ORM\Query\AST\Functions\SimpleFunction - quarter: Oro\ORM\Query\AST\Functions\SimpleFunction - second: Oro\ORM\Query\AST\Functions\SimpleFunction - year: Oro\ORM\Query\AST\Functions\SimpleFunction - sign: Oro\ORM\Query\AST\Functions\Numeric\Sign - pow: Oro\ORM\Query\AST\Functions\Numeric\Pow - round: Oro\ORM\Query\AST\Functions\Numeric\Round - ceil: Oro\ORM\Query\AST\Functions\SimpleFunction - string_functions: - md5: Oro\ORM\Query\AST\Functions\SimpleFunction - group_concat: Oro\ORM\Query\AST\Functions\String\GroupConcat - concat_ws: Oro\ORM\Query\AST\Functions\String\ConcatWs - cast: Oro\ORM\Query\AST\Functions\Cast - replace: Oro\ORM\Query\AST\Functions\String\Replace - date_format: Oro\ORM\Query\AST\Functions\String\DateFormat - -when@test: - doctrine: - dbal: - # "TEST_TOKEN" is typically set by ParaTest - dbname_suffix: "_test%env(default::TEST_TOKEN)%" - -when@prod: - doctrine: - orm: - auto_generate_proxy_classes: false - proxy_dir: "%kernel.build_dir%/doctrine/orm/Proxies" - query_cache_driver: - type: pool - pool: doctrine.system_cache_pool - result_cache_driver: - type: pool - pool: doctrine.result_cache_pool - - framework: - cache: - pools: - doctrine.result_cache_pool: - adapter: cache.app - doctrine.system_cache_pool: - adapter: cache.system From 4c853eba96e814278584df1e4ad68e08667e0bd0 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 20:20:53 +0800 Subject: [PATCH 30/77] fix(twig): Use underscore instead of dot for global variables --- config/packages/twig.php | 4 ++-- templates/components/Challenge/Comments.html.twig | 4 ++-- templates/components/Challenge/Ui.html.twig | 6 +++--- templates/components/Navbar.html.twig | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/packages/twig.php b/config/packages/twig.php index 597a455..15f19b3 100644 --- a/config/packages/twig.php +++ b/config/packages/twig.php @@ -14,6 +14,6 @@ ->global('umami_domain', env('UMAMI_DOMAIN')) ->global('umami_website_id', env('UMAMI_WEBSITE_ID')) ->strictVariables(true) - ->global('appfeatures.hint', param('app.features.hint')) - ->global('appfeatures.comment', param('app.features.comment')); + ->global('app_features_hint', param('app.features.hint')) + ->global('app_features_comment', param('app.features.comment')); }; diff --git a/templates/components/Challenge/Comments.html.twig b/templates/components/Challenge/Comments.html.twig index 54f5b7f..20b1037 100644 --- a/templates/components/Challenge/Comments.html.twig +++ b/templates/components/Challenge/Comments.html.twig @@ -1,5 +1,5 @@ -
- {% if appfeatures.comment %} +
+ {% if app_features_comment %}
diff --git a/templates/components/Challenge/Ui.html.twig b/templates/components/Challenge/Ui.html.twig index 39ade25..de3d888 100644 --- a/templates/components/Challenge/Ui.html.twig +++ b/templates/components/Challenge/Ui.html.twig @@ -3,7 +3,7 @@ {% if question.solutionVideo %} {% endif %} - {% if appfeatures.hint %} + {% if app_features_hint %} {% endif %} @@ -15,7 +15,7 @@
- {% if appfeatures.hint %} + {% if app_features_hint %} {% endif %} {% if question.solutionVideo %} @@ -35,7 +35,7 @@
- {% if appfeatures.comment %} + {% if app_features_comment %}

留言區

diff --git a/templates/components/Navbar.html.twig b/templates/components/Navbar.html.twig index 0d9bc31..5a05e0e 100644 --- a/templates/components/Navbar.html.twig +++ b/templates/components/Navbar.html.twig @@ -20,7 +20,7 @@ name: '留言一覽', icon: 'bi bi-chat-left-text-fill', path: path('app_comments'), - disabled: not appfeatures.comment, + disabled: not app_features_comment, }, { pageId: 'complementary', From 48bae101569d2859420132f31df07ef8905f90e9 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 23:24:09 +0800 Subject: [PATCH 31/77] refactor(config): Migrate monolog.yaml to PHP --- config/packages/monolog.php | 67 ++++++++++++++++++++++++++++++++++++ config/packages/monolog.yaml | 62 --------------------------------- 2 files changed, 67 insertions(+), 62 deletions(-) create mode 100644 config/packages/monolog.php delete mode 100644 config/packages/monolog.yaml diff --git a/config/packages/monolog.php b/config/packages/monolog.php new file mode 100644 index 0000000..51dfa3d --- /dev/null +++ b/config/packages/monolog.php @@ -0,0 +1,67 @@ +channels(['deprecation']); + + if ('dev' === $containerConfigurator->env()) { + $monologConfig->handler('main') + ->type('stream') + ->path('%kernel.logs_dir%/%kernel.environment%.log') + ->level('debug') + ->channels() + ->elements(['!event']); + $monologConfig->handler('console') + ->type('console') + ->processPsr3Messages(false) + ->channels() + ->elements(['!event', '!doctrine', '!console']); + } + + if ('test' === $containerConfigurator->env()) { + $monologConfig->handler('main') + ->type('fingers_crossed') + ->actionLevel('error') + ->handler('nested') + ->excludedHttpCode(404) + ->excludedHttpCode(405) + ->channels() + ->elements(['!event']); + $monologConfig->handler('nested') + ->type('stream') + ->path('%kernel.logs_dir%/%kernel.environment%.log') + ->level('debug'); + } + + if ('prod' === $containerConfigurator->env()) { + $monologConfig->handler('main') + ->type('fingers_crossed') + ->actionLevel('error') + ->handler('nested') + ->excludedHttpCode(404) + ->excludedHttpCode(405) + // How many messages should be saved? Prevent memory leaks + ->bufferSize(50); + $monologConfig->handler('nested') + ->type('stream') + ->path('php://stderr') + ->level('debug') + ->formatter('monolog.formatter.json'); + $monologConfig->handler('console') + ->type('console') + ->processPsr3Messages(false) + ->channels() + ->elements(['!event', '!doctrine']); + $monologConfig->handler('deprecation') + ->type('stream') + ->path('php://stderr') + ->formatter('monolog.formatter.json') + ->channels() + ->elements(['deprecation']); + } +}; diff --git a/config/packages/monolog.yaml b/config/packages/monolog.yaml deleted file mode 100644 index c2da281..0000000 --- a/config/packages/monolog.yaml +++ /dev/null @@ -1,62 +0,0 @@ -monolog: - channels: - - deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists - -when@dev: - monolog: - handlers: - main: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - channels: ["!event"] - # uncomment to get logging in your browser - # you may have to allow bigger header sizes in your Web server configuration - #firephp: - # type: firephp - # level: info - #chromephp: - # type: chromephp - # level: info - console: - type: console - process_psr_3_messages: false - channels: ["!event", "!doctrine", "!console"] - -when@test: - monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - excluded_http_codes: [404, 405] - channels: ["!event"] - nested: - type: stream - path: "%kernel.logs_dir%/%kernel.environment%.log" - level: debug - -when@prod: - monolog: - handlers: - main: - type: fingers_crossed - action_level: error - handler: nested - excluded_http_codes: [404, 405] - buffer_size: 50 # How many messages should be saved? Prevent memory leaks - nested: - type: stream - path: php://stderr - level: debug - formatter: monolog.formatter.json - console: - type: console - process_psr_3_messages: false - channels: ["!event", "!doctrine"] - deprecation: - type: stream - channels: [deprecation] - path: php://stderr - formatter: monolog.formatter.json From e2cf70d6c61664718f788c4e477a1f684a5e8246 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 23:35:50 +0800 Subject: [PATCH 32/77] refactor(config): Migrate security.yaml to PHP --- config/packages/security.php | 87 +++++++++++++++++++++++++++++++++++ config/packages/security.yaml | 64 -------------------------- 2 files changed, 87 insertions(+), 64 deletions(-) create mode 100644 config/packages/security.php delete mode 100644 config/packages/security.yaml diff --git a/config/packages/security.php b/config/packages/security.php new file mode 100644 index 0000000..566c37b --- /dev/null +++ b/config/packages/security.php @@ -0,0 +1,87 @@ +passwordHasher(PasswordAuthenticatedUserInterface::class, 'auto'); + + // used to reload user from session & other features (e.g. switch_user) + $securityConfig + ->provider('app_user_provider') + ->entity() + ->class(User::class) + ->property('email'); + + $securityConfig + ->firewall('dev') + ->pattern('^/(_(profiler|wdt)|css|images|js)/') + ->security(false); + + $mainFirewall = $securityConfig->firewall('main'); + + $mainFirewall + ->lazy(true) + ->provider('app_user_provider'); + + $mainFirewall + ->formLogin() + ->loginPath('app_login') + ->checkPath('app_login') + ->enableCsrf(true); + + $mainFirewall + ->logout() + ->path('app_logout') + ->target('app_home'); + + $mainFirewall + ->rememberMe() + ->secret(param('kernel.secret')) + ->lifetime(604800 /* 1 week in seconds */); + + // https://symfony.com/doc/current/security/impersonating_user.html + $mainFirewall->switchUser(); + + // Allow anonymous access to the login form. + $securityConfig + ->accessControl() + ->route('app_login') + ->roles('PUBLIC_ACCESS'); + + // Allow anonymous access to the feedback form. + $securityConfig + ->accessControl() + ->route('app_feedback') + ->roles('PUBLIC_ACCESS'); + + // Admin + $securityConfig + ->accessControl() + ->path('^/admin') + ->roles('ROLE_ADMIN'); + + // Others (for example, apps) + $securityConfig + ->accessControl() + ->path('^/') + ->roles('ROLE_USER'); + + if ('test' === $containerConfigurator->env()) { + $passwordHasher = $securityConfig->passwordHasher(PasswordAuthenticatedUserInterface::class); + assert($passwordHasher instanceof PasswordHasherConfig); + + $passwordHasher + ->algorithm('auto') + ->cost(4) + ->timeCost(3) + ->memoryCost(10); + } +}; diff --git a/config/packages/security.yaml b/config/packages/security.yaml deleted file mode 100644 index 6a6fdee..0000000 --- a/config/packages/security.yaml +++ /dev/null @@ -1,64 +0,0 @@ -security: - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords - password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: "auto" - # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider - providers: - # used to reload user from session & other features (e.g. switch_user) - app_user_provider: - entity: - class: App\Entity\User - property: email - firewalls: - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - main: - lazy: true - provider: app_user_provider - form_login: - login_path: app_login - check_path: app_login - enable_csrf: true - logout: - path: app_logout - target: app_home - remember_me: - secret: "%kernel.secret%" # required - lifetime: 604800 # 1 week in seconds - - # https://symfony.com/doc/current/security/impersonating_user.html - switch_user: true - - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#the-firewall - - # Easy way to control access for large sections of your site - # Note: Only the *first* access control that matches will be used - access_control: - # login page - - { route: app_login, roles: PUBLIC_ACCESS } - - # feedback page - # Note that we provide the feedback form in login page, - # so we need to allow public access to this page. - - { route: app_feedback, roles: PUBLIC_ACCESS } - - # admin - - { path: ^/admin, roles: ROLE_ADMIN } - - # others (for example, apps) - - { path: ^/, roles: ROLE_USER } - -when@test: - security: - password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: - algorithm: auto - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon From 9aa88c90fe9325aa8661feddee71b64c2551e9aa Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Tue, 3 Dec 2024 23:55:00 +0800 Subject: [PATCH 33/77] refactor(config): Migrate messenger.yaml to PHP --- config/packages/messenger.php | 40 ++++++++++++++++++++++++++++++++++ config/packages/messenger.yaml | 27 ----------------------- 2 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 config/packages/messenger.php delete mode 100644 config/packages/messenger.yaml diff --git a/config/packages/messenger.php b/config/packages/messenger.php new file mode 100644 index 0000000..716d872 --- /dev/null +++ b/config/packages/messenger.php @@ -0,0 +1,40 @@ +messenger(); + + $messenger->failureTransport('failed'); + + $asyncTransport = $messenger->transport('async'); + assert($asyncTransport instanceof TransportConfig); + $asyncTransport->dsn(env('MESSENGER_TRANSPORT_DSN')); + /* @phpstan-ignore-next-line argument.type https://github.com/symfony/symfony/issues/18988 */ + $asyncTransport->options([ + 'use_notify' => true, + 'check_delayed_interval' => 60000, + ]); + $asyncTransport->retryStrategy() + ->maxRetries(3) + ->multiplier(2); + + $failedTransport = $messenger->transport('failed'); + assert($failedTransport instanceof TransportConfig); + $failedTransport->dsn('doctrine://default?queue_name=failed'); + + $messenger->bus('messenger.bus.default'); + $messenger->defaultBus('messenger.bus.default'); + + $routingConfig = $messenger->routing(SendEmailMessage::class); + assert($routingConfig instanceof RoutingConfig); + $routingConfig->senders(['async']); +}; diff --git a/config/packages/messenger.yaml b/config/packages/messenger.yaml deleted file mode 100644 index fd7f8cd..0000000 --- a/config/packages/messenger.yaml +++ /dev/null @@ -1,27 +0,0 @@ -framework: - messenger: - failure_transport: failed - - transports: - # https://symfony.com/doc/current/messenger.html#transport-configuration - async: - dsn: "%env(MESSENGER_TRANSPORT_DSN)%" - options: - use_notify: true - check_delayed_interval: 60000 - retry_strategy: - max_retries: 3 - multiplier: 2 - failed: "doctrine://default?queue_name=failed" - # sync: 'sync://' - - default_bus: messenger.bus.default - - buses: - messenger.bus.default: [] - - routing: - Symfony\Component\Mailer\Messenger\SendEmailMessage: async - - # Route your messages to the transports - # 'App\Message\YourMessage': async From 82f76ce9302ca79b79924c5350456e8b78e9fb13 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Mon, 2 Dec 2024 15:06:32 +0800 Subject: [PATCH 34/77] feat: Email Event, admin and preview page --- migrations/Version20241202060920.php | 61 +++++++++++++++ src/Controller/Admin/DashboardController.php | 2 + .../Admin/EmailEventCrudController.php | 64 +++++++++++++++ src/Controller/EmailController.php | 29 +++++++ src/Entity/EmailEvent.php | 78 +++++++++++++++++++ src/Entity/User.php | 37 +++++++++ src/Repository/EmailEventRepository.php | 37 +++++++++ templates/email/preview.html.twig | 38 +++++++++ translations/messages.zh_TW.yaml | 5 ++ 9 files changed, 351 insertions(+) create mode 100644 migrations/Version20241202060920.php create mode 100644 src/Controller/Admin/EmailEventCrudController.php create mode 100644 src/Controller/EmailController.php create mode 100644 src/Entity/EmailEvent.php create mode 100644 src/Repository/EmailEventRepository.php create mode 100644 templates/email/preview.html.twig diff --git a/migrations/Version20241202060920.php b/migrations/Version20241202060920.php new file mode 100644 index 0000000..990c9e4 --- /dev/null +++ b/migrations/Version20241202060920.php @@ -0,0 +1,61 @@ +addSql(<<<'SQL' + CREATE TABLE email_event ( + id UUID NOT NULL, + to_user_id INT DEFAULT NULL, + created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, + to_address VARCHAR(512) NOT NULL, + subject VARCHAR(4096) NOT NULL, + content TEXT NOT NULL, + PRIMARY KEY(id) + ) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_A6E34B2829F6EE60 ON email_event (to_user_id) + SQL); + $this->addSql(<<<'SQL' + COMMENT ON COLUMN email_event.id IS '(DC2Type:ulid)' + SQL); + $this->addSql(<<<'SQL' + COMMENT ON COLUMN email_event.created_at IS '(DC2Type:datetime_immutable)' + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE + email_event + ADD + CONSTRAINT FK_A6E34B2829F6EE60 FOREIGN KEY (to_user_id) REFERENCES "user" (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE email_event DROP CONSTRAINT FK_A6E34B2829F6EE60 + SQL); + $this->addSql(<<<'SQL' + DROP TABLE email_event + SQL); + } +} diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index db432a6..35a77b0 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -7,6 +7,7 @@ use App\Entity\Announcement; use App\Entity\Comment; use App\Entity\CommentLikeEvent; +use App\Entity\EmailEvent; use App\Entity\Feedback; use App\Entity\Group; use App\Entity\HintOpenEvent; @@ -68,6 +69,7 @@ public function configureMenuItems(): iterable yield MenuItem::linkToCrud('SolutionVideoEvent', 'fa fa-video', SolutionVideoEvent::class); yield MenuItem::linkToCrud('HintOpenEvent', 'fa fa-lightbulb', HintOpenEvent::class); yield MenuItem::linkToCrud('LoginEvent', 'fa fa-right-to-bracket', LoginEvent::class); + yield MenuItem::linkToCrud('EmailEvent', 'fa fa-envelope', EmailEvent::class); yield MenuItem::section('Feedback'); yield MenuItem::linkToCrud('Feedback', 'fa fa-comments', Feedback::class); diff --git a/src/Controller/Admin/EmailEventCrudController.php b/src/Controller/Admin/EmailEventCrudController.php new file mode 100644 index 0000000..1a4438e --- /dev/null +++ b/src/Controller/Admin/EmailEventCrudController.php @@ -0,0 +1,64 @@ +hideOnIndex()->setDisabled(), + AssociationField::new('toUser'), + TextField::new('toAddress')->hideOnIndex(), + TextField::new('subject'), + TextEditorField::new('content'), + DateTimeField::new('createdAt', 'Created at')->setDisabled(), + ]; + } + + public function configureFilters(Filters $filters): Filters + { + return $filters + ->add('toUser') + ; + } + + public function configureActions(Actions $actions): Actions + { + $previewAction = Action::new('preview', 'Preview', 'fa fa-eye') + ->linkToUrl(fn (EmailEvent $entity) => $this->generateUrl( + 'app_email_preview', + ['id' => $entity->getId()] + )); + + return $actions + ->disable(Action::DELETE, Action::EDIT, Action::NEW) + ->add(Crud::PAGE_INDEX, Action::DETAIL) + ->add(Crud::PAGE_INDEX, $previewAction) + ->add(Crud::PAGE_DETAIL, $previewAction); + } + + public function configureCrud(Crud $crud): Crud + { + return $crud->setDefaultSort(['createdAt' => 'DESC']); + } +} diff --git a/src/Controller/EmailController.php b/src/Controller/EmailController.php new file mode 100644 index 0000000..6a3bea9 --- /dev/null +++ b/src/Controller/EmailController.php @@ -0,0 +1,29 @@ +getToUser() !== $user && !$this->isGranted('ROLE_ADMIN')) { + throw $this->createAccessDeniedException('You are not authorized to access this email.'); + } + + return $this->render('email/preview.html.twig', [ + 'email' => $emailEvent, + ]); + } +} diff --git a/src/Entity/EmailEvent.php b/src/Entity/EmailEvent.php new file mode 100644 index 0000000..321191b --- /dev/null +++ b/src/Entity/EmailEvent.php @@ -0,0 +1,78 @@ +toUser; + } + + public function setToUser(?User $toUser): static + { + $this->toUser = $toUser; + + return $this; + } + + public function getToAddress(): ?string + { + return $this->toAddress; + } + + public function setToAddress(string $toAddress): static + { + $this->toAddress = $toAddress; + + return $this; + } + + public function getSubject(): ?string + { + return $this->subject; + } + + public function setSubject(string $subject): static + { + $this->subject = $subject; + + return $this; + } + + public function getContent(): ?string + { + return $this->content; + } + + public function setContent(string $content): static + { + $this->content = $content; + + return $this; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 3d2c1e6..92e9f20 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -90,6 +90,12 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface #[ORM\OneToMany(targetEntity: Feedback::class, mappedBy: 'sender')] private Collection $feedback; + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: EmailEvent::class, mappedBy: 'toUser')] + private Collection $emailEvents; + public function __construct() { $this->solutionEvents = new ArrayCollection(); @@ -99,6 +105,7 @@ public function __construct() $this->hintOpenEvents = new ArrayCollection(); $this->loginEvents = new ArrayCollection(); $this->feedback = new ArrayCollection(); + $this->emailEvents = new ArrayCollection(); } public function getId(): ?int @@ -419,4 +426,34 @@ public function removeFeedback(Feedback $feedback): static return $this; } + + /** + * @return Collection + */ + public function getEmailEvents(): Collection + { + return $this->emailEvents; + } + + public function addEmailEvent(EmailEvent $emailEvent): static + { + if (!$this->emailEvents->contains($emailEvent)) { + $this->emailEvents->add($emailEvent); + $emailEvent->setToUser($this); + } + + return $this; + } + + public function removeEmailEvent(EmailEvent $emailEvent): static + { + if ($this->emailEvents->removeElement($emailEvent)) { + // set the owning side to null (unless already changed) + if ($emailEvent->getToUser() === $this) { + $emailEvent->setToUser(null); + } + } + + return $this; + } } diff --git a/src/Repository/EmailEventRepository.php b/src/Repository/EmailEventRepository.php new file mode 100644 index 0000000..438433c --- /dev/null +++ b/src/Repository/EmailEventRepository.php @@ -0,0 +1,37 @@ + + */ +class EmailEventRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, EmailEvent::class); + } + + /** + * Find the email target to the user. + * + * @param User $user The user to find the email target + * + * @return list + */ + public function findBySendTarget(User $user): array + { + return $this->findBy([ + 'toUser' => $user, + ], orderBy: [ + 'createdAt' => 'DESC', + ]); + } +} diff --git a/templates/email/preview.html.twig b/templates/email/preview.html.twig new file mode 100644 index 0000000..6cdb724 --- /dev/null +++ b/templates/email/preview.html.twig @@ -0,0 +1,38 @@ +{% extends 'app.html.twig' %} + +{% block nav %}{% endblock %} +{% block title %}信件預覽{% endblock %} + +{% block app %} +
+
+
+

+ + {{ email.subject }} +

+ +
+ +
+ {{ email.content|raw }} +
+
+ + +
+
+{% endblock %} diff --git a/translations/messages.zh_TW.yaml b/translations/messages.zh_TW.yaml index 92a3780..c52eb14 100644 --- a/translations/messages.zh_TW.yaml +++ b/translations/messages.zh_TW.yaml @@ -59,6 +59,11 @@ System Management: 系統管理 Announcement: 公告 URL: 網址 Published: 發布 +Preview: 預覽 +To User: 收件使用者 +To Address: 收件信箱 +Subject: 主旨 +EmailEvent: 信件事件 result_presenter.tabs.result: 執行結果 result_presenter.tabs.answer: 正確答案 From 9441b7f938eecca35a5aed3a2746d27920e31ccf Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 5 Dec 2024 00:18:19 +0800 Subject: [PATCH 35/77] feat: Email Delivery Event, admin and preview page --- migrations/Version20241202060920.php | 61 --------- migrations/Version20241204160558.php | 90 +++++++++++++ src/Controller/Admin/DashboardController.php | 8 +- src/Controller/Admin/EmailCrudController.php | 41 ++++++ ...p => EmailDeliveryEventCrudController.php} | 14 +-- src/Controller/EmailController.php | 8 +- src/Entity/Email.php | 119 ++++++++++++++++++ src/Entity/EmailDeliveryEvent.php | 61 +++++++++ src/Entity/EmailEvent.php | 78 ------------ src/Entity/EmailKind.php | 22 ++++ src/Entity/User.php | 30 ++--- ...y.php => EmailDeliveryEventRepository.php} | 10 +- src/Repository/EmailRepository.php | 20 +++ templates/email/preview.html.twig | 14 ++- translations/messages.zh_TW.yaml | 8 +- 15 files changed, 404 insertions(+), 180 deletions(-) delete mode 100644 migrations/Version20241202060920.php create mode 100644 migrations/Version20241204160558.php create mode 100644 src/Controller/Admin/EmailCrudController.php rename src/Controller/Admin/{EmailEventCrudController.php => EmailDeliveryEventCrudController.php} (80%) create mode 100644 src/Entity/Email.php create mode 100644 src/Entity/EmailDeliveryEvent.php delete mode 100644 src/Entity/EmailEvent.php create mode 100644 src/Entity/EmailKind.php rename src/Repository/{EmailEventRepository.php => EmailDeliveryEventRepository.php} (69%) create mode 100644 src/Repository/EmailRepository.php diff --git a/migrations/Version20241202060920.php b/migrations/Version20241202060920.php deleted file mode 100644 index 990c9e4..0000000 --- a/migrations/Version20241202060920.php +++ /dev/null @@ -1,61 +0,0 @@ -addSql(<<<'SQL' - CREATE TABLE email_event ( - id UUID NOT NULL, - to_user_id INT DEFAULT NULL, - created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, - to_address VARCHAR(512) NOT NULL, - subject VARCHAR(4096) NOT NULL, - content TEXT NOT NULL, - PRIMARY KEY(id) - ) - SQL); - $this->addSql(<<<'SQL' - CREATE INDEX IDX_A6E34B2829F6EE60 ON email_event (to_user_id) - SQL); - $this->addSql(<<<'SQL' - COMMENT ON COLUMN email_event.id IS '(DC2Type:ulid)' - SQL); - $this->addSql(<<<'SQL' - COMMENT ON COLUMN email_event.created_at IS '(DC2Type:datetime_immutable)' - SQL); - $this->addSql(<<<'SQL' - ALTER TABLE - email_event - ADD - CONSTRAINT FK_A6E34B2829F6EE60 FOREIGN KEY (to_user_id) REFERENCES "user" (id) NOT DEFERRABLE INITIALLY IMMEDIATE - SQL); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql(<<<'SQL' - ALTER TABLE email_event DROP CONSTRAINT FK_A6E34B2829F6EE60 - SQL); - $this->addSql(<<<'SQL' - DROP TABLE email_event - SQL); - } -} diff --git a/migrations/Version20241204160558.php b/migrations/Version20241204160558.php new file mode 100644 index 0000000..b0a009a --- /dev/null +++ b/migrations/Version20241204160558.php @@ -0,0 +1,90 @@ +addSql(<<<'SQL' + CREATE SEQUENCE email_id_seq INCREMENT BY 1 MINVALUE 1 START 1 + SQL); + $this->addSql(<<<'SQL' + CREATE TABLE email ( + id INT NOT NULL, + subject VARCHAR(4096) NOT NULL, + content TEXT NOT NULL, + kind VARCHAR(255) NOT NULL, + PRIMARY KEY(id) + ) + SQL); + $this->addSql(<<<'SQL' + CREATE TABLE email_delivery_event ( + id UUID NOT NULL, + to_user_id INT DEFAULT NULL, + email_id INT NOT NULL, + created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, + to_address VARCHAR(512) NOT NULL, + PRIMARY KEY(id) + ) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_F35AF0FC29F6EE60 ON email_delivery_event (to_user_id) + SQL); + $this->addSql(<<<'SQL' + CREATE INDEX IDX_F35AF0FCA832C1C9 ON email_delivery_event (email_id) + SQL); + $this->addSql(<<<'SQL' + COMMENT ON COLUMN email_delivery_event.id IS '(DC2Type:ulid)' + SQL); + $this->addSql(<<<'SQL' + COMMENT ON COLUMN email_delivery_event.created_at IS '(DC2Type:datetime_immutable)' + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE + email_delivery_event + ADD + CONSTRAINT FK_F35AF0FC29F6EE60 FOREIGN KEY (to_user_id) REFERENCES "user" (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE + email_delivery_event + ADD + CONSTRAINT FK_F35AF0FCA832C1C9 FOREIGN KEY (email_id) REFERENCES email (id) NOT DEFERRABLE INITIALLY IMMEDIATE + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + DROP SEQUENCE email_id_seq CASCADE + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE email_delivery_event DROP CONSTRAINT FK_F35AF0FC29F6EE60 + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE email_delivery_event DROP CONSTRAINT FK_F35AF0FCA832C1C9 + SQL); + $this->addSql(<<<'SQL' + DROP TABLE email + SQL); + $this->addSql(<<<'SQL' + DROP TABLE email_delivery_event + SQL); + } +} diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index 35a77b0..44b4b12 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -7,7 +7,8 @@ use App\Entity\Announcement; use App\Entity\Comment; use App\Entity\CommentLikeEvent; -use App\Entity\EmailEvent; +use App\Entity\Email; +use App\Entity\EmailDeliveryEvent; use App\Entity\Feedback; use App\Entity\Group; use App\Entity\HintOpenEvent; @@ -64,12 +65,15 @@ public function configureMenuItems(): iterable yield MenuItem::linkToCrud('Comment', 'fa fa-comment', Comment::class); yield MenuItem::linkToCrud('CommentLikeEvent', 'fa fa-thumbs-up', CommentLikeEvent::class); + yield MenuItem::section('Mails'); + yield MenuItem::linkToCrud('Email', 'fa fa-envelope', Email::class); + yield MenuItem::linkToCrud('EmailDeliveryEvent', 'fa fa-paper-plane', EmailDeliveryEvent::class); + yield MenuItem::section('Events'); yield MenuItem::linkToCrud('SolutionEvent', 'fa fa-check', SolutionEvent::class); yield MenuItem::linkToCrud('SolutionVideoEvent', 'fa fa-video', SolutionVideoEvent::class); yield MenuItem::linkToCrud('HintOpenEvent', 'fa fa-lightbulb', HintOpenEvent::class); yield MenuItem::linkToCrud('LoginEvent', 'fa fa-right-to-bracket', LoginEvent::class); - yield MenuItem::linkToCrud('EmailEvent', 'fa fa-envelope', EmailEvent::class); yield MenuItem::section('Feedback'); yield MenuItem::linkToCrud('Feedback', 'fa fa-comments', Feedback::class); diff --git a/src/Controller/Admin/EmailCrudController.php b/src/Controller/Admin/EmailCrudController.php new file mode 100644 index 0000000..93abf4e --- /dev/null +++ b/src/Controller/Admin/EmailCrudController.php @@ -0,0 +1,41 @@ +hideOnForm(), + TextField::new('subject'), + TextEditorField::new('content'), + ChoiceField::new('kind'), + ]; + } + + public function configureFilters(Filters $filters): Filters + { + return $filters->add(ChoiceFilter::new('kind')->setTranslatableChoices([ + 'email-kind.transactional' => EmailKind::Transactional, + 'email-kind.marketing' => EmailKind::Marketing, + ])); + } +} diff --git a/src/Controller/Admin/EmailEventCrudController.php b/src/Controller/Admin/EmailDeliveryEventCrudController.php similarity index 80% rename from src/Controller/Admin/EmailEventCrudController.php rename to src/Controller/Admin/EmailDeliveryEventCrudController.php index 1a4438e..155d4d2 100644 --- a/src/Controller/Admin/EmailEventCrudController.php +++ b/src/Controller/Admin/EmailDeliveryEventCrudController.php @@ -4,7 +4,7 @@ namespace App\Controller\Admin; -use App\Entity\EmailEvent; +use App\Entity\EmailDeliveryEvent; use EasyCorp\Bundle\EasyAdminBundle\Config\Action; use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; @@ -13,14 +13,13 @@ use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; -use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; -class EmailEventCrudController extends AbstractCrudController +class EmailDeliveryEventCrudController extends AbstractCrudController { public static function getEntityFqcn(): string { - return EmailEvent::class; + return EmailDeliveryEvent::class; } public function configureFields(string $pageName): iterable @@ -29,8 +28,7 @@ public function configureFields(string $pageName): iterable IdField::new('id')->hideOnIndex()->setDisabled(), AssociationField::new('toUser'), TextField::new('toAddress')->hideOnIndex(), - TextField::new('subject'), - TextEditorField::new('content'), + AssociationField::new('email'), DateTimeField::new('createdAt', 'Created at')->setDisabled(), ]; } @@ -45,13 +43,13 @@ public function configureFilters(Filters $filters): Filters public function configureActions(Actions $actions): Actions { $previewAction = Action::new('preview', 'Preview', 'fa fa-eye') - ->linkToUrl(fn (EmailEvent $entity) => $this->generateUrl( + ->linkToUrl(fn (EmailDeliveryEvent $entity) => $this->generateUrl( 'app_email_preview', ['id' => $entity->getId()] )); return $actions - ->disable(Action::DELETE, Action::EDIT, Action::NEW) +// ->disable(Action::DELETE, Action::EDIT, Action::NEW) ->add(Crud::PAGE_INDEX, Action::DETAIL) ->add(Crud::PAGE_INDEX, $previewAction) ->add(Crud::PAGE_DETAIL, $previewAction); diff --git a/src/Controller/EmailController.php b/src/Controller/EmailController.php index 6a3bea9..7154ccc 100644 --- a/src/Controller/EmailController.php +++ b/src/Controller/EmailController.php @@ -4,7 +4,7 @@ namespace App\Controller; -use App\Entity\EmailEvent; +use App\Entity\EmailDeliveryEvent; use App\Entity\User; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Response; @@ -14,16 +14,16 @@ class EmailController extends AbstractController { #[Route('/email/{id}', name: 'app_email_preview')] - public function details(#[CurrentUser] User $user, EmailEvent $emailEvent): Response + public function details(#[CurrentUser] User $user, EmailDeliveryEvent $emailDeliveryEvent): Response { // if this email is not owned by the current user and the user is not an admin, // we deny the access. - if ($emailEvent->getToUser() !== $user && !$this->isGranted('ROLE_ADMIN')) { + if ($emailDeliveryEvent->getToUser() !== $user && !$this->isGranted('ROLE_ADMIN')) { throw $this->createAccessDeniedException('You are not authorized to access this email.'); } return $this->render('email/preview.html.twig', [ - 'email' => $emailEvent, + 'emailDeliveryEvent' => $emailDeliveryEvent, ]); } } diff --git a/src/Entity/Email.php b/src/Entity/Email.php new file mode 100644 index 0000000..d58a8d4 --- /dev/null +++ b/src/Entity/Email.php @@ -0,0 +1,119 @@ + + */ + #[ORM\OneToMany(targetEntity: EmailDeliveryEvent::class, mappedBy: 'email')] + private Collection $emailDeliveryEvents; + + #[ORM\Column(enumType: EmailKind::class)] + private EmailKind $kind = EmailKind::Transactional; + + public function __construct() + { + $this->emailDeliveryEvents = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function __toString(): string + { + return $this->getSubject(); + } + + public function getSubject(): string + { + return $this->subject; + } + + public function setSubject(string $subject): static + { + $this->subject = $subject; + + return $this; + } + + public function getContent(): string + { + return $this->content; + } + + public function setContent(string $content): static + { + $this->content = $content; + + return $this; + } + + /** + * @return Collection + */ + public function getEmailDeliveryEvents(): Collection + { + return $this->emailDeliveryEvents; + } + + public function addEmailDeliveryEvent(EmailDeliveryEvent $emailDeliveryEvent): static + { + if (!$this->emailDeliveryEvents->contains($emailDeliveryEvent)) { + $this->emailDeliveryEvents->add($emailDeliveryEvent); + $emailDeliveryEvent->setEmail($this); + } + + return $this; + } + + public function removeEmailDeliveryEvent(EmailDeliveryEvent $emailDeliveryEvent): static + { + if ($this->emailDeliveryEvents->removeElement($emailDeliveryEvent)) { + // set the owning side to null (unless already changed) + if ($emailDeliveryEvent->getEmail() === $this) { + $emailDeliveryEvent->setEmail(new self()); + } + } + + return $this; + } + + public function getKind(): EmailKind + { + return $this->kind; + } + + public function setKind(EmailKind $kind): static + { + $this->kind = $kind; + + return $this; + } +} diff --git a/src/Entity/EmailDeliveryEvent.php b/src/Entity/EmailDeliveryEvent.php new file mode 100644 index 0000000..ec6aa73 --- /dev/null +++ b/src/Entity/EmailDeliveryEvent.php @@ -0,0 +1,61 @@ +toUser; + } + + public function setToUser(?User $toUser): static + { + $this->toUser = $toUser; + + return $this; + } + + public function getToAddress(): ?string + { + return $this->toAddress; + } + + public function setToAddress(string $toAddress): static + { + $this->toAddress = $toAddress; + + return $this; + } + + public function getEmail(): Email + { + return $this->email; + } + + public function setEmail(Email $email): static + { + $this->email = $email; + + return $this; + } +} diff --git a/src/Entity/EmailEvent.php b/src/Entity/EmailEvent.php deleted file mode 100644 index 321191b..0000000 --- a/src/Entity/EmailEvent.php +++ /dev/null @@ -1,78 +0,0 @@ -toUser; - } - - public function setToUser(?User $toUser): static - { - $this->toUser = $toUser; - - return $this; - } - - public function getToAddress(): ?string - { - return $this->toAddress; - } - - public function setToAddress(string $toAddress): static - { - $this->toAddress = $toAddress; - - return $this; - } - - public function getSubject(): ?string - { - return $this->subject; - } - - public function setSubject(string $subject): static - { - $this->subject = $subject; - - return $this; - } - - public function getContent(): ?string - { - return $this->content; - } - - public function setContent(string $content): static - { - $this->content = $content; - - return $this; - } -} diff --git a/src/Entity/EmailKind.php b/src/Entity/EmailKind.php new file mode 100644 index 0000000..efacac7 --- /dev/null +++ b/src/Entity/EmailKind.php @@ -0,0 +1,22 @@ + $translator->trans('email-kind.transactional', locale: $locale), + self::Marketing => $translator->trans('email-kind.marketing', locale: $locale), + }; + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php index 92e9f20..3755ff9 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -91,10 +91,10 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface private Collection $feedback; /** - * @var Collection + * @var Collection */ - #[ORM\OneToMany(targetEntity: EmailEvent::class, mappedBy: 'toUser')] - private Collection $emailEvents; + #[ORM\OneToMany(targetEntity: EmailDeliveryEvent::class, mappedBy: 'toUser')] + private Collection $emailDeliveryEvents; public function __construct() { @@ -105,7 +105,7 @@ public function __construct() $this->hintOpenEvents = new ArrayCollection(); $this->loginEvents = new ArrayCollection(); $this->feedback = new ArrayCollection(); - $this->emailEvents = new ArrayCollection(); + $this->emailDeliveryEvents = new ArrayCollection(); } public function getId(): ?int @@ -428,29 +428,29 @@ public function removeFeedback(Feedback $feedback): static } /** - * @return Collection + * @return Collection */ - public function getEmailEvents(): Collection + public function getEmailDeliveryEvents(): Collection { - return $this->emailEvents; + return $this->emailDeliveryEvents; } - public function addEmailEvent(EmailEvent $emailEvent): static + public function addEmailDeliveryEvent(EmailDeliveryEvent $emailDeliveryEvent): static { - if (!$this->emailEvents->contains($emailEvent)) { - $this->emailEvents->add($emailEvent); - $emailEvent->setToUser($this); + if (!$this->emailDeliveryEvents->contains($emailDeliveryEvent)) { + $this->emailDeliveryEvents->add($emailDeliveryEvent); + $emailDeliveryEvent->setToUser($this); } return $this; } - public function removeEmailEvent(EmailEvent $emailEvent): static + public function removeEmailDeliveryEvent(EmailDeliveryEvent $emailDeliveryEvent): static { - if ($this->emailEvents->removeElement($emailEvent)) { + if ($this->emailDeliveryEvents->removeElement($emailDeliveryEvent)) { // set the owning side to null (unless already changed) - if ($emailEvent->getToUser() === $this) { - $emailEvent->setToUser(null); + if ($emailDeliveryEvent->getToUser() === $this) { + $emailDeliveryEvent->setToUser(null); } } diff --git a/src/Repository/EmailEventRepository.php b/src/Repository/EmailDeliveryEventRepository.php similarity index 69% rename from src/Repository/EmailEventRepository.php rename to src/Repository/EmailDeliveryEventRepository.php index 438433c..96c446b 100644 --- a/src/Repository/EmailEventRepository.php +++ b/src/Repository/EmailDeliveryEventRepository.php @@ -4,19 +4,19 @@ namespace App\Repository; -use App\Entity\EmailEvent; +use App\Entity\EmailDeliveryEvent; use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; /** - * @extends ServiceEntityRepository + * @extends ServiceEntityRepository */ -class EmailEventRepository extends ServiceEntityRepository +class EmailDeliveryEventRepository extends ServiceEntityRepository { public function __construct(ManagerRegistry $registry) { - parent::__construct($registry, EmailEvent::class); + parent::__construct($registry, EmailDeliveryEvent::class); } /** @@ -24,7 +24,7 @@ public function __construct(ManagerRegistry $registry) * * @param User $user The user to find the email target * - * @return list + * @return list */ public function findBySendTarget(User $user): array { diff --git a/src/Repository/EmailRepository.php b/src/Repository/EmailRepository.php new file mode 100644 index 0000000..41248d8 --- /dev/null +++ b/src/Repository/EmailRepository.php @@ -0,0 +1,20 @@ + + */ +class EmailRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Email::class); + } +} diff --git a/templates/email/preview.html.twig b/templates/email/preview.html.twig index 6cdb724..9dd3b11 100644 --- a/templates/email/preview.html.twig +++ b/templates/email/preview.html.twig @@ -9,13 +9,13 @@

- {{ email.subject }} + {{ emailDeliveryEvent.email.subject }}


- {{ email.content|raw }} + {{ emailDeliveryEvent.email.content|raw }}
@@ -23,13 +23,15 @@
  • - 收件人: - {{ email.toAddress }} + 收件人:{{ emailDeliveryEvent.toAddress }}
  • - 發件日期: - {{ email.createdAt|date('Y-m-d H:i:s') }} + 發件日期:{{ emailDeliveryEvent.createdAt|date('Y-m-d H:i:s') }} +
  • +
  • + + 信件種類:{{ emailDeliveryEvent.email.kind|trans }}
diff --git a/translations/messages.zh_TW.yaml b/translations/messages.zh_TW.yaml index c52eb14..b1ce42e 100644 --- a/translations/messages.zh_TW.yaml +++ b/translations/messages.zh_TW.yaml @@ -62,8 +62,10 @@ Published: 發布 Preview: 預覽 To User: 收件使用者 To Address: 收件信箱 +Mails: 郵件 Subject: 主旨 -EmailEvent: 信件事件 +EmailDeliveryEvent: 郵件投遞事件 +Kind: 種類 result_presenter.tabs.result: 執行結果 result_presenter.tabs.answer: 正確答案 @@ -178,3 +180,7 @@ challenge: answer-query-failure: 正確答案也是個錯誤的 SQL 查詢:%error% user-query-error: 你的 SQL 查詢執行失敗:%error% user-query-failure: 你的 SQL 查詢不正確:%error% + +email-kind: + transactional: 通知型信件 + marketing: 行銷型信件 From b8765f575a3251c6cf0ff4d279f12db99cb78cad Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 5 Dec 2024 00:23:44 +0800 Subject: [PATCH 36/77] feat(email): Add "test" kind --- src/Entity/EmailKind.php | 2 ++ translations/messages.zh_TW.yaml | 1 + 2 files changed, 3 insertions(+) diff --git a/src/Entity/EmailKind.php b/src/Entity/EmailKind.php index efacac7..8e7447f 100644 --- a/src/Entity/EmailKind.php +++ b/src/Entity/EmailKind.php @@ -11,12 +11,14 @@ enum EmailKind: string implements TranslatableInterface { case Transactional = 'transactional'; case Marketing = 'marketing'; + case Test = 'test'; public function trans(TranslatorInterface $translator, ?string $locale = null): string { return match ($this) { self::Transactional => $translator->trans('email-kind.transactional', locale: $locale), self::Marketing => $translator->trans('email-kind.marketing', locale: $locale), + self::Test => $translator->trans('email-kind.test', locale: $locale), }; } } diff --git a/translations/messages.zh_TW.yaml b/translations/messages.zh_TW.yaml index b1ce42e..6050f4b 100644 --- a/translations/messages.zh_TW.yaml +++ b/translations/messages.zh_TW.yaml @@ -184,3 +184,4 @@ challenge: email-kind: transactional: 通知型信件 marketing: 行銷型信件 + test: 測試用信件 From d3fdc564be101caf1d91795a230de4ed1bb0dba8 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 5 Dec 2024 12:59:11 +0800 Subject: [PATCH 37/77] feat(email): EmailCreatedSubscriber --- src/Entity/EmailKind.php | 24 ++++ .../EmailCreatedSubscriber.php | 108 ++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/EventSubscriber/EmailCreatedSubscriber.php diff --git a/src/Entity/EmailKind.php b/src/Entity/EmailKind.php index 8e7447f..35606d6 100644 --- a/src/Entity/EmailKind.php +++ b/src/Entity/EmailKind.php @@ -4,6 +4,7 @@ namespace App\Entity; +use Symfony\Component\Mime\Header\Headers; use Symfony\Contracts\Translation\TranslatableInterface; use Symfony\Contracts\Translation\TranslatorInterface; @@ -21,4 +22,27 @@ public function trans(TranslatorInterface $translator, ?string $locale = null): self::Test => $translator->trans('email-kind.test', locale: $locale), }; } + + /** + * @throws \InvalidArgumentException + */ + public static function fromEmailHeader(Headers $headers): self + { + $kind = $headers->getHeaderBody('X-Email-Kind'); + if (!\is_string($kind)) { + throw new \InvalidArgumentException('The email kind header is missing or is invalid type.'); + } + + return match ($kind) { + 'transactional' => self::Transactional, + 'marketing' => self::Marketing, + 'test' => self::Test, + default => throw new \InvalidArgumentException("Invalid email kind: $kind"), + }; + } + + public function addToEmailHeader(Headers $headers): Headers + { + return $headers->addTextHeader('X-Email-Kind', $this->value); + } } diff --git a/src/EventSubscriber/EmailCreatedSubscriber.php b/src/EventSubscriber/EmailCreatedSubscriber.php new file mode 100644 index 0000000..a29e225 --- /dev/null +++ b/src/EventSubscriber/EmailCreatedSubscriber.php @@ -0,0 +1,108 @@ +getMessage(); + if (!($message instanceof EmailMessage)) { + $this->logger->warning('The message is not an instance of Email.', [ + 'message' => $message, + ]); + + return; + } + + $subject = $message->getSubject(); + if (!\is_string($subject)) { + $this->logger->warning('The message does not have a valid subject.', [ + 'message' => $message, + 'subject' => $subject, + ]); + + return; + } + + $body = $message->getHtmlBody(); + if (!\is_string($body)) { + $this->logger->warning('The message does not have an valid HTML body.', [ + 'message' => $message, + 'body' => $body, + ]); + + return; + } + + try { + $kind = EmailKind::fromEmailHeader($message->getHeaders()); + } catch (\InvalidArgumentException $exception) { + $this->logger->warning('The message does not have a valid email kind.', [ + 'message' => $message, + 'exception' => $exception, + ]); + + return; + } + + $email = (new EmailEntity()) + ->setSubject($subject) + ->setContent($body) + ->setKind($kind); + $this->entityManager->persist($email); + + /** + * @var list
$recipients + */ + $recipients = [ + ...$message->getTo(), + ...$message->getCc(), + ...$message->getBcc(), + ]; + + foreach ($recipients as $recipient) { + $emailDeliveryEvent = (new EmailDeliveryEvent()) + ->setToAddress($recipient->getAddress()) + ->setEmail($email); + + $user = $this->userRepository->findOneBy([ + 'email' => $recipient->getAddress(), + ]); + if (null !== $user) { + $emailDeliveryEvent->setToUser($user); + } + + $this->entityManager->persist($emailDeliveryEvent); + } + + $this->entityManager->flush(); + } + + public static function getSubscribedEvents(): array + { + return [ + MessageEvent::class => 'onMessageEvent', + ]; + } +} From 6231bc6e876685c1f89fd8d63b312d11a99fa0e1 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Thu, 5 Dec 2024 13:28:51 +0800 Subject: [PATCH 38/77] test(email): EmailCreatedSubscriberTest --- .../EmailCreatedSubscriberTest.php | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/EventListener/EmailCreatedSubscriberTest.php diff --git a/tests/EventListener/EmailCreatedSubscriberTest.php b/tests/EventListener/EmailCreatedSubscriberTest.php new file mode 100644 index 0000000..4b49fbe --- /dev/null +++ b/tests/EventListener/EmailCreatedSubscriberTest.php @@ -0,0 +1,84 @@ +subject('subject') + ->html('
bodyfrom('demo-dbplay@example.com') + ->to('test@example.com'); + + $headers = $message->getHeaders(); + $headers = EmailKind::Test->addToEmailHeader($headers); + $message->setHeaders($headers); + + $envelope = Envelope::create($message); + + $userRepository = self::createMock(UserRepository::class); + $userRepository + ->expects(self::once()) + ->method('findOneBy') + ->with(['email' => 'test@example.com']) + ->willReturn(new UserEntity()); + + $invokedCount = self::exactly(2); + /** + * @var Email|null $emailInstance + */ + $emailInstance = null; + $entityManager = self::createMock(EntityManagerInterface::class); + $entityManager + ->expects(self::exactly(2)) + ->method('persist') + ->willReturnCallback(function (mixed ...$parameters) use ($invokedCount, &$emailInstance): void { + switch ($invokedCount->numberOfInvocations()) { + case 1: + $email = $parameters[0]; + \assert($email instanceof EmailEntity); + + self::assertEquals('subject', $email->getSubject()); + self::assertEquals('
body
', $email->getContent()); + self::assertEquals(EmailKind::Test, $email->getKind()); + + $emailInstance = $email; + break; + case 2: + $event = $parameters[0]; + \assert($event instanceof EmailDeliveryEventEntity); + + self::assertEquals('test@example.com', $event->getToAddress()); + self::assertEquals($emailInstance, $event->getEmail()); + break; + } + }); + + $subscriber = new EmailCreatedSubscriber($logger, $userRepository, $entityManager); + $dispatcher = new EventDispatcher(); + $event = new MessageEvent($message, $envelope, ''); + + $dispatcher->addSubscriber($subscriber); + $dispatcher->dispatch($event); + } +} From 32dbdca01f833a612d052c4c1c9a28f9387e3113 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Sun, 8 Dec 2024 21:47:47 +0800 Subject: [PATCH 39/77] chore: Upgrade dependencies --- composer.lock | 368 +++++++++++++++++++++++++------------------------ devenv.lock | 20 +-- importmap.php | 2 +- package.json | 2 +- pnpm-lock.yaml | 194 +++++++++++++------------- 5 files changed, 296 insertions(+), 290 deletions(-) diff --git a/composer.lock b/composer.lock index 419ec2a..a8200c8 100644 --- a/composer.lock +++ b/composer.lock @@ -464,25 +464,23 @@ "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "0d0c25d81be87f7143d1dfc635974d255e26291c" + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0d0c25d81be87f7143d1dfc635974d255e26291c", - "reference": "0d0c25d81be87f7143d1dfc635974d255e26291c", + "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" @@ -504,7 +502,7 @@ "issues": "https://github.com/doctrine/deprecations/issues", "source": "https://github.com/doctrine/deprecations/tree/1.1.x" }, - "time": "2024-12-01T07:04:16+00:00" + "time": "2024-12-07T21:18:45+00:00" }, { "name": "doctrine/doctrine-bundle", @@ -1154,12 +1152,12 @@ "source": { "type": "git", "url": "https://github.com/doctrine/orm.git", - "reference": "50d7a0f95ea6b8623589a90a2c51ddb7389f71c2" + "reference": "60071544846105bb67a4ac06fcc581c8893b6632" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/orm/zipball/50d7a0f95ea6b8623589a90a2c51ddb7389f71c2", - "reference": "50d7a0f95ea6b8623589a90a2c51ddb7389f71c2", + "url": "https://api.github.com/repos/doctrine/orm/zipball/60071544846105bb67a4ac06fcc581c8893b6632", + "reference": "60071544846105bb67a4ac06fcc581c8893b6632", "shasum": "" }, "require": { @@ -1237,7 +1235,7 @@ "issues": "https://github.com/doctrine/orm/issues", "source": "https://github.com/doctrine/orm/tree/3.4.x" }, - "time": "2024-11-23T21:01:13+00:00" + "time": "2024-12-04T06:52:51+00:00" }, { "name": "doctrine/persistence", @@ -1398,12 +1396,12 @@ "source": { "type": "git", "url": "https://github.com/EasyCorp/EasyAdminBundle.git", - "reference": "2b49b51ccd128ac3983ef1c585d74d60ee5c6efa" + "reference": "8d6b02d39da311f6ca1e56a2ec5328062bcba891" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/2b49b51ccd128ac3983ef1c585d74d60ee5c6efa", - "reference": "2b49b51ccd128ac3983ef1c585d74d60ee5c6efa", + "url": "https://api.github.com/repos/EasyCorp/EasyAdminBundle/zipball/8d6b02d39da311f6ca1e56a2ec5328062bcba891", + "reference": "8d6b02d39da311f6ca1e56a2ec5328062bcba891", "shasum": "" }, "require": { @@ -1431,7 +1429,8 @@ "symfony/twig-bundle": "^5.4|^6.0|^7.0", "symfony/uid": "^5.4|^6.0|^7.0", "symfony/ux-twig-component": "^2.21", - "symfony/validator": "^5.4|^6.0|^7.0" + "symfony/validator": "^5.4|^6.0|^7.0", + "twig/twig": "^3.15" }, "require-dev": { "doctrine/doctrine-fixtures-bundle": "^3.4|3.5.x-dev", @@ -1446,7 +1445,8 @@ "symfony/debug-bundle": "^5.4|^6.0|^7.0", "symfony/dom-crawler": "^5.4|^6.0|^7.0", "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/phpunit-bridge": "^6.1|^7.0" + "symfony/phpunit-bridge": "^6.1|^7.0", + "symfony/process": "^5.4|^6.0|^7.0" }, "default-branch": true, "type": "symfony-bundle", @@ -1487,7 +1487,7 @@ "type": "github" } ], - "time": "2024-12-02T19:53:15+00:00" + "time": "2024-12-07T16:15:10+00:00" }, { "name": "egulias/email-validator", @@ -1635,12 +1635,12 @@ "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "bbef3301fda0b97bbd8d7a897800424fb5937c9e" + "reference": "b561666ac77bfbde7f0cc9bc4091e0db56b9f573" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/bbef3301fda0b97bbd8d7a897800424fb5937c9e", - "reference": "bbef3301fda0b97bbd8d7a897800424fb5937c9e", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/b561666ac77bfbde7f0cc9bc4091e0db56b9f573", + "reference": "b561666ac77bfbde7f0cc9bc4091e0db56b9f573", "shasum": "" }, "require": { @@ -1665,8 +1665,9 @@ "phpstan/phpstan": "^1.8.2", "phpunit/phpunit": "^9.5.21 || ^10.5.9 || ^11.0.0", "scrutinizer/ocular": "^1.8.1", - "symfony/finder": "^5.3 | ^6.0 || ^7.0", - "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 || ^7.0", + "symfony/finder": "^5.3 | ^6.0 | ^7.0", + "symfony/process": "^5.4 | ^6.0 | ^7.0", + "symfony/yaml": "^2.3 | ^3.0 | ^4.0 | ^5.0 | ^6.0 | ^7.0", "unleashedtech/php-coding-standard": "^3.1.1", "vimeo/psalm": "^4.24.0 || ^5.0.0" }, @@ -1676,7 +1677,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.6-dev" + "dev-main": "2.7-dev" } }, "autoload": { @@ -1733,7 +1734,7 @@ "type": "tidelift" } ], - "time": "2024-09-08T13:06:11+00:00" + "time": "2024-12-07T15:56:47+00:00" }, { "name": "league/config", @@ -1971,12 +1972,12 @@ "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "e94000419394ff1bec801dad310432228f9fc19c" + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/e94000419394ff1bec801dad310432228f9fc19c", - "reference": "e94000419394ff1bec801dad310432228f9fc19c", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/aef6ee73a77a66e404dd6540934a9ef1b3c855b4", + "reference": "aef6ee73a77a66e404dd6540934a9ef1b3c855b4", "shasum": "" }, "require": { @@ -2055,7 +2056,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/main" + "source": "https://github.com/Seldaek/monolog/tree/3.8.1" }, "funding": [ { @@ -2067,7 +2068,7 @@ "type": "tidelift" } ], - "time": "2024-11-17T12:30:33+00:00" + "time": "2024-12-05T17:15:07+00:00" }, { "name": "nette/schema", @@ -2648,12 +2649,12 @@ "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c" + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/f3558a4c23426d12bffeaab463f8a8d8b681193c", - "reference": "f3558a4c23426d12bffeaab463f8a8d8b681193c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", "shasum": "" }, "require": { @@ -2703,9 +2704,9 @@ "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.6.0" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.1" }, - "time": "2024-11-12T11:25:25+00:00" + "time": "2024-12-07T09:39:29+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -3477,12 +3478,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/asset-mapper.git", - "reference": "ffb733232bb6bb85ef6a994f47c817e7c2ecab9c" + "reference": "ad8b44c933ed9cddc5bdd259ba3ea58adef31966" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/ffb733232bb6bb85ef6a994f47c817e7c2ecab9c", - "reference": "ffb733232bb6bb85ef6a994f47c817e7c2ecab9c", + "url": "https://api.github.com/repos/symfony/asset-mapper/zipball/ad8b44c933ed9cddc5bdd259ba3ea58adef31966", + "reference": "ad8b44c933ed9cddc5bdd259ba3ea58adef31966", "shasum": "" }, "require": { @@ -3504,6 +3505,7 @@ "symfony/framework-bundle": "^6.4|^7.0", "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", "symfony/web-link": "^6.4|^7.0" }, "type": "library", @@ -3532,7 +3534,7 @@ "description": "Maps directories of assets & makes them available in a public directory with versioned filenames.", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/asset-mapper/tree/v7.2.0" + "source": "https://github.com/symfony/asset-mapper/tree/7.3" }, "funding": [ { @@ -3548,7 +3550,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T11:17:29+00:00" + "time": "2024-12-06T14:18:58+00:00" }, { "name": "symfony/cache", @@ -3556,12 +3558,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/cache.git", - "reference": "2c926bc348184b4b235f2200fcbe8fcf3c8c5b8a" + "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/cache/zipball/2c926bc348184b4b235f2200fcbe8fcf3c8c5b8a", - "reference": "2c926bc348184b4b235f2200fcbe8fcf3c8c5b8a", + "url": "https://api.github.com/repos/symfony/cache/zipball/e7e983596b744c4539f31e79b0350a6cf5878a20", + "reference": "e7e983596b744c4539f31e79b0350a6cf5878a20", "shasum": "" }, "require": { @@ -3630,7 +3632,7 @@ "psr6" ], "support": { - "source": "https://github.com/symfony/cache/tree/v7.2.0" + "source": "https://github.com/symfony/cache/tree/7.2" }, "funding": [ { @@ -3646,7 +3648,7 @@ "type": "tidelift" } ], - "time": "2024-11-25T15:21:05+00:00" + "time": "2024-12-07T08:08:50+00:00" }, { "name": "symfony/cache-contracts", @@ -3880,12 +3882,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" + "reference": "13189efa32034e487366fd541ef2f0d9a16a9b8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "url": "https://api.github.com/repos/symfony/console/zipball/13189efa32034e487366fd541ef2f0d9a16a9b8d", + "reference": "13189efa32034e487366fd541ef2f0d9a16a9b8d", "shasum": "" }, "require": { @@ -3949,7 +3951,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.0" + "source": "https://github.com/symfony/console/tree/7.2" }, "funding": [ { @@ -3965,7 +3967,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/dependency-injection", @@ -4121,12 +4123,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/doctrine-bridge.git", - "reference": "09dbb7c731430335e9ae89ee5054b5f5580c49bf" + "reference": "b492be51eb703723d682851a0c9fb39b9d1a7bfb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/09dbb7c731430335e9ae89ee5054b5f5580c49bf", - "reference": "09dbb7c731430335e9ae89ee5054b5f5580c49bf", + "url": "https://api.github.com/repos/symfony/doctrine-bridge/zipball/b492be51eb703723d682851a0c9fb39b9d1a7bfb", + "reference": "b492be51eb703723d682851a0c9fb39b9d1a7bfb", "shasum": "" }, "require": { @@ -4206,7 +4208,7 @@ "description": "Provides integration for Doctrine with various Symfony components", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/doctrine-bridge/tree/v7.2.0" + "source": "https://github.com/symfony/doctrine-bridge/tree/7.2" }, "funding": [ { @@ -4222,7 +4224,7 @@ "type": "tidelift" } ], - "time": "2024-11-25T12:10:02+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/doctrine-messenger", @@ -4376,12 +4378,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "672b3dd1ef8b87119b446d67c58c106c43f965fe" + "reference": "422c8d8d7ee1e2b8d871de434b4f706982f0f029" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/672b3dd1ef8b87119b446d67c58c106c43f965fe", - "reference": "672b3dd1ef8b87119b446d67c58c106c43f965fe", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/422c8d8d7ee1e2b8d871de434b4f706982f0f029", + "reference": "422c8d8d7ee1e2b8d871de434b4f706982f0f029", "shasum": "" }, "require": { @@ -4427,7 +4429,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.0" + "source": "https://github.com/symfony/error-handler/tree/7.3" }, "funding": [ { @@ -4443,7 +4445,7 @@ "type": "tidelift" } ], - "time": "2024-11-05T15:35:02+00:00" + "time": "2024-12-07T11:46:47+00:00" }, { "name": "symfony/event-dispatcher", @@ -4904,12 +4906,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/framework-bundle.git", - "reference": "a8d0da4110fe643ab3cde7c938a03e222fe787c6" + "reference": "6ef18ca0278849477ab097df8784938327f5fd09" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/a8d0da4110fe643ab3cde7c938a03e222fe787c6", - "reference": "a8d0da4110fe643ab3cde7c938a03e222fe787c6", + "url": "https://api.github.com/repos/symfony/framework-bundle/zipball/6ef18ca0278849477ab097df8784938327f5fd09", + "reference": "6ef18ca0278849477ab097df8784938327f5fd09", "shasum": "" }, "require": { @@ -5030,7 +5032,7 @@ "description": "Provides a tight integration between Symfony components and the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/framework-bundle/tree/v7.2.0" + "source": "https://github.com/symfony/framework-bundle/tree/7.3" }, "funding": [ { @@ -5046,7 +5048,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T16:27:35+00:00" + "time": "2024-12-07T08:55:22+00:00" }, { "name": "symfony/http-client", @@ -5054,19 +5056,19 @@ "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "955e43336aff03df1e8a8e17daefabb0127a313b" + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/955e43336aff03df1e8a8e17daefabb0127a313b", - "reference": "955e43336aff03df1e8a8e17daefabb0127a313b", + "url": "https://api.github.com/repos/symfony/http-client/zipball/ff4df2b68d1c67abb9fef146e6540ea16b58d99e", + "reference": "ff4df2b68d1c67abb9fef146e6540ea16b58d99e", "shasum": "" }, "require": { "php": ">=8.2", "psr/log": "^1|^2|^3", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/http-client-contracts": "~3.4.3|^3.5.1", + "symfony/http-client-contracts": "~3.4.4|^3.5.2", "symfony/service-contracts": "^2.5|^3" }, "conflict": { @@ -5141,7 +5143,7 @@ "type": "tidelift" } ], - "time": "2024-11-29T08:22:02+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/http-client-contracts", @@ -5149,12 +5151,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/http-client-contracts.git", - "reference": "7917bba9674e386bc2726c4bb9ad5440f7831d66" + "reference": "c5d993f8c4ed8c1773ce8c0e92de39a90fae6ac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7917bba9674e386bc2726c4bb9ad5440f7831d66", - "reference": "7917bba9674e386bc2726c4bb9ad5440f7831d66", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/c5d993f8c4ed8c1773ce8c0e92de39a90fae6ac3", + "reference": "c5d993f8c4ed8c1773ce8c0e92de39a90fae6ac3", "shasum": "" }, "require": { @@ -5163,12 +5165,12 @@ "default-branch": true, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { "dev-main": "3.6-dev" - }, - "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" } }, "autoload": { @@ -5220,7 +5222,7 @@ "type": "tidelift" } ], - "time": "2024-11-19T10:11:42+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/http-foundation", @@ -5306,12 +5308,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "52b0f33f21683a1a78a61a762a7632f45942f97d" + "reference": "1078dfea48501bcf5f5e1fdc59b273a2c130f3e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/52b0f33f21683a1a78a61a762a7632f45942f97d", - "reference": "52b0f33f21683a1a78a61a762a7632f45942f97d", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/1078dfea48501bcf5f5e1fdc59b273a2c130f3e5", + "reference": "1078dfea48501bcf5f5e1fdc59b273a2c130f3e5", "shasum": "" }, "require": { @@ -5412,7 +5414,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T12:24:43+00:00" + "time": "2024-12-07T08:55:22+00:00" }, { "name": "symfony/intl", @@ -5735,12 +5737,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/messenger.git", - "reference": "2512b9bc1e7093c8bd5adec579a364a198059f4d" + "reference": "cc0e820c02a0a887a88ddb52b7c4de4634677ce6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/messenger/zipball/2512b9bc1e7093c8bd5adec579a364a198059f4d", - "reference": "2512b9bc1e7093c8bd5adec579a364a198059f4d", + "url": "https://api.github.com/repos/symfony/messenger/zipball/cc0e820c02a0a887a88ddb52b7c4de4634677ce6", + "reference": "cc0e820c02a0a887a88ddb52b7c4de4634677ce6", "shasum": "" }, "require": { @@ -5798,7 +5800,7 @@ "description": "Helps applications send and receive messages to/from other applications or via message queues", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/messenger/tree/v7.2.0" + "source": "https://github.com/symfony/messenger/tree/7.2" }, "funding": [ { @@ -5814,7 +5816,7 @@ "type": "tidelift" } ], - "time": "2024-11-26T10:00:31+00:00" + "time": "2024-12-07T08:08:50+00:00" }, { "name": "symfony/mime", @@ -5822,12 +5824,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d" + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/cc84a4b81f62158c3846ac7ff10f696aae2b524d", - "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d", + "url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283", "shasum": "" }, "require": { @@ -5882,7 +5884,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.0" + "source": "https://github.com/symfony/mime/tree/7.2" }, "funding": [ { @@ -5898,7 +5900,7 @@ "type": "tidelift" } ], - "time": "2024-11-23T09:19:39+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/monolog-bridge", @@ -6911,12 +6913,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/property-info.git", - "reference": "b00580d9d7c9654e1df95df85105d0da67418b3f" + "reference": "65fb9be15380f949d72ff405473cce733364b8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/property-info/zipball/b00580d9d7c9654e1df95df85105d0da67418b3f", - "reference": "b00580d9d7c9654e1df95df85105d0da67418b3f", + "url": "https://api.github.com/repos/symfony/property-info/zipball/65fb9be15380f949d72ff405473cce733364b8b4", + "reference": "65fb9be15380f949d72ff405473cce733364b8b4", "shasum": "" }, "require": { @@ -6970,7 +6972,7 @@ "validator" ], "support": { - "source": "https://github.com/symfony/property-info/tree/v7.2.0" + "source": "https://github.com/symfony/property-info/tree/7.2" }, "funding": [ { @@ -6986,7 +6988,7 @@ "type": "tidelift" } ], - "time": "2024-11-27T09:50:52+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/routing", @@ -7154,12 +7156,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/security-bundle.git", - "reference": "4bed2029576bf02a6915c5a58bc8a174af338e6f" + "reference": "1e3fa977bb4a3eccff81819996549bd1f9967f0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-bundle/zipball/4bed2029576bf02a6915c5a58bc8a174af338e6f", - "reference": "4bed2029576bf02a6915c5a58bc8a174af338e6f", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/1e3fa977bb4a3eccff81819996549bd1f9967f0c", + "reference": "1e3fa977bb4a3eccff81819996549bd1f9967f0c", "shasum": "" }, "require": { @@ -7173,7 +7175,7 @@ "symfony/http-foundation": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/password-hasher": "^6.4|^7.0", - "symfony/security-core": "^7.2", + "symfony/security-core": "^7.3", "symfony/security-csrf": "^6.4|^7.0", "symfony/security-http": "^7.2", "symfony/service-contracts": "^2.5|^3" @@ -7236,7 +7238,7 @@ "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-bundle/tree/v7.2.0" + "source": "https://github.com/symfony/security-bundle/tree/7.3" }, "funding": [ { @@ -7252,7 +7254,7 @@ "type": "tidelift" } ], - "time": "2024-10-23T08:31:32+00:00" + "time": "2024-12-07T13:40:54+00:00" }, { "name": "symfony/security-core", @@ -7260,12 +7262,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/security-core.git", - "reference": "fdbf318b939a86f89b0c071f60b9d551261d3cc1" + "reference": "312a726ac8b92bf1544355eefd2d290211d3119b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-core/zipball/fdbf318b939a86f89b0c071f60b9d551261d3cc1", - "reference": "fdbf318b939a86f89b0c071f60b9d551261d3cc1", + "url": "https://api.github.com/repos/symfony/security-core/zipball/312a726ac8b92bf1544355eefd2d290211d3119b", + "reference": "312a726ac8b92bf1544355eefd2d290211d3119b", "shasum": "" }, "require": { @@ -7323,7 +7325,7 @@ "description": "Symfony Security Component - Core Library", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-core/tree/v7.2.0" + "source": "https://github.com/symfony/security-core/tree/7.3" }, "funding": [ { @@ -7339,7 +7341,7 @@ "type": "tidelift" } ], - "time": "2024-11-27T09:50:52+00:00" + "time": "2024-12-07T13:40:54+00:00" }, { "name": "symfony/security-csrf", @@ -7417,12 +7419,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/security-http.git", - "reference": "0d0ab4d491f22306c893b2d30ce73ea911201a61" + "reference": "125844598d9cef4fe72a9f6c4a78ac7c59c3f532" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/security-http/zipball/0d0ab4d491f22306c893b2d30ce73ea911201a61", - "reference": "0d0ab4d491f22306c893b2d30ce73ea911201a61", + "url": "https://api.github.com/repos/symfony/security-http/zipball/125844598d9cef4fe72a9f6c4a78ac7c59c3f532", + "reference": "125844598d9cef4fe72a9f6c4a78ac7c59c3f532", "shasum": "" }, "require": { @@ -7481,7 +7483,7 @@ "description": "Symfony Security Component - HTTP Integration", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/security-http/tree/v7.2.0" + "source": "https://github.com/symfony/security-http/tree/7.2" }, "funding": [ { @@ -7497,7 +7499,7 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:40:36+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/serializer", @@ -7687,12 +7689,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/stimulus-bundle.git", - "reference": "2e840a3b12f06b33441cc3eb8907f51b806a7e4b" + "reference": "e13034d428354023c82a1db108d40fdf6cec2d36" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/2e840a3b12f06b33441cc3eb8907f51b806a7e4b", - "reference": "2e840a3b12f06b33441cc3eb8907f51b806a7e4b", + "url": "https://api.github.com/repos/symfony/stimulus-bundle/zipball/e13034d428354023c82a1db108d40fdf6cec2d36", + "reference": "e13034d428354023c82a1db108d40fdf6cec2d36", "shasum": "" }, "require": { @@ -7733,7 +7735,7 @@ "symfony-ux" ], "support": { - "source": "https://github.com/symfony/stimulus-bundle/tree/v2.22.0" + "source": "https://github.com/symfony/stimulus-bundle/tree/2.x" }, "funding": [ { @@ -7749,7 +7751,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T07:57:38+00:00" + "time": "2024-12-06T14:30:33+00:00" }, { "name": "symfony/stopwatch", @@ -8080,12 +8082,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/twig-bridge.git", - "reference": "1343696b988e72beafc63a6cf386922ccb314a08" + "reference": "d5cdf4d59da5ab44ebd7503480c22d8235887de0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/1343696b988e72beafc63a6cf386922ccb314a08", - "reference": "1343696b988e72beafc63a6cf386922ccb314a08", + "url": "https://api.github.com/repos/symfony/twig-bridge/zipball/d5cdf4d59da5ab44ebd7503480c22d8235887de0", + "reference": "d5cdf4d59da5ab44ebd7503480c22d8235887de0", "shasum": "" }, "require": { @@ -8182,7 +8184,7 @@ "type": "tidelift" } ], - "time": "2024-12-02T12:43:01+00:00" + "time": "2024-12-07T09:50:32+00:00" }, { "name": "symfony/twig-bundle", @@ -8274,12 +8276,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/type-info.git", - "reference": "e0bfd95bceb3886c59487828537691aecb7d9c6b" + "reference": "cf153a6172679757551365a8762a06ab6603c714" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/type-info/zipball/e0bfd95bceb3886c59487828537691aecb7d9c6b", - "reference": "e0bfd95bceb3886c59487828537691aecb7d9c6b", + "url": "https://api.github.com/repos/symfony/type-info/zipball/cf153a6172679757551365a8762a06ab6603c714", + "reference": "cf153a6172679757551365a8762a06ab6603c714", "shasum": "" }, "require": { @@ -8330,7 +8332,7 @@ "type" ], "support": { - "source": "https://github.com/symfony/type-info/tree/v7.2.0" + "source": "https://github.com/symfony/type-info/tree/7.2" }, "funding": [ { @@ -8346,7 +8348,7 @@ "type": "tidelift" } ], - "time": "2024-11-18T09:51:31+00:00" + "time": "2024-12-07T08:08:50+00:00" }, { "name": "symfony/uid", @@ -8354,12 +8356,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/uid.git", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426" + "reference": "f5e643aee01ab83011b08a466b5194131763dd3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/uid/zipball/2d294d0c48df244c71c105a169d0190bfb080426", - "reference": "2d294d0c48df244c71c105a169d0190bfb080426", + "url": "https://api.github.com/repos/symfony/uid/zipball/f5e643aee01ab83011b08a466b5194131763dd3e", + "reference": "f5e643aee01ab83011b08a466b5194131763dd3e", "shasum": "" }, "require": { @@ -8404,7 +8406,7 @@ "uuid" ], "support": { - "source": "https://github.com/symfony/uid/tree/v7.2.0" + "source": "https://github.com/symfony/uid/tree/7.3" }, "funding": [ { @@ -8420,7 +8422,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-12-07T08:11:28+00:00" }, { "name": "symfony/ux-chartjs", @@ -8428,12 +8430,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/ux-chartjs.git", - "reference": "32476b05eb1bd76dc049a2747cf398e76a9a44a5" + "reference": "6e7de01ea469840da2b7458b660b52e846e279e1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-chartjs/zipball/32476b05eb1bd76dc049a2747cf398e76a9a44a5", - "reference": "32476b05eb1bd76dc049a2747cf398e76a9a44a5", + "url": "https://api.github.com/repos/symfony/ux-chartjs/zipball/6e7de01ea469840da2b7458b660b52e846e279e1", + "reference": "6e7de01ea469840da2b7458b660b52e846e279e1", "shasum": "" }, "require": { @@ -8485,7 +8487,7 @@ "symfony-ux" ], "support": { - "source": "https://github.com/symfony/ux-chartjs/tree/v2.22.0" + "source": "https://github.com/symfony/ux-chartjs/tree/2.x" }, "funding": [ { @@ -8501,7 +8503,7 @@ "type": "tidelift" } ], - "time": "2024-11-20T07:57:38+00:00" + "time": "2024-12-05T14:25:02+00:00" }, { "name": "symfony/ux-live-component", @@ -8509,12 +8511,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/ux-live-component.git", - "reference": "2df6a25f25788864e65cb8812d85e14ef80b6b44" + "reference": "060e0c64e64125a4dfbf37dec281157faade1feb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-live-component/zipball/2df6a25f25788864e65cb8812d85e14ef80b6b44", - "reference": "2df6a25f25788864e65cb8812d85e14ef80b6b44", + "url": "https://api.github.com/repos/symfony/ux-live-component/zipball/060e0c64e64125a4dfbf37dec281157faade1feb", + "reference": "060e0c64e64125a4dfbf37dec281157faade1feb", "shasum": "" }, "require": { @@ -8580,7 +8582,7 @@ "twig" ], "support": { - "source": "https://github.com/symfony/ux-live-component/tree/v2.22.0" + "source": "https://github.com/symfony/ux-live-component/tree/2.x" }, "funding": [ { @@ -8596,7 +8598,7 @@ "type": "tidelift" } ], - "time": "2024-11-29T15:31:04+00:00" + "time": "2024-12-07T10:13:15+00:00" }, { "name": "symfony/ux-turbo", @@ -8604,12 +8606,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/ux-turbo.git", - "reference": "f7af0aa09190354dd4630ea330d8a3fc3e8ef278" + "reference": "97718ea4bca26f0db843c3c0de338d6900c5a002" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/f7af0aa09190354dd4630ea330d8a3fc3e8ef278", - "reference": "f7af0aa09190354dd4630ea330d8a3fc3e8ef278", + "url": "https://api.github.com/repos/symfony/ux-turbo/zipball/97718ea4bca26f0db843c3c0de338d6900c5a002", + "reference": "97718ea4bca26f0db843c3c0de338d6900c5a002", "shasum": "" }, "require": { @@ -8679,7 +8681,7 @@ "turbo-stream" ], "support": { - "source": "https://github.com/symfony/ux-turbo/tree/v2.22.0" + "source": "https://github.com/symfony/ux-turbo/tree/2.x" }, "funding": [ { @@ -8695,7 +8697,7 @@ "type": "tidelift" } ], - "time": "2024-11-29T15:25:16+00:00" + "time": "2024-12-05T14:25:02+00:00" }, { "name": "symfony/ux-twig-component", @@ -8703,12 +8705,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/ux-twig-component.git", - "reference": "03177a494399fbdcbb1f5f2aee017ccf8df581d9" + "reference": "9b347f6ca2d9e18cee630787f0a6aa453982bf18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/03177a494399fbdcbb1f5f2aee017ccf8df581d9", - "reference": "03177a494399fbdcbb1f5f2aee017ccf8df581d9", + "url": "https://api.github.com/repos/symfony/ux-twig-component/zipball/9b347f6ca2d9e18cee630787f0a6aa453982bf18", + "reference": "9b347f6ca2d9e18cee630787f0a6aa453982bf18", "shasum": "" }, "require": { @@ -8763,7 +8765,7 @@ "twig" ], "support": { - "source": "https://github.com/symfony/ux-twig-component/tree/v2.22.0" + "source": "https://github.com/symfony/ux-twig-component/tree/2.x" }, "funding": [ { @@ -8779,7 +8781,7 @@ "type": "tidelift" } ], - "time": "2024-11-23T06:59:34+00:00" + "time": "2024-12-07T18:05:50+00:00" }, { "name": "symfony/validator", @@ -8884,12 +8886,12 @@ "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "340357866b7ebea88d2f325c167cb5e4dd3383e4" + "reference": "de6124d690069ee8d4cd21b00050aa231c0434e3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/340357866b7ebea88d2f325c167cb5e4dd3383e4", - "reference": "340357866b7ebea88d2f325c167cb5e4dd3383e4", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/de6124d690069ee8d4cd21b00050aa231c0434e3", + "reference": "de6124d690069ee8d4cd21b00050aa231c0434e3", "shasum": "" }, "require": { @@ -8959,7 +8961,7 @@ "type": "tidelift" } ], - "time": "2024-11-28T14:07:15+00:00" + "time": "2024-11-28T16:26:37+00:00" }, { "name": "symfony/var-exporter", @@ -9254,12 +9256,12 @@ "source": { "type": "git", "url": "https://github.com/twbs/bootstrap.git", - "reference": "ec96eacd0e6f297a64ee058b22ce9f567c0860e3" + "reference": "760b4cc41dcf09ad7f68c6b3b11768755ff2ec0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twbs/bootstrap/zipball/ec96eacd0e6f297a64ee058b22ce9f567c0860e3", - "reference": "ec96eacd0e6f297a64ee058b22ce9f567c0860e3", + "url": "https://api.github.com/repos/twbs/bootstrap/zipball/760b4cc41dcf09ad7f68c6b3b11768755ff2ec0b", + "reference": "760b4cc41dcf09ad7f68c6b3b11768755ff2ec0b", "shasum": "" }, "replace": { @@ -9297,7 +9299,7 @@ "issues": "https://github.com/twbs/bootstrap/issues", "source": "https://github.com/twbs/bootstrap/tree/main" }, - "time": "2024-11-22T09:54:10+00:00" + "time": "2024-12-07T05:31:01+00:00" }, { "name": "twig/extra-bundle", @@ -9982,12 +9984,12 @@ "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "23534bff9eb778be9b1c55570548492741c3393c" + "reference": "0fa6fe639a961a0765d4bc8fb341731cf1c4e03f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/23534bff9eb778be9b1c55570548492741c3393c", - "reference": "23534bff9eb778be9b1c55570548492741c3393c", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/0fa6fe639a961a0765d4bc8fb341731cf1c4e03f", + "reference": "0fa6fe639a961a0765d4bc8fb341731cf1c4e03f", "shasum": "" }, "require": { @@ -10078,7 +10080,7 @@ "type": "github" } ], - "time": "2024-11-30T08:32:38+00:00" + "time": "2024-12-04T01:12:42+00:00" }, { "name": "masterminds/html5", @@ -10440,12 +10442,12 @@ "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "927be0d98b92d1b0636d78e95bf9a7ba6042a5c8" + "reference": "4b7df8d682ecb61c69acbde9fb16f273105f6e1b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/927be0d98b92d1b0636d78e95bf9a7ba6042a5c8", - "reference": "927be0d98b92d1b0636d78e95bf9a7ba6042a5c8", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/4b7df8d682ecb61c69acbde9fb16f273105f6e1b", + "reference": "4b7df8d682ecb61c69acbde9fb16f273105f6e1b", "shasum": "" }, "require": { @@ -10491,7 +10493,7 @@ "type": "github" } ], - "time": "2024-12-03T14:24:47+00:00" + "time": "2024-12-07T19:32:47+00:00" }, { "name": "phpstan/phpstan-doctrine", @@ -10744,12 +10746,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "5a5c19843209812d6c8cb91d30478dfce3d8bdad" + "reference": "e656040b5bdab9d6ca046287327bbb47c62be886" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5a5c19843209812d6c8cb91d30478dfce3d8bdad", - "reference": "5a5c19843209812d6c8cb91d30478dfce3d8bdad", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e656040b5bdab9d6ca046287327bbb47c62be886", + "reference": "e656040b5bdab9d6ca046287327bbb47c62be886", "shasum": "" }, "require": { @@ -10814,7 +10816,7 @@ "type": "github" } ], - "time": "2024-10-31T05:58:31+00:00" + "time": "2024-12-07T06:07:48+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11067,12 +11069,12 @@ "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "e75fd04dce45b3a064a44107d8037b411ecbd307" + "reference": "51b65819c798fa8d7d8b5ede4d4320d98cf19d55" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/e75fd04dce45b3a064a44107d8037b411ecbd307", - "reference": "e75fd04dce45b3a064a44107d8037b411ecbd307", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/51b65819c798fa8d7d8b5ede4d4320d98cf19d55", + "reference": "51b65819c798fa8d7d8b5ede4d4320d98cf19d55", "shasum": "" }, "require": { @@ -11160,7 +11162,7 @@ "type": "tidelift" } ], - "time": "2024-11-30T07:38:36+00:00" + "time": "2024-12-07T09:46:57+00:00" }, { "name": "react/cache", @@ -13247,12 +13249,12 @@ "source": { "type": "git", "url": "https://github.com/VincentLanglet/Twig-CS-Fixer.git", - "reference": "0c50bdb80b2de1a39e6d3d3ad96255a0ebdbeab0" + "reference": "a193004602d7a9b1c17408eb8b8a1632532a28a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/0c50bdb80b2de1a39e6d3d3ad96255a0ebdbeab0", - "reference": "0c50bdb80b2de1a39e6d3d3ad96255a0ebdbeab0", + "url": "https://api.github.com/repos/VincentLanglet/Twig-CS-Fixer/zipball/a193004602d7a9b1c17408eb8b8a1632532a28a7", + "reference": "a193004602d7a9b1c17408eb8b8a1632532a28a7", "shasum": "" }, "require": { @@ -13312,7 +13314,7 @@ "homepage": "https://github.com/VincentLanglet/Twig-CS-Fixer", "support": { "issues": "https://github.com/VincentLanglet/Twig-CS-Fixer/issues", - "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/main" + "source": "https://github.com/VincentLanglet/Twig-CS-Fixer/tree/3.4.0" }, "funding": [ { @@ -13320,7 +13322,7 @@ "type": "github" } ], - "time": "2024-11-30T08:25:10+00:00" + "time": "2024-12-04T18:37:59+00:00" } ], "aliases": [], diff --git a/devenv.lock b/devenv.lock index 61f5c0f..84183d9 100644 --- a/devenv.lock +++ b/devenv.lock @@ -3,10 +3,10 @@ "devenv": { "locked": { "dir": "src/modules", - "lastModified": 1732896163, + "lastModified": 1733595596, "owner": "cachix", "repo": "devenv", - "rev": "2c928a199d56191d7a53f29ccafa56238c8ce4e5", + "rev": "c1ca69791bfa466e77b3d21e0e2f492810d83250", "type": "github" }, "original": { @@ -19,10 +19,10 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1732722421, + "lastModified": 1733328505, "owner": "edolstra", "repo": "flake-compat", - "rev": "9ed2ac151eada2306ca8c418ebd97807bb08f6ac", + "rev": "ff81ac966bb2cae68946d5ed5fc4994f96d0ffec", "type": "github" }, "original": { @@ -53,10 +53,10 @@ }, "nixpkgs": { "locked": { - "lastModified": 1733097829, + "lastModified": 1733376361, "owner": "nixos", "repo": "nixpkgs", - "rev": "2c15aa59df0017ca140d9ba302412298ab4bf22a", + "rev": "929116e316068c7318c54eb4d827f7d9756d5e9c", "type": "github" }, "original": { @@ -68,10 +68,10 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1733016324, + "lastModified": 1733384649, "owner": "NixOS", "repo": "nixpkgs", - "rev": "7e1ca67996afd8233d9033edd26e442836cc2ad6", + "rev": "190c31a89e5eec80dd6604d7f9e5af3802a58a13", "type": "github" }, "original": { @@ -91,10 +91,10 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1732021966, + "lastModified": 1733318908, "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "3308484d1a443fc5bc92012435d79e80458fe43c", + "rev": "6f4e2a2112050951a314d2733a994fbab94864c6", "type": "github" }, "original": { diff --git a/importmap.php b/importmap.php index c37c8d7..9f8ae71 100644 --- a/importmap.php +++ b/importmap.php @@ -50,7 +50,7 @@ 'version' => '6.8.0', ], '@codemirror/view' => [ - 'version' => '6.35.0', + 'version' => '6.35.2', ], '@codemirror/state' => [ 'version' => '6.4.1', diff --git a/package.json b/package.json index b76c669..77de21d 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ }, "dependencies": { "@swc/cli": "^0.5.2", - "@swc/core": "^1.9.3" + "@swc/core": "^1.10.0" }, "packageManager": "pnpm@9.14.4+sha512.c8180b3fbe4e4bca02c94234717896b5529740a6cbadf19fa78254270403ea2f27d4e1d46a08a0f56c89b63dc8ebfd3ee53326da720273794e6200fcf0d184ab", "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42325b8..5857098 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: dependencies: "@swc/cli": specifier: ^0.5.2 - version: 0.5.2(@swc/core@1.9.3) + version: 0.5.2(@swc/core@1.10.0) "@swc/core": - specifier: ^1.9.3 - version: 1.9.3 + specifier: ^1.10.0 + version: 1.10.0 devDependencies: "@codemirror/lang-sql": specifier: ^6.8.0 - version: 6.8.0(@codemirror/view@6.35.0) + version: 6.8.0(@codemirror/view@6.35.2) "@codemirror/state": specifier: ^6.4.1 version: 6.4.1 @@ -95,9 +95,9 @@ packages: integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==, } - "@codemirror/view@6.35.0": + "@codemirror/view@6.35.2": resolution: { - integrity: sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==, + integrity: sha512-u04R04XFCYCNaHoNRr37WUUAfnxKPwPdqV+370NiO6i85qB1J/qCD/WbbMJsyJfRWhXIJXAe2BG/oTzAggqv4A==, } "@dprint/darwin-arm64@0.47.6": @@ -177,15 +177,15 @@ packages: } engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - "@eslint/config-array@0.19.0": + "@eslint/config-array@0.19.1": resolution: { - integrity: sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==, + integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - "@eslint/core@0.9.0": + "@eslint/core@0.9.1": resolution: { - integrity: sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==, + integrity: sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } @@ -201,15 +201,15 @@ packages: } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - "@eslint/object-schema@2.1.4": + "@eslint/object-schema@2.1.5": resolution: { - integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==, + integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } - "@eslint/plugin-kit@0.2.3": + "@eslint/plugin-kit@0.2.4": resolution: { - integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==, + integrity: sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==, } engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } @@ -451,89 +451,89 @@ packages: chokidar: optional: true - "@swc/core-darwin-arm64@1.9.3": + "@swc/core-darwin-arm64@1.10.0": resolution: { - integrity: sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==, + integrity: sha512-wCeUpanqZyzvgqWRtXIyhcFK3CqukAlYyP+fJpY2gWc/+ekdrenNIfZMwY7tyTFDkXDYEKzvn3BN/zDYNJFowQ==, } engines: { node: ">=10" } cpu: [arm64] os: [darwin] - "@swc/core-darwin-x64@1.9.3": + "@swc/core-darwin-x64@1.10.0": resolution: { - integrity: sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==, + integrity: sha512-0CZPzqTynUBO+SHEl/qKsFSahp2Jv/P2ZRjFG0gwZY5qIcr1+B/v+o74/GyNMBGz9rft+F2WpU31gz2sJwyF4A==, } engines: { node: ">=10" } cpu: [x64] os: [darwin] - "@swc/core-linux-arm-gnueabihf@1.9.3": + "@swc/core-linux-arm-gnueabihf@1.10.0": resolution: { - integrity: sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==, + integrity: sha512-oq+DdMu5uJOFPtRkeiITc4kxmd+QSmK+v+OBzlhdGkSgoH3yRWZP+H2ao0cBXo93ZgCr2LfjiER0CqSKhjGuNA==, } engines: { node: ">=10" } cpu: [arm] os: [linux] - "@swc/core-linux-arm64-gnu@1.9.3": + "@swc/core-linux-arm64-gnu@1.10.0": resolution: { - integrity: sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==, + integrity: sha512-Y6+PC8knchEViRxiCUj3j8wsGXaIhuvU+WqrFqV834eiItEMEI9+Vh3FovqJMBE3L7d4E4ZQtgImHCXjrHfxbw==, } engines: { node: ">=10" } cpu: [arm64] os: [linux] - "@swc/core-linux-arm64-musl@1.9.3": + "@swc/core-linux-arm64-musl@1.10.0": resolution: { - integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==, + integrity: sha512-EbrX9A5U4cECCQQfky7945AW9GYnTXtCUXElWTkTYmmyQK87yCyFfY8hmZ9qMFIwxPOH6I3I2JwMhzdi8Qoz7g==, } engines: { node: ">=10" } cpu: [arm64] os: [linux] - "@swc/core-linux-x64-gnu@1.9.3": + "@swc/core-linux-x64-gnu@1.10.0": resolution: { - integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==, + integrity: sha512-TaxpO6snTjjfLXFYh5EjZ78se69j2gDcqEM8yB9gguPYwkCHi2Ylfmh7iVaNADnDJFtjoAQp0L41bTV/Pfq9Cg==, } engines: { node: ">=10" } cpu: [x64] os: [linux] - "@swc/core-linux-x64-musl@1.9.3": + "@swc/core-linux-x64-musl@1.10.0": resolution: { - integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==, + integrity: sha512-IEGvDd6aEEKEyZFZ8oCKuik05G5BS7qwG5hO5PEMzdGeh8JyFZXxsfFXbfeAqjue4UaUUrhnoX+Ze3M2jBVMHw==, } engines: { node: ">=10" } cpu: [x64] os: [linux] - "@swc/core-win32-arm64-msvc@1.9.3": + "@swc/core-win32-arm64-msvc@1.10.0": resolution: { - integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==, + integrity: sha512-UkQ952GSpY+Z6XONj9GSW8xGSkF53jrCsuLj0nrcuw7Dvr1a816U/9WYZmmcYS8tnG2vHylhpm6csQkyS8lpCw==, } engines: { node: ">=10" } cpu: [arm64] os: [win32] - "@swc/core-win32-ia32-msvc@1.9.3": + "@swc/core-win32-ia32-msvc@1.10.0": resolution: { - integrity: sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==, + integrity: sha512-a2QpIZmTiT885u/mUInpeN2W9ClCnqrV2LnMqJR1/Fgx1Afw/hAtiDZPtQ0SqS8yDJ2VR5gfNZo3gpxWMrqdVA==, } engines: { node: ">=10" } cpu: [ia32] os: [win32] - "@swc/core-win32-x64-msvc@1.9.3": + "@swc/core-win32-x64-msvc@1.10.0": resolution: { - integrity: sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==, + integrity: sha512-tZcCmMwf483nwsEBfUk5w9e046kMa1iSik4bP9Kwi2FGtOfHuDfIcwW4jek3hdcgF5SaBW1ktnK/lgQLDi5AtA==, } engines: { node: ">=10" } cpu: [x64] os: [win32] - "@swc/core@1.9.3": + "@swc/core@1.10.0": resolution: { - integrity: sha512-oRj0AFePUhtatX+BscVhnzaAmWjpfAeySpM1TCbxA1rtBDeH/JDhi5yYzAKneDYtVtBvA7ApfeuzhMC9ye4xSg==, + integrity: sha512-+CuuTCmQFfzaNGg1JmcZvdUVITQXJk9sMnl1C2TiDLzOSVOJRwVD4dNo5dljX/qxpMAN+2BIYlwjlSkoGi6grg==, } engines: { node: ">=10" } peerDependencies: @@ -920,9 +920,9 @@ packages: } engines: { node: ">= 8" } - debug@4.3.7: + debug@4.4.0: resolution: { - integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, + integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==, } engines: { node: ">=6.0" } peerDependencies: @@ -1497,9 +1497,9 @@ packages: } engines: { node: ">=8.6" } - piscina@4.7.0: + piscina@4.8.0: resolution: { - integrity: sha512-b8hvkpp9zS0zsfa939b/jXbe64Z2gZv0Ha7FYPNUiDIB1y2AtxcOZdfP8xN8HFjUaqQiT9gRlfjAsoL8vdJ1Iw==, + integrity: sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==, } prelude-ls@1.2.1: @@ -1679,9 +1679,9 @@ packages: integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==, } - text-decoder@1.2.1: + text-decoder@1.2.2: resolution: { - integrity: sha512-x9v3H/lTKIJKQQe7RPQkLfKAnc9lUTkWDypIQgTzPJAq+5/GCDHonmshfvlsNSj58yyshbIJJDLmU15qNERrXQ==, + integrity: sha512-/MDslo7ZyWTA2vnk1j7XoDVfXsGk3tp+zFEJHJGm0UjIlQifonVFwlVbQDFh8KJzTBnT8ie115TYqir6bclddA==, } through@2.3.8: @@ -1781,23 +1781,23 @@ packages: engines: { node: ">=10" } snapshots: - "@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3)": + "@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.2)(@lezer/common@1.2.3)": dependencies: "@codemirror/language": 6.10.6 "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 "@lezer/common": 1.2.3 "@codemirror/commands@6.7.1": dependencies: "@codemirror/language": 6.10.6 "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 "@lezer/common": 1.2.3 - "@codemirror/lang-sql@6.8.0(@codemirror/view@6.35.0)": + "@codemirror/lang-sql@6.8.0(@codemirror/view@6.35.2)": dependencies: - "@codemirror/autocomplete": 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + "@codemirror/autocomplete": 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.2)(@lezer/common@1.2.3) "@codemirror/language": 6.10.6 "@codemirror/state": 6.4.1 "@lezer/common": 1.2.3 @@ -1809,7 +1809,7 @@ snapshots: "@codemirror/language@6.10.6": dependencies: "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 "@lezer/common": 1.2.3 "@lezer/highlight": 1.2.1 "@lezer/lr": 1.4.2 @@ -1818,18 +1818,18 @@ snapshots: "@codemirror/lint@6.8.4": dependencies: "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 crelt: 1.0.6 "@codemirror/search@6.5.8": dependencies: "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 crelt: 1.0.6 "@codemirror/state@6.4.1": {} - "@codemirror/view@6.35.0": + "@codemirror/view@6.35.2": dependencies: "@codemirror/state": 6.4.1 style-mod: 4.1.2 @@ -1869,20 +1869,22 @@ snapshots: "@eslint-community/regexpp@4.12.1": {} - "@eslint/config-array@0.19.0": + "@eslint/config-array@0.19.1": dependencies: - "@eslint/object-schema": 2.1.4 - debug: 4.3.7 + "@eslint/object-schema": 2.1.5 + debug: 4.4.0 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - "@eslint/core@0.9.0": {} + "@eslint/core@0.9.1": + dependencies: + "@types/json-schema": 7.0.15 "@eslint/eslintrc@3.2.0": dependencies: ajv: 6.12.6 - debug: 4.3.7 + debug: 4.4.0 espree: 10.3.0 globals: 14.0.0 ignore: 5.3.2 @@ -1895,9 +1897,9 @@ snapshots: "@eslint/js@9.16.0": {} - "@eslint/object-schema@2.1.4": {} + "@eslint/object-schema@2.1.5": {} - "@eslint/plugin-kit@0.2.3": + "@eslint/plugin-kit@0.2.4": dependencies: levn: 0.4.1 @@ -2016,64 +2018,64 @@ snapshots: "@sindresorhus/is@5.6.0": {} - "@swc/cli@0.5.2(@swc/core@1.9.3)": + "@swc/cli@0.5.2(@swc/core@1.10.0)": dependencies: - "@swc/core": 1.9.3 + "@swc/core": 1.10.0 "@swc/counter": 0.1.3 "@xhmikosr/bin-wrapper": 13.0.5 commander: 8.3.0 fast-glob: 3.3.2 minimatch: 9.0.5 - piscina: 4.7.0 + piscina: 4.8.0 semver: 7.6.3 slash: 3.0.0 source-map: 0.7.4 - "@swc/core-darwin-arm64@1.9.3": + "@swc/core-darwin-arm64@1.10.0": optional: true - "@swc/core-darwin-x64@1.9.3": + "@swc/core-darwin-x64@1.10.0": optional: true - "@swc/core-linux-arm-gnueabihf@1.9.3": + "@swc/core-linux-arm-gnueabihf@1.10.0": optional: true - "@swc/core-linux-arm64-gnu@1.9.3": + "@swc/core-linux-arm64-gnu@1.10.0": optional: true - "@swc/core-linux-arm64-musl@1.9.3": + "@swc/core-linux-arm64-musl@1.10.0": optional: true - "@swc/core-linux-x64-gnu@1.9.3": + "@swc/core-linux-x64-gnu@1.10.0": optional: true - "@swc/core-linux-x64-musl@1.9.3": + "@swc/core-linux-x64-musl@1.10.0": optional: true - "@swc/core-win32-arm64-msvc@1.9.3": + "@swc/core-win32-arm64-msvc@1.10.0": optional: true - "@swc/core-win32-ia32-msvc@1.9.3": + "@swc/core-win32-ia32-msvc@1.10.0": optional: true - "@swc/core-win32-x64-msvc@1.9.3": + "@swc/core-win32-x64-msvc@1.10.0": optional: true - "@swc/core@1.9.3": + "@swc/core@1.10.0": dependencies: "@swc/counter": 0.1.3 "@swc/types": 0.1.17 optionalDependencies: - "@swc/core-darwin-arm64": 1.9.3 - "@swc/core-darwin-x64": 1.9.3 - "@swc/core-linux-arm-gnueabihf": 1.9.3 - "@swc/core-linux-arm64-gnu": 1.9.3 - "@swc/core-linux-arm64-musl": 1.9.3 - "@swc/core-linux-x64-gnu": 1.9.3 - "@swc/core-linux-x64-musl": 1.9.3 - "@swc/core-win32-arm64-msvc": 1.9.3 - "@swc/core-win32-ia32-msvc": 1.9.3 - "@swc/core-win32-x64-msvc": 1.9.3 + "@swc/core-darwin-arm64": 1.10.0 + "@swc/core-darwin-x64": 1.10.0 + "@swc/core-linux-arm-gnueabihf": 1.10.0 + "@swc/core-linux-arm64-gnu": 1.10.0 + "@swc/core-linux-arm64-musl": 1.10.0 + "@swc/core-linux-x64-gnu": 1.10.0 + "@swc/core-linux-x64-musl": 1.10.0 + "@swc/core-win32-arm64-msvc": 1.10.0 + "@swc/core-win32-ia32-msvc": 1.10.0 + "@swc/core-win32-x64-msvc": 1.10.0 "@swc/counter@0.1.3": {} @@ -2132,7 +2134,7 @@ snapshots: "@typescript-eslint/types": 8.17.0 "@typescript-eslint/typescript-estree": 8.17.0(typescript@5.7.2) "@typescript-eslint/visitor-keys": 8.17.0 - debug: 4.3.7 + debug: 4.4.0 eslint: 9.16.0 optionalDependencies: typescript: 5.7.2 @@ -2148,7 +2150,7 @@ snapshots: dependencies: "@typescript-eslint/typescript-estree": 8.17.0(typescript@5.7.2) "@typescript-eslint/utils": 8.17.0(eslint@9.16.0)(typescript@5.7.2) - debug: 4.3.7 + debug: 4.4.0 eslint: 9.16.0 ts-api-utils: 1.4.3(typescript@5.7.2) optionalDependencies: @@ -2162,7 +2164,7 @@ snapshots: dependencies: "@typescript-eslint/types": 8.17.0 "@typescript-eslint/visitor-keys": 8.17.0 - debug: 4.3.7 + debug: 4.4.0 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 @@ -2350,13 +2352,13 @@ snapshots: codemirror@6.0.1(@lezer/common@1.2.3): dependencies: - "@codemirror/autocomplete": 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)(@lezer/common@1.2.3) + "@codemirror/autocomplete": 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.2)(@lezer/common@1.2.3) "@codemirror/commands": 6.7.1 "@codemirror/language": 6.10.6 "@codemirror/lint": 6.8.4 "@codemirror/search": 6.5.8 "@codemirror/state": 6.4.1 - "@codemirror/view": 6.35.0 + "@codemirror/view": 6.35.2 transitivePeerDependencies: - "@lezer/common" @@ -2384,7 +2386,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - debug@4.3.7: + debug@4.4.0: dependencies: ms: 2.1.3 @@ -2427,11 +2429,11 @@ snapshots: dependencies: "@eslint-community/eslint-utils": 4.4.1(eslint@9.16.0) "@eslint-community/regexpp": 4.12.1 - "@eslint/config-array": 0.19.0 - "@eslint/core": 0.9.0 + "@eslint/config-array": 0.19.1 + "@eslint/core": 0.9.1 "@eslint/eslintrc": 3.2.0 "@eslint/js": 9.16.0 - "@eslint/plugin-kit": 0.2.3 + "@eslint/plugin-kit": 0.2.4 "@humanfs/node": 0.16.6 "@humanwhocodes/module-importer": 1.0.1 "@humanwhocodes/retry": 0.4.1 @@ -2440,7 +2442,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.3.7 + debug: 4.4.0 escape-string-regexp: 4.0.0 eslint-scope: 8.2.0 eslint-visitor-keys: 4.2.0 @@ -2752,7 +2754,7 @@ snapshots: picomatch@2.3.1: {} - piscina@4.7.0: + piscina@4.8.0: optionalDependencies: "@napi-rs/nice": 1.0.1 @@ -2824,7 +2826,7 @@ snapshots: dependencies: fast-fifo: 1.3.2 queue-tick: 1.0.1 - text-decoder: 1.2.1 + text-decoder: 1.2.2 optionalDependencies: bare-events: 2.5.0 @@ -2854,7 +2856,9 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.21.0 - text-decoder@1.2.1: {} + text-decoder@1.2.2: + dependencies: + b4a: 1.6.7 through@2.3.8: {} From bead57e0eb6021e046be7016a95bf7ec1dd94835 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Sun, 8 Dec 2024 21:48:09 +0800 Subject: [PATCH 40/77] fix(command): Remove unneeded conditions --- src/Command/CreateUsersCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Command/CreateUsersCommand.php b/src/Command/CreateUsersCommand.php index a20489f..04c36c0 100644 --- a/src/Command/CreateUsersCommand.php +++ b/src/Command/CreateUsersCommand.php @@ -151,7 +151,7 @@ private static function parseUsers(string $filename): array $roles = $row[$rolesIndex]; $group = false !== $groupIndex ? $row[$groupIndex] : null; - if (!\is_string($email) || !\is_string($name) || !\is_string($roles) || (!\is_string($group) && null !== $group)) { + if (!\is_string($email) || !\is_string($name) || !\is_string($roles) || !\is_string($group)) { throw new \RuntimeException("Invalid row in $filename."); } From 8edd260e2cdab51a3e43fccb966f791cd006f242 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Sun, 8 Dec 2024 23:19:28 +0800 Subject: [PATCH 41/77] feat(email): Allow adding plain text content --- migrations/Version20241208144321.php | 41 +++++++++++++++++++ src/Entity/Email.php | 26 +++++++++--- .../EmailCreatedSubscriber.php | 19 +++++++-- .../EmailCreatedSubscriberTest.php | 4 +- 4 files changed, 80 insertions(+), 10 deletions(-) create mode 100644 migrations/Version20241208144321.php diff --git a/migrations/Version20241208144321.php b/migrations/Version20241208144321.php new file mode 100644 index 0000000..ac2ce25 --- /dev/null +++ b/migrations/Version20241208144321.php @@ -0,0 +1,41 @@ +addSql(<<<'SQL' + ALTER TABLE email ADD text_content TEXT NOT NULL DEFAULT '' + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE email RENAME COLUMN content TO html_content + SQL); + } + + public function down(Schema $schema): void + { + // this down() migration is auto-generated, please modify it to your needs + $this->addSql(<<<'SQL' + ALTER TABLE email RENAME COLUMN html_content TO content + SQL); + $this->addSql(<<<'SQL' + ALTER TABLE email DROP text_content + SQL); + } +} diff --git a/src/Entity/Email.php b/src/Entity/Email.php index d58a8d4..b4692ae 100644 --- a/src/Entity/Email.php +++ b/src/Entity/Email.php @@ -25,7 +25,11 @@ class Email #[ORM\Column(type: Types::TEXT)] #[NotBlank] - private string $content = ''; + private string $textContent = ''; + + #[ORM\Column(type: Types::TEXT)] + #[NotBlank] + private string $htmlContent = ''; /** * @var Collection @@ -63,14 +67,26 @@ public function setSubject(string $subject): static return $this; } - public function getContent(): string + public function getTextContent(): string + { + return $this->textContent; + } + + public function setTextContent(string $textContent): static + { + $this->textContent = $textContent; + + return $this; + } + + public function getHtmlContent(): string { - return $this->content; + return $this->htmlContent; } - public function setContent(string $content): static + public function setHtmlContent(string $htmlContent): static { - $this->content = $content; + $this->htmlContent = $htmlContent; return $this; } diff --git a/src/EventSubscriber/EmailCreatedSubscriber.php b/src/EventSubscriber/EmailCreatedSubscriber.php index a29e225..6fc0624 100644 --- a/src/EventSubscriber/EmailCreatedSubscriber.php +++ b/src/EventSubscriber/EmailCreatedSubscriber.php @@ -45,11 +45,21 @@ public function onMessageEvent(MessageEvent $event): void return; } - $body = $message->getHtmlBody(); - if (!\is_string($body)) { + $textBody = $message->getTextBody(); + if (!\is_string($textBody)) { + $this->logger->warning('The message does not have an valid text body.', [ + 'message' => $message, + 'body' => $textBody, + ]); + + return; + } + + $htmlBody = $message->getHtmlBody(); + if (!\is_string($htmlBody)) { $this->logger->warning('The message does not have an valid HTML body.', [ 'message' => $message, - 'body' => $body, + 'body' => $htmlBody, ]); return; @@ -68,7 +78,8 @@ public function onMessageEvent(MessageEvent $event): void $email = (new EmailEntity()) ->setSubject($subject) - ->setContent($body) + ->setTextContent($textBody) + ->setHtmlContent($htmlBody) ->setKind($kind); $this->entityManager->persist($email); diff --git a/tests/EventListener/EmailCreatedSubscriberTest.php b/tests/EventListener/EmailCreatedSubscriberTest.php index 4b49fbe..45f8bac 100644 --- a/tests/EventListener/EmailCreatedSubscriberTest.php +++ b/tests/EventListener/EmailCreatedSubscriberTest.php @@ -26,6 +26,7 @@ public function testTransactionalEmail(): void $message = (new Email()) ->subject('subject') + ->text('body') ->html('
bodyfrom('demo-dbplay@example.com') ->to('test@example.com'); @@ -59,7 +60,8 @@ public function testTransactionalEmail(): void \assert($email instanceof EmailEntity); self::assertEquals('subject', $email->getSubject()); - self::assertEquals('
body
', $email->getContent()); + self::assertEquals('body', $email->getTextContent()); + self::assertEquals('
body
', $email->getHtmlContent()); self::assertEquals(EmailKind::Test, $email->getKind()); $emailInstance = $email; From 4259ba7593a6b92f60fb276452aab87c1484faf4 Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Sun, 8 Dec 2024 23:43:55 +0800 Subject: [PATCH 42/77] feat(email): Allow adding plain text content --- src/Controller/Admin/EmailCrudController.php | 5 ++- templates/email/preview.html.twig | 41 ++++++++++++++++---- translations/messages.zh_TW.yaml | 2 + 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/Controller/Admin/EmailCrudController.php b/src/Controller/Admin/EmailCrudController.php index 93abf4e..4206789 100644 --- a/src/Controller/Admin/EmailCrudController.php +++ b/src/Controller/Admin/EmailCrudController.php @@ -9,8 +9,8 @@ use EasyCorp\Bundle\EasyAdminBundle\Config\Filters; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; +use EasyCorp\Bundle\EasyAdminBundle\Field\CodeEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\IdField; -use EasyCorp\Bundle\EasyAdminBundle\Field\TextEditorField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; use EasyCorp\Bundle\EasyAdminBundle\Filter\ChoiceFilter; @@ -26,7 +26,8 @@ public function configureFields(string $pageName): iterable return [ IdField::new('id')->hideOnForm(), TextField::new('subject'), - TextEditorField::new('content'), + CodeEditorField::new('textContent'), + CodeEditorField::new('htmlContent', 'HTML Content')->setLanguage('xml'), ChoiceField::new('kind'), ]; } diff --git a/templates/email/preview.html.twig b/templates/email/preview.html.twig index 9dd3b11..6a36e20 100644 --- a/templates/email/preview.html.twig +++ b/templates/email/preview.html.twig @@ -1,24 +1,49 @@ {% extends 'app.html.twig' %} -{% block nav %}{% endblock %} +{% block nav %} + {% endblock %} {% block title %}信件預覽{% endblock %} {% block app %}
-
+

{{ emailDeliveryEvent.email.subject }}

-
- -
- {{ emailDeliveryEvent.email.content|raw }} -
-
+ +
+
+ {{ emailDeliveryEvent.email.htmlContent|raw }} +
+
+
{{ emailDeliveryEvent.email.textContent }}
+
+
+
- 進行測驗 + 進行測驗 {% set passRate = this.passRate %}
通過率 {{ passRate.passRate }}%
From b21874d5898f9117a5171015e7b67ebd503dc28d Mon Sep 17 00:00:00 2001 From: Yi-Jyun Pan Date: Mon, 9 Dec 2024 00:04:32 +0800 Subject: [PATCH 45/77] feat(email): Hide empty rendered result --- templates/email/preview.html.twig | 59 +++++++++++++++++++------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/templates/email/preview.html.twig b/templates/email/preview.html.twig index 6a36e20..f065327 100644 --- a/templates/email/preview.html.twig +++ b/templates/email/preview.html.twig @@ -5,6 +5,10 @@ {% block title %}信件預覽{% endblock %} {% block app %} + {% set textContent = emailDeliveryEvent.email.textContent %} + {% set htmlContent = emailDeliveryEvent.email.htmlContent %} + {% set hasText, hasHtml = textContent|length > 0, htmlContent|length > 0 %} +
@@ -15,33 +19,42 @@
-
- {{ emailDeliveryEvent.email.htmlContent|raw }} -
-
-
{{ emailDeliveryEvent.email.textContent }}
-
+ {% if hasHtml %} +
+ {{ emailDeliveryEvent.email.htmlContent|raw }} +
+ {% endif %} + {% if hasText %} +
+
{{ emailDeliveryEvent.email.textContent }}
+
+ {% endif %}