mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-26 08:35:00 +00:00
Revert "Merge branch 'revert-671bb6d5' into 'master'"
This reverts merge request !732
This commit is contained in:
@@ -22,9 +22,7 @@ use Symfony\Component\Workflow\Event\Event;
|
||||
|
||||
final readonly class WorkflowByUserCounter implements NotificationCounterInterface, EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private EntityWorkflowStepRepository $workflowStepRepository, private CacheItemPoolInterface $cacheItemPool)
|
||||
{
|
||||
}
|
||||
public function __construct(private EntityWorkflowStepRepository $workflowStepRepository, private CacheItemPoolInterface $cacheItemPool) {}
|
||||
|
||||
public function addNotification(UserInterface $u): int
|
||||
{
|
||||
|
@@ -14,6 +14,9 @@ namespace Chill\MainBundle\Workflow;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
|
||||
/**
|
||||
* @template T of object
|
||||
*/
|
||||
interface EntityWorkflowHandlerInterface
|
||||
{
|
||||
/**
|
||||
@@ -25,6 +28,9 @@ interface EntityWorkflowHandlerInterface
|
||||
|
||||
public function getEntityTitle(EntityWorkflow $entityWorkflow, array $options = []): string;
|
||||
|
||||
/**
|
||||
* @return T|null
|
||||
*/
|
||||
public function getRelatedEntity(EntityWorkflow $entityWorkflow): ?object;
|
||||
|
||||
public function getRelatedObjects(object $object): array;
|
||||
@@ -51,4 +57,9 @@ interface EntityWorkflowHandlerInterface
|
||||
public function supports(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
||||
|
||||
public function supportsFreeze(EntityWorkflow $entityWorkflow, array $options = []): bool;
|
||||
|
||||
/**
|
||||
* @return list<EntityWorkflow>
|
||||
*/
|
||||
public function findByRelatedEntity(object $object): array;
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Workflow\Exception\HandlerNotFoundException;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
@@ -20,9 +21,7 @@ class EntityWorkflowManager
|
||||
/**
|
||||
* @param \Chill\MainBundle\Workflow\EntityWorkflowHandlerInterface[] $handlers
|
||||
*/
|
||||
public function __construct(private readonly iterable $handlers, private readonly Registry $registry)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly iterable $handlers, private readonly Registry $registry) {}
|
||||
|
||||
public function getHandler(EntityWorkflow $entityWorkflow, array $options = []): EntityWorkflowHandlerInterface
|
||||
{
|
||||
@@ -39,4 +38,29 @@ class EntityWorkflowManager
|
||||
{
|
||||
return $this->registry->all($entityWorkflow);
|
||||
}
|
||||
|
||||
public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject
|
||||
{
|
||||
foreach ($this->handlers as $handler) {
|
||||
if ($handler instanceof EntityWorkflowWithStoredObjectHandlerInterface && $handler->supports($entityWorkflow)) {
|
||||
return $handler->getAssociatedStoredObject($entityWorkflow);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<EntityWorkflow>
|
||||
*/
|
||||
public function findByRelatedEntity(object $object): array
|
||||
{
|
||||
foreach ($this->handlers as $handler) {
|
||||
if ([] !== $workflows = $handler->findByRelatedEntity($object)) {
|
||||
return $workflows;
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Symfony\Component\Workflow\Marking;
|
||||
use Symfony\Component\Workflow\MarkingStore\MarkingStoreInterface;
|
||||
|
||||
final readonly class EntityWorkflowMarkingStore implements MarkingStoreInterface
|
||||
{
|
||||
public function getMarking(object $subject): Marking
|
||||
{
|
||||
if (!$subject instanceof EntityWorkflow) {
|
||||
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||
}
|
||||
$step = $subject->getCurrentStep();
|
||||
|
||||
return new Marking([$step->getCurrentStep() => 1]);
|
||||
}
|
||||
|
||||
public function setMarking(object $subject, Marking $marking, array $context = []): void
|
||||
{
|
||||
if (!$subject instanceof EntityWorkflow) {
|
||||
throw new \UnexpectedValueException('Expected instance of EntityWorkflow');
|
||||
}
|
||||
|
||||
$places = $marking->getPlaces();
|
||||
if (1 < count($places)) {
|
||||
throw new \LogicException('Expected maximum one place');
|
||||
}
|
||||
$next = array_keys($places)[0];
|
||||
|
||||
$transitionDTO = $context['context'] ?? null;
|
||||
$transition = $context['transition'];
|
||||
$byUser = $context['byUser'] ?? null;
|
||||
$at = $context['transitionAt'];
|
||||
|
||||
if (!$transitionDTO instanceof WorkflowTransitionContextDTO) {
|
||||
throw new \UnexpectedValueException(sprintf('Expected instance of %s', WorkflowTransitionContextDTO::class));
|
||||
}
|
||||
|
||||
$subject->setStep($next, $transitionDTO, $transition, $at, $byUser);
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
|
||||
/**
|
||||
* Add methods to handle workflows associated with @see{StoredObject}.
|
||||
*
|
||||
* @template T of object
|
||||
*
|
||||
* @template-extends EntityWorkflowHandlerInterface<T>
|
||||
*/
|
||||
interface EntityWorkflowWithStoredObjectHandlerInterface extends EntityWorkflowHandlerInterface
|
||||
{
|
||||
public function getAssociatedStoredObject(EntityWorkflow $entityWorkflow): ?StoredObject;
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow\EventSubscriber;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Security\Authorization\EntityWorkflowTransitionVoter;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
use Symfony\Component\Workflow\TransitionBlocker;
|
||||
|
||||
/**
|
||||
* Prevent apply a transition on an entity workflow.
|
||||
*
|
||||
* This apply logic and rules to decide if a transition can be applyed.
|
||||
*
|
||||
* Those rules are:
|
||||
*
|
||||
* - if the transition is system-only or is allowed for user;
|
||||
* - if the user is present in the dest users for a workflow;
|
||||
* - or if the user have permission to apply all the transitions
|
||||
*/
|
||||
class EntityWorkflowGuardTransition implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserRender $userRender,
|
||||
private readonly Security $security,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'workflow.guard' => [
|
||||
['guardEntityWorkflow', 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function guardEntityWorkflow(GuardEvent $event)
|
||||
{
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var EntityWorkflow $entityWorkflow */
|
||||
$entityWorkflow = $event->getSubject();
|
||||
|
||||
if ($entityWorkflow->isFinal()) {
|
||||
$event->addTransitionBlocker(
|
||||
new TransitionBlocker(
|
||||
'workflow.The workflow is finalized',
|
||||
'd6306280-7535-11ec-a40d-1f7bee26e2c0'
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$user = $this->security->getUser();
|
||||
$metadata = $event->getWorkflow()->getMetadataStore()->getTransitionMetadata($event->getTransition());
|
||||
$systemTransitions = explode('+', $metadata['transitionGuard'] ?? 'only-dest');
|
||||
|
||||
if (null === $user) {
|
||||
if (in_array('system', $systemTransitions, true)) {
|
||||
// it is safe to apply this transition
|
||||
return;
|
||||
}
|
||||
|
||||
$event->addTransitionBlocker(
|
||||
new TransitionBlocker(
|
||||
'workflow.Transition is not allowed for system',
|
||||
'd9e39a18-704c-11ef-b235-8fe0619caee7'
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// for users
|
||||
if (!in_array('only-dest', $systemTransitions, true)) {
|
||||
$event->addTransitionBlocker(
|
||||
new TransitionBlocker(
|
||||
'workflow.Only system can apply this transition',
|
||||
'5b6b95e0-704d-11ef-a5a9-4b6fc11a8eeb'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
!$entityWorkflow->getCurrentStep()->getAllDestUser()->contains($user)
|
||||
) {
|
||||
if ($event->getMarking()->has('initial')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->security->isGranted(EntityWorkflowTransitionVoter::APPLY_ALL_TRANSITIONS, $entityWorkflow->getCurrentStep())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$event->addTransitionBlocker(new TransitionBlocker(
|
||||
'workflow.You are not allowed to apply a transition on this workflow. Only those users are allowed: %users%',
|
||||
'f3eeb57c-7532-11ec-9495-e7942a2ac7bc',
|
||||
[
|
||||
'%users%' => implode(
|
||||
', ',
|
||||
$entityWorkflow->getCurrentStep()->getAllDestUser()->map(fn (User $u) => $this->userRender->renderString($u, []))->toArray()
|
||||
),
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,41 +13,17 @@ namespace Chill\MainBundle\Workflow\EventSubscriber;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Templating\Entity\UserRender;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
use Symfony\Component\Workflow\Event\Event;
|
||||
use Symfony\Component\Workflow\Event\GuardEvent;
|
||||
use Symfony\Component\Workflow\TransitionBlocker;
|
||||
|
||||
class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
||||
final readonly class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private readonly LoggerInterface $chillLogger, private readonly Security $security, private readonly UserRender $userRender)
|
||||
{
|
||||
}
|
||||
|
||||
public function addDests(Event $event): void
|
||||
{
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var EntityWorkflow $entityWorkflow */
|
||||
$entityWorkflow = $event->getSubject();
|
||||
|
||||
foreach ($entityWorkflow->futureCcUsers as $user) {
|
||||
$entityWorkflow->getCurrentStep()->addCcUser($user);
|
||||
}
|
||||
|
||||
foreach ($entityWorkflow->futureDestUsers as $user) {
|
||||
$entityWorkflow->getCurrentStep()->addDestUser($user);
|
||||
}
|
||||
|
||||
foreach ($entityWorkflow->futureDestEmails as $email) {
|
||||
$entityWorkflow->getCurrentStep()->addDestEmail($email);
|
||||
}
|
||||
}
|
||||
public function __construct(
|
||||
private LoggerInterface $chillLogger,
|
||||
private Security $security,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
@@ -55,52 +31,16 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
||||
'workflow.transition' => 'onTransition',
|
||||
'workflow.completed' => [
|
||||
['markAsFinal', 2048],
|
||||
['addDests', 2048],
|
||||
],
|
||||
'workflow.guard' => [
|
||||
['guardEntityWorkflow', 0],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function guardEntityWorkflow(GuardEvent $event)
|
||||
{
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var EntityWorkflow $entityWorkflow */
|
||||
$entityWorkflow = $event->getSubject();
|
||||
|
||||
if ($entityWorkflow->isFinal()) {
|
||||
$event->addTransitionBlocker(
|
||||
new TransitionBlocker(
|
||||
'workflow.The workflow is finalized',
|
||||
'd6306280-7535-11ec-a40d-1f7bee26e2c0'
|
||||
)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$entityWorkflow->getCurrentStep()->getAllDestUser()->contains($this->security->getUser())) {
|
||||
if (!$event->getMarking()->has('initial')) {
|
||||
$event->addTransitionBlocker(new TransitionBlocker(
|
||||
'workflow.You are not allowed to apply a transition on this workflow. Only those users are allowed: %users%',
|
||||
'f3eeb57c-7532-11ec-9495-e7942a2ac7bc',
|
||||
[
|
||||
'%users%' => implode(
|
||||
', ',
|
||||
$entityWorkflow->getCurrentStep()->getAllDestUser()->map(fn (User $u) => $this->userRender->renderString($u, []))->toArray()
|
||||
),
|
||||
]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function markAsFinal(Event $event): void
|
||||
{
|
||||
// NOTE: it is not possible to move this method to the marking store, because
|
||||
// there is dependency between the Workflow definition and the MarkingStoreInterface (the workflow
|
||||
// constructor need a MarkingStoreInterface)
|
||||
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
}
|
||||
@@ -125,18 +65,14 @@ class EntityWorkflowTransitionEventSubscriber implements EventSubscriberInterfac
|
||||
|
||||
/** @var EntityWorkflow $entityWorkflow */
|
||||
$entityWorkflow = $event->getSubject();
|
||||
$step = $entityWorkflow->getCurrentStep();
|
||||
|
||||
$step
|
||||
->setTransitionAfter($event->getTransition()->getName())
|
||||
->setTransitionAt(new \DateTimeImmutable('now'))
|
||||
->setTransitionBy($this->security->getUser());
|
||||
$user = $this->security->getUser();
|
||||
|
||||
$this->chillLogger->info('[workflow] apply transition on entityWorkflow', [
|
||||
'relatedEntityClass' => $entityWorkflow->getRelatedEntityClass(),
|
||||
'relatedEntityId' => $entityWorkflow->getRelatedEntityId(),
|
||||
'transition' => $event->getTransition()->getName(),
|
||||
'by_user' => $this->security->getUser(),
|
||||
'by_user' => $user instanceof User ? $user->getId() : (string) $user?->getUserIdentifier(),
|
||||
'entityWorkflow' => $entityWorkflow->getId(),
|
||||
]);
|
||||
}
|
||||
|
@@ -23,9 +23,13 @@ use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class NotificationOnTransition implements EventSubscriberInterface
|
||||
{
|
||||
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Security $security, private readonly Registry $registry)
|
||||
{
|
||||
}
|
||||
public function __construct(
|
||||
private readonly EntityManagerInterface $entityManager,
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly MetadataExtractor $metadataExtractor,
|
||||
private readonly Security $security,
|
||||
private readonly Registry $registry,
|
||||
) {}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
@@ -87,7 +91,10 @@ class NotificationOnTransition implements EventSubscriberInterface
|
||||
'dest' => $subscriber,
|
||||
'place' => $place,
|
||||
'workflow' => $workflow,
|
||||
'is_dest' => \in_array($subscriber->getId(), array_map(static fn (User $u) => $u->getId(), $entityWorkflow->futureDestUsers), true),
|
||||
'is_dest' => \in_array($subscriber->getId(), array_map(
|
||||
static fn (User $u) => $u->getId(),
|
||||
$entityWorkflow->getCurrentStep()->getDestUser()->toArray()
|
||||
), true),
|
||||
];
|
||||
|
||||
$notification = new Notification();
|
||||
|
@@ -20,9 +20,13 @@ use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class SendAccessKeyEventSubscriber
|
||||
{
|
||||
public function __construct(private readonly \Twig\Environment $engine, private readonly MetadataExtractor $metadataExtractor, private readonly Registry $registry, private readonly EntityWorkflowManager $entityWorkflowManager, private readonly MailerInterface $mailer)
|
||||
{
|
||||
}
|
||||
public function __construct(
|
||||
private readonly \Twig\Environment $engine,
|
||||
private readonly MetadataExtractor $metadataExtractor,
|
||||
private readonly Registry $registry,
|
||||
private readonly EntityWorkflowManager $entityWorkflowManager,
|
||||
private readonly MailerInterface $mailer,
|
||||
) {}
|
||||
|
||||
public function postPersist(EntityWorkflowStep $step): void
|
||||
{
|
||||
@@ -34,7 +38,7 @@ class SendAccessKeyEventSubscriber
|
||||
);
|
||||
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
||||
|
||||
foreach ($entityWorkflow->futureDestEmails as $emailAddress) {
|
||||
foreach ($step->getDestEmail() as $emailAddress) {
|
||||
$context = [
|
||||
'entity_workflow' => $entityWorkflow,
|
||||
'dest' => $emailAddress,
|
||||
|
@@ -11,6 +11,4 @@ declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Workflow\Exception;
|
||||
|
||||
class HandlerNotFoundException extends \RuntimeException
|
||||
{
|
||||
}
|
||||
class HandlerNotFoundException extends \RuntimeException {}
|
||||
|
@@ -19,9 +19,7 @@ use Symfony\Component\Workflow\WorkflowInterface;
|
||||
|
||||
class MetadataExtractor
|
||||
{
|
||||
public function __construct(private readonly Registry $registry, private readonly TranslatableStringHelperInterface $translatableStringHelper)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly Registry $registry, private readonly TranslatableStringHelperInterface $translatableStringHelper) {}
|
||||
|
||||
public function availableWorkflowFor(string $relatedEntityClass, ?int $relatedEntityId = 0): array
|
||||
{
|
||||
|
@@ -20,9 +20,7 @@ use Symfony\Component\Security\Core\Security;
|
||||
|
||||
class WorkflowNotificationHandler implements NotificationHandlerInterface
|
||||
{
|
||||
public function __construct(private readonly EntityWorkflowRepository $entityWorkflowRepository, private readonly EntityWorkflowManager $entityWorkflowManager, private readonly Security $security)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly EntityWorkflowRepository $entityWorkflowRepository, private readonly EntityWorkflowManager $entityWorkflowManager, private readonly Security $security) {}
|
||||
|
||||
public function getTemplate(Notification $notification, array $options = []): string
|
||||
{
|
||||
|
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowSignatureStateEnum;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepSignature;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Symfony\Component\Clock\ClockInterface;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class SignatureStepStateChanger
|
||||
{
|
||||
private const LOG_PREFIX = '[SignatureStepStateChanger] ';
|
||||
|
||||
public function __construct(
|
||||
private readonly Registry $registry,
|
||||
private readonly ClockInterface $clock,
|
||||
private readonly LoggerInterface $logger,
|
||||
) {}
|
||||
|
||||
public function markSignatureAsSigned(EntityWorkflowStepSignature $signature, ?int $atIndex): void
|
||||
{
|
||||
$signature
|
||||
->setState(EntityWorkflowSignatureStateEnum::SIGNED)
|
||||
->setZoneSignatureIndex($atIndex)
|
||||
->setStateDate($this->clock->now())
|
||||
;
|
||||
|
||||
$this->logger->info(self::LOG_PREFIX.'Mark signature entity as signed', ['signatureId' => $signature->getId(), 'index' => (string) $atIndex]);
|
||||
|
||||
if (!EntityWorkflowStepSignature::isAllSignatureNotPendingForStep($signature->getStep())) {
|
||||
$this->logger->info(self::LOG_PREFIX.'This is not the last signature, skipping transition to another place', ['signatureId' => $signature->getId()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->debug(self::LOG_PREFIX.'Continuing the process to find a transition', ['signatureId' => $signature->getId()]);
|
||||
|
||||
$entityWorkflow = $signature->getStep()->getEntityWorkflow();
|
||||
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
|
||||
$metadataStore = $workflow->getMetadataStore();
|
||||
|
||||
// find a transition
|
||||
$marking = $workflow->getMarking($entityWorkflow);
|
||||
$places = $marking->getPlaces();
|
||||
|
||||
$transition = null;
|
||||
foreach ($places as $place => $int) {
|
||||
$metadata = $metadataStore->getPlaceMetadata($place);
|
||||
if (array_key_exists('onSignatureCompleted', $metadata)) {
|
||||
$transition = $metadata['onSignatureCompleted']['transitionName'];
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $transition) {
|
||||
$this->logger->info(self::LOG_PREFIX.'The transition is not configured, will not apply a transition', ['signatureId' => $signature->getId()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$previousUser = $this->getPreviousSender($signature->getStep());
|
||||
|
||||
if (null === $previousUser) {
|
||||
$this->logger->info(self::LOG_PREFIX.'No previous user, will not apply a transition', ['signatureId' => $signature->getId()]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$transitionDto = new WorkflowTransitionContextDTO($entityWorkflow);
|
||||
$transitionDto->futureDestUsers[] = $previousUser;
|
||||
|
||||
$workflow->apply($entityWorkflow, $transition, [
|
||||
'context' => $transitionDto,
|
||||
'transitionAt' => $this->clock->now(),
|
||||
'transition' => $transition,
|
||||
]);
|
||||
|
||||
$this->logger->info(self::LOG_PREFIX.'Transition automatically applied', ['signatureId' => $signature->getId()]);
|
||||
}
|
||||
|
||||
private function getPreviousSender(EntityWorkflowStep $entityWorkflowStep): ?User
|
||||
{
|
||||
$stepsChained = $entityWorkflowStep->getEntityWorkflow()->getStepsChained();
|
||||
|
||||
foreach ($stepsChained as $stepChained) {
|
||||
if ($stepChained === $entityWorkflowStep) {
|
||||
if (null === $previous = $stepChained->getPrevious()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (null !== $previousUser = $previous->getTransitionBy()) {
|
||||
return $previousUser;
|
||||
}
|
||||
|
||||
return $this->getPreviousSender($previous);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \LogicException('no same step found');
|
||||
}
|
||||
}
|
@@ -23,9 +23,7 @@ use Twig\Extension\RuntimeExtensionInterface;
|
||||
|
||||
class WorkflowTwigExtensionRuntime implements RuntimeExtensionInterface
|
||||
{
|
||||
public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager, private readonly Registry $registry, private readonly EntityWorkflowRepository $repository, private readonly MetadataExtractor $metadataExtractor, private readonly NormalizerInterface $normalizer)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager, private readonly Registry $registry, private readonly EntityWorkflowRepository $repository, private readonly MetadataExtractor $metadataExtractor, private readonly NormalizerInterface $normalizer) {}
|
||||
|
||||
public function getTransitionByString(EntityWorkflow $entityWorkflow, string $key): ?Transition
|
||||
{
|
||||
|
@@ -17,9 +17,8 @@ namespace Chill\MainBundle\Workflow\Validator;
|
||||
* * a handler exists;
|
||||
* * a related entity does exists;
|
||||
* * a workflow can be associated with this entity.
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
class EntityWorkflowCreation extends \Symfony\Component\Validator\Constraint
|
||||
{
|
||||
public string $messageEntityNotFound = 'Related entity is not found';
|
||||
|
@@ -21,9 +21,7 @@ use Symfony\Component\Workflow\WorkflowInterface;
|
||||
|
||||
class EntityWorkflowCreationValidator extends \Symfony\Component\Validator\ConstraintValidator
|
||||
{
|
||||
public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager)
|
||||
{
|
||||
}
|
||||
public function __construct(private readonly EntityWorkflowManager $entityWorkflowManager) {}
|
||||
|
||||
/**
|
||||
* @param EntityWorkflow $value
|
||||
|
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Chill is a software for social workers
|
||||
*
|
||||
* For the full copyright and license information, please view
|
||||
* the LICENSE file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Chill\MainBundle\Workflow;
|
||||
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\PersonBundle\Entity\Person;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
|
||||
/**
|
||||
* Context for a transition on an workflow entity.
|
||||
*/
|
||||
class WorkflowTransitionContextDTO
|
||||
{
|
||||
/**
|
||||
* a list of future dest users for the next steps.
|
||||
*
|
||||
* This is in used in order to let controller inform who will be the future users which will validate
|
||||
* the next step. This is necessary to perform some computation about the next users, before they are
|
||||
* associated to the entity EntityWorkflowStep.
|
||||
*
|
||||
* @var array|User[]
|
||||
*/
|
||||
public array $futureDestUsers = [];
|
||||
|
||||
/**
|
||||
* a list of future cc users for the next steps.
|
||||
*
|
||||
* @var array|User[]
|
||||
*/
|
||||
public array $futureCcUsers = [];
|
||||
|
||||
/**
|
||||
* a list of future dest emails for the next steps.
|
||||
*
|
||||
* This is in used in order to let controller inform who will be the future emails which will validate
|
||||
* the next step. This is necessary to perform some computation about the next emails, before they are
|
||||
* associated to the entity EntityWorkflowStep.
|
||||
*
|
||||
* @var array|string[]
|
||||
*/
|
||||
public array $futureDestEmails = [];
|
||||
|
||||
/**
|
||||
* A list of future @see{Person} with will sign the next step.
|
||||
*
|
||||
* @var list<Person>
|
||||
*/
|
||||
public array $futurePersonSignatures = [];
|
||||
|
||||
/**
|
||||
* An eventual user which is requested to apply a signature.
|
||||
*/
|
||||
public ?User $futureUserSignature = null;
|
||||
|
||||
public ?Transition $transition = null;
|
||||
|
||||
public string $comment = '';
|
||||
|
||||
public function __construct(
|
||||
public EntityWorkflow $entityWorkflow,
|
||||
) {}
|
||||
|
||||
#[Assert\Callback()]
|
||||
public function validateCCUserIsNotInDest(ExecutionContextInterface $context, $payload): void
|
||||
{
|
||||
foreach ($this->futureDestUsers as $u) {
|
||||
if (in_array($u, $this->futureCcUsers, true)) {
|
||||
$context
|
||||
->buildViolation('workflow.The user in cc cannot be a dest user in the same workflow step')
|
||||
->atPath('ccUsers')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user