*/ #[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 */ #[ORM\ManyToMany(targetEntity: User::class)] #[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user')] private Collection $destUser; /** * @var Collection */ #[ORM\ManyToMany(targetEntity: UserGroup::class)] #[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_group')] private Collection $destUserGroups; /** * @var Collection */ #[ORM\ManyToMany(targetEntity: User::class)] #[ORM\JoinTable(name: 'chill_main_workflow_entity_step_user_by_accesskey')] private Collection $destUserByAccessKey; /** * @var Collection */ #[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepSignature::class, cascade: ['persist', 'remove'], 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 Collection */ #[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class)] private Collection $holdsOnStep; /** * @var Collection */ #[ORM\OneToMany(mappedBy: 'entityWorkflowStep', targetEntity: EntityWorkflowSend::class, cascade: ['persist', 'remove'], orphanRemoval: true)] 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 */ 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 * * @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 */ public function getSignatures(): Collection { return $this->signatures; } /** * @return Collection */ public function getSends(): Collection { return $this->sends; } 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; } }