diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSend.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSend.php new file mode 100644 index 000000000..ef0422fe0 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSend.php @@ -0,0 +1,164 @@ + ''])] + private string $destineeEmail = ''; + + #[ORM\Column(type: 'uuid', unique: true, nullable: false)] + private UuidInterface $uuid; + + #[ORM\Column(type: Types::STRING, length: 255, nullable: false)] + private string $privateToken; + + #[ORM\Column(type: Types::INTEGER, nullable: false, options: ['default' => 0])] + private int $numberOfErrorTrials = 0; + + /** + * @var Collection + */ + #[ORM\OneToMany(targetEntity: EntityWorkflowSendView::class, mappedBy: 'send')] + private Collection $views; + + public function __construct( + #[ORM\ManyToOne(targetEntity: EntityWorkflowStep::class, inversedBy: 'sends')] + #[ORM\JoinColumn(nullable: false)] + private EntityWorkflowStep $entityWorkflowStep, + string|ThirdParty $destinee, + #[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: false)] + private \DateTimeImmutable $expireAt, + ) { + $this->uuid = Uuid::uuid4(); + $random = new Randomizer(); + $this->privateToken = bin2hex($random->getBytes(48)); + + $this->entityWorkflowStep->addSend($this); + + if ($destinee instanceof ThirdParty) { + $this->destineeThirdParty = $destinee; + } else { + $this->destineeEmail = $destinee; + } + + $this->views = new ArrayCollection(); + } + + /** + * @internal use the @see{EntityWorkflowSendView}'s constructor instead + */ + public function addView(EntityWorkflowSendView $view): self + { + if (!$this->views->contains($view)) { + $this->views->add($view); + } + + return $this; + } + + public function getDestineeEmail(): string + { + return $this->destineeEmail; + } + + public function getDestineeThirdParty(): ?ThirdParty + { + return $this->destineeThirdParty; + } + + public function getId(): ?int + { + return $this->id; + } + + public function getNumberOfErrorTrials(): int + { + return $this->numberOfErrorTrials; + } + + public function getPrivateToken(): string + { + return $this->privateToken; + } + + public function getUuid(): UuidInterface + { + return $this->uuid; + } + + public function increaseErrorTrials(): void + { + $this->numberOfErrorTrials = $this->numberOfErrorTrials + 1; + } + + public function getDestinee(): string|ThirdParty + { + if (null !== $this->getDestineeThirdParty()) { + return $this->getDestineeThirdParty(); + } + + return $this->getDestineeEmail(); + } + + /** + * Determines the kind of destinee based on whether the destinee is a thirdParty or an emailAddress. + * + * @return 'thirdParty'|'email' 'thirdParty' if the destinee is a third party, 'email' otherwise + */ + public function getDestineeKind(): string + { + if (null !== $this->getDestineeThirdParty()) { + return 'thirdParty'; + } + + return 'email'; + } + + public function isViewed(): bool + { + return $this->views->count() > 0; + } + + public function isExpired(\DateTimeImmutable $now): bool + { + return $now >= $this->expireAt; + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSendView.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSendView.php new file mode 100644 index 000000000..40d9d9736 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowSendView.php @@ -0,0 +1,60 @@ +send->addView($this); + } + + public function getId(): ?int + { + return $this->id; + } + + public function getRemoteIp(): string + { + return $this->remoteIp; + } + + public function getSend(): EntityWorkflowSend + { + return $this->send; + } + + public function getViewAt(): \DateTimeInterface + { + return $this->viewAt; + } +} diff --git a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php index 3daacd49d..4f42dee6e 100644 --- a/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php +++ b/src/Bundle/ChillMainBundle/Entity/Workflow/EntityWorkflowStep.php @@ -112,6 +112,11 @@ class EntityWorkflowStep #[ORM\OneToMany(mappedBy: 'step', targetEntity: EntityWorkflowStepHold::class)] private Collection $holdsOnStep; + /** + * @var Collection + */ + private Collection $sends; + public function __construct() { $this->ccUser = new ArrayCollection(); @@ -120,6 +125,7 @@ class EntityWorkflowStep $this->destUserByAccessKey = new ArrayCollection(); $this->signatures = new ArrayCollection(); $this->holdsOnStep = new ArrayCollection(); + $this->sends = new ArrayCollection(); $this->accessKey = bin2hex(openssl_random_pseudo_bytes(32)); } @@ -190,6 +196,18 @@ class EntityWorkflowStep 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)) { diff --git a/src/Bundle/ChillMainBundle/migrations/Version20241003094904.php b/src/Bundle/ChillMainBundle/migrations/Version20241003094904.php new file mode 100644 index 000000000..a309a927d --- /dev/null +++ b/src/Bundle/ChillMainBundle/migrations/Version20241003094904.php @@ -0,0 +1,61 @@ +addSql('CREATE SEQUENCE chill_main_workflow_entity_send_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE SEQUENCE chill_main_workflow_entity_send_views_id_seq INCREMENT BY 1 MINVALUE 1 START 1'); + $this->addSql('CREATE TABLE chill_main_workflow_entity_send (id INT NOT NULL,' + .' destineeEmail TEXT DEFAULT \'\' NOT NULL, uuid UUID NOT NULL, privateToken VARCHAR(255) NOT NULL,' + .' numberOfErrorTrials INT DEFAULT 0 NOT NULL, expireAt TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, ' + .'createdAt TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, destineeThirdParty_id INT DEFAULT NULL, ' + .'entityWorkflowStep_id INT NOT NULL, createdBy_id INT DEFAULT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE UNIQUE INDEX UNIQ_A0C0620FD17F50A6 ON chill_main_workflow_entity_send (uuid)'); + $this->addSql('CREATE INDEX IDX_A0C0620FDDFA98DE ON chill_main_workflow_entity_send (destineeThirdParty_id)'); + $this->addSql('CREATE INDEX IDX_A0C0620F3912FED6 ON chill_main_workflow_entity_send (entityWorkflowStep_id)'); + $this->addSql('CREATE INDEX IDX_A0C0620F3174800F ON chill_main_workflow_entity_send (createdBy_id)'); + $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_send.uuid IS \'(DC2Type:uuid)\''); + $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_send.expireAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_send.createdAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('CREATE TABLE chill_main_workflow_entity_send_views (id INT NOT NULL, send_id INT NOT NULL, ' + .'viewAt TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, remoteIp TEXT NOT NULL, PRIMARY KEY(id))'); + $this->addSql('CREATE INDEX IDX_2659558513933E7B ON chill_main_workflow_entity_send_views (send_id)'); + $this->addSql('COMMENT ON COLUMN chill_main_workflow_entity_send_views.viewAt IS \'(DC2Type:datetime_immutable)\''); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send ADD CONSTRAINT FK_A0C0620FDDFA98DE FOREIGN KEY (destineeThirdParty_id) REFERENCES chill_3party.third_party (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send ADD CONSTRAINT FK_A0C0620F3912FED6 FOREIGN KEY (entityWorkflowStep_id) REFERENCES chill_main_workflow_entity_step (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send ADD CONSTRAINT FK_A0C0620F3174800F FOREIGN KEY (createdBy_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send_views ADD CONSTRAINT FK_2659558513933E7B FOREIGN KEY (send_id) REFERENCES chill_main_workflow_entity_send (id) NOT DEFERRABLE INITIALLY IMMEDIATE'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP SEQUENCE chill_main_workflow_entity_send_id_seq CASCADE'); + $this->addSql('DROP SEQUENCE chill_main_workflow_entity_send_views_id_seq CASCADE'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send DROP CONSTRAINT FK_A0C0620FDDFA98DE'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send DROP CONSTRAINT FK_A0C0620F3912FED6'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send DROP CONSTRAINT FK_A0C0620F3174800F'); + $this->addSql('ALTER TABLE chill_main_workflow_entity_send_views DROP CONSTRAINT FK_2659558513933E7B'); + $this->addSql('DROP TABLE chill_main_workflow_entity_send'); + $this->addSql('DROP TABLE chill_main_workflow_entity_send_views'); + } +}