From 91a4b45607c89c07d6f7e5aeed383bf02c488ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Fri, 18 Oct 2024 19:25:03 +0200 Subject: [PATCH] Add notification to user groups on workflow transition Implemented NotificationToUserGroupsOnTransition to send group emails upon workflow completion. Also updated NotificationOnTransition to prevent double notifications and created a unit test for the new functionality. --- ...ompleted_content_to_user_group.fr.txt.twig | 9 ++ ...tificationToUserGroupsOnTransitionTest.php | 141 ++++++++++++++++++ .../NotificationOnTransition.php | 9 +- .../NotificationToUserGroupsOnTransition.php | 90 +++++++++++ 4 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig create mode 100644 src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationToUserGroupsOnTransitionTest.php create mode 100644 src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationToUserGroupsOnTransition.php diff --git a/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig new file mode 100644 index 000000000..343d450c9 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Resources/views/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig @@ -0,0 +1,9 @@ +Chers membres du groupe {{ user_group.label|localize_translatable_string }}, + +Un suivi "{{ workflow.text }}" a atteint une nouvelle étape: {{ place.text }} + +Vous pouvez visualiser le workflow sur cette page: + +{{ absolute_url(path('chill_main_workflow_show', {'id': entity_workflow.id, '_locale': 'fr'})) }} + +Cordialement, diff --git a/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationToUserGroupsOnTransitionTest.php b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationToUserGroupsOnTransitionTest.php new file mode 100644 index 000000000..8c5bce7a6 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Tests/Workflow/EventSubscriber/NotificationToUserGroupsOnTransitionTest.php @@ -0,0 +1,141 @@ +twig = self::getContainer()->get('twig'); + $this->bodyRenderer = self::getContainer()->get(BodyRendererInterface::class); + } + + public function testOnCompletedSendNotificationToUserGroupWithEmailAddress(): void + { + $entityWorkflow = new EntityWorkflow(); + $reflection = new \ReflectionClass($entityWorkflow); + $idProperty = $reflection->getProperty('id'); + $idProperty->setValue($entityWorkflow, 1); + + $entityWorkflow->setWorkflowName('dummy'); + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->futureDestUsers = [$ug = new UserGroup()]; + $ug->setEmail('test@email.com')->setLabel(['fr' => 'test group']); + + $mailer = $this->prophesize(MailerInterface::class); + $sendMethod = $mailer->send(Argument::that(function (RawMessage $message): bool { + if (!$message instanceof TemplatedEmail) { + return false; + } + + $this->bodyRenderer->render($message); + + return 'test@email.com' === $message->getTo()[0]->getAddress(); + })); + $sendMethod->shouldBeCalledOnce(); + + $metadataExtractor = $this->prophesize(MetadataExtractor::class); + $metadataExtractor->buildArrayPresentationForWorkflow(Argument::type(Workflow::class))->willReturn(['name' => 'dummy', 'text' => 'Dummy Workflow']); + $metadataExtractor->buildArrayPresentationForPlace($entityWorkflow)->willReturn(['name' => 'to_one', 'text' => 'Dummy Place']); + + $registry = $this->buildRegistryWithEventSubscriber($mailer->reveal(), $metadataExtractor->reveal()); + $workflow = $registry->get($entityWorkflow, 'dummy'); + + $workflow->apply($entityWorkflow, 'to_one', ['context' => $dto, 'transition' => 'to_one', 'transitionAt' => new \DateTimeImmutable(), 'byUser' => new User()]); + } + + public function testOnCompletedSendNotificationToUserGroupWithoutAnyEmailAddress(): void + { + $entityWorkflow = new EntityWorkflow(); + $reflection = new \ReflectionClass($entityWorkflow); + $idProperty = $reflection->getProperty('id'); + $idProperty->setValue($entityWorkflow, 1); + + $entityWorkflow->setWorkflowName('dummy'); + $dto = new WorkflowTransitionContextDTO($entityWorkflow); + $dto->futureDestUsers = [$ug = new UserGroup()]; + + $mailer = $this->prophesize(MailerInterface::class); + $mailer->send(Argument::any())->shouldNotBeCalled(); + + $metadataExtractor = $this->prophesize(MetadataExtractor::class); + $metadataExtractor->buildArrayPresentationForWorkflow(Argument::type(Workflow::class))->willReturn(['name' => 'dummy', 'text' => 'Dummy Workflow']); + $metadataExtractor->buildArrayPresentationForPlace($entityWorkflow)->willReturn(['name' => 'to_one', 'text' => 'Dummy Place']); + + $registry = $this->buildRegistryWithEventSubscriber($mailer->reveal(), $metadataExtractor->reveal()); + $workflow = $registry->get($entityWorkflow, 'dummy'); + + $workflow->apply($entityWorkflow, 'to_one', ['context' => $dto, 'transition' => 'to_one', 'transitionAt' => new \DateTimeImmutable(), 'byUser' => new User()]); + } + + public function buildRegistryWithEventSubscriber(MailerInterface $mailer, MetadataExtractor $metadataExtractor): Registry + { + $builder = new DefinitionBuilder(); + $builder + ->setInitialPlaces('initial') + ->addPlaces(['initial', 'to_one']) + ->addTransition(new Transition('to_one', 'initial', 'to_one')); + + $metadata = new InMemoryMetadataStore( + ['label' => ['fr' => 'dummy workflow']], + ); + $builder->setMetadataStore($metadata); + + $workflow = new Workflow($builder->build(), new EntityWorkflowMarkingStore(), $eventDispatcher = new EventDispatcher(), 'dummy'); + $registry = new Registry(); + $registry->addWorkflow($workflow, new class () implements WorkflowSupportStrategyInterface { + public function supports(WorkflowInterface $workflow, object $subject): bool + { + return true; + } + }); + + $notificationEventSubscriber = new NotificationToUserGroupsOnTransition($this->twig, $metadataExtractor, $registry, $mailer); + $eventDispatcher->addSubscriber($notificationEventSubscriber); + + return $registry; + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php index 89ae65f4c..5e33fc3c0 100644 --- a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationOnTransition.php @@ -42,8 +42,8 @@ class NotificationOnTransition implements EventSubscriberInterface /** * Send a notification to:. * - * * the dests of the new step; - * * the users which subscribed to workflow, on each step, or on final + * * the dests of the new step, or the members of a user group if the user group has no email; + * * the users which subscribed to workflow, on each step, or on final; * * **Warning** take care that this method must be executed **after** the dest users are added to * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during @@ -74,6 +74,11 @@ class NotificationOnTransition implements EventSubscriberInterface // the users within groups $entityWorkflow->getCurrentStep()->getDestUserGroups()->reduce( function (array $accumulator, UserGroup $userGroup) { + if ($userGroup->hasEmail()) { + // this prevent users to be notified twice if they will already be notiied by the group + return $accumulator; + } + foreach ($userGroup->getUsers() as $user) { $accumulator[] = $user; } diff --git a/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationToUserGroupsOnTransition.php b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationToUserGroupsOnTransition.php new file mode 100644 index 000000000..8ac5ab750 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Workflow/EventSubscriber/NotificationToUserGroupsOnTransition.php @@ -0,0 +1,90 @@ + ['onCompletedSendNotification', 2048], + ]; + } + + /** + * Send a notification to:. + * + * * the dests of the new step; + * * the users which subscribed to workflow, on each step, or on final + * + * **Warning** take care that this method must be executed **after** the dest users are added to + * the step (@see{EntityWorkflowStep::addDestUser}). Currently, this is done during + * + * @see{EntityWorkflowTransitionEventSubscriber::addDests}. + */ + public function onCompletedSendNotification(Event $event): void + { + if (!$event->getSubject() instanceof EntityWorkflow) { + return; + } + + /** @var EntityWorkflow $entityWorkflow */ + $entityWorkflow = $event->getSubject(); + + $place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow); + $workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow( + $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()) + ); + + // send to groups + foreach ($entityWorkflow->getCurrentStep()->getDestUserGroups() as $userGroup) { + if (!$userGroup->hasEmail()) { + continue; + } + + $context = [ + 'entity_workflow' => $entityWorkflow, + 'user_group' => $userGroup, + 'place' => $place, + 'workflow' => $workflow, + 'is_dest' => true, + ]; + + $email = new TemplatedEmail(); + $email + ->htmlTemplate('@ChillMain/Workflow/workflow_notification_on_transition_completed_content_to_user_group.fr.txt.twig') + ->context($context) + ->subject( + $this->engine->render('@ChillMain/Workflow/workflow_notification_on_transition_completed_title.fr.txt.twig', $context) + ) + ->to($userGroup->getEmail()); + + $this->mailer->send($email); + } + } +}