diff --git a/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php b/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php index 44766a583..ee45d1e56 100644 --- a/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php +++ b/src/Bundle/ChillMainBundle/Security/Authorization/EntityWorkflowVoter.php @@ -13,6 +13,7 @@ namespace Chill\MainBundle\Security\Authorization; use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Workflow\EntityWorkflowManager; +use Chill\MainBundle\Workflow\Helper\DuplicateEntityWorkflowFinder; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\Security; @@ -27,7 +28,11 @@ class EntityWorkflowVoter extends Voter final public const SHOW_ENTITY_LINK = 'CHILL_MAIN_WORKFLOW_LINK_SHOW'; - public function __construct(private readonly EntityWorkflowManager $manager, private readonly Security $security) {} + public function __construct( + private readonly EntityWorkflowManager $manager, + private readonly Security $security, + private readonly DuplicateEntityWorkflowFinder $duplicateEntityWorkflowFinder, + ) {} protected function supports($attribute, $subject) { @@ -41,6 +46,15 @@ class EntityWorkflowVoter extends Voter { switch ($attribute) { case self::CREATE: + if (false === $this->voteOnAttribute(self::SEE, $subject, $token)) { + return false; + } + + if ($this->duplicateEntityWorkflowFinder->hasDuplicateOpenedOrFinalPositiveEntityWorkflow($subject)) { + return false; + } + + return true; case self::SEE: $handler = $this->manager->getHandler($subject); diff --git a/src/Bundle/ChillMainBundle/Workflow/Helper/DuplicateEntityWorkflowFinder.php b/src/Bundle/ChillMainBundle/Workflow/Helper/DuplicateEntityWorkflowFinder.php new file mode 100644 index 000000000..7ade75223 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Workflow/Helper/DuplicateEntityWorkflowFinder.php @@ -0,0 +1,57 @@ +entityWorkflowRepository->findByRelatedEntity($entityWorkflow->getRelatedEntityClass(), $entityWorkflow->getRelatedEntityId()); + + foreach ($otherWorkflows as $otherWorkflow) { + if ($entityWorkflow === $otherWorkflow) { + continue; + } + + if ($otherWorkflow->getWorkflowName() !== $entityWorkflow->getWorkflowName()) { + continue; + } + + if (!$this->openedEntityWorkflowHelper->isFinal($otherWorkflow)) { + return true; + } + + if ($this->openedEntityWorkflowHelper->isFinalPositive($otherWorkflow)) { + return true; + } + } + + return false; + } +} diff --git a/src/Bundle/ChillMainBundle/Workflow/Helper/MetadataExtractor.php b/src/Bundle/ChillMainBundle/Workflow/Helper/MetadataExtractor.php index f99449dc3..0774ad10a 100644 --- a/src/Bundle/ChillMainBundle/Workflow/Helper/MetadataExtractor.php +++ b/src/Bundle/ChillMainBundle/Workflow/Helper/MetadataExtractor.php @@ -19,20 +19,33 @@ 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, + private readonly DuplicateEntityWorkflowFinder $duplicateEntityWorkflowFinder, + ) {} public function availableWorkflowFor(string $relatedEntityClass, ?int $relatedEntityId = 0): array { $blankEntityWorkflow = new EntityWorkflow(); $blankEntityWorkflow ->setRelatedEntityId($relatedEntityId) - ->setRelatedEntityClass($relatedEntityClass); + ->setRelatedEntityClass($relatedEntityClass) + ; // build the list of available workflows, and extract their names from metadata $workflows = $this->registry->all($blankEntityWorkflow); $workflowsList = []; foreach ($workflows as $workflow) { + // shortcut: we must not be able to create a new workflow if there are already created workflows, + // so, we find if there are workflows opened or final positive for the same entity + $blankEntityWorkflow->setWorkflowName($workflow->getName()); + if ($this->duplicateEntityWorkflowFinder->hasDuplicateOpenedOrFinalPositiveEntityWorkflow($blankEntityWorkflow)) { + // if yes, we skip suggesting workflow + continue; + } + $metadata = $workflow->getMetadataStore()->getWorkflowMetadata(); $text = \array_key_exists('label', $metadata) ? $this->translatableStringHelper->localize($metadata['label']) : $workflow->getName(); diff --git a/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php index 332ccd2e5..87fbf4498 100644 --- a/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php +++ b/src/Bundle/ChillMainBundle/Workflow/Templating/WorkflowTwigExtensionRuntime.php @@ -59,7 +59,6 @@ class WorkflowTwigExtensionRuntime implements RuntimeExtensionInterface return $environment->render('@ChillMain/Workflow/_extension_list_workflow_for.html.twig', [ 'entity_workflows_json' => $this->normalizer->normalize($entityWorkflows, 'json', ['groups' => 'read']), - 'blank_workflow' => $blankEntityWorkflow, 'workflows_available' => $workflowsAvailable, 'relatedEntityClass' => $relatedEntityClass, 'relatedEntityId' => $relatedEntityId,