diff --git a/README.md b/README.md index d58b118..eacb94a 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ This library add Symfony Workflow component integration within Sonata Admin. [WorkflowControllerTrait](src/Controller/WorkflowControllerTrait.php) - a Controller : [WorkflowController](src/Controller/WorkflowController.php) +- a Translator : + [TranslatorInterface](src/Translator/TranslatorInterface.php) Installation @@ -109,7 +111,7 @@ sonata_admin: - admin.pull_request ``` -> **note**: You may noticed that we also registered the controller +> **note**: You may have noticed that we also registered the controller `Yokai\SonataWorkflow\Controller\WorkflowController` as a service. It is important, because it needs the workflow registry service to work. @@ -252,6 +254,60 @@ class PullRequestController extends CRUDController } ``` +Configure the translations +-------------------------- + +The bundle will sometime need to acquire translations. +A utility named `TranslatorInterface` comes with a default implementation, that is using sonata standards: +[StandardTranslator](src/Translator/StandardTranslator.php) + +But you might want to change how these translations are generated. +For instance, you could have different translations keys for each workflow/transition. + +You need to create a new implementation of this interface, for example: + +```php +getName()}.{$transition}.success", + domain: 'workflows', + ); + } + + public function transitionErrorFlashMessage( + AdminInterface $admin, + WorkflowInterface $workflow, + object $object, + string $transition, + ): TranslatableInterface { + return new TranslatableMessage( + message: "{$workflow->getName()}.{$transition}.error", + domain: 'workflows', + ); + } +} +``` + MIT License diff --git a/src/Controller/WorkflowControllerTrait.php b/src/Controller/WorkflowControllerTrait.php index 72e5ea8..55609d3 100644 --- a/src/Controller/WorkflowControllerTrait.php +++ b/src/Controller/WorkflowControllerTrait.php @@ -4,20 +4,19 @@ namespace Yokai\SonataWorkflow\Controller; -use Psr\Container\ContainerInterface; -use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Exception\LockException; use Sonata\AdminBundle\Exception\ModelManagerException; use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\Workflow\Exception\InvalidArgumentException; use Symfony\Component\Workflow\Exception\LogicException; use Symfony\Component\Workflow\Registry; -use Symfony\Component\Workflow\Workflow; use Symfony\Component\Workflow\WorkflowInterface; +use Symfony\Contracts\Service\Attribute\Required; +use Yokai\SonataWorkflow\Translator\StandardTranslator; +use Yokai\SonataWorkflow\Translator\TranslatorInterface; /** * @@ -26,13 +25,15 @@ trait WorkflowControllerTrait { private Registry $workflowRegistry; + private TranslatorInterface $translator; - /** - * @required Symfony DI autowiring - */ - public function setWorkflowRegistry(Registry $workflowRegistry): void - { + #[Required] + public function autowireWorkflowControllerTrait( + Registry $workflowRegistry, + TranslatorInterface|null $translator, + ): void { $this->workflowRegistry = $workflowRegistry; + $this->translator = $translator ?? new StandardTranslator(); } public function workflowApplyTransitionAction(Request $request): Response @@ -94,11 +95,7 @@ public function workflowApplyTransitionAction(Request $request): Response $this->addFlash( 'sonata_flash_success', - $this->trans( - 'flash_edit_success', - ['%name%' => $this->escapeHtml($this->admin->toString($existingObject))], - 'SonataAdminBundle' - ) + $this->translator->transitionSuccessFlashMessage($this->admin, $workflow, $existingObject, $transition), ); } catch (LogicException $e) { throw new BadRequestHttpException( @@ -111,6 +108,10 @@ public function workflowApplyTransitionAction(Request $request): Response ); } catch (ModelManagerException $e) { $this->handleModelManagerException($e); + $this->addFlash( + 'sonata_flash_error', + $this->translator->transitionErrorFlashMessage($this->admin, $workflow, $existingObject, $transition), + ); } catch (LockException $e) { $this->addFlash( 'sonata_flash_error', @@ -132,30 +133,18 @@ public function workflowApplyTransitionAction(Request $request): Response /** * @throws InvalidArgumentException */ - protected function getWorkflow(object $object): WorkflowInterface + final protected function getWorkflow(object $object): WorkflowInterface { - $registry = $this->workflowRegistry ?? null; - if ($registry === null) { - try { - if (method_exists($this, 'get')) { - $registry = $this->get('workflow.registry'); - } elseif (method_exists($this, 'getContainer')) { - $registry = $this->getContainer()->get('workflow.registry'); - } else { - $registry = $this->container->get('workflow.registry'); - } - } catch (ServiceNotFoundException $exception) { - throw new \LogicException( - 'Could not find the "workflow.registry" service. ' . - 'You should either provide it via setter injection in your controller service definition ' . - 'or make it public in your project.', - 0, - $exception - ); - } + if (!isset($this->workflowRegistry)) { + throw new \LogicException('Workflow registry was not set on controller.'); } - return $registry->get($object); + return $this->workflowRegistry->get($object); + } + + final protected function getWorkflowTranslator(): TranslatorInterface + { + return $this->translator ?? new StandardTranslator(); } protected function preApplyTransition(object $object, string $transition): ?Response diff --git a/src/Translator/StandardTranslator.php b/src/Translator/StandardTranslator.php new file mode 100644 index 0000000..3034b63 --- /dev/null +++ b/src/Translator/StandardTranslator.php @@ -0,0 +1,48 @@ + $this->escapeHtml($admin->toString($object)), + ], + domain: 'SonataAdminBundle', + ); + } + + public function transitionErrorFlashMessage( + AdminInterface $admin, + WorkflowInterface $workflow, + object $object, + string $transition, + ): TranslatableInterface { + return new TranslatableMessage( + message: 'flash_edit_error', + parameters: [ + '%name%' => $this->escapeHtml($admin->toString($object)), + ], + domain: 'SonataAdminBundle', + ); + } + + private function escapeHtml(string $string): string + { + return \htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE); + } +} diff --git a/src/Translator/TranslatorInterface.php b/src/Translator/TranslatorInterface.php new file mode 100644 index 0000000..040e259 --- /dev/null +++ b/src/Translator/TranslatorInterface.php @@ -0,0 +1,26 @@ +flashBag->peek('sonata_flash_success'); self::assertCount(1, $successes); - self::assertSame('[trans]flash_edit_success[/trans]', $successes[0]); + $success = $successes[0]; + self::assertInstanceOf(TranslatableInterface::class, $success); + self::assertSame('[trans]flash_edit_success[/trans]', $success->trans($this->container->get('translator'))); } public function testWorkflowApplyTransitionActionPreApply(): void @@ -312,6 +315,10 @@ private function controller(): PullRequestWorkflowController $controller->setContainer($this->container); $controller->configureAdmin($this->request); + $controller->autowireWorkflowControllerTrait( + $this->container->get('workflow.registry'), + null, + ); return $controller; }