mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
entity workflow: handle sending an access key by email
This commit is contained in:
parent
ff1ff8f5bb
commit
1479e2ae9a
@ -21,7 +21,6 @@ use Chill\MainBundle\Pagination\PaginatorFactory;
|
||||
use Chill\MainBundle\Repository\Workflow\EntityWorkflowRepository;
|
||||
use Chill\MainBundle\Security\Authorization\EntityWorkflowVoter;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||
use Chill\MainBundle\Workflow\Validator\StepDestValid;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
@ -152,9 +151,6 @@ class WorkflowController extends AbstractController
|
||||
throw new AccessDeniedException('Not a valid user');
|
||||
}
|
||||
|
||||
dump($accessKey);
|
||||
dump($entityWorkflowStep->getAccessKey());
|
||||
|
||||
if ($entityWorkflowStep->getAccessKey() !== $accessKey) {
|
||||
throw new AccessDeniedException('Access key is invalid');
|
||||
}
|
||||
@ -264,6 +260,7 @@ class WorkflowController extends AbstractController
|
||||
}
|
||||
|
||||
$entityWorkflow->futureDestUsers = $transitionForm['future_dest_users']->getData();
|
||||
$entityWorkflow->futureDestEmails = $transitionForm['future_dest_emails']->getData();
|
||||
|
||||
$workflow->apply($entityWorkflow, $transition);
|
||||
|
||||
@ -271,18 +268,13 @@ class WorkflowController extends AbstractController
|
||||
$entityWorkflow->getCurrentStep()->addDestUser($user);
|
||||
}
|
||||
|
||||
$errors = $this->validator->validate(
|
||||
$entityWorkflow->getCurrentStep(),
|
||||
new StepDestValid()
|
||||
);
|
||||
|
||||
if (count($errors) === 0) {
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
|
||||
foreach ($transitionForm['future_dest_emails']->getData() as $email) {
|
||||
$entityWorkflow->getCurrentStep()->addDestEmail($email);
|
||||
}
|
||||
|
||||
return new Response((string) $errors, Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
$this->entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('chill_main_workflow_show', ['id' => $entityWorkflow->getId()]);
|
||||
}
|
||||
|
||||
if ($transitionForm->isSubmitted() && !$transitionForm->isValid()) {
|
||||
|
@ -41,6 +41,17 @@ class EntityWorkflow implements TrackCreationInterface, TrackUpdateInterface
|
||||
|
||||
use TrackUpdateTrait;
|
||||
|
||||
/**
|
||||
* 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 dest users for the next steps.
|
||||
*
|
||||
|
@ -142,7 +142,7 @@ class EntityWorkflowStep
|
||||
|
||||
public function addDestUserByAccessKey(User $user): self
|
||||
{
|
||||
if (!$this->destUserByAccessKey->contains($user)) {
|
||||
if (!$this->destUserByAccessKey->contains($user) && !$this->destUser->contains($user)) {
|
||||
$this->destUserByAccessKey[] = $user;
|
||||
$this->getEntityWorkflow()
|
||||
->addSubscriberToFinal($user)
|
||||
|
@ -13,6 +13,7 @@ namespace Chill\MainBundle\Form;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflow;
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
||||
use Chill\MainBundle\Form\Type\ChillCollectionType;
|
||||
use Chill\MainBundle\Form\Type\ChillTextareaType;
|
||||
use Chill\MainBundle\Form\Type\PickUserDynamicType;
|
||||
use Chill\MainBundle\Templating\TranslatableStringHelperInterface;
|
||||
@ -21,8 +22,14 @@ use LogicException;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
use Symfony\Component\Validator\Constraints\Callback;
|
||||
use Symfony\Component\Validator\Constraints\Email;
|
||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||
use Symfony\Component\Validator\Constraints\NotNull;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
use Symfony\Component\Workflow\Transition;
|
||||
use function array_key_exists;
|
||||
@ -146,6 +153,22 @@ class WorkflowStepType extends AbstractType
|
||||
'label' => 'workflow.dest for next steps',
|
||||
'multiple' => true,
|
||||
'mapped' => false,
|
||||
])
|
||||
->add('future_dest_emails', ChillCollectionType::class, [
|
||||
'label' => 'workflow.dest by email',
|
||||
'help' => 'workflow.dest by email help',
|
||||
'mapped' => false,
|
||||
'allow_add' => true,
|
||||
'entry_type' => EmailType::class,
|
||||
'button_add_label' => 'workflow.Add an email',
|
||||
'button_remove_label' => 'workflow.Remove an email',
|
||||
'empty_collection_explain' => 'workflow.Any email',
|
||||
'entry_options' => [
|
||||
'constraints' => [
|
||||
new NotNull(), new NotBlank(), new Email(['checkMX' => true]),
|
||||
],
|
||||
'label' => 'Email',
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
@ -175,6 +198,37 @@ class WorkflowStepType extends AbstractType
|
||||
->setRequired('transition')
|
||||
->setAllowedTypes('transition', 'bool')
|
||||
->setRequired('entity_workflow')
|
||||
->setAllowedTypes('entity_workflow', EntityWorkflow::class);
|
||||
->setAllowedTypes('entity_workflow', EntityWorkflow::class)
|
||||
->setDefault('constraints', [
|
||||
new Callback(
|
||||
function ($step, ExecutionContextInterface $context, $payload) {
|
||||
/** @var EntityWorkflowStep $step */
|
||||
$form = $context->getObject();
|
||||
$workflow = $this->registry->get($step->getEntityWorkflow(), $step->getEntityWorkflow()->getWorkflowName());
|
||||
$transition = $form['transition']->getData();
|
||||
$toFinal = true;
|
||||
|
||||
foreach ($transition->getTos() as $to) {
|
||||
$meta = $workflow->getMetadataStore()->getPlaceMetadata($to);
|
||||
|
||||
if (
|
||||
!array_key_exists('isFinal', $meta) || false === $meta['isFinal']
|
||||
) {
|
||||
$toFinal = false;
|
||||
}
|
||||
}
|
||||
|
||||
$destUsers = $form['future_dest_users']->getData();
|
||||
$destEmails = $form['future_dest_emails']->getData();
|
||||
|
||||
if (!$toFinal && [] === $destUsers && [] === $destEmails) {
|
||||
$context
|
||||
->buildViolation('workflow.You must add at least one dest user or email')
|
||||
->atPath('future_dest_users')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import {ShowHide} from 'ChillMainAssets/lib/show_hide/show_hide.js';
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
let
|
||||
divTransitions = document.querySelector('#transitions'),
|
||||
futureDestUsersContainer = document.querySelector('#futureDestUsers')
|
||||
futureDestUsersContainer = document.querySelector('#futureDests')
|
||||
;
|
||||
|
||||
if (null !== divTransitions) {
|
||||
@ -67,24 +67,4 @@ window.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
}
|
||||
|
||||
// validate form
|
||||
let form = document.querySelector('form[name="workflow_step"]');
|
||||
|
||||
if (form === null) {
|
||||
console.error('form to validate not found');
|
||||
}
|
||||
|
||||
form.addEventListener('submit', function (event) {
|
||||
const datas = new FormData(event.target);
|
||||
|
||||
if (datas.has('workflow_step[future_dest_users]')) {
|
||||
const dests = JSON.parse(datas.get('workflow_step[future_dest_users]'));
|
||||
if (dests === null || (dests instanceof Array && dests.length === 0)) {
|
||||
event.preventDefault();
|
||||
console.log('no users!');
|
||||
window.alert('Indiquez un utilisateur pour traiter la prochaine étape.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -3,6 +3,8 @@
|
||||
{% if transition_form is not null %}
|
||||
{{ form_start(transition_form) }}
|
||||
|
||||
{{ form_errors(transition_form) }}
|
||||
|
||||
{% set step = entity_workflow.currentStepChained %}
|
||||
{% set labels = workflow_metadata(entity_workflow, 'label', step.currentStep) %}
|
||||
{% set label = labels is null ? step.currentStep : labels|localize_translatable_string %}
|
||||
@ -60,8 +62,10 @@
|
||||
{{ form_row(transition_form.freezeAfter) }}
|
||||
{% endif %}
|
||||
|
||||
<div id="futureDestUsers">
|
||||
<div id="futureDests">
|
||||
{{ form_row(transition_form.future_dest_users) }}
|
||||
|
||||
{{ form_row(transition_form.future_dest_emails) }}
|
||||
</div>
|
||||
|
||||
<p>{{ form_label(transition_form.comment) }}</p>
|
||||
|
@ -0,0 +1,15 @@
|
||||
Madame, Monsieur,
|
||||
|
||||
Un suivi "{{ workflow.text }}" a atteint une nouvelle étape: {{ workflow.text }}.
|
||||
|
||||
Titre du workflow: "{{ entityTitle }}".
|
||||
|
||||
Vous êtes invité·e à valider cette étape. Pour obtenir un accès, vous pouvez cliquer sur le lien suivant:
|
||||
|
||||
{{ absolute_url(path('chill_main_workflow_grant_access_by_key', {'id': entity_workflow.currentStep.id, 'accessKey': entity_workflow.currentStep.accessKey})) }}
|
||||
|
||||
Dès que vous aurez cliqué une fois sur le lien, vous serez autorisé à valider cette étape.
|
||||
|
||||
Notez que vous devez disposer d'un compte utilisateur valide dans Chill.
|
||||
|
||||
Cordialement,
|
@ -0,0 +1 @@
|
||||
Un suivi {{ workflow.text }} demande votre attention: {{ entityTitle }}
|
@ -52,11 +52,11 @@ class NotificationOnTransition implements EventSubscriberInterface
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
'workflow.completed' => 'onCompleted',
|
||||
'workflow.completed' => 'onCompletedSendNotification',
|
||||
];
|
||||
}
|
||||
|
||||
public function onCompleted(Event $event): void
|
||||
public function onCompletedSendNotification(Event $event): void
|
||||
{
|
||||
if (!$event->getSubject() instanceof EntityWorkflow) {
|
||||
return;
|
||||
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Chill\MainBundle\Workflow\EventSubscriber;
|
||||
|
||||
use Chill\MainBundle\Entity\Workflow\EntityWorkflowStep;
|
||||
use Chill\MainBundle\Workflow\EntityWorkflowManager;
|
||||
use Chill\MainBundle\Workflow\Helper\MetadataExtractor;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Symfony\Component\Templating\EngineInterface;
|
||||
use Symfony\Component\Workflow\Registry;
|
||||
|
||||
class SendAccessKeyEventSubscriber
|
||||
{
|
||||
private EngineInterface $engine;
|
||||
|
||||
private EntityWorkflowManager $entityWorkflowManager;
|
||||
|
||||
private MailerInterface $mailer;
|
||||
|
||||
private MetadataExtractor $metadataExtractor;
|
||||
|
||||
private Registry $registry;
|
||||
|
||||
public function __construct(EngineInterface $engine, MetadataExtractor $metadataExtractor, Registry $registry, EntityWorkflowManager $entityWorkflowManager, MailerInterface $mailer)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
$this->metadataExtractor = $metadataExtractor;
|
||||
$this->registry = $registry;
|
||||
$this->entityWorkflowManager = $entityWorkflowManager;
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
||||
public function postPersist(EntityWorkflowStep $step): void
|
||||
{
|
||||
$entityWorkflow = $step->getEntityWorkflow();
|
||||
|
||||
$place = $this->metadataExtractor->buildArrayPresentationForPlace($entityWorkflow);
|
||||
$workflow = $this->metadataExtractor->buildArrayPresentationForWorkflow(
|
||||
$this->registry->get($entityWorkflow, $entityWorkflow->getWorkflowName())
|
||||
);
|
||||
$handler = $this->entityWorkflowManager->getHandler($entityWorkflow);
|
||||
|
||||
foreach ($entityWorkflow->futureDestEmails as $emailAddress) {
|
||||
$context = [
|
||||
'entity_workflow' => $entityWorkflow,
|
||||
'dest' => $emailAddress,
|
||||
'place' => $place,
|
||||
'workflow' => $workflow,
|
||||
'entityTitle' => $handler->getEntityTitle($entityWorkflow),
|
||||
];
|
||||
|
||||
$email = new Email();
|
||||
$email
|
||||
->addTo($emailAddress)
|
||||
->subject($this->engine->render('@ChillMain/Workflow/workflow_send_access_key_title.fr.txt.twig', $context))
|
||||
->text($this->engine->render('@ChillMain/Workflow/workflow_send_access_key.fr.txt.twig', $context));
|
||||
|
||||
$this->mailer->send($email);
|
||||
}
|
||||
}
|
||||
}
|
@ -38,6 +38,17 @@ services:
|
||||
arguments:
|
||||
$handlers: !tagged_iterator chill_main.workflow_handler
|
||||
|
||||
Chill\MainBundle\Workflow\EventSubscriber\SendAccessKeyEventSubscriber:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
tags:
|
||||
-
|
||||
name: 'doctrine.orm.entity_listener'
|
||||
event: 'postPersist'
|
||||
entity: 'Chill\MainBundle\Entity\Workflow\EntityWorkflowStep'
|
||||
# set the 'lazy' option to TRUE to only instantiate listeners when they are used
|
||||
lazy: true
|
||||
|
||||
# other stuffes
|
||||
|
||||
chill.main.helper.translatable_string:
|
||||
|
@ -403,6 +403,12 @@ workflow:
|
||||
Steps is not waiting for transition. Maybe someone apply the transition before you ?: L'étape que vous cherchez a déjà été modifiée par un autre utilisateur. Peut-être quelqu'un a-t-il modifié cette étape avant vous ?
|
||||
You get access to this step: Vous avez acquis les droits pour appliquer une transition sur ce workflow.
|
||||
Those users are also granted to apply a transition by using an access key: Ces utilisateurs peuvent également valider cette étape, grâce à un lien d'accès
|
||||
dest by email: Liens d'autorisation par email
|
||||
dest by email help: Les adresses email mentionnées ici recevront un lien d'accès. Ce lien d'accès permettra à l'utilisateur de valider cette étape.
|
||||
Add an email: Ajouter une adresse email
|
||||
Remove an email: Enlever cette adresse email
|
||||
Any email: Aucune adresse email
|
||||
|
||||
|
||||
Subscribe final: Recevoir une notification à l'étape finale
|
||||
Subscribe all steps: Recevoir une notification à chaque étape
|
||||
|
@ -28,3 +28,6 @@ notification:
|
||||
At least one addressee: Indiquez au moins un destinataire
|
||||
Title must be defined: Un titre doit être indiqué
|
||||
Comment content might not be blank: Le commentaire ne peut pas être vide
|
||||
|
||||
workflow:
|
||||
You must add at least one dest user or email: Indiquez au moins un destinataire ou une adresse email
|
||||
|
Loading…
x
Reference in New Issue
Block a user