Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
77.48% |
172 / 222 |
|
20.00% |
2 / 10 |
CRAP | |
0.00% |
0 / 1 |
NotificationEventSubscriber | |
77.48% |
172 / 222 |
|
20.00% |
2 / 10 |
244.74 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getSubscribedEvents | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
onFlush | |
91.18% |
31 / 34 |
|
0.00% |
0 / 1 |
24.40 | |||
createNotifications | |
97.83% |
45 / 46 |
|
0.00% |
0 / 1 |
15 | |||
createNotificationForUsers | |
93.33% |
28 / 30 |
|
0.00% |
0 / 1 |
14.06 | |||
saveEmailNotificationForRefOp | |
41.18% |
7 / 17 |
|
0.00% |
0 / 1 |
10.09 | |||
saveEmailNotificationForDPO | |
92.00% |
23 / 25 |
|
0.00% |
0 / 1 |
12.07 | |||
saveEmailNotificationForRespTrait | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
90 | |||
getObjectSimpleValue | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
6.03 | |||
getSubjectForNotification | |
59.26% |
16 / 27 |
|
0.00% |
0 / 1 |
54.73 |
1 | <?php |
2 | |
3 | declare(strict_types=1); |
4 | |
5 | namespace App\Domain\Notification\Symfony\EventSubscriber\Doctrine; |
6 | |
7 | use App\Application\Interfaces\CollectivityRelated; |
8 | use App\Domain\AIPD\Dictionary\StatutAnalyseImpactDictionary; |
9 | use App\Domain\AIPD\Model\AnalyseImpact; |
10 | use App\Domain\Documentation\Model\Document; |
11 | use App\Domain\Notification\Model\Notification; |
12 | use App\Domain\Notification\Model\NotificationUser; |
13 | use App\Domain\Notification\Serializer\NotificationNormalizer; |
14 | use App\Domain\Registry\Dictionary\MesurementStatusDictionary; |
15 | use App\Domain\Registry\Dictionary\ProofTypeDictionary; |
16 | use App\Domain\Registry\Dictionary\ViolationNatureDictionary; |
17 | use App\Domain\Registry\Model\Contractor; |
18 | use App\Domain\Registry\Model\Mesurement; |
19 | use App\Domain\Registry\Model\Proof; |
20 | use App\Domain\Registry\Model\Request; |
21 | use App\Domain\Registry\Model\Treatment; |
22 | use App\Domain\Registry\Model\Violation; |
23 | use App\Domain\User\Dictionary\UserMoreInfoDictionary; |
24 | use App\Domain\User\Model\Collectivity; |
25 | use App\Domain\User\Model\User; |
26 | use App\Domain\User\Repository\User as UserRepository; |
27 | use App\Infrastructure\ORM\Notification\Repository\Notification as NotificationRepository; |
28 | use App\Infrastructure\ORM\Notification\Repository\NotificationUser as NotificationUserRepository; |
29 | use Doctrine\Common\Collections\ArrayCollection; |
30 | use Doctrine\Common\EventSubscriber; |
31 | use Doctrine\ORM\Event\OnFlushEventArgs; |
32 | use Doctrine\ORM\Events; |
33 | use Symfony\Component\Security\Core\Security; |
34 | use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; |
35 | use Symfony\Component\Serializer\Normalizer\NormalizerInterface; |
36 | use Symfony\Contracts\Translation\TranslatorInterface; |
37 | |
38 | /** |
39 | * This subscriber handles events that are generated by doctrine, and creates notifications from them if necessary. |
40 | */ |
41 | class NotificationEventSubscriber implements EventSubscriber |
42 | { |
43 | protected array $classes = [ |
44 | AnalyseImpact::class, |
45 | Treatment::class, |
46 | Mesurement::class, |
47 | Violation::class, |
48 | Proof::class, |
49 | Contractor::class, |
50 | Request::class, |
51 | Document::class, |
52 | ]; |
53 | |
54 | protected array $recipients = [ |
55 | AnalyseImpact::class => Notification::NOTIFICATION_COLLECTIVITY | Notification::NOTIFICATION_DPO, |
56 | Treatment::class => Notification::NOTIFICATION_DPO, |
57 | Mesurement::class => Notification::NOTIFICATION_DPO, |
58 | Violation::class => Notification::NOTIFICATION_DPO, |
59 | Proof::class => Notification::NOTIFICATION_DPO, |
60 | Contractor::class => Notification::NOTIFICATION_DPO, |
61 | Request::class => Notification::NOTIFICATION_DPO, |
62 | Document::class => Notification::NOTIFICATION_COLLECTIVITY, |
63 | ]; |
64 | |
65 | protected NotificationRepository $notificationRepository; |
66 | protected NotificationUserRepository $notificationUserRepository; |
67 | protected \App\Infrastructure\ORM\AIPD\Repository\AnalyseImpact $aipdRepository; |
68 | protected UserRepository $userRepository; |
69 | protected Security $security; |
70 | protected NormalizerInterface $normalizer; |
71 | protected TranslatorInterface $translator; |
72 | protected string $requestDays; |
73 | protected string $surveyDays; |
74 | |
75 | public function __construct( |
76 | NotificationRepository $notificationRepository, |
77 | NotificationNormalizer $normalizer, |
78 | UserRepository $userRepository, |
79 | NotificationUserRepository $notificationUserRepository, |
80 | \App\Infrastructure\ORM\AIPD\Repository\AnalyseImpact $aipdRepository, |
81 | Security $security, |
82 | TranslatorInterface $translator, |
83 | string $requestDays, |
84 | string $surveyDays, |
85 | ) { |
86 | $this->notificationRepository = $notificationRepository; |
87 | $this->normalizer = $normalizer; |
88 | $this->userRepository = $userRepository; |
89 | $this->notificationUserRepository = $notificationUserRepository; |
90 | $this->aipdRepository = $aipdRepository; |
91 | $this->security = $security; |
92 | $this->translator = $translator; |
93 | $this->requestDays = $requestDays; |
94 | $this->surveyDays = $surveyDays; |
95 | } |
96 | |
97 | public function getSubscribedEvents(): array |
98 | { |
99 | return [ |
100 | Events::onFlush, |
101 | ]; |
102 | } |
103 | |
104 | public function onFlush(OnFlushEventArgs $eventArgs) |
105 | { |
106 | /** @var User|null $user */ |
107 | $user = $this->security->getUser(); |
108 | |
109 | if (isset($user) && is_object($user) && User::class === get_class($user) && $user->isNotGeneratesNotifications()) { |
110 | // User does not generate notifications, exit now |
111 | return; |
112 | } |
113 | $em = $eventArgs->getObjectManager(); |
114 | $uow = $em->getUnitOfWork(); |
115 | |
116 | foreach ($uow->getScheduledEntityInsertions() as $entity) { |
117 | $class = get_class($entity); |
118 | if (!in_array($class, $this->classes) || Request::class === $class || AnalyseImpact::class === $class) { |
119 | continue; |
120 | } |
121 | |
122 | $this->createNotifications($entity, 'create', $em); |
123 | } |
124 | |
125 | foreach ($uow->getScheduledEntityUpdates() as $entity) { |
126 | $class = get_class($entity); |
127 | |
128 | if (!in_array($class, $this->classes) || Document::class === $class) { |
129 | continue; |
130 | } |
131 | $action = 'update'; |
132 | if (Request::class === $class) { |
133 | $ch = $uow->getEntityChangeSet($entity); |
134 | // Exit if the request has no state change |
135 | if (!isset($ch['state'])) { |
136 | continue; |
137 | } |
138 | $action = 'state_change'; |
139 | } |
140 | |
141 | if (AnalyseImpact::class === $class) { |
142 | $ch = $uow->getEntityChangeSet($entity); |
143 | // Exit if the request has no state change |
144 | if (!isset($ch['statut']) && !isset($ch['isReadyForValidation'])) { |
145 | continue; |
146 | } |
147 | |
148 | if (isset($ch['statut'])) { |
149 | $action = 'validated'; |
150 | } elseif (isset($ch['isReadyForValidation'])) { |
151 | $action = 'validation'; |
152 | } |
153 | } |
154 | |
155 | $this->createNotifications($entity, $action, $em); |
156 | } |
157 | foreach ($uow->getScheduledEntityDeletions() as $entity) { |
158 | $class = get_class($entity); |
159 | if (!in_array($class, $this->classes) || Request::class == $class || Document::class === $class || AnalyseImpact::class === $class) { |
160 | continue; |
161 | } |
162 | $this->createNotifications($entity, 'delete', $em); |
163 | } |
164 | } |
165 | |
166 | private function createNotifications($object, $action, $em): array |
167 | { |
168 | $notifications = []; |
169 | $recipients = $this->recipients[get_class($object)]; |
170 | |
171 | if (AnalyseImpact::class === get_class($object) && 'validation' === $action) { |
172 | // DO not send status change of AIPD to collectivity |
173 | $recipients = Notification::NOTIFICATION_DPO; |
174 | } |
175 | |
176 | $uow = $em->getUnitOfWork(); |
177 | |
178 | $normalized = $this->normalizer->normalize($object, null, |
179 | [ |
180 | AbstractObjectNormalizer::CIRCULAR_REFERENCE_HANDLER => function ($o) { |
181 | return $this->getObjectSimpleValue($o); |
182 | }, |
183 | 'maxDepth' => 1, |
184 | AbstractObjectNormalizer::ENABLE_MAX_DEPTH => true, |
185 | AbstractObjectNormalizer::CIRCULAR_REFERENCE_LIMIT => 1, |
186 | AbstractObjectNormalizer::MAX_DEPTH_HANDLER => function ($o) { |
187 | if (is_iterable($o)) { |
188 | $d = []; |
189 | foreach ($o as $item) { |
190 | $d[] = $this->getObjectSimpleValue($item); |
191 | } |
192 | |
193 | return $d; |
194 | } |
195 | |
196 | return $this->getObjectSimpleValue($o); |
197 | }, |
198 | ], |
199 | ); |
200 | |
201 | if ($recipients & Notification::NOTIFICATION_DPO) { |
202 | $notification = $this->createNotificationForUsers($object, $action, $normalized); |
203 | $notifications[] = $notification; |
204 | } |
205 | |
206 | if ($recipients & Notification::NOTIFICATION_COLLECTIVITY) { |
207 | // get all non-DPO users |
208 | $collectivity = method_exists($object, 'getCollectivity') ? $object->getCollectivity() : null; |
209 | |
210 | if (AnalyseImpact::class === get_class($object) && $object->getConformiteTraitement() && $object->getConformiteTraitement()->getTraitement()) { |
211 | $collectivity = $object->getConformiteTraitement()->getTraitement()->getCollectivity(); |
212 | } |
213 | |
214 | if ($collectivity) { |
215 | $users = $this->userRepository->findNonDpoUsersForCollectivity($collectivity); |
216 | } else { |
217 | $users = $this->userRepository->findNonDpoUsers(); |
218 | } |
219 | |
220 | $notification = $this->createNotificationForUsers($object, $action, $normalized, $users); |
221 | $notifications[] = $notification; |
222 | } |
223 | |
224 | // Insert notifications and notification_users |
225 | |
226 | $meta = $em->getClassMetadata(Notification::class); |
227 | $meta2 = $em->getClassMetadata(NotificationUser::class); |
228 | |
229 | foreach ($notifications as $notif) { |
230 | $notif->setSubject($this->getSubjectForNotification($notif)); |
231 | $em->persist($notif); |
232 | /** |
233 | * @var Notification $notif |
234 | */ |
235 | if ($notif->getNotificationUsers()) { |
236 | foreach ($notif->getNotificationUsers() as $u) { |
237 | $em->persist($u); |
238 | $uow->computeChangeSet($meta2, $u); |
239 | } |
240 | } |
241 | |
242 | $uow->computeChangeSet($meta, $notif); |
243 | } |
244 | |
245 | return $notifications; |
246 | } |
247 | |
248 | private function createNotificationForUsers($object, $action, $normalized, $users = null): Notification |
249 | { |
250 | $notification = new Notification(); |
251 | $mod = Notification::MODULES[get_class($object)]; |
252 | $recipients = $this->recipients[get_class($object)]; |
253 | $notification->setModule('notification.modules.' . $mod); |
254 | $collectivity = method_exists($object, 'getCollectivity') ? $object->getCollectivity() : null; |
255 | |
256 | if (AnalyseImpact::class === get_class($object) && $object->getConformiteTraitement() && $object->getConformiteTraitement()->getTraitement()) { |
257 | $collectivity = $object->getConformiteTraitement()->getTraitement()->getCollectivity(); |
258 | } |
259 | |
260 | $user = $this->security->getUser(); |
261 | |
262 | if ($recipients & Notification::NOTIFICATION_DPO) { |
263 | $notification->setDpo(true); |
264 | } |
265 | |
266 | $notification->setCollectivity($collectivity); |
267 | $notification->setName(method_exists($object, 'getName') ? $object->getName() : $object->__toString()); |
268 | $notification->setAction('notification.actions.' . $action); |
269 | if ($user && is_object($user) && User::class === get_class($user)) { |
270 | $notification->setCreatedBy($user); |
271 | } |
272 | |
273 | $notification->setObject((object) $normalized); |
274 | |
275 | $nus = []; |
276 | |
277 | if ($users) { |
278 | $nus = $this->notificationUserRepository->saveUsers($notification, $users); |
279 | |
280 | $notification->setNotificationUsers($nus); |
281 | } else { |
282 | if (AnalyseImpact::class === get_class($object)) { |
283 | $nus = $this->saveEmailNotificationForDPO($notification, $object->getConformiteTraitement()->getTraitement()->getCollectivity()); |
284 | $notification->setNotificationUsers($nus); |
285 | } |
286 | } |
287 | |
288 | if (Document::class === get_class($object)) { |
289 | $newnus = array_merge($this->saveEmailNotificationForRefOp($notification), $nus); |
290 | $notification->setNotificationUsers($newnus); |
291 | } |
292 | if (Violation::class === get_class($object)) { |
293 | $newnus = array_merge($this->saveEmailNotificationForRespTrait($notification, $object), $nus); |
294 | $notification->setNotificationUsers($newnus); |
295 | } |
296 | |
297 | return $notification; |
298 | } |
299 | |
300 | private function saveEmailNotificationForRefOp(Notification $notification): array |
301 | { |
302 | $nus = []; |
303 | // Get referent operationnels for this collectivity |
304 | $refs = (new ArrayCollection($this->userRepository->findAll()))->filter(function (User $u) { |
305 | $mi = $u->getMoreInfos(); |
306 | |
307 | return $mi && isset($mi[UserMoreInfoDictionary::MOREINFO_OPERATIONNAL]) && $mi[UserMoreInfoDictionary::MOREINFO_OPERATIONNAL]; |
308 | }); |
309 | |
310 | // Add notification with email address for the référents |
311 | foreach ($refs as $ref) { |
312 | $nu = new NotificationUser(); |
313 | if (User::class === get_class($ref)) { |
314 | $nu->setMail($ref->getEmail()); |
315 | $nu->setUser($ref); |
316 | } else { |
317 | $nu->setMail($ref); |
318 | } |
319 | |
320 | $nu->setNotification($notification); |
321 | $nu->setActive(false); |
322 | $nu->setToken(sha1($notification->getName() . microtime() . $nu->getMail())); |
323 | $nu->setSent(false); |
324 | $nus[] = $nu; |
325 | } |
326 | |
327 | return $nus; |
328 | } |
329 | |
330 | private function saveEmailNotificationForDPO(Notification $notification, Collectivity $collectivity): array |
331 | { |
332 | // Get DPOS |
333 | $refs = (new ArrayCollection($this->userRepository->findAll()))->filter(function (User $u) use ($collectivity) { |
334 | $mi = $u->getMoreInfos(); |
335 | |
336 | $refColIds = []; |
337 | /** @var Collectivity $col */ |
338 | foreach ($u->getCollectivitesReferees() as $col) { |
339 | $refColIds[] = $col->getId()->toString(); |
340 | } |
341 | |
342 | return in_array('ROLE_ADMIN', $u->getRoles()) |
343 | || (in_array('ROLE_REFERENT', $u->getRoles()) && in_array($collectivity->getId()->toString(), $refColIds)) |
344 | || ($mi && isset($mi[UserMoreInfoDictionary::MOREINFO_DPD]) && $mi[UserMoreInfoDictionary::MOREINFO_DPD]); |
345 | }); |
346 | |
347 | // Also get DPOs from collectivity |
348 | if ($collectivity->getDpo()) { |
349 | if ($collectivity->getDpo()->getNotification()) { |
350 | $refs[] = $collectivity->getDpo()->getMail(); |
351 | } |
352 | } |
353 | |
354 | $nus = []; |
355 | // Add notification with email address for the référents |
356 | foreach ($refs as $ref) { |
357 | $nu = new NotificationUser(); |
358 | if (is_object($ref) && $ref instanceof User) { |
359 | $nu->setMail($ref->getEmail()); |
360 | $nu->setUser($ref); |
361 | } else { |
362 | $nu->setMail($ref); |
363 | } |
364 | |
365 | $nu->setNotification($notification); |
366 | $nu->setActive(false); |
367 | $nu->setToken(sha1($notification->getName() . microtime() . $nu->getMail())); |
368 | $nu->setSent(false); |
369 | $nus[] = $nu; |
370 | } |
371 | |
372 | return $nus; |
373 | } |
374 | |
375 | private function saveEmailNotificationForRespTrait(Notification $notification, CollectivityRelated $object): array |
376 | { |
377 | $nus = []; |
378 | // Get referent operationnels for this collectivity |
379 | $refs = $object->getCollectivity()->getUsers()->filter(function (User $u) { |
380 | $mi = $u->getMoreInfos(); |
381 | |
382 | return $mi && isset($mi[UserMoreInfoDictionary::MOREINFO_TREATMENT]) && $mi[UserMoreInfoDictionary::MOREINFO_TREATMENT]; |
383 | }); |
384 | if (0 === $refs->count()) { |
385 | // No ref OP, get from collectivity |
386 | if ($object->getCollectivity() && $object->getCollectivity()->getLegalManager()) { |
387 | $refs = [$object->getCollectivity()->getLegalManager()->getMail()]; |
388 | } |
389 | } |
390 | |
391 | foreach ($refs as $ref) { |
392 | $nu = new NotificationUser(); |
393 | if (is_object($ref) && User::class === get_class($ref)) { |
394 | $nu->setMail($ref->getEmail()); |
395 | $nu->setUser($ref); |
396 | } else { |
397 | $nu->setMail($ref); |
398 | } |
399 | |
400 | $nu->setToken(sha1($notification->getName() . microtime() . $nu->getMail())); |
401 | $nu->setNotification($notification); |
402 | $nu->setActive(false); |
403 | $nu->setSent(false); |
404 | $nus[] = $nu; |
405 | // $this->notificationUserRepository->persist($nu); |
406 | } |
407 | |
408 | return $nus; |
409 | } |
410 | |
411 | private function getObjectSimpleValue($object) |
412 | { |
413 | if (is_object($object)) { |
414 | if (method_exists($object, 'getId')) { |
415 | return $object->getId(); |
416 | } elseif (method_exists($object, '__toString')) { |
417 | return $object->__toString(); |
418 | } elseif (method_exists($object, 'format')) { |
419 | return $object->format(DATE_ATOM); |
420 | } |
421 | |
422 | return ''; |
423 | } |
424 | |
425 | if (is_array($object)) { |
426 | return join(', ', $object); |
427 | } |
428 | |
429 | return $object; |
430 | } |
431 | |
432 | private function getSubjectForNotification(Notification $notification): string |
433 | { |
434 | if ( |
435 | 'notification.modules.violation' === $notification->getModule() |
436 | ) { |
437 | $ob = $notification->getObject(); |
438 | if (isset($ob->violationNatures)) { |
439 | $vn = explode(',', $ob->violationNatures); |
440 | if (is_array($vn) && count($vn) > 0) { |
441 | return join(', ', array_map(function ($v) { |
442 | return ViolationNatureDictionary::getNatures()[trim($v)] ?? ''; |
443 | }, $vn)); |
444 | } |
445 | } elseif (isset($ob->violationNature)) { |
446 | return ViolationNatureDictionary::getNatures()[$ob->violationNature] ?? ''; |
447 | } |
448 | |
449 | return ''; |
450 | } |
451 | |
452 | if ( |
453 | 'notification.modules.proof' === $notification->getModule() |
454 | ) { |
455 | $type = $notification->getObject()->type; |
456 | |
457 | return $type && isset(ProofTypeDictionary::getTypes()[$type]) ? ProofTypeDictionary::getTypes()[$type] : ''; |
458 | } |
459 | |
460 | if ('notification.modules.protect_action' === $notification->getModule()) { |
461 | return MesurementStatusDictionary::getStatus()[$notification->getObject()->status] ?? ''; |
462 | } |
463 | |
464 | if ('notification.modules.request' === $notification->getModule() && ('notifications.actions.state_change' === $notification->getAction() || 'notification.actions.state_change' === $notification->getAction())) { |
465 | return $notification->getObject()->state; |
466 | } |
467 | |
468 | if ('notification.modules.aipd' === $notification->getModule() && ('notifications.actions.state_change' === $notification->getAction() || 'notification.actions.state_change' === $notification->getAction())) { |
469 | return StatutAnalyseImpactDictionary::getStatuts()[$notification->getObject()->statut]; |
470 | } |
471 | |
472 | if ('notification.modules.aipd' === $notification->getModule() && ('notifications.actions.validated' === $notification->getAction() || 'notification.actions.validated' === $notification->getAction())) { |
473 | return StatutAnalyseImpactDictionary::getStatuts()[$notification->getObject()->statut]; |
474 | } |
475 | |
476 | if ('notification.modules.document' === $notification->getModule()) { |
477 | return $notification->getObject()->typeName; |
478 | } |
479 | |
480 | if ('notifications.actions.validation' === $notification->getAction() || 'notification.actions.validation' === $notification->getAction()) { |
481 | return $this->translator->trans('notifications.subject.validation'); |
482 | } |
483 | |
484 | return ''; |
485 | } |
486 | } |