mirror of
				https://gitlab.com/Chill-Projet/chill-bundles.git
				synced 2025-10-31 01:08:26 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			436 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			436 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?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\Controller;
 | |
| 
 | |
| use Chill\MainBundle\Entity\User;
 | |
| use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
 | |
| use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
 | |
| use Chill\MainBundle\Form\WorkflowSignatureMetadataType;
 | |
| use Chill\MainBundle\Form\WorkflowStepType;
 | |
| use Chill\MainBundle\Pagination\PaginatorFactory;
 | |
| use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
 | |
| use Chill\MainBundle\Repository\Workflow\EntityWorkflowStepSignatureRepository;
 | |
| use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
 | |
| use Chill\MainBundle\Security\ChillSecurity;
 | |
| use Chill\MainBundle\Workflow\EntityWorkflowManager;
 | |
| use Chill\MainBundle\Workflow\WorkflowTransitionContextDTO;
 | |
| use Doctrine\ORM\EntityManagerInterface;
 | |
| use Doctrine\ORM\NonUniqueResultException;
 | |
| use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
 | |
| use Symfony\Component\Clock\ClockInterface;
 | |
| use Symfony\Component\Form\Extension\Core\Type\FormType;
 | |
| use Symfony\Component\Form\Extension\Core\Type\SubmitType;
 | |
| use Symfony\Component\HttpFoundation\Request;
 | |
| use Symfony\Component\HttpFoundation\Response;
 | |
| use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 | |
| use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 | |
| use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 | |
| use Symfony\Component\Routing\Annotation\Route;
 | |
| use Symfony\Component\Validator\Validator\ValidatorInterface;
 | |
| use Symfony\Component\Workflow\Registry;
 | |
| use Symfony\Component\Workflow\TransitionBlocker;
 | |
| use Symfony\Contracts\Translation\TranslatorInterface;
 | |
| 
 | |
| class WorkflowController extends AbstractController
 | |
| {
 | |
|     public function __construct(
 | |
|         private readonly EntityWorkflowManager $entityWorkflowManager,
 | |
|         private readonly EntityWorkflowRepository $entityWorkflowRepository,
 | |
|         private readonly ValidatorInterface $validator,
 | |
|         private readonly PaginatorFactory $paginatorFactory,
 | |
|         private readonly Registry $registry,
 | |
|         private readonly EntityManagerInterface $entityManager,
 | |
|         private readonly TranslatorInterface $translator,
 | |
|         private readonly ChillSecurity $security,
 | |
|         private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
 | |
|         private readonly ClockInterface $clock,
 | |
|         private readonly EntityWorkflowStepSignatureRepository $entityWorkflowStepSignatureRepository,
 | |
|     ) {}
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/create', name: 'chill_main_workflow_create')]
 | |
|     public function create(Request $request): Response
 | |
|     {
 | |
|         if (!$request->query->has('entityClass')) {
 | |
|             throw new BadRequestHttpException('Missing entityClass parameter');
 | |
|         }
 | |
| 
 | |
|         if (!$request->query->has('entityId')) {
 | |
|             throw new BadRequestHttpException('missing entityId parameter');
 | |
|         }
 | |
| 
 | |
|         if (!$request->query->has('workflow')) {
 | |
|             throw new BadRequestHttpException('missing workflow parameter');
 | |
|         }
 | |
| 
 | |
|         $entityWorkflow = new EntityWorkflow();
 | |
|         $entityWorkflow
 | |
|             ->setRelatedEntityClass($request->query->get('entityClass'))
 | |
|             ->setRelatedEntityId($request->query->getInt('entityId'))
 | |
|             ->setWorkflowName($request->query->get('workflow'))
 | |
|             ->addSubscriberToFinal($this->security->getUser());
 | |
| 
 | |
|         $errors = $this->validator->validate($entityWorkflow, null, ['creation']);
 | |
| 
 | |
|         if (\count($errors) > 0) {
 | |
|             $msg = [];
 | |
| 
 | |
|             foreach ($errors as $error) {
 | |
|                 /* @var \Symfony\Component\Validator\ConstraintViolationInterface $error */
 | |
|                 $msg[] = $error->getMessage();
 | |
|             }
 | |
| 
 | |
|             return new Response(implode("\n", $msg), Response::HTTP_UNPROCESSABLE_ENTITY);
 | |
|         }
 | |
| 
 | |
|         $this->denyAccessUnlessGranted(EntityWorkflowVoter::CREATE, $entityWorkflow);
 | |
| 
 | |
|         $em = $this->managerRegistry->getManager();
 | |
|         $em->persist($entityWorkflow);
 | |
|         $em->flush();
 | |
| 
 | |
|         return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/{id}/delete', name: 'chill_main_workflow_delete')]
 | |
|     public function delete(EntityWorkflow $entityWorkflow, Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted(EntityWorkflowVoter::DELETE, $entityWorkflow);
 | |
| 
 | |
|         $form = $this->createForm(FormType::class);
 | |
|         $form->add('submit', SubmitType::class, ['label' => 'workflow.Delete workflow']);
 | |
|         $form->handleRequest($request);
 | |
| 
 | |
|         if ($form->isSubmitted() && $form->isValid()) {
 | |
|             $this->entityManager->remove($entityWorkflow);
 | |
|             $this->entityManager->flush();
 | |
| 
 | |
|             $this->addFlash('success', $this->translator->trans('workflow.Workflow deleted with success'));
 | |
| 
 | |
|             return $this->redirectToRoute('chill_main_homepage');
 | |
|         }
 | |
| 
 | |
|         return $this->render('@ChillMain/Workflow/delete.html.twig', [
 | |
|             'entityWorkflow' => $entityWorkflow,
 | |
|             'delete_form' => $form->createView(),
 | |
|             'handler' => $this->entityWorkflowManager->getHandler($entityWorkflow),
 | |
|         ]);
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow-step/{id}/access_key', name: 'chill_main_workflow_grant_access_by_key')]
 | |
|     public function getAccessByAccessKey(EntityWorkflowStep $entityWorkflowStep, Request $request): Response
 | |
|     {
 | |
|         if (null === $accessKey = $request->query->get('accessKey', null)) {
 | |
|             throw new BadRequestHttpException('accessKey is missing');
 | |
|         }
 | |
| 
 | |
|         if (!$this->getUser() instanceof User) {
 | |
|             throw new AccessDeniedHttpException('Not a valid user');
 | |
|         }
 | |
| 
 | |
|         if ($entityWorkflowStep->getAccessKey() !== $accessKey) {
 | |
|             throw new AccessDeniedHttpException('Access key is invalid');
 | |
|         }
 | |
| 
 | |
|         if (!$entityWorkflowStep->isWaitingForTransition()) {
 | |
|             $this->addFlash('error', $this->translator->trans('workflow.Steps is not waiting for transition. Maybe someone apply the transition before you ?'));
 | |
|         } else {
 | |
|             $entityWorkflowStep->addDestUserByAccessKey($this->security->getUser());
 | |
|             $this->entityManager->flush();
 | |
|             $this->addFlash('success', $this->translator->trans('workflow.You get access to this step'));
 | |
|         }
 | |
| 
 | |
|         return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflowStep->getEntityWorkflow()->getId()]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Previous workflows where the user has applyed a transition.
 | |
|      */
 | |
|     #[Route(path: '/{_locale}/main/workflow/list/previous_transitionned', name: 'chill_main_workflow_list_previous_transitionned')]
 | |
|     public function myPreviousWorkflowsTransitionned(Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
 | |
| 
 | |
|         $total = $this->entityWorkflowRepository->countByPreviousTransitionned($this->security->getUser());
 | |
|         $paginator = $this->paginatorFactory->create($total);
 | |
| 
 | |
|         $workflows = $this->entityWorkflowRepository->findByPreviousTransitionned(
 | |
|             $this->security->getUser(),
 | |
|             ['createdAt' => 'DESC'],
 | |
|             $paginator->getItemsPerPage(),
 | |
|             $paginator->getCurrentPageFirstItemNumber()
 | |
|         );
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/list.html.twig',
 | |
|             [
 | |
|                 'help' => 'workflow.Previous workflow transitionned help',
 | |
|                 'workflows' => $this->buildHandler($workflows),
 | |
|                 'paginator' => $paginator,
 | |
|                 'step' => 'previous_transitionned',
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Previous workflows where the user was mentioned, but did not give any reaction.
 | |
|      */
 | |
|     #[Route(path: '/{_locale}/main/workflow/list/previous_without_reaction', name: 'chill_main_workflow_list_previous_without_reaction')]
 | |
|     public function myPreviousWorkflowsWithoutReaction(Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
 | |
| 
 | |
|         $total = $this->entityWorkflowRepository->countByPreviousDestWithoutReaction($this->security->getUser());
 | |
|         $paginator = $this->paginatorFactory->create($total);
 | |
| 
 | |
|         $workflows = $this->entityWorkflowRepository->findByPreviousDestWithoutReaction(
 | |
|             $this->security->getUser(),
 | |
|             ['createdAt' => 'DESC'],
 | |
|             $paginator->getItemsPerPage(),
 | |
|             $paginator->getCurrentPageFirstItemNumber()
 | |
|         );
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/list.html.twig',
 | |
|             [
 | |
|                 'help' => 'workflow.Previous workflow without reaction help',
 | |
|                 'workflows' => $this->buildHandler($workflows),
 | |
|                 'paginator' => $paginator,
 | |
|                 'step' => 'previous_without_reaction',
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/list/cc', name: 'chill_main_workflow_list_cc')]
 | |
|     public function myWorkflowsCc(Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
 | |
| 
 | |
|         $total = $this->entityWorkflowRepository->countByDest($this->security->getUser());
 | |
|         $paginator = $this->paginatorFactory->create($total);
 | |
| 
 | |
|         $workflows = $this->entityWorkflowRepository->findByCc(
 | |
|             $this->security->getUser(),
 | |
|             ['createdAt' => 'DESC'],
 | |
|             $paginator->getItemsPerPage(),
 | |
|             $paginator->getCurrentPageFirstItemNumber()
 | |
|         );
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/list.html.twig',
 | |
|             [
 | |
|                 'workflows' => $this->buildHandler($workflows),
 | |
|                 'paginator' => $paginator,
 | |
|                 'step' => 'cc',
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/list/dest', name: 'chill_main_workflow_list_dest')]
 | |
|     public function myWorkflowsDest(Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
 | |
| 
 | |
|         $total = $this->entityWorkflowRepository->countByDest($this->security->getUser());
 | |
|         $paginator = $this->paginatorFactory->create($total);
 | |
| 
 | |
|         $workflows = $this->entityWorkflowRepository->findByDest(
 | |
|             $this->security->getUser(),
 | |
|             ['createdAt' => 'DESC'],
 | |
|             $paginator->getItemsPerPage(),
 | |
|             $paginator->getCurrentPageFirstItemNumber()
 | |
|         );
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/list.html.twig',
 | |
|             [
 | |
|                 'workflows' => $this->buildHandler($workflows),
 | |
|                 'paginator' => $paginator,
 | |
|                 'step' => 'dest',
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/list/subscribed', name: 'chill_main_workflow_list_subscribed')]
 | |
|     public function myWorkflowsSubscribed(Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted('IS_AUTHENTICATED_REMEMBERED');
 | |
| 
 | |
|         $total = $this->entityWorkflowRepository->countBySubscriber($this->security->getUser(), false);
 | |
|         $paginator = $this->paginatorFactory->create($total);
 | |
| 
 | |
|         $workflows = $this->entityWorkflowRepository->findBySubscriber(
 | |
|             $this->security->getUser(),
 | |
|             false,
 | |
|             ['createdAt' => 'DESC'],
 | |
|             $paginator->getItemsPerPage(),
 | |
|             $paginator->getCurrentPageFirstItemNumber()
 | |
|         );
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/list.html.twig',
 | |
|             [
 | |
|                 'workflows' => $this->buildHandler($workflows),
 | |
|                 'paginator' => $paginator,
 | |
|                 'step' => 'subscribed',
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @throws NonUniqueResultException
 | |
|      */
 | |
|     #[Route(path: '/{_locale}/main/workflow/{id}/show', name: 'chill_main_workflow_show')]
 | |
|     public function show(EntityWorkflow $entityWorkflow, Request $request): Response
 | |
|     {
 | |
|         $this->denyAccessUnlessGranted(EntityWorkflowVoter::SEE, $entityWorkflow);
 | |
| 
 | |
|         $handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
 | |
|         $workflow = $this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName());
 | |
|         $errors = [];
 | |
|         $signatures = $entityWorkflow->getCurrentStep()->getSignatures();
 | |
| 
 | |
|         if (\count($workflow->getEnabledTransitions($entityWorkflow)) > 0) {
 | |
|             // possible transition
 | |
|             $stepDTO = new WorkflowTransitionContextDTO($entityWorkflow);
 | |
| 
 | |
|             $transitionForm = $this->createForm(
 | |
|                 WorkflowStepType::class,
 | |
|                 $stepDTO,
 | |
|                 [
 | |
|                     'entity_workflow' => $entityWorkflow,
 | |
|                 ]
 | |
|             );
 | |
| 
 | |
|             $transitionForm->handleRequest($request);
 | |
| 
 | |
|             if ($transitionForm->isSubmitted() && $transitionForm->isValid()) {
 | |
|                 if (!$workflow->can($entityWorkflow, $transition = $transitionForm['transition']->getData()->getName())) {
 | |
|                     $blockers = $workflow->buildTransitionBlockerList($entityWorkflow, $transition);
 | |
|                     $msgs = array_map(fn (TransitionBlocker $tb) => $this->translator->trans(
 | |
|                         $tb->getMessage(),
 | |
|                         $tb->getParameters()
 | |
|                     ), iterator_to_array($blockers));
 | |
| 
 | |
|                     throw $this->createAccessDeniedException(sprintf("not allowed to apply transition {$transition}: %s", implode(', ', $msgs)));
 | |
|                 }
 | |
| 
 | |
|                 $byUser = $this->security->getUser();
 | |
| 
 | |
|                 $workflow->apply($entityWorkflow, $transition, [
 | |
|                     'context' => $stepDTO,
 | |
|                     'byUser' => $byUser,
 | |
|                     'transition' => $transition,
 | |
|                     'transitionAt' => $this->clock->now(),
 | |
|                 ]);
 | |
| 
 | |
|                 $this->entityManager->flush();
 | |
| 
 | |
|                 return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
 | |
|             }
 | |
| 
 | |
|             if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) {
 | |
|                 $this->addFlash('error', $this->translator->trans('This form contains errors'));
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/index.html.twig',
 | |
|             [
 | |
|                 'handler' => $handler,
 | |
|                 'handler_template' => $handler->getTemplate($entityWorkflow),
 | |
|                 'handler_template_data' => $handler->getTemplateData($entityWorkflow),
 | |
|                 'transition_form' => isset($transitionForm) ? $transitionForm->createView() : null,
 | |
|                 'entity_workflow' => $entityWorkflow,
 | |
|                 'transition_form_errors' => $errors,
 | |
|                 'signatures' => $signatures,
 | |
|                 'related_accompanying_period' => $this->entityWorkflowManager->getRelatedAccompanyingPeriod($entityWorkflow),
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     private function buildHandler(array $workflows): array
 | |
|     {
 | |
|         $lines = [];
 | |
| 
 | |
|         foreach ($workflows as $workflow) {
 | |
|             $handler = $this->entityWorkflowManager->getHandler($workflow);
 | |
|             $lines[] = [
 | |
|                 'handler' => $handler,
 | |
|                 'entity_workflow' => $workflow,
 | |
|             ];
 | |
|         }
 | |
| 
 | |
|         return $lines;
 | |
|     }
 | |
| 
 | |
|     #[Route(path: '/{_locale}/main/workflow/signature/{signature_id}/metadata', name: 'chill_main_workflow_signature_metadata')]
 | |
|     public function addSignatureMetadata(int $signature_id, Request $request): Response
 | |
|     {
 | |
|         $signature = $this->entityWorkflowStepSignatureRepository->find($signature_id);
 | |
| 
 | |
|         if (null === $signature) {
 | |
|             throw new NotFoundHttpException('signature not found');
 | |
|         }
 | |
| 
 | |
|         if ($signature->isSigned()) {
 | |
|             $this->addFlash(
 | |
|                 'notice',
 | |
|                 $this->translator->trans('workflow.signature_zone.already_signed_alert')
 | |
|             );
 | |
| 
 | |
|             return $this->redirectToRoute('chill_main_workflow_show', ['id' => $signature->getStep()->getEntityWorkflow()->getId()]);
 | |
|         }
 | |
| 
 | |
|         if ($signature->getSigner() instanceof User) {
 | |
|             return $this->redirectToRoute('chill_main_workflow_signature_add', [
 | |
|                 'id' => $signature_id,
 | |
|                 'returnPath' => $request->query->get('returnPath', null),
 | |
|             ]);
 | |
|         }
 | |
| 
 | |
|         $metadataForm = $this->createForm(WorkflowSignatureMetadataType::class);
 | |
|         $metadataForm->add('submit', SubmitType::class, ['label' => $this->translator->trans('Save')]);
 | |
| 
 | |
|         $metadataForm->handleRequest($request);
 | |
| 
 | |
|         if ($metadataForm->isSubmitted() && $metadataForm->isValid()) {
 | |
|             $data = $metadataForm->getData();
 | |
| 
 | |
|             $signature->setSignatureMetadata(
 | |
|                 [
 | |
|                     'base_signer' => [
 | |
|                         'document_type' => $data['documentType'],
 | |
|                         'document_number' => $data['documentNumber'],
 | |
|                         'expiration_date' => $data['expirationDate'],
 | |
|                     ],
 | |
|                 ]
 | |
|             );
 | |
| 
 | |
|             $this->entityManager->persist($signature);
 | |
|             $this->entityManager->flush();
 | |
| 
 | |
|             return $this->redirectToRoute('chill_main_workflow_signature_add', [
 | |
|                 'id' => $signature_id,
 | |
|                 'returnPath' => $request->query->get('returnPath', null),
 | |
|             ]);
 | |
|         }
 | |
| 
 | |
|         return $this->render(
 | |
|             '@ChillMain/Workflow/signature_metadata.html.twig',
 | |
|             [
 | |
|                 'metadata_form' => $metadataForm->createView(),
 | |
|                 'person' => $signature->getSigner(),
 | |
|             ]
 | |
|         );
 | |
|     }
 | |
| }
 |