Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 108 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
NotificationsSendCommand | |
0.00% |
0 / 108 |
|
0.00% |
0 / 6 |
992 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
configure | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 70 |
|
0.00% |
0 / 1 |
462 | |||
getNextTimeToSendFromPreferences | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
42 | |||
getEnglishDayFromNumber | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
getEnglishNumber | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace App\Domain\Notification\Command; |
4 | |
5 | use App\Domain\Notification\Model\Notification; |
6 | use App\Domain\Notification\Model\NotificationUser; |
7 | use App\Domain\User\Model\EmailNotificationPreference; |
8 | use App\Domain\User\Model\User; |
9 | use App\Domain\User\Repository\User as UserRepository; |
10 | use App\Infrastructure\ORM\Notification\Repository\NotificationUser as NotificationUserRepository; |
11 | use Doctrine\Common\Collections\ArrayCollection; |
12 | use Symfony\Bridge\Twig\Mime\TemplatedEmail; |
13 | use Symfony\Component\Console\Command\Command; |
14 | use Symfony\Component\Console\Input\InputInterface; |
15 | use Symfony\Component\Console\Output\OutputInterface; |
16 | use Symfony\Component\Console\Style\SymfonyStyle; |
17 | use Symfony\Component\Mailer\MailerInterface; |
18 | use Symfony\Component\Mime\Address; |
19 | use Symfony\Contracts\Translation\TranslatorInterface; |
20 | |
21 | class NotificationsSendCommand extends Command |
22 | { |
23 | protected static $defaultName = 'notifications:send'; |
24 | protected static $defaultDescription = 'Send notifications by email to users that have requested it.'; |
25 | |
26 | protected UserRepository $userRepository; |
27 | protected NotificationUserRepository $notificationUserRepository; |
28 | |
29 | protected MailerInterface $mailer; |
30 | protected TranslatorInterface $translator; |
31 | |
32 | private bool $activeNotifications; |
33 | |
34 | public function __construct( |
35 | UserRepository $userRepository, |
36 | NotificationUserRepository $notificationUserRepository, |
37 | MailerInterface $mailer, |
38 | TranslatorInterface $translator, |
39 | bool $activeNotifications, |
40 | ?string $name = null, |
41 | ) { |
42 | parent::__construct($name); |
43 | $this->userRepository = $userRepository; |
44 | $this->mailer = $mailer; |
45 | $this->notificationUserRepository = $notificationUserRepository; |
46 | $this->translator = $translator; |
47 | $this->activeNotifications = $activeNotifications; |
48 | } |
49 | |
50 | protected function configure(): void |
51 | { |
52 | $this |
53 | ->setDescription(self::$defaultDescription) |
54 | ; |
55 | } |
56 | |
57 | protected function execute(InputInterface $input, OutputInterface $output): int |
58 | { |
59 | $io = new SymfonyStyle($input, $output); |
60 | |
61 | if (!$this->activeNotifications) { |
62 | $io->warning('Notifications are not active. Exiting now'); |
63 | exit(0); |
64 | } |
65 | |
66 | // Get all users |
67 | $users = new ArrayCollection($this->userRepository->findAll()); |
68 | // Foreach user that has email notifications enabled |
69 | |
70 | // Get all user notifications that have not been sent yet |
71 | |
72 | $qb = $this->notificationUserRepository->getQueryBuilder(); |
73 | $qb->where('o.active = 0'); |
74 | $qb->andWhere('o.sent = 0'); |
75 | $qb->andWhere('o.mail IS NOT NULL'); |
76 | |
77 | $notificationUsers = $qb->getQuery()->getResult(); |
78 | |
79 | $output->writeln(count($notificationUsers) . ' notifications to send'); |
80 | |
81 | $groupedByMail = []; |
82 | |
83 | foreach ($notificationUsers as $notificationUser) { |
84 | if (!isset($groupedByMail[$notificationUser->getMail()])) { |
85 | $groupedByMail[$notificationUser->getMail()] = []; |
86 | } |
87 | $groupedByMail[$notificationUser->getMail()][] = $notificationUser; |
88 | } |
89 | |
90 | foreach ($groupedByMail as $mail => $notifUsers) { |
91 | $notifications = []; |
92 | /** |
93 | * @var User $user |
94 | */ |
95 | $user = $notifUsers[0]->getUser(); |
96 | |
97 | $output->writeln('Prepare notifications for ' . $mail); |
98 | |
99 | // If not a registered user, everything will be sent by email |
100 | $prefs = new EmailNotificationPreference(); |
101 | $prefs->setFrequency(EmailNotificationPreference::FREQUENCY_EACH); |
102 | $prefs->setEnabled(true); |
103 | $prefs->setLastSent(new \DateTime('2012-01-01 00:00:00')); |
104 | $prefs->setNotificationMask(0x7FF); |
105 | |
106 | if ($user && $user->getEmailNotificationPreference()) { |
107 | // if a registered user, respect their preferences. |
108 | $prefs = $user->getEmailNotificationPreference(); |
109 | } |
110 | |
111 | if ( |
112 | !$prefs |
113 | || !$prefs->getEnabled() |
114 | || EmailNotificationPreference::FREQUENCY_NONE == $prefs->getFrequency() |
115 | || 0 === $prefs->getNotificationMask() |
116 | ) { |
117 | // Exit if user has email notifications disabled |
118 | $output->writeln('User ' . $mail . ' has disabled notifications'); |
119 | continue; |
120 | } |
121 | |
122 | $nextTimeToSend = $this->getNextTimeToSendFromPreferences($prefs); |
123 | |
124 | if ($nextTimeToSend->format('Ymdhi') !== date('Ymdhi')) { |
125 | $output->writeln('Not the time to send. Programmed time is ' . $nextTimeToSend->format('YmdHi') . ' and current time is ' . date('YmdHi')); |
126 | // Now is not the time to send for this user, abort |
127 | continue; |
128 | } |
129 | |
130 | $lastSent = $prefs->getLastSent(); |
131 | foreach ($notifUsers as $nu) { |
132 | /* |
133 | * @var NotificationUser $notifUser |
134 | */ |
135 | |
136 | if ($nu->getCreatedAt() > $lastSent) { |
137 | /** |
138 | * @var Notification $notif |
139 | */ |
140 | $notif = $nu->getNotification(); |
141 | // Check if the notification module is is the user preferences mask |
142 | |
143 | // Get the raw name of the module |
144 | $mod = str_replace('notification.modules.', '', $notif->getModule()); |
145 | |
146 | $availableModules = array_keys(EmailNotificationPreference::MODULES); |
147 | if (in_array($mod, $availableModules) && (EmailNotificationPreference::MODULES[$mod] & $prefs->getNotificationMask())) { |
148 | // Current notification module is enabled in user preferences |
149 | $notifications[] = $nu->getNotification(); |
150 | } else { |
151 | $output->writeln('User ' . $mail . ' does not have notifications active for module ' . $mod); |
152 | } |
153 | } |
154 | } |
155 | |
156 | if (0 === count($notifications)) { |
157 | $output->writeln('No notifications to send to ' . $mail); |
158 | continue; |
159 | } |
160 | |
161 | $email = new TemplatedEmail(); |
162 | $email->subject($this->translator->trans('notifications.label.email_subject')); |
163 | $email->to(new Address($mail)); |
164 | $email->htmlTemplate('Notification/Mail/notifications.html.twig'); |
165 | $email->context([ |
166 | 'notifications' => $notifications, |
167 | 'user' => $user, |
168 | ]); |
169 | try { |
170 | $this->mailer->send($email); |
171 | |
172 | if ($user) { |
173 | $prefs->setLastSent(new \DateTime()); |
174 | $user->setEmailNotificationPreference($prefs); |
175 | $this->userRepository->update($user); |
176 | } |
177 | |
178 | foreach ($notifUsers as $nu) { |
179 | /* |
180 | * @var NotificationUser $notifUser |
181 | */ |
182 | if ($nu->getCreatedAt() > $lastSent) { |
183 | $nu->setSent(true); |
184 | $this->notificationUserRepository->update($nu); |
185 | } |
186 | } |
187 | |
188 | $output->writeln('Notifications email sent to ' . $mail); |
189 | } catch (\Exception $e) { |
190 | // dump($e); |
191 | $output->writeln('Could not send email to ' . $mail); |
192 | } |
193 | } |
194 | |
195 | // check the date of the last email they were sent |
196 | // Get all notifications generated after that date |
197 | // Send them an email with those notifications |
198 | |
199 | return 0; |
200 | } |
201 | |
202 | private function getNextTimeToSendFromPreferences(EmailNotificationPreference $prefs): \DateTime |
203 | { |
204 | switch ($prefs->getFrequency()) { |
205 | case EmailNotificationPreference::FREQUENCY_EACH: |
206 | return new \DateTime(); |
207 | case EmailNotificationPreference::FREQUENCY_HOUR: |
208 | $lastSent = $prefs->getLastSent(); |
209 | |
210 | return $lastSent->add(new \DateInterval('PT' . $prefs->getHour() . 'H')); |
211 | case EmailNotificationPreference::FREQUENCY_DAY: |
212 | // set cutoff date to today at the specified time |
213 | return (new \DateTime())->setTime($prefs->getHour(), 0); |
214 | case EmailNotificationPreference::FREQUENCY_WEEK: |
215 | $now = new \DateTime(); |
216 | |
217 | // set cutoff date to this week at the specified day and time |
218 | return (new \DateTime())->setTime($prefs->getHour(), 0)->setISODate($now->format('Y'), $now->format('W'), $prefs->getDay()); |
219 | case EmailNotificationPreference::FREQUENCY_MONTH: |
220 | // set cutoff date to this months selected day and week |
221 | return (new \DateTime())->modify($this->getEnglishNumber($prefs->getWeek()) . ' ' . $this->getEnglishDayFromNumber($prefs->getDay()) . ' of this month')->setTime($prefs->getHour(), 0); |
222 | } |
223 | |
224 | return new \DateTime(); |
225 | } |
226 | |
227 | private function getEnglishDayFromNumber(int $day): string |
228 | { |
229 | return match ($day) { |
230 | default => 'monday', |
231 | 2 => 'tuesday', |
232 | 3 => 'wednesday', |
233 | 4 => 'thursday', |
234 | 5 => 'friday', |
235 | 6 => 'saturday', |
236 | 7 => 'sunday', |
237 | }; |
238 | } |
239 | |
240 | private function getEnglishNumber(int $num): string |
241 | { |
242 | return match ($num) { |
243 | default => 'first', |
244 | 2 => 'second', |
245 | 3 => 'third', |
246 | 4 => 'fourth', |
247 | }; |
248 | } |
249 | } |