diff --git a/.github/workflows/sylius.yaml b/.github/workflows/sylius.yaml index 745e4d7..6ef7e50 100644 --- a/.github/workflows/sylius.yaml +++ b/.github/workflows/sylius.yaml @@ -19,13 +19,12 @@ jobs: - 8.2 - 8.3 sylius: - - 1.12.0 - - 1.13.0 - - 1.14.0 + - 2.0.0 symfony: - 6.4 + - 7.2 node: - - 14.x + - 20.x env: APP_ENV: test package-name: synolia/sylius-gdpr-plugin @@ -45,14 +44,14 @@ jobs: with: node-version: '${{ matrix.node }}' - - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: 'Composer - Get Cache Directory' id: composer-cache run: 'echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT' - name: 'Composer - Set cache' - uses: actions/cache@v3 + uses: actions/cache@v4 id: cache-composer with: path: '${{ steps.composer-cache.outputs.dir }}' @@ -71,7 +70,7 @@ jobs: run: 'echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT' - name: 'Yarn - Set Cache' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: '${{ steps.yarn-cache.outputs.dir }}' key: 'node-${{ matrix.node }}-yarn-${{ hashFiles(''**/package.json **/yarn.lock'') }}' diff --git a/Makefile b/Makefile index 5af0ba5..323632a 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ CONSOLE=cd ${TEST_DIRECTORY} && php bin/console -e test COMPOSER=cd ${TEST_DIRECTORY} && composer YARN=cd ${TEST_DIRECTORY} && yarn -SYLIUS_VERSION=1.14.0 +SYLIUS_VERSION=2.0.0 SYMFONY_VERSION=6.4 PHP_VERSION=8.2 PLUGIN_NAME=synolia/sylius-gdpr-plugin @@ -44,6 +44,10 @@ sylius-standard: update-dependencies: ${COMPOSER} config extra.symfony.require "~${SYMFONY_VERSION}" ${COMPOSER} require symfony/asset:~${SYMFONY_VERSION} --no-scripts --no-update +ifeq ($(SYLIUS_VERSION)$(SYMFONY_VERSION), 2.0.06.4) + ${COMPOSER} update --no-progress -n --no-scripts + test -f ${TEST_DIRECTORY}/config/packages/csrf.yaml && rm ${TEST_DIRECTORY}/config/packages/csrf.yaml || true +endif ${COMPOSER} update --no-progress -n install-plugin: diff --git a/README.md b/README.md index 208c6e6..4d8cada 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ | | Version | |:-------|:--------| | PHP | ^8.2 | -| Sylius | ^1.12 | +| Sylius | ^2.0 | ## Installation diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md new file mode 100644 index 0000000..90b9be2 --- /dev/null +++ b/UPGRADE-2.0.md @@ -0,0 +1,5 @@ +# UPGRADE FROM `v1.X` TO `v2.0` + +* For each implementation of `Synolia\SyliusGDPRPlugin\Loader\LoaderInterface`, add the `getDefaultPriority` static function. +* Use attribute instead of annotation. +* The service tag `anonymization_loader` has been removed. Use `Synolia\SyliusGDPRPlugin\Loader\LoaderInterface` instead. diff --git a/composer.json b/composer.json index 0fe013e..702fb58 100644 --- a/composer.json +++ b/composer.json @@ -14,8 +14,8 @@ "php": "^8.2", "php-http/message-factory": "^1.1", "phpdocumentor/reflection-docblock": "^5.3", - "sylius/sylius": "^1.12", - "symfony/property-info": "^6.4" + "sylius/sylius": "^2.0", + "symfony/property-info": "^6.4|^7.0" }, "require-dev": { "j13k/yaml-lint": "^1.1", @@ -45,7 +45,8 @@ "dealerdirect/phpcodesniffer-composer-installer": true, "symfony/thanks": true, "phpro/grumphp": true, - "phpstan/extension-installer": true + "phpstan/extension-installer": true, + "php-http/discovery": true } }, "autoload": { diff --git a/src/Annotation/Anonymize.php b/src/Attribute/Anonymize.php similarity index 62% rename from src/Annotation/Anonymize.php rename to src/Attribute/Anonymize.php index ffefa31..a2023d1 100644 --- a/src/Annotation/Anonymize.php +++ b/src/Attribute/Anonymize.php @@ -2,29 +2,23 @@ declare(strict_types=1); -namespace Synolia\SyliusGDPRPlugin\Annotation; +namespace Synolia\SyliusGDPRPlugin\Attribute; use Doctrine\ORM\Mapping\MappingAttribute; use Synolia\SyliusGDPRPlugin\Validator\FakerOptionsValidator; -/** - * @Annotation - * - * @Target({"PROPERTY","ANNOTATION"}) - */ +#[\Attribute(\Attribute::TARGET_PROPERTY)] final class Anonymize implements MappingAttribute { - public ?string $faker; + public readonly ?string $faker; - public array $args = []; + public readonly array $args; - public bool $unique = false; + public readonly bool $unique; - /** @var string|int|null */ - public $prefix = ''; + public readonly string|int|null $prefix; - /** @var string|int|array|bool|null */ - public $value; + public readonly string|int|array|bool|null $value; public function __construct(array $options = []) { diff --git a/src/DependencyInjection/CompilerPass/RegisterAnonymizationLoader.php b/src/DependencyInjection/CompilerPass/RegisterAnonymizationLoader.php index d6b1b7b..3980f5c 100644 --- a/src/DependencyInjection/CompilerPass/RegisterAnonymizationLoader.php +++ b/src/DependencyInjection/CompilerPass/RegisterAnonymizationLoader.php @@ -6,7 +6,6 @@ use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; use Synolia\SyliusGDPRPlugin\Loader\ArrayLoader; use Synolia\SyliusGDPRPlugin\Loader\LoaderChain; @@ -14,19 +13,16 @@ final class RegisterAnonymizationLoader implements CompilerPassInterface { public function process(ContainerBuilder $container): void { - $services = $container->findTaggedServiceIds('anonymization_loader'); - $chainLoader = $container->getDefinition(LoaderChain::class); - - foreach (\array_keys($services) as $id) { - $definition = $container->getDefinition($id); - if (ArrayLoader::class === $definition->getClass()) { - $definition->setArgument( - 0, - $container->getParameter('synolia_anonymization_mapping'), - ); - } - $chainLoader->addMethodCall('addLoader', [new Reference($id)]); + if (!$container->has(LoaderChain::class)) { + return; } + + $arrayLoader = $container->getDefinition(ArrayLoader::class); + $arrayLoader->setArgument( + 0, + $container->getParameter('synolia_anonymization_mapping'), + ); + $container->getParameterBag()->remove('synolia_anonymization_mapping'); } } diff --git a/src/Form/Type/Actions/AnonymizeCustomersNotLoggedBeforeType.php b/src/Form/Type/Actions/AnonymizeCustomersNotLoggedBeforeType.php index f7857f4..bb6cfaf 100644 --- a/src/Form/Type/Actions/AnonymizeCustomersNotLoggedBeforeType.php +++ b/src/Form/Type/Actions/AnonymizeCustomersNotLoggedBeforeType.php @@ -19,11 +19,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->add('anonymize_customers_not_logged_before_date', DateType::class, [ 'label' => false, 'widget' => 'single_text', - 'row_attr' => ['class' => 'ui field'], + 'row_attr' => ['class' => ''], ]) ->add('anonymize_customers_not_logged_submit', SubmitType::class, [ 'label' => 'sylius.ui.execute', - 'attr' => ['class' => 'ui blue button'], + 'attr' => ['class' => 'btn btn-warning mb-3'], ]) ; } diff --git a/src/Form/Type/Actions/AnonymizeCustomersWithoutAnyOrdersBeforeType.php b/src/Form/Type/Actions/AnonymizeCustomersWithoutAnyOrdersBeforeType.php index 6a77da4..3301736 100644 --- a/src/Form/Type/Actions/AnonymizeCustomersWithoutAnyOrdersBeforeType.php +++ b/src/Form/Type/Actions/AnonymizeCustomersWithoutAnyOrdersBeforeType.php @@ -19,11 +19,11 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ->add('anonymize_customer_without_any_orders_before_date', DateType::class, [ 'label' => false, 'widget' => 'single_text', - 'row_attr' => ['class' => 'ui field'], + 'row_attr' => ['class' => ''], ]) ->add('anonymize_customer_without_any_orders_submit', SubmitType::class, [ 'label' => 'sylius.ui.execute', - 'attr' => ['class' => 'ui blue button'], + 'attr' => ['class' => 'btn btn-warning mb-3'], ]) ; } diff --git a/src/Loader/ArrayLoader.php b/src/Loader/ArrayLoader.php index 89eddff..543de89 100644 --- a/src/Loader/ArrayLoader.php +++ b/src/Loader/ArrayLoader.php @@ -11,6 +11,8 @@ final readonly class ArrayLoader implements LoaderInterface { + private const PRIORITY = -1024; + public function __construct(private array $mappings = []) { } @@ -72,4 +74,9 @@ private function assignAttributeMetaDataCollection( return $attributeMetaDataCollection; } + + public static function getDefaultPriority(): int + { + return self::PRIORITY; + } } diff --git a/src/Loader/AnnotationLoader.php b/src/Loader/AttributeLoader.php similarity index 51% rename from src/Loader/AnnotationLoader.php rename to src/Loader/AttributeLoader.php index 862e040..71881f3 100644 --- a/src/Loader/AnnotationLoader.php +++ b/src/Loader/AttributeLoader.php @@ -4,17 +4,14 @@ namespace Synolia\SyliusGDPRPlugin\Loader; -use Doctrine\Common\Annotations\Reader; use Doctrine\Common\Util\ClassUtils; -use Synolia\SyliusGDPRPlugin\Annotation\Anonymize; +use Synolia\SyliusGDPRPlugin\Attribute\Anonymize; use Synolia\SyliusGDPRPlugin\Loader\Mapping\AttributeMetaData; use Synolia\SyliusGDPRPlugin\Loader\Mapping\AttributeMetadataCollection; -final readonly class AnnotationLoader implements LoaderInterface +final readonly class AttributeLoader implements LoaderInterface { - public function __construct(private Reader $annotationReader) - { - } + private const PRIORITY = 1024; /** @throws \ReflectionException */ public function loadClassMetadata(string $className): AttributeMetadataCollection @@ -23,20 +20,27 @@ public function loadClassMetadata(string $className): AttributeMetadataCollectio $properties = $reflectionClass->getProperties(); $attributeMetaDataCollection = new AttributeMetadataCollection(); foreach ($properties as $property) { - $annotation = $this->annotationReader->getPropertyAnnotation( - $property, - Anonymize::class, - ); + $attributes = $property->getAttributes(Anonymize::class, \ReflectionAttribute::IS_INSTANCEOF); - if (!$annotation instanceof Anonymize) { + if (\count($attributes) === 0) { continue; } - - $attributeMetaData = new AttributeMetaData($annotation->faker, $annotation->args, $annotation->unique, $annotation->prefix, $annotation->value); + $attributesInstances = []; + foreach ($attributes as $attribute) { + $attributesInstances[] = $attribute->newInstance(); + } + /** @var Anonymize $attribute */ + $attribute = $attributesInstances[0]; + $attributeMetaData = new AttributeMetaData($attribute->faker, $attribute->args, $attribute->unique, $attribute->prefix, $attribute->value); $attributeMetaDataCollection->add($property->name, $attributeMetaData); } return $attributeMetaDataCollection; } + + public static function getDefaultPriority(): int + { + return self::PRIORITY; + } } diff --git a/src/Loader/LoaderChain.php b/src/Loader/LoaderChain.php index 5478413..66dce4e 100644 --- a/src/Loader/LoaderChain.php +++ b/src/Loader/LoaderChain.php @@ -4,6 +4,7 @@ namespace Synolia\SyliusGDPRPlugin\Loader; +use Symfony\Component\DependencyInjection\Attribute\AutowireIterator; use Synolia\SyliusGDPRPlugin\Loader\Mapping\AttributeMetadataCollection; final class LoaderChain implements LoaderInterface @@ -11,6 +12,18 @@ final class LoaderChain implements LoaderInterface /** @var LoaderInterface[] */ private array $loaders = []; + public function __construct( + #[AutowireIterator(LoaderInterface::class, defaultPriorityMethod: 'getDefaultPriority', exclude: [self::class])] + iterable $loaders, + ) { + foreach ($loaders as $loader) { + if (!$loader instanceof LoaderInterface) { + throw new \LogicException('Not an anonymization loader'); + } + $this->addLoader($loader); + } + } + public function addLoader(LoaderInterface $loader): void { if ($loader instanceof self) { @@ -33,4 +46,9 @@ public function loadClassMetadata(string $className): AttributeMetadataCollectio return $fullCollect; } + + public static function getDefaultPriority(): int + { + return 0; + } } diff --git a/src/Loader/LoaderInterface.php b/src/Loader/LoaderInterface.php index 40026c0..4a836cb 100644 --- a/src/Loader/LoaderInterface.php +++ b/src/Loader/LoaderInterface.php @@ -4,9 +4,13 @@ namespace Synolia\SyliusGDPRPlugin\Loader; +use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag; use Synolia\SyliusGDPRPlugin\Loader\Mapping\AttributeMetadataCollection; +#[AutoconfigureTag] interface LoaderInterface { public function loadClassMetadata(string $className): AttributeMetadataCollection; + + public static function getDefaultPriority(): int; } diff --git a/src/Menu/AdminMenuListener.php b/src/Menu/AdminMenuListener.php index 9a592ea..b048fc5 100644 --- a/src/Menu/AdminMenuListener.php +++ b/src/Menu/AdminMenuListener.php @@ -15,6 +15,8 @@ public function addAdminMenuItems(MenuBuilderEvent $event): void $menu = $event->getMenu(); $gdprMenu = $menu->addChild('gdpr'); + $gdprMenu->setLabel('sylius.ui.admin.synolia_gdpr.customer.gdpr_title'); + $gdprMenu->setLabelAttribute('icon', 'tabler:database-star'); $gdprMenu ->addChild('sylius.ui.admin.synolia_gdpr.advanced_actions.title', [ 'route' => 'synolia_sylius_gdpr_admin_advanced_actions', diff --git a/src/Resources/config/app/config.yaml b/src/Resources/config/app/config.yaml index fe12115..7474a45 100644 --- a/src/Resources/config/app/config.yaml +++ b/src/Resources/config/app/config.yaml @@ -1,2 +1,2 @@ imports: - - { resource: "@SynoliaSyliusGDPRPlugin/Resources/config/packages/ui.yaml" } \ No newline at end of file + - { resource: "@SynoliaSyliusGDPRPlugin/Resources/config/packages/twig_hooks.yaml" } diff --git a/src/Resources/config/mappings/ShopUser.yaml b/src/Resources/config/mappings/ShopUser.yaml index f82b4db..f89869a 100644 --- a/src/Resources/config/mappings/ShopUser.yaml +++ b/src/Resources/config/mappings/ShopUser.yaml @@ -22,11 +22,6 @@ Sylius\Component\Core\Model\ShopUser: faker: dateTime verifiedAt: faker: dateTime - locked: - faker: boolean - args: [100] - credentialsExpireAt: - faker: dateTime email: faker: email unique: true diff --git a/src/Resources/config/packages/twig_hooks.yaml b/src/Resources/config/packages/twig_hooks.yaml new file mode 100644 index 0000000..5f81550 --- /dev/null +++ b/src/Resources/config/packages/twig_hooks.yaml @@ -0,0 +1,48 @@ +sylius_twig_hooks: + hooks: + 'sylius_admin.customer.show.content.sections': + gdpr: + template: '@SynoliaSyliusGDPRPlugin/Admin/Customer/Show/gdpr.html.twig' + priority: -100 + + 'sylius_admin.synolia_gdpr_plugin.index': + sidebar: + template: '@SyliusAdmin/shared/crud/common/sidebar.html.twig' + priority: 200 + navbar: + template: '@SyliusAdmin/shared/crud/common/navbar.html.twig' + priority: 100 + content: + template: '@SyliusAdmin/shared/crud/common/content.html.twig' + priority: 0 + + 'sylius_admin.synolia_gdpr_plugin.index.content': + flashes: + template: '@SyliusAdmin/shared/crud/common/content/flashes.html.twig' + priority: 300 + header: + template: '@SyliusAdmin/shared/crud/common/content/header.html.twig' + priority: 200 + form: + template: '@SynoliaSyliusGDPRPlugin/Gdpr/part/form.html.twig' + priority: 100 + footer: + template: '@SynoliaSyliusGDPRPlugin/Gdpr/part/footer.html.twig' + priority: -100 + + 'sylius_admin.synolia_gdpr_plugin.index.content.header': + breadcrumbs: + template: '@SynoliaSyliusGDPRPlugin/Gdpr/part/header/breadcrumbs.html.twig' + priority: 100 + title_block: + template: '@SyliusAdmin/shared/crud/common/content/header/title_block.html.twig' + priority: 0 + + 'sylius_admin.synolia_gdpr_plugin.index.content.header.title_block': + title: + template: '@SyliusAdmin/shared/crud/common/content/header/title_block/title.html.twig' + configuration: + title: sylius.ui.admin.synolia_gdpr.advanced_actions.title + sylius_test_html_attribute: 'dashboard-header' + priority: 100 + diff --git a/src/Resources/config/packages/ui.yaml b/src/Resources/config/packages/ui.yaml deleted file mode 100644 index 31c46c2..0000000 --- a/src/Resources/config/packages/ui.yaml +++ /dev/null @@ -1,5 +0,0 @@ -sylius_ui: - events: - sylius.admin.customer.show.information: - blocks: - gdpr: '@SynoliaSyliusGDPRPlugin\Admin\Customer\Show\gdpr.html.twig' diff --git a/src/Resources/views/Admin/Customer/Show/gdpr.html.twig b/src/Resources/views/Admin/Customer/Show/gdpr.html.twig index fd4c68a..a8982a3 100644 --- a/src/Resources/views/Admin/Customer/Show/gdpr.html.twig +++ b/src/Resources/views/Admin/Customer/Show/gdpr.html.twig @@ -2,18 +2,27 @@ sylius_plus_rbac_gdpr_has_permission("synolia_sylius_gdpr_admin_anonymize_customer") or sylius_plus_rbac_gdpr_has_permission("synolia_sylius_gdpr_admin_export_customer_data") %} -