WIP add user group as possible addressee to notification

This commit is contained in:
Julie Lenaerts 2025-06-23 15:29:03 +02:00
parent f88f1f1859
commit ac16060441
6 changed files with 135 additions and 45 deletions

View File

@ -82,9 +82,9 @@ framework:
'Chill\MainBundle\Workflow\Messenger\PostSignatureStateChangeMessage': priority
'Chill\MainBundle\Workflow\Messenger\PostPublicViewMessage': async
'Chill\MainBundle\Service\Workflow\CancelStaleWorkflowMessage': async
'Chill\MainBundle\Notification\Email\SendImmediateNotificationEmailMessage': immediate_email
'Chill\MainBundle\Notification\Email\ScheduleDailyNotificationEmailMessage': daily_email
'Chill\MainBundle\Notification\Email\SendDailyDigestMessage': daily_email
'Chill\MainBundle\Notification\Email\NotificationEmailMessages\SendImmediateNotificationEmailMessage': immediate_email
'Chill\MainBundle\Notification\Email\NotificationEmailMessages\ScheduleDailyNotificationEmailMessage': daily_email
'Chill\MainBundle\Notification\Email\NotificationEmailMessages\SendDailyDigestMessage': daily_email
# end of routes added by chill-bundles recipes
# Route your messages to the transports
# 'App\Message\YourMessage': async

View File

@ -22,7 +22,7 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[ORM\Entity]
#[ORM\HasLifecycleCallbacks]
#[ORM\Table(name: 'chill_main_notification')]
#[ORM\Index(name: 'chill_main_notification_related_entity_idx', columns: ['relatedentityclass', 'relatedentityid'])]
#[ORM\Index(columns: ['relatedentityclass', 'relatedentityid'], name: 'chill_main_notification_related_entity_idx')]
class Notification implements TrackUpdateInterface
{
#[ORM\Column(type: Types::TEXT, nullable: false)]
@ -38,7 +38,15 @@ class Notification implements TrackUpdateInterface
private Collection $addressees;
/**
* a list of destinee which will receive notifications.
* @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[]
*/
@ -46,7 +54,8 @@ class Notification implements TrackUpdateInterface
private array $addressesEmails = [];
/**
* a list of emails adresses which were added to the notification.
* @deprecated
* a list of emails adresses which were added to the notification
*
* @var array|string[]
*/
@ -107,22 +116,31 @@ class Notification implements TrackUpdateInterface
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 $addressee): self
public function addAddressee(User|UserGroup $addressee): self
{
if ($addressee instanceof User) {
if (!$this->addressees->contains($addressee)) {
$this->addressees[] = $addressee;
$this->addedAddresses[] = $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)) {
@ -156,13 +174,26 @@ class Notification implements TrackUpdateInterface
#[Assert\Callback]
public function assertCountAddresses(ExecutionContextInterface $context, $payload): void
{
if (0 === (\count($this->getAddressesEmails()) + \count($this->getAddressees()))) {
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;
@ -186,8 +217,27 @@ class Notification implements TrackUpdateInterface
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
{
@ -196,6 +246,8 @@ class Notification implements TrackUpdateInterface
/**
* @return array|string[]
*
* @deprecated
*/
public function getAddressesEmailsAdded(): array
{
@ -307,15 +359,24 @@ class Notification implements TrackUpdateInterface
$this->addressesOnLoad = null;
}
public function removeAddressee(User $addressee): self
public function removeAddressee(User|UserGroup $addressee): self
{
if ($this->addressees->removeElement($addressee)) {
$this->removedAddresses[] = $addressee;
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)) {

View File

@ -12,17 +12,13 @@ declare(strict_types=1);
namespace Chill\MainBundle\Form;
use Chill\MainBundle\Entity\Notification;
use Chill\MainBundle\Form\Type\ChillCollectionType;
use Chill\MainBundle\Form\Type\ChillTextareaType;
use Chill\MainBundle\Form\Type\PickUserDynamicType;
use Chill\MainBundle\Form\Type\PickUserGroupOrUserDynamicType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
class NotificationType extends AbstractType
{
@ -33,29 +29,13 @@ class NotificationType extends AbstractType
'label' => 'Title',
'required' => true,
])
->add('addressees', PickUserDynamicType::class, [
->add('addressees', PickUserGroupOrUserDynamicType::class, [
'multiple' => true,
'required' => false,
'empty_data' => '[]',
'required' => true,
])
->add('message', ChillTextareaType::class, [
'required' => false,
])
->add('addressesEmails', ChillCollectionType::class, [
'label' => 'notification.dest by email',
'help' => 'notification.dest by email help',
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
'entry_type' => EmailType::class,
'button_add_label' => 'notification.Add an email',
'button_remove_label' => 'notification.Remove an email',
'empty_collection_explain' => 'notification.Any email',
'entry_options' => [
'constraints' => [
new NotNull(), new NotBlank(), new Email(),
],
'label' => 'Email',
],
]);
}

View File

@ -72,16 +72,16 @@ readonly class NotificationMailer
*/
public function postPersistNotification(Notification $notification, PostPersistEventArgs $eventArgs): void
{
$this->sendNotificationEmailsToAddresses($notification);
$this->sendNotificationEmailsToAddressesEmails($notification);
$this->sendNotificationEmailsToAddressees($notification);
// $this->sendNotificationEmailsToAddressesEmails($notification);
}
public function postUpdateNotification(Notification $notification, PostUpdateEventArgs $eventArgs): void
{
$this->sendNotificationEmailsToAddressesEmails($notification);
$this->sendNotificationEmailsToAddressees($notification);
}
private function sendNotificationEmailsToAddresses(Notification $notification): void
private function sendNotificationEmailsToAddressees(Notification $notification): void
{
if (null === $notification->getType()) {
$this->logger->warning('[NotificationMailer] Notification has no type, skipping email processing', [
@ -91,7 +91,7 @@ readonly class NotificationMailer
return;
}
foreach ($notification->getAddressees() as $addressee) {
foreach ($notification->getAllAddressees() as $addressee) {
if (null === $addressee->getEmail()) {
continue;
}
@ -232,6 +232,9 @@ readonly class NotificationMailer
}
}
/**
* @deprecated
*/
private function sendNotificationEmailsToAddressesEmails(Notification $notification): void
{
foreach ($notification->getAddressesEmailsAdded() as $emailAddress) {

View File

@ -21,8 +21,6 @@
{{ form_row(form.title, { 'label': 'notification.subject'|trans }) }}
{{ form_row(form.addressees, { 'label': 'notification.sent_to'|trans }) }}
{{ form_row(form.addressesEmails) }}
{% include handler.template(notification) with handler.templateData(notification) %}
<div class="mb-3 row">

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Chill\Migrations\Main;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250623120824 extends AbstractMigration
{
public function getDescription(): string
{
return 'Add addressee user groups to notifications';
}
public function up(Schema $schema): void
{
$this->addSql(<<<'SQL'
CREATE TABLE chill_main_notification_addressee_user_group (notification_id INT NOT NULL, usergroup_id INT NOT NULL, PRIMARY KEY(notification_id, usergroup_id))
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_ECF81C07EF1A9D84 ON chill_main_notification_addressee_user_group (notification_id)
SQL);
$this->addSql(<<<'SQL'
CREATE INDEX IDX_ECF81C07D2112630 ON chill_main_notification_addressee_user_group (usergroup_id)
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE chill_main_notification_addressee_user_group ADD CONSTRAINT FK_ECF81C07EF1A9D84 FOREIGN KEY (notification_id) REFERENCES chill_main_notification (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE chill_main_notification_addressee_user_group ADD CONSTRAINT FK_ECF81C07D2112630 FOREIGN KEY (usergroup_id) REFERENCES chill_main_user_group (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE
SQL);
}
public function down(Schema $schema): void
{
$this->addSql(<<<'SQL'
ALTER TABLE chill_main_notification_addressee_user_group DROP CONSTRAINT FK_ECF81C07EF1A9D84
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE chill_main_notification_addressee_user_group DROP CONSTRAINT FK_ECF81C07D2112630
SQL);
$this->addSql(<<<'SQL'
DROP TABLE chill_main_notification_addressee_user_group
SQL);
}
}