Adjust logic for removing the hold on a workflow only by user who owns the hold and when a transition is applied on the workflow

This commit is contained in:
Julie Lenaerts 2024-08-30 12:59:08 +02:00 committed by Julien Fastré
parent 41ffc470a0
commit 745a29f742
Signed by: julienfastre
GPG Key ID: BDE2190974723FCB
5 changed files with 48 additions and 26 deletions

View File

@ -298,7 +298,7 @@ class WorkflowController extends AbstractController
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
$errors = []; $errors = [];
$signatures = $entityWorkflow->getCurrentStep()->getSignatures(); $signatures = $entityWorkflow->getCurrentStep()->getSignatures();
$holdOnStepByUser = $this->entityWorkflowStepHoldRepository->findOneByStepAndUser($entityWorkflow->getCurrentStep(), $this->security->getUser()); $onHoldStep = $this->entityWorkflowStepHoldRepository->findByWorkflow($entityWorkflow);
if (\count($workflow->getEnabledTransitions($entityWorkflow)) > 0) { if (\count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
// possible transition // possible transition
@ -343,6 +343,10 @@ class WorkflowController extends AbstractController
$this->entityManager->flush(); $this->entityManager->flush();
if ($onHoldStep) {
$this->entityManager->remove($onHoldStep);
}
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
@ -361,7 +365,7 @@ class WorkflowController extends AbstractController
'entity_workflow' => $entityWorkflow, 'entity_workflow' => $entityWorkflow,
'transition_form_errors' => $errors, 'transition_form_errors' => $errors,
'signatures' => $signatures, 'signatures' => $signatures,
'holdOnStepByUser' => $holdOnStepByUser, 'onHoldStep' => $onHoldStep,
] ]
); );
} }

View File

@ -4,6 +4,7 @@ namespace Chill\MainBundle\Controller;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow; use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepHold; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepHold;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepHoldRepository; use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepHoldRepository;
use Chill\MainBundle\Security\ChillSecurity; use Chill\MainBundle\Security\ChillSecurity;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
@ -20,32 +21,23 @@ class WorkflowOnHoldController extends AbstractController
private readonly EntityManagerInterface $entityManager, private readonly EntityManagerInterface $entityManager,
private readonly Security $security, private readonly Security $security,
private readonly Registry $registry, private readonly Registry $registry,
private readonly EntityWorkflowStepHoldRepository $entityWorkflowStepHoldRepository private readonly EntityWorkflowStepHoldRepository $entityWorkflowStepHoldRepository,
private readonly EntityWorkflowRepository $entityWorkflowRepository
) {} ) {}
#[Route(path: '/{_locale}/main/workflow/{id}/hold', name: 'chill_main_workflow_on_hold')] #[Route(path: '/{_locale}/main/workflow/{id}/hold', name: 'chill_main_workflow_on_hold')]
public function putOnHold(EntityWorkflow $entityWorkflow, Request $request): Response public function putOnHold(EntityWorkflow $entityWorkflow, Request $request): Response
{ {
$entityWorkflow = $this->entityWorkflowRepository->find($entityWorkflow);
$currentStep = $entityWorkflow->getCurrentStep(); $currentStep = $entityWorkflow->getCurrentStep();
$currentUser = $this->security->getUser(); $currentUser = $this->security->getUser();
$workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName()); $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
$enabledTransitions = $workflow->getEnabledTransitions($entityWorkflow); $enabledTransitions = $workflow->getEnabledTransitions($entityWorkflow);
if (\count($enabledTransitions) === 0) {
throw $this->createAccessDeniedException('No transitions are available for the current workflow state.');
}
$isTransitionAllowed = false; if (!count($enabledTransitions) > 0) {
foreach ($enabledTransitions as $transition) { throw $this->createAccessDeniedException('You are not allowed to apply any transitions to this workflow, therefore you cannot toggle the hold status.');
if ($workflow->can($entityWorkflow, $transition->getName())) {
$isTransitionAllowed = true;
break;
}
}
if (!$isTransitionAllowed) {
throw $this->createAccessDeniedException('You are not allowed to apply any transitions to this workflow, therefore you cannot put it on hold.');
} }
$stepHold = new EntityWorkflowStepHold($currentStep, $currentUser); $stepHold = new EntityWorkflowStepHold($currentStep, $currentUser);
@ -61,10 +53,16 @@ class WorkflowOnHoldController extends AbstractController
{ {
$hold = $this->entityWorkflowStepHoldRepository->findById($holdId); $hold = $this->entityWorkflowStepHoldRepository->findById($holdId);
$entityWorkflow = $hold->getStep()->getEntityWorkflow(); $entityWorkflow = $hold->getStep()->getEntityWorkflow();
$currentUser = $this->security->getUser();
if ($hold->getByUser() !== $currentUser) {
throw $this->createAccessDeniedException('You are not allowed to remove the hold status.');
}
$this->entityManager->remove($hold); $this->entityManager->remove($hold);
$this->entityManager->flush(); $this->entityManager->flush();
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]); return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
} }
} }

View File

@ -12,12 +12,14 @@ declare(strict_types=1);
namespace Chill\MainBundle\Repository\Workflow; namespace Chill\MainBundle\Repository\Workflow;
use Chill\MainBundle\Entity\User; use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepHold; use Chill\MainBundle\Entity\Workflow\EntityWorkflowStepHold;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException; use Doctrine\ORM\NoResultException;
use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ManagerRegistry;
use Symfony\Component\Workflow\Workflow;
/** /**
* @template-extends ServiceEntityRepository<EntityWorkflowStepHold> * @template-extends ServiceEntityRepository<EntityWorkflowStepHold>
@ -58,6 +60,23 @@ class EntityWorkflowStepHoldRepository extends ServiceEntityRepository
return $this->findBy(['step' => $step]); return $this->findBy(['step' => $step]);
} }
/**
* @throws NonUniqueResultException
*/
public function findByWorkflow(EntityWorkflow $workflow): ?EntityWorkflowStepHold
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('h')
->from(EntityWorkflowStepHold::class, 'h')
->join('h.step', 's')
->join('s.entityWorkflow', 'w')
->where('w = :workflow')
->setParameter('workflow', $workflow);
return $qb->getQuery()->getOneOrNullResult();
}
/** /**
* Find a single EntityWorkflowStepHold by step and user. * Find a single EntityWorkflowStepHold by step and user.
* *

View File

@ -68,9 +68,9 @@
<section class="step my-4">{% include '@ChillMain/Workflow/_history.html.twig' %}</section> <section class="step my-4">{% include '@ChillMain/Workflow/_history.html.twig' %}</section>
<ul class="record_actions sticky-form-buttons"> <ul class="record_actions sticky-form-buttons">
{% if holdOnStepByUser|length > 0 %} {% if onHoldStep is not null %}
<li> <li>
<a class="btn btn-misc" href="{{ path('chill_main_workflow_remove_hold', {'holdId': holdOnStepByUser.id}) }}"><i class="fa fa-hourglass"></i> <a class="btn btn-misc" href="{{ path('chill_main_workflow_remove_hold', {'holdId': onHoldStep.id}) }}"><i class="fa fa-hourglass"></i>
{{ 'workflow.Remove hold'|trans }} {{ 'workflow.Remove hold'|trans }}
</a> </a>
</li> </li>

View File

@ -30,6 +30,7 @@ class WorkflowOnHoldControllerTest extends KernelTestCase
private $workflow; private $workflow;
private $currentStep; private $currentStep;
private $currentUser; private $currentUser;
private $controller;
protected function setUp(): void protected function setUp(): void
{ {
@ -53,6 +54,13 @@ class WorkflowOnHoldControllerTest extends KernelTestCase
$this->registry->method('get')->with($this->entityWorkflow, 'workflow_name')->willReturn($this->workflow); $this->registry->method('get')->with($this->entityWorkflow, 'workflow_name')->willReturn($this->workflow);
$this->workflow->method('getEnabledTransitions')->with($this->entityWorkflow)->willReturn([]); $this->workflow->method('getEnabledTransitions')->with($this->entityWorkflow)->willReturn([]);
$this->entityWorkflow->method('getUsersInvolved')->willReturn([]); $this->entityWorkflow->method('getUsersInvolved')->willReturn([]);
$this->controller = new WorkflowOnHoldController(
$this->entityManager,
$this->security,
$this->registry,
$this->entityWorkflowStepHoldRepository
);
} }
public function testPutOnHoldPersistence(): void public function testPutOnHoldPersistence(): void
@ -76,13 +84,6 @@ class WorkflowOnHoldControllerTest extends KernelTestCase
$this->entityManager->expects($this->once()) $this->entityManager->expects($this->once())
->method('flush'); ->method('flush');
$controller = new WorkflowOnHoldController(
$this->entityManager,
$this->security,
$this->registry,
$this->entityWorkflowStepHoldRepository
);
$request = new Request(); $request = new Request();
$controller->putOnHold($this->entityWorkflow, $request); $controller->putOnHold($this->entityWorkflow, $request);
} }