diff --git a/.gitignore b/.gitignore index 08d1445..f50c428 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ /etc/build/* !/etc/build/.gitignore +/etc/export/* +!/etc/export/.gitignore /tests/TestApplication/.env.local /tests/TestApplication/.env.*.local diff --git a/assets/admin/entrypoint.js b/assets/admin/entrypoint.js index e69de29..52b428d 100644 --- a/assets/admin/entrypoint.js +++ b/assets/admin/entrypoint.js @@ -0,0 +1 @@ +import './js/bulk_export'; diff --git a/assets/admin/js/bulk_export.js b/assets/admin/js/bulk_export.js new file mode 100644 index 0000000..c272477 --- /dev/null +++ b/assets/admin/js/bulk_export.js @@ -0,0 +1,48 @@ +function syliusBulkExport(form) { + const groupName = form.getAttribute('data-bulk-export'); + const groupItems = Array.from(document.querySelectorAll(`input[data-check-all-group="${groupName}"]`)); + + form.addEventListener('submit', (e) => { + e.preventDefault(); + + const idsInput = form.querySelector('input[name$="[ids]"]'); + const ids = []; + + groupItems.forEach((item) => { + if (item.checked) { + ids.push(item.value); + } + }); + idsInput.setAttribute('value', ids.join(',')); + + e.target.submit(); + }); +} + +function passDisabledAttributeToDropdownTrigger(trigger) { + const observer = new MutationObserver((mutationsList) => { + for (const mutation of mutationsList) { + if (mutation.type === 'attributes' && mutation.attributeName === 'disabled') { + const button = mutation.target; + + if (button.hasAttribute('disabled')) { + button.classList.add('disabled'); + } else { + button.classList.remove('disabled'); + } + } + } + }); + + const groupName = trigger.getAttribute('data-check-all'); + const dropdownActionButtons = Array.from(document.querySelectorAll(`.dropdown-toggle[data-check-all-action="${groupName}"]`)); + + dropdownActionButtons.forEach(button => { + observer.observe(button, {'attributes': true}); + }) +} + +(function () { + document.querySelectorAll('[data-check-all]').forEach(passDisabledAttributeToDropdownTrigger); + document.querySelectorAll('[data-bulk-export]').forEach(syliusBulkExport); +}()); diff --git a/config/config/sylius_grid.yaml b/config/config/sylius_grid.yaml index ba81fdf..566261b 100644 --- a/config/config/sylius_grid.yaml +++ b/config/config/sylius_grid.yaml @@ -2,6 +2,9 @@ sylius_grid: templates: action: export: '@SyliusGridImportExport\admin\import_export\grid\action\export.html.twig' + bulk_action: + export: '@SyliusGridImportExport\admin\import_export\grid\bulk_action\export.html.twig' + grids: sylius_grid_import_export_admin_process: driver: diff --git a/config/routes.yaml b/config/routes.yaml index e5e6e4a..223b8f6 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,33 +1,3 @@ -sylius_import_export.admin.resource.export: - path: /export/{grid} - methods: [POST] - defaults: - _controller: sylius_import_export.controller.export_action - _sylius: - filterable: true - -sylius_grid_import_export_admin_process: - resource: | - alias: sylius_grid_import_export.process - section: admin - templates: "@SyliusAdmin\\shared\\crud" - except: ['show', 'create', 'update'] - redirect: index - grid: sylius_grid_import_export_admin_process - permission: true - vars: - all: - hook_prefix: 'sylius_b2b.admin.import_export.process' - type: sylius.resource - -sylius_grid_import_export_admin_process_show: - path: /import-export/processes/{id} - methods: [ GET ] - defaults: - _controller: sylius_grid_import_export.controller.process::showAction - _sylius: - section: admin - template: '@SyliusAdmin/shared/crud/show.html.twig' - permission: true - vars: - hook_prefix: 'sylius_b2b.admin.import_export.process' +sylius_import_export_admin: + resource: routes/admin.yaml + prefix: '%sylius_admin.path_name%' diff --git a/config/routes/admin.yaml b/config/routes/admin.yaml new file mode 100644 index 0000000..e5e6e4a --- /dev/null +++ b/config/routes/admin.yaml @@ -0,0 +1,33 @@ +sylius_import_export.admin.resource.export: + path: /export/{grid} + methods: [POST] + defaults: + _controller: sylius_import_export.controller.export_action + _sylius: + filterable: true + +sylius_grid_import_export_admin_process: + resource: | + alias: sylius_grid_import_export.process + section: admin + templates: "@SyliusAdmin\\shared\\crud" + except: ['show', 'create', 'update'] + redirect: index + grid: sylius_grid_import_export_admin_process + permission: true + vars: + all: + hook_prefix: 'sylius_b2b.admin.import_export.process' + type: sylius.resource + +sylius_grid_import_export_admin_process_show: + path: /import-export/processes/{id} + methods: [ GET ] + defaults: + _controller: sylius_grid_import_export.controller.process::showAction + _sylius: + section: admin + template: '@SyliusAdmin/shared/crud/show.html.twig' + permission: true + vars: + hook_prefix: 'sylius_b2b.admin.import_export.process' diff --git a/config/services.xml b/config/services.xml index c00e0a0..225a6c2 100644 --- a/config/services.xml +++ b/config/services.xml @@ -76,7 +76,18 @@ + + + + + %sylius_import_export.export.form_class% + + @@ -110,10 +121,10 @@ - + - + diff --git a/etc/export/.gitignore b/etc/export/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/src/Controller/ExportAction.php b/src/Controller/ExportAction.php index 07c7a14..4137e52 100644 --- a/src/Controller/ExportAction.php +++ b/src/Controller/ExportAction.php @@ -44,12 +44,7 @@ public function __invoke(Request $request, string $grid): Response $format = $data['format']; $resourceClass = $data['resourceClass']; - $metadata = $this->metadataRegistry->getByClass($resourceClass); - - $resourceIds = $this->resourcesIdsProvider->getResourceIds( - metadata: $metadata, - context: ['request' => $request, 'currentPage' => $data['currentPage'] ?? false], - ); + $resourceIds = $this->resolveResourceIds($request, $data); $this->commandBus->dispatch(new ExportCommand( resource: $resourceClass, @@ -59,4 +54,18 @@ public function __invoke(Request $request, string $grid): Response return new RedirectResponse($request->headers->get('referer') ?? '/'); } + + private function resolveResourceIds(Request $request, array $formData): array + { + if (isset($formData['ids']) && [] !== $formData['ids']) { + return $formData['ids']; + } + + $metadata = $this->metadataRegistry->getByClass($formData['resourceClass']); + + return $this->resourcesIdsProvider->getResourceIds( + metadata: $metadata, + context: ['request' => $request], + ); + } } diff --git a/src/Form/Type/ExportResourceType.php b/src/Form/Type/ExportResourceType.php index 0a0036a..e8ff6d2 100644 --- a/src/Form/Type/ExportResourceType.php +++ b/src/Form/Type/ExportResourceType.php @@ -14,8 +14,8 @@ namespace Sylius\GridImportExport\Form\Type; use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\CallbackTransformer; use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\FormBuilderInterface; @@ -34,11 +34,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void 'label' => 'sylius_grid_import_export.grid.form.format', 'choice_loader' => $this->choiceLoader, ]) - ->add('currentPage', CheckboxType::class, [ - 'label' => 'sylius_grid_import_export.grid.form.current_page', - ]) + ->add('ids', HiddenType::class) ->add('resourceClass', HiddenType::class) ; + + $builder->get('ids')->addModelTransformer(new CallbackTransformer( + static fn (?array $ids): string => $ids ? implode(',', $ids) : '', + static fn (?string $ids): array => $ids ? array_filter(array_map('trim', explode(',', $ids))) : [], + )); } public function getBlockPrefix(): string diff --git a/src/Grid/Listener/ExportActionAdminGridListener.php b/src/Grid/Listener/ExportActionAdminGridListener.php index 4845459..543f39c 100644 --- a/src/Grid/Listener/ExportActionAdminGridListener.php +++ b/src/Grid/Listener/ExportActionAdminGridListener.php @@ -13,20 +13,24 @@ namespace Sylius\GridImportExport\Grid\Listener; +use Sylius\Bundle\GridBundle\Builder\ActionGroup\ActionGroupInterface; use Sylius\Bundle\GridBundle\Doctrine\ORM\Driver as ORMDriver; use Sylius\Component\Grid\Definition\Action; use Sylius\Component\Grid\Definition\ActionGroup; +use Sylius\Component\Grid\Definition\Grid; use Sylius\Component\Grid\Event\GridDefinitionConverterEvent; use Sylius\GridImportExport\Grid\Checker\ExportableCheckerInterface; final readonly class ExportActionAdminGridListener { + private const EXPORT_ACTION_NAME = 'export'; + public function __construct( private ExportableCheckerInterface $exportableChecker, ) { } - public function addExportMainAction(GridDefinitionConverterEvent $event): void + public function addExportActions(GridDefinitionConverterEvent $event): void { $grid = $event->getGrid(); if (ORMDriver::NAME !== $grid->getDriver()) { @@ -37,16 +41,22 @@ public function addExportMainAction(GridDefinitionConverterEvent $event): void return; } - if (!$grid->hasActionGroup('main')) { - $grid->addActionGroup(ActionGroup::named('main')); + $this->addInActionGroup($grid, ActionGroupInterface::MAIN_GROUP); + $this->addInActionGroup($grid, ActionGroupInterface::BULK_GROUP); + } + + private function addInActionGroup(Grid $grid, string $groupName): void + { + if (!$grid->hasActionGroup($groupName)) { + $grid->addActionGroup(ActionGroup::named($groupName)); } - $actionGroup = $grid->getActionGroup('main'); - if ($actionGroup->hasAction('export')) { + $actionGroup = $grid->getActionGroup($groupName); + if ($actionGroup->hasAction(self::EXPORT_ACTION_NAME)) { return; } - $action = Action::fromNameAndType('export', 'export'); + $action = Action::fromNameAndType(self::EXPORT_ACTION_NAME, self::EXPORT_ACTION_NAME); $actionGroup->addAction($action); } diff --git a/src/Provider/RequestBasedResourcesIdsProvider.php b/src/Provider/RequestBasedResourcesIdsProvider.php index 7558d5d..7e290fc 100644 --- a/src/Provider/RequestBasedResourcesIdsProvider.php +++ b/src/Provider/RequestBasedResourcesIdsProvider.php @@ -38,10 +38,10 @@ public function getResourceIds(MetadataInterface $metadata, array $context = []) throw new ProviderException('Request is missing from the context.'); } - return $this->doGetResourceIds($metadata, $request, $context['currentPage'] ?? false); + return $this->doGetResourceIds($metadata, $request); } - private function doGetResourceIds(MetadataInterface $metadata, Request $request, bool $currentPage): array + private function doGetResourceIds(MetadataInterface $metadata, Request $request): array { $resourceClass = $metadata->getClass('model'); /** @var RepositoryInterface $repository */ @@ -64,14 +64,6 @@ private function doGetResourceIds(MetadataInterface $metadata, Request $request, } $ids = []; - if ($currentPage) { - foreach ($paginator->getCurrentPageResults() as $item) { - $ids[] = (string) $item->getId(); - } - - return $ids; - } - foreach ($paginator->autoPagingIterator() as $item) { $ids[] = (string) $item->getId(); } diff --git a/templates/admin/import_export/component/bulk_export_form.html.twig b/templates/admin/import_export/component/bulk_export_form.html.twig new file mode 100644 index 0000000..9ca6e23 --- /dev/null +++ b/templates/admin/import_export/component/bulk_export_form.html.twig @@ -0,0 +1,32 @@ +{# Rendered with \Sylius\GridImportExport\Twig\Component\ExportResourceFormComponent #} + +{% import '@SyliusAdmin/shared/helper/button.html.twig' as button %} + +{% form_theme form '@SyliusAdmin/shared/form_theme.html.twig' %} + +{% set export_parameters = { + 'grid': grid, + 'criteria': app.request.query.all()['criteria']|default({}), +} %} + + diff --git a/templates/admin/import_export/component/export_form.html.twig b/templates/admin/import_export/component/main_export_form.html.twig similarity index 96% rename from templates/admin/import_export/component/export_form.html.twig rename to templates/admin/import_export/component/main_export_form.html.twig index 12fa4a3..32d3d96 100644 --- a/templates/admin/import_export/component/export_form.html.twig +++ b/templates/admin/import_export/component/main_export_form.html.twig @@ -23,7 +23,6 @@
{{ form_row(form.format) }} - {{ form_row(form.currentPage) }}
diff --git a/templates/admin/import_export/grid/bulk_action/export.html.twig b/templates/admin/import_export/grid/bulk_action/export.html.twig new file mode 100644 index 0000000..9092cfe --- /dev/null +++ b/templates/admin/import_export/grid/bulk_action/export.html.twig @@ -0,0 +1 @@ +{{ component('sylius_import_export:admin:bulk_export', {'grid': grid.definition.code, 'resourceClass': grid.definition.driverConfiguration['class']}) }} diff --git a/tests/TestApplication/config/config.yaml b/tests/TestApplication/config/config.yaml index 99d6b16..f07d71b 100644 --- a/tests/TestApplication/config/config.yaml +++ b/tests/TestApplication/config/config.yaml @@ -2,6 +2,9 @@ imports: - { resource: "@SyliusGridImportExportBundle/config/config.yaml" } - { resource: "services_test.php" } +parameters: + sylius_grid_import_export.export_files_directory: '%kernel.project_dir%/../../../etc/export' + twig: paths: '%kernel.project_dir%/../../../tests/TestApplication/templates': ~ diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 863aac2..06388b4 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -3,7 +3,6 @@ sylius_grid_import_export: form: export: Export format: Format - current_page: Current page ui: export: "Export" import_export: "Import / export"