481 lines
12 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;
use Chill\MainBundle\Doctrine\Model\TrackUpdateInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
#[ORM\Table(name: 'chill_main_notification')]
#[ORM\Index(columns: ['relatedentityclass', 'relatedentityid'], name: 'chill_main_notification_related_entity_idx')]
class Notification implements TrackUpdateInterface
{
#[ORM\Column(type: Types::TEXT, nullable: false)]
private string $accessKey;
private array $addedAddresses = [];
/**
* @var Collection<int, User>
*/
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'chill_main_notification_addresses_user')]
private Collection $addressees;
/**
* @var Collection<int, User>
*/
#[ORM\ManyToMany(targetEntity: UserGroup::class)]
#[ORM\JoinTable(name: 'chill_main_notification_addressee_user_group')]
private Collection $addresseeUserGroups;
/**
* @deprecated
* a list of destinee which will receive notifications
*
* @var array|string[]
*/
#[ORM\Column(type: Types::JSON, options: ['default' => '[]', 'jsonb' => true])]
private array $addressesEmails = [];
/**
* @deprecated
* a list of emails adresses which were added to the notification
*
* @var array|string[]
*/
private array $addressesEmailsAdded = [];
private ?ArrayCollection $addressesOnLoad = null;
/**
* @var Collection<int, NotificationComment>
*/
#[ORM\OneToMany(mappedBy: 'notification', targetEntity: NotificationComment::class, orphanRemoval: true)]
#[ORM\OrderBy(['createdAt' => \Doctrine\Common\Collections\Criteria::ASC])]
private Collection $comments;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $date;
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;
#[ORM\Column(type: Types::TEXT)]
private string $message = '';
#[ORM\Column(type: Types::STRING, length: 255)]
private string $relatedEntityClass = '';
#[ORM\Column(type: Types::INTEGER)]
private int $relatedEntityId;
private array $removedAddresses = [];
#[ORM\ManyToOne(targetEntity: User::class)]
#[ORM\JoinColumn(nullable: true)]
private ?User $sender = null;
#[Assert\NotBlank(message: 'notification.Title must be defined')]
#[ORM\Column(type: Types::TEXT, options: ['default' => ''])]
private string $title = '';
/**
* @var Collection<int, User>
*/
#[ORM\ManyToMany(targetEntity: User::class)]
#[ORM\JoinTable(name: 'chill_main_notification_addresses_unread')]
private Collection $unreadBy;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private ?\DateTimeImmutable $updatedAt = null;
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $updatedBy = null;
#[ORM\Column(name: 'type', type: Types::STRING, nullable: true)]
private string $type;
public function __construct()
{
$this->addressees = new ArrayCollection();
$this->addresseeUserGroups = new ArrayCollection();
$this->unreadBy = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->setDate(new \DateTimeImmutable());
$this->accessKey = bin2hex(openssl_random_pseudo_bytes(24));
}
public function addAddressee(User|UserGroup $addressee): self
{
if ($addressee instanceof User) {
if (!$this->addressees->contains($addressee)) {
$this->addressees->add($addressee);
return $this;
}
}
$this->addresseeUserGroups->add($addressee);
return $this;
}
/**
* @deprecated
*/
public function addAddressesEmail(string $email)
{
if (!\in_array($email, $this->addressesEmails, true)) {
$this->addressesEmails[] = $email;
$this->addressesEmailsAdded[] = $email;
}
}
public function addComment(NotificationComment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setNotification($this);
}
return $this;
}
public function addUnreadBy(User $user): self
{
if (!$this->unreadBy->contains($user)) {
$this->unreadBy[] = $user;
}
return $this;
}
/**
* @param array $payload
*/
#[Assert\Callback]
public function assertCountAddresses(ExecutionContextInterface $context, $payload): void
{
if (0 === (\count($this->getAddresseeUserGroups()) + \count($this->getAddressees()))) {
$context->buildViolation('notification.At least one addressee')
->atPath('addressees')
->addViolation();
}
}
/**
* @return Collection<UserGroup>
*/
public function getAddresseeUserGroups(): Collection
{
return $this->addresseeUserGroups;
}
public function setAddresseeUserGroups(Collection $addresseeUserGroups): void
{
$this->addresseeUserGroups = $addresseeUserGroups;
}
public function getAccessKey(): string
{
return $this->accessKey;
}
public function getAddedAddresses(): array
{
return $this->addedAddresses;
}
/**
* @return Collection|User[]
*/
public function getAddressees(): Collection
{
// keep a copy to compute changes later
if (null === $this->addressesOnLoad) {
$this->addressesOnLoad = new ArrayCollection($this->addressees->toArray());
}
return $this->addressees;
}
public function getAllAddressees(): array
{
$allUsers = [];
foreach ($this->getAddressees() as $user) {
$allUsers[$user->getId()] = $user;
}
foreach ($this->getAddresseeUserGroups() as $userGroup) {
foreach ($userGroup->getUsers() as $user) {
$allUsers[$user->getId()] = $user;
}
}
return array_values($allUsers);
}
/**
* @return array|string[]
*
* @deprecated
*/
public function getAddressesEmails(): array
{
return $this->addressesEmails;
}
/**
* @return array|string[]
*
* @deprecated
*/
public function getAddressesEmailsAdded(): array
{
return $this->addressesEmailsAdded;
}
public function getComments(): Collection
{
return $this->comments;
}
public function getDate(): ?\DateTimeImmutable
{
return $this->date;
}
public function getId(): ?int
{
return $this->id;
}
public function getMessage(): ?string
{
return $this->message;
}
public function getRelatedEntityClass(): ?string
{
return $this->relatedEntityClass;
}
public function getRelatedEntityId(): ?int
{
return $this->relatedEntityId;
}
public function getSender(): ?User
{
return $this->sender;
}
public function getTitle(): string
{
return $this->title;
}
public function getUnreadBy(): Collection
{
return $this->unreadBy;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function getUpdatedBy(): ?User
{
return $this->updatedBy;
}
public function isReadBy(User $user): bool
{
return !$this->unreadBy->contains($user);
}
public function isSystem(): bool
{
return null === $this->sender;
}
public function markAsReadBy(User $user): self
{
return $this->removeUnreadBy($user);
}
public function markAsUnreadBy(User $user): self
{
return $this->addUnreadBy($user);
}
#[ORM\PreFlush]
public function registerUnread()
{
foreach ($this->addedAddresses as $addressee) {
$this->addUnreadBy($addressee);
}
foreach ($this->removedAddresses as $addressee) {
$this->removeAddressee($addressee);
}
if (null !== $this->addressesOnLoad) {
foreach ($this->addressees as $existingAddresse) {
if (!$this->addressesOnLoad->contains($existingAddresse)) {
$this->addUnreadBy($existingAddresse);
}
}
foreach ($this->addressesOnLoad as $onLoadAddressee) {
if (!$this->addressees->contains($onLoadAddressee)) {
$this->removeUnreadBy($onLoadAddressee);
}
}
}
$this->removedAddresses = [];
$this->addedAddresses = [];
$this->addressesOnLoad = null;
}
public function removeAddressee(User|UserGroup $addressee): self
{
if ($addressee instanceof User) {
if ($this->addressees->contains($addressee)) {
$this->addressees->removeElement($addressee);
return $this;
}
}
$this->addresseeUserGroups->removeElement($addressee);
return $this;
}
/**
* @deprecated
*/
public function removeAddressesEmail(string $email)
{
if (\in_array($email, $this->addressesEmails, true)) {
$this->addressesEmails = array_filter($this->addressesEmails, static fn ($e) => $e !== $email);
$this->addressesEmailsAdded = array_filter($this->addressesEmailsAdded, static fn ($e) => $e !== $email);
}
}
public function removeComment(NotificationComment $comment): self
{
$this->comments->removeElement($comment);
return $this;
}
public function removeUnreadBy(User $user): self
{
$this->unreadBy->removeElement($user);
return $this;
}
public function setDate(\DateTimeImmutable $date): self
{
$this->date = $date;
return $this;
}
public function setMessage(?string $message): self
{
$this->message = (string) $message;
return $this;
}
public function setRelatedEntityClass(string $relatedEntityClass): self
{
$this->relatedEntityClass = $relatedEntityClass;
return $this;
}
public function setRelatedEntityId(int $relatedEntityId): self
{
$this->relatedEntityId = $relatedEntityId;
return $this;
}
public function setSender(?User $sender): self
{
$this->sender = $sender;
return $this;
}
public function setTitle(?string $title): Notification
{
$this->title = (string) $title;
return $this;
}
public function setUpdatedAt(\DateTimeInterface $datetime): self
{
$this->updatedAt = $datetime;
return $this;
}
public function setUpdatedBy(User $user): self
{
$this->updatedBy = $user;
return $this;
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getType(): string
{
return $this->type;
}
public function isSendImmediately(User $user): bool
{
$notificationFlags = $user->getNotificationFlags();
$notificationType = $this->getType();
// Check if the user has a preference for this notification type
$emailPreference = $notificationFlags[$notificationType] ?? null;
return 'immediate-email' === $emailPreference;
}
}