mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Introduced EntityWorkflowSend and EntityWorkflowSendView entities to enable tracking of workflow content sent to external parties. Updated EntityWorkflowStep to associate with these entities and added a corresponding database migration script.
529 lines
13 KiB
PHP
529 lines
13 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\Entity\Workflow;
|
|
|
|
use Chill\MainBundle\Entity\User;
|
|
use Chill\MainBundle\Entity\UserGroup;
|
|
use Doctrine\Common\Collections\ArrayCollection;
|
|
use Doctrine\Common\Collections\Collection;
|
|
use Doctrine\ORM\Mapping as ORM;
|
|
use Symfony\Component\Validator\Constraints as Assert;
|
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|
|
|
#[ORM\Entity]
|
|
#[ORM\Table('chill_main_workflow_entity_step')]
|
|
class EntityWorkflowStep
|
|
{
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: false)]
|
|
private string $accessKey;
|
|
|
|
/**
|
|
* @var Collection<int, User>
|
|
*/
|
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_cc_user')]
|
|
private Collection $ccUser;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, options: ['default' => ''])]
|
|
private string $comment = '';
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT)]
|
|
private ?string $currentStep = '';
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::JSON)]
|
|
private array $destEmail = [];
|
|
|
|
/**
|
|
* @var Collection<int, User>
|
|
*/
|
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user')]
|
|
private Collection $destUser;
|
|
|
|
/**
|
|
* @var Collection<int, UserGroup>
|
|
*/
|
|
#[ORM\ManyToMany(targetEntity: UserGroup::class)]
|
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_group')]
|
|
private Collection $destUserGroups;
|
|
|
|
/**
|
|
* @var Collection<int, User>
|
|
*/
|
|
#[ORM\ManyToMany(targetEntity: User::class)]
|
|
#[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_by_accesskey')]
|
|
private Collection $destUserByAccessKey;
|
|
|
|
/**
|
|
* @var Collection <int, EntityWorkflowStepSignature>
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepSignature::class, cascade: ['persist'], orphanRemoval: true)]
|
|
private Collection $signatures;
|
|
|
|
#[ORM\ManyToOne(targetEntity: EntityWorkflow::class, inversedBy: 'steps')]
|
|
private ?EntityWorkflow $entityWorkflow = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => false])]
|
|
private bool $freezeAfter = false;
|
|
|
|
#[ORM\Id]
|
|
#[ORM\GeneratedValue]
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::INTEGER)]
|
|
private ?int $id = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::BOOLEAN, options: ['default' => false])]
|
|
private bool $isFinal = false;
|
|
|
|
/**
|
|
* filled by @see{EntityWorkflow::getStepsChained}.
|
|
*/
|
|
private ?EntityWorkflowStep $next = null;
|
|
|
|
/**
|
|
* filled by @see{EntityWorkflow::getStepsChained}.
|
|
*/
|
|
private ?EntityWorkflowStep $previous = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true, options: ['default' => null])]
|
|
private ?string $transitionAfter = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE, nullable: true, options: ['default' => null])]
|
|
private ?\DateTimeImmutable $transitionAt = null;
|
|
|
|
#[ORM\ManyToOne(targetEntity: User::class)]
|
|
#[ORM\JoinColumn(nullable: true)]
|
|
private ?User $transitionBy = null;
|
|
|
|
#[ORM\Column(type: \Doctrine\DBAL\Types\Types::TEXT, nullable: true)]
|
|
private ?string $transitionByEmail = null;
|
|
|
|
/**
|
|
* @var \Doctrine\Common\Collections\Collection<int, \Chill\MainBundle\Entity\Workflow\EntityWorkflowStepHold>
|
|
*/
|
|
#[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class)]
|
|
private Collection $holdsOnStep;
|
|
|
|
/**
|
|
* @var Collection<int, EntityWorkflowSend>
|
|
*/
|
|
private Collection $sends;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->ccUser = new ArrayCollection();
|
|
$this->destUser = new ArrayCollection();
|
|
$this->destUserGroups = new ArrayCollection();
|
|
$this->destUserByAccessKey = new ArrayCollection();
|
|
$this->signatures = new ArrayCollection();
|
|
$this->holdsOnStep = new ArrayCollection();
|
|
$this->sends = new ArrayCollection();
|
|
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(32));
|
|
}
|
|
|
|
public function addCcUser(User $user): self
|
|
{
|
|
if (!$this->ccUser->contains($user)) {
|
|
$this->ccUser[] = $user;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
*/
|
|
public function addDestEmail(string $email): self
|
|
{
|
|
if (!\in_array($email, $this->destEmail, true)) {
|
|
$this->destEmail[] = $email;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addDestUser(User $user): self
|
|
{
|
|
if (!$this->destUser->contains($user)) {
|
|
$this->destUser[] = $user;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addDestUserGroup(UserGroup $userGroup): self
|
|
{
|
|
if (!$this->destUserGroups->contains($userGroup)) {
|
|
$this->destUserGroups[] = $userGroup;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeDestUserGroup(UserGroup $userGroup): self
|
|
{
|
|
$this->destUserGroups->removeElement($userGroup);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function addDestUserByAccessKey(User $user): self
|
|
{
|
|
if (!$this->destUserByAccessKey->contains($user) && !$this->destUser->contains($user)) {
|
|
$this->destUserByAccessKey[] = $user;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal use @see{EntityWorkflowStepSignature}'s constructor instead
|
|
*/
|
|
public function addSignature(EntityWorkflowStepSignature $signature): self
|
|
{
|
|
if (!$this->signatures->contains($signature)) {
|
|
$this->signatures[] = $signature;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal use @see{EntityWorkflowSend}'s constructor instead
|
|
*/
|
|
public function addSend(EntityWorkflowSend $send): self
|
|
{
|
|
if (!$this->sends->contains($send)) {
|
|
$this->sends[] = $send;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeSignature(EntityWorkflowStepSignature $signature): self
|
|
{
|
|
if ($this->signatures->contains($signature)) {
|
|
$this->signatures->removeElement($signature);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getAccessKey(): string
|
|
{
|
|
return $this->accessKey;
|
|
}
|
|
|
|
/**
|
|
* get all the users which are allowed to apply a transition: those added manually, and
|
|
* those added automatically by using an access key.
|
|
*
|
|
* This method exclude the users associated with user groups
|
|
*
|
|
* @psalm-suppress DuplicateArrayKey
|
|
*/
|
|
public function getAllDestUser(): Collection
|
|
{
|
|
return new ArrayCollection(
|
|
[
|
|
...$this->getDestUser()->toArray(),
|
|
...$this->getDestUserByAccessKey()->toArray(),
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, UserGroup>
|
|
*/
|
|
public function getDestUserGroups(): Collection
|
|
{
|
|
return $this->destUserGroups;
|
|
}
|
|
|
|
public function getCcUser(): Collection
|
|
{
|
|
return $this->ccUser;
|
|
}
|
|
|
|
public function getComment(): string
|
|
{
|
|
return $this->comment;
|
|
}
|
|
|
|
public function getCurrentStep(): ?string
|
|
{
|
|
return $this->currentStep;
|
|
}
|
|
|
|
/**
|
|
* @return array<string>
|
|
*
|
|
* @deprecated
|
|
*/
|
|
public function getDestEmail(): array
|
|
{
|
|
return $this->destEmail;
|
|
}
|
|
|
|
/**
|
|
* get dest users added by the creator.
|
|
*
|
|
* You should **not** rely on this method to get all users which are able to
|
|
* apply a transition on this step. Use @see{EntityWorkflowStep::getAllDestUser} instead.
|
|
*/
|
|
public function getDestUser(): Collection
|
|
{
|
|
return $this->destUser;
|
|
}
|
|
|
|
public function getDestUserByAccessKey(): Collection
|
|
{
|
|
return $this->destUserByAccessKey;
|
|
}
|
|
|
|
public function getEntityWorkflow(): ?EntityWorkflow
|
|
{
|
|
return $this->entityWorkflow;
|
|
}
|
|
|
|
/**
|
|
* @return Collection<int, EntityWorkflowStepSignature>
|
|
*/
|
|
public function getSignatures(): Collection
|
|
{
|
|
return $this->signatures;
|
|
}
|
|
|
|
public function getId(): ?int
|
|
{
|
|
return $this->id;
|
|
}
|
|
|
|
public function getNext(): ?EntityWorkflowStep
|
|
{
|
|
return $this->next;
|
|
}
|
|
|
|
public function getPrevious(): ?EntityWorkflowStep
|
|
{
|
|
return $this->previous;
|
|
}
|
|
|
|
public function getTransitionAfter(): ?string
|
|
{
|
|
return $this->transitionAfter;
|
|
}
|
|
|
|
public function getTransitionAt(): ?\DateTimeImmutable
|
|
{
|
|
return $this->transitionAt;
|
|
}
|
|
|
|
public function getTransitionBy(): ?User
|
|
{
|
|
return $this->transitionBy;
|
|
}
|
|
|
|
public function getTransitionByEmail(): ?string
|
|
{
|
|
return $this->transitionByEmail;
|
|
}
|
|
|
|
public function isFinal(): bool
|
|
{
|
|
return $this->isFinal;
|
|
}
|
|
|
|
public function isFreezeAfter(): bool
|
|
{
|
|
return $this->freezeAfter;
|
|
}
|
|
|
|
public function isOnHoldByUser(User $user): bool
|
|
{
|
|
foreach ($this->getHoldsOnStep() as $onHold) {
|
|
if ($onHold->getByUser() === $user) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function isWaitingForTransition(): bool
|
|
{
|
|
if (null !== $this->transitionAfter) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->isFinal()) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function removeCcUser(User $user): self
|
|
{
|
|
$this->ccUser->removeElement($user);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeDestEmail(string $email): self
|
|
{
|
|
$this->destEmail = array_filter($this->destEmail, static fn (string $existing) => $email !== $existing);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeDestUser(User $user): self
|
|
{
|
|
$this->destUser->removeElement($user);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function removeDestUserByAccessKey(User $user): self
|
|
{
|
|
$this->destUserByAccessKey->removeElement($user);
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setComment(?string $comment): EntityWorkflowStep
|
|
{
|
|
$this->comment = (string) $comment;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setCurrentStep(?string $currentStep): EntityWorkflowStep
|
|
{
|
|
$this->currentStep = $currentStep;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setDestEmail(array $destEmail): EntityWorkflowStep
|
|
{
|
|
$this->destEmail = $destEmail;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal use @see(EntityWorkflow::addStep} instead
|
|
*/
|
|
public function setEntityWorkflow(?EntityWorkflow $entityWorkflow): EntityWorkflowStep
|
|
{
|
|
$this->entityWorkflow = $entityWorkflow;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setFreezeAfter(bool $freezeAfter): EntityWorkflowStep
|
|
{
|
|
$this->freezeAfter = $freezeAfter;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setIsFinal(bool $isFinal): EntityWorkflowStep
|
|
{
|
|
$this->isFinal = $isFinal;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
public function setNext(?EntityWorkflowStep $next): self
|
|
{
|
|
$this->next = $next;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
*/
|
|
public function setPrevious(?EntityWorkflowStep $previous): self
|
|
{
|
|
$this->previous = $previous;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setTransitionAfter(?string $transitionAfter): EntityWorkflowStep
|
|
{
|
|
$this->transitionAfter = $transitionAfter;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setTransitionAt(?\DateTimeImmutable $transitionAt): EntityWorkflowStep
|
|
{
|
|
$this->transitionAt = $transitionAt;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setTransitionBy(?User $transitionBy): EntityWorkflowStep
|
|
{
|
|
$this->transitionBy = $transitionBy;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function setTransitionByEmail(?string $transitionByEmail): EntityWorkflowStep
|
|
{
|
|
$this->transitionByEmail = $transitionByEmail;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getHoldsOnStep(): Collection
|
|
{
|
|
return $this->holdsOnStep;
|
|
}
|
|
|
|
#[Assert\Callback]
|
|
public function validateOnCreation(ExecutionContextInterface $context, mixed $payload): void
|
|
{
|
|
return;
|
|
|
|
if ($this->isFinalizeAfter()) {
|
|
if (0 !== \count($this->getDestUser())) {
|
|
$context->buildViolation('workflow.No dest users when the workflow is finalized')
|
|
->atPath('finalizeAfter')
|
|
->addViolation();
|
|
}
|
|
} else {
|
|
if (0 === \count($this->getDestUser())) {
|
|
$context->buildViolation('workflow.The next step must count at least one dest')
|
|
->atPath('finalizeAfter')
|
|
->addViolation();
|
|
}
|
|
}
|
|
}
|
|
|
|
public function addOnHold(EntityWorkflowStepHold $onHold): self
|
|
{
|
|
if (!$this->holdsOnStep->contains($onHold)) {
|
|
$this->holdsOnStep->add($onHold);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
}
|