From aa872294f111c5a54b219e85bf37ffb35615e87a Mon Sep 17 00:00:00 2001 From: Adrian Dumitrache Date: Wed, 16 Oct 2024 02:06:28 +0300 Subject: [PATCH] [SoantaExtraBundle] Allow AdminAction to generate multiple actions "buttons" (#298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --------- Co-authored-by: Martin Poirier Théorêt --- .../Admin/SetPreferredLocaleAction.php | 28 ++++++ app/src/Sonata/Admin/UserAdmin.php | 23 ++++- .../ActionableAdmin/AdminAction.php | 60 ++++++++++++ .../Extension/ActionableAdminExtension.php | 18 +++- .../Resources/views/Action/action.html.twig | 12 ++- .../views/Action/list_action.html.twig | 16 ++-- .../Test/AdminTestHelperTrait.php | 96 +++++++++++++++++++ packages/sonata-extra-bundle/composer.json | 1 + 8 files changed, 236 insertions(+), 18 deletions(-) create mode 100644 app/src/Controller/Admin/SetPreferredLocaleAction.php create mode 100644 packages/sonata-extra-bundle/Test/AdminTestHelperTrait.php diff --git a/app/src/Controller/Admin/SetPreferredLocaleAction.php b/app/src/Controller/Admin/SetPreferredLocaleAction.php new file mode 100644 index 00000000..ac4409f4 --- /dev/null +++ b/app/src/Controller/Admin/SetPreferredLocaleAction.php @@ -0,0 +1,28 @@ +execute( + static function (User $user) use ($request, $objectActionExecutioner): void { + $user->setPreferredLocale($request->get('_locale') ?? 'en'); + + $objectActionExecutioner->getAdmin()->update($user); + } + ); + } +} diff --git a/app/src/Sonata/Admin/UserAdmin.php b/app/src/Sonata/Admin/UserAdmin.php index 032dc6ac..074c4ac0 100644 --- a/app/src/Sonata/Admin/UserAdmin.php +++ b/app/src/Sonata/Admin/UserAdmin.php @@ -4,6 +4,7 @@ use App\Controller\Admin\AddRolesAminAction; use App\Controller\Admin\MakeAdminAction; +use App\Controller\Admin\SetPreferredLocaleAction; use App\Entity\Tag; use App\Entity\User; use Draw\Bundle\SonataExtraBundle\ActionableAdmin\ActionableAdminInterface; @@ -108,6 +109,7 @@ protected function configureShowFields(ShowMapper $show): void ->add('childObject2') ->add('email') ->add('dateOfBirth') + ->add('preferredLocale') ->add('roles', 'json') ->add('rolesList', 'list') ->add('static', 'static', ['virtual_field' => true, 'value' => 'Static value']) @@ -213,13 +215,32 @@ protected function configureFormFields(FormMapper $form): void public function getActions(): array { return [ - 'makeAdin' => (new AdminAction('makeAdmin', true)) + 'makeAdmin' => (new AdminAction('makeAdmin', true)) ->setController(MakeAdminAction::class) ->setIcon('fa fa-user-plus') ->setBatchController(MakeAdminAction::class), 'addRoles' => (new AdminAction('addRoles', true)) ->setController(AddRolesAminAction::class) ->setBatchController(AddRolesAminAction::class), + 'setPreferredLocale' => (new AdminAction('setPreferredLocale', true)) + ->setIcon('fa fa-language') + ->setController(SetPreferredLocaleAction::class) + ->setRoutePattern( + \sprintf( + '%s/preferred-locale/{_locale}', + $this->getRouterIdParameter(), + ) + ) + ->setActionsCallback( + static function (AdminAction $action): iterable { + foreach (['en', 'fr'] as $locale) { + yield (clone $action) + ->setLabel(strtoupper($locale)) + ->setRouteParameters(['_locale' => $locale]) + ; + } + } + ), ]; } } diff --git a/packages/sonata-extra-bundle/ActionableAdmin/AdminAction.php b/packages/sonata-extra-bundle/ActionableAdmin/AdminAction.php index a763bfe4..0e859739 100644 --- a/packages/sonata-extra-bundle/ActionableAdmin/AdminAction.php +++ b/packages/sonata-extra-bundle/ActionableAdmin/AdminAction.php @@ -2,6 +2,7 @@ namespace Draw\Bundle\SonataExtraBundle\ActionableAdmin; +use Sonata\AdminBundle\Admin\AdminInterface; use Symfony\Component\DependencyInjection\Attribute\Exclude; use Symfony\Component\String\UnicodeString; @@ -22,11 +23,20 @@ class AdminAction private string $urlSuffix; + private ?string $routePattern = null; + + private array $routeParameters = []; + private string $access; private string|false|null $label; private string|false|null $translationDomain = null; + /** + * @var callable + */ + private $actionsCallback; + public function __construct( private string $name, private bool $targetEntity, @@ -44,6 +54,8 @@ public function __construct( ->replace('_', '-') ->toString() ; + + $this->actionsCallback = fn () => [$this]; } public function getName(): string @@ -80,6 +92,18 @@ public function setUrlSuffix(string $urlSuffix): self return $this; } + public function getRoutePattern(): ?string + { + return $this->routePattern; + } + + public function setRoutePattern(?string $routePattern): self + { + $this->routePattern = $routePattern; + + return $this; + } + public function getLabel(): bool|string|null { return $this->label; @@ -197,4 +221,40 @@ public function isForAction(string $action): bool { return $this->forActions[$action] ?? $this->forActions['_default']; } + + public function getActions(): iterable + { + return \call_user_func($this->actionsCallback, $this); + } + + public function getActionsCallback(): callable + { + return $this->actionsCallback; + } + + public function setActionsCallback(callable $actionsCallback): self + { + $this->actionsCallback = $actionsCallback; + + return $this; + } + + public function getRouteParameters(): array + { + return $this->routeParameters; + } + + public function setRouteParameters(array $routeParameters): self + { + $this->routeParameters = $routeParameters; + + return $this; + } + + public function generateUrl(AdminInterface $admin, mixed $subject = null): string + { + return null !== $subject + ? $admin->generateObjectUrl($this->name, $subject, $this->routeParameters) + : $admin->generateUrl($this->name, $this->routeParameters); + } } diff --git a/packages/sonata-extra-bundle/ActionableAdmin/Extension/ActionableAdminExtension.php b/packages/sonata-extra-bundle/ActionableAdmin/Extension/ActionableAdminExtension.php index e66775ff..f67d429e 100644 --- a/packages/sonata-extra-bundle/ActionableAdmin/Extension/ActionableAdminExtension.php +++ b/packages/sonata-extra-bundle/ActionableAdmin/Extension/ActionableAdminExtension.php @@ -2,6 +2,7 @@ namespace Draw\Bundle\SonataExtraBundle\ActionableAdmin\Extension; +use Draw\Bundle\SonataExtraBundle\ActionableAdmin\AdminAction; use Draw\Bundle\SonataExtraBundle\ActionableAdmin\AdminActionLoader; use Sonata\AdminBundle\Admin\AbstractAdminExtension; use Sonata\AdminBundle\Admin\AdminInterface; @@ -36,14 +37,10 @@ public function configureRoutes(AdminInterface $admin, RouteCollectionInterface $defaults['_controller'] = $action->getController(); } - $pattern = $action->getTargetEntity() - ? $admin->getRouterIdParameter().'/'.$action->getUrlSuffix() - : $action->getUrlSuffix(); - $collection ->add( $action->getName(), - $pattern, + $this->getRoutePattern($admin, $action), defaults: $defaults ) ; @@ -144,4 +141,15 @@ public function configureActionButtons( return $list; } + + private function getRoutePattern(AdminInterface $admin, AdminAction $action): string + { + if (null !== $pattern = $action->getRoutePattern()) { + return $pattern; + } + + return $action->getTargetEntity() + ? \sprintf('%s/%s', $admin->getRouterIdParameter(), $action->getUrlSuffix()) + : $action->getUrlSuffix(); + } } diff --git a/packages/sonata-extra-bundle/Resources/views/Action/action.html.twig b/packages/sonata-extra-bundle/Resources/views/Action/action.html.twig index 462a4b6c..0e8f6f81 100644 --- a/packages/sonata-extra-bundle/Resources/views/Action/action.html.twig +++ b/packages/sonata-extra-bundle/Resources/views/Action/action.html.twig @@ -1,8 +1,10 @@ +{% for action in item.action.actions %}
  • - - {% if item.action.icon %} - + + {% if action.icon %} + {% endif %} - {{ item.action|translate_label }} + {{ action|translate_label }} -
  • \ No newline at end of file + +{% endfor %} diff --git a/packages/sonata-extra-bundle/Resources/views/Action/list_action.html.twig b/packages/sonata-extra-bundle/Resources/views/Action/list_action.html.twig index 96f21305..a3deba25 100644 --- a/packages/sonata-extra-bundle/Resources/views/Action/list_action.html.twig +++ b/packages/sonata-extra-bundle/Resources/views/Action/list_action.html.twig @@ -1,8 +1,10 @@ {% if admin.hasAccess(actions.action.name, object) %} - - {% if actions.action.icon %} - - {% endif %} - {{ actions.action|translate_label }} - -{% endif %} \ No newline at end of file + {% for action in actions.action.actions %} + + {% if action.icon %} + + {% endif %} + {{ action|translate_label }} + + {% endfor %} +{% endif %} diff --git a/packages/sonata-extra-bundle/Test/AdminTestHelperTrait.php b/packages/sonata-extra-bundle/Test/AdminTestHelperTrait.php new file mode 100644 index 00000000..e6562d04 --- /dev/null +++ b/packages/sonata-extra-bundle/Test/AdminTestHelperTrait.php @@ -0,0 +1,96 @@ +filter(\sprintf('.sonata-ba-list-field-actions[objectid="%s"]', $identifier)) + ->selectLink($action) + ->link() + ; + } + + protected function selectMenuObjectLink( + Crawler $crawler, + string $action, + ): Link { + return $crawler + ->filter('ul.dropdown-menu') + ->selectLink($action) + ->link() + ; + } + + protected function submitBatchAction( + KernelBrowser $client, + array $indexes, + string $action, + string $button = 'OK', + ): void { + $crawler = $client->submitForm( + $button, + [ + 'idx' => $indexes, + 'action' => $action, + ] + ); + + $client->submit( + $crawler->selectButton('Yes, execute')->form() + ); + } + + protected function appendDefaultFiltersToListUrl(string $listUrl): string + { + return \sprintf( + '%s%s%s', + $listUrl, + null === parse_url($listUrl, \PHP_URL_QUERY) ? '?' : '&', + http_build_query( + [ + 'filter' => [ + '_page' => 1, + '_per_page' => 25, + ], + ] + ) + ); + } + + protected function filterListByIdentifier( + KernelBrowser $client, + string $listUrl, + int|string $identifier, + string $identifierName = 'id', + ): Crawler { + return $client->request( + 'GET', + \sprintf( + '%s?%s', + $listUrl, + http_build_query( + [ + 'filter' => [ + $identifierName => [ + 'type' => 3, + 'value' => $identifier, + ], + ], + ] + ) + ) + ); + } +} diff --git a/packages/sonata-extra-bundle/composer.json b/packages/sonata-extra-bundle/composer.json index 1433eda5..f8c80ea9 100644 --- a/packages/sonata-extra-bundle/composer.json +++ b/packages/sonata-extra-bundle/composer.json @@ -10,6 +10,7 @@ "require": { "php": ">=8.2", "draw/dependency-injection": "^0.12", + "symfony/browser-kit": "^6.4.0", "symfony/framework-bundle": "^6.4.0", "symfony/expression-language": "^6.4.0", "symfony/string": "^6.4.0"