Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
32.55% covered (danger)
32.55%
69 / 212
25.00% covered (danger)
25.00%
3 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 1
Notification
32.55% covered (danger)
32.55%
69 / 212
25.00% covered (danger)
25.00%
3 / 12
1321.07
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 getModelClass
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getQueryBuilder
65.52% covered (warning)
65.52%
19 / 29
0.00% covered (danger)
0.00%
0 / 1
10.62
 findAll
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 findAllUnread
83.33% covered (warning)
83.33%
20 / 24
0.00% covered (danger)
0.00%
0 / 1
3.04
 persist
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 findOneBy
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 count
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 findPaginated
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
 addTableSearches
15.49% covered (danger)
15.49%
11 / 71
0.00% covered (danger)
0.00%
0 / 1
314.09
 addTableOrder
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 1
182
 objectExists
70.00% covered (warning)
70.00%
7 / 10
0.00% covered (danger)
0.00%
0 / 1
5.68
1<?php
2
3/**
4 * This file is part of the MADIS - RGPD Management application.
5 *
6 * @copyright Copyright (c) 2018-2019 Soluris - Solutions Numériques Territoriales Innovantes
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22declare(strict_types=1);
23
24namespace App\Infrastructure\ORM\Notification\Repository;
25
26use App\Application\Doctrine\Repository\CRUDRepository;
27use App\Application\Traits\RepositoryUtils;
28use App\Domain\Notification\Model;
29use App\Domain\Notification\Repository;
30use App\Domain\Registry\Model\ConformiteTraitement\ConformiteTraitement;
31use App\Domain\Registry\Model\Mesurement;
32use App\Domain\User\Dictionary\UserMoreInfoDictionary;
33use App\Domain\User\Dictionary\UserRoleDictionary;
34use App\Domain\User\Model\Collectivity;
35use App\Domain\User\Model\User;
36use Doctrine\Common\Collections\ArrayCollection;
37use Doctrine\Common\Collections\Collection;
38use Doctrine\ORM\Query\Expr\OrderBy;
39use Doctrine\ORM\QueryBuilder;
40use Doctrine\ORM\Tools\Pagination\Paginator;
41use Doctrine\Persistence\ManagerRegistry;
42use Symfony\Component\Security\Core\Security;
43
44class Notification extends CRUDRepository implements Repository\Notification
45{
46    use RepositoryUtils;
47
48    protected Security $security;
49    protected bool $notificationsActive;
50    protected string $saveNotificationsDuration;
51
52    public function __construct(ManagerRegistry $registry, Security $security, bool $notificationsActive, string $saveNotificationsDuration)
53    {
54        parent::__construct($registry);
55        $this->security                  = $security;
56        $this->notificationsActive       = $notificationsActive;
57        $this->saveNotificationsDuration = $saveNotificationsDuration;
58    }
59
60    protected function getModelClass(): string
61    {
62        return Model\Notification::class;
63    }
64
65    private function getQueryBuilder(array $order = ['createdAt' => 'DESC']): QueryBuilder
66    {
67        /**
68         * @var User $user
69         */
70        $user = $this->security->getUser();
71
72        $allNotifs = false;
73
74        $allowedRoles = [UserRoleDictionary::ROLE_REFERENT, UserRoleDictionary::ROLE_ADMIN];
75        if ($user && (count($user->getRoles()) && in_array($user->getRoles()[0], $allowedRoles)) || in_array(UserMoreInfoDictionary::MOREINFO_DPD, $user->getMoreInfos())) {
76            // Find notifications with null user if current user is dpo
77            $allNotifs = true;
78        }
79
80        $qb = $this->createQueryBuilder()
81            ->addSelect('collectivity')
82            ->leftJoin('o.collectivity', 'collectivity')
83        ;
84
85        if ($allNotifs) {
86            $qb->where('o.dpo = 1');
87            if (!in_array(UserRoleDictionary::ROLE_ADMIN, $user->getRoles())) {
88                // For non admin users
89                if (in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) {
90                    $cf = $user->getCollectivitesReferees();
91                    $cf = new ArrayCollection([...$cf]);
92                    //                    if (!is_object($cf) || ArrayCollection::class !== get_class($cf)) {
93                    //                        $cf = new ArrayCollection([...$cf]);
94                    //                    }
95                    $collectivityIds = $cf->map(function (Collectivity $c) {return $c->getId()->__toString(); })->toArray();
96                } else {
97                    $collectivityIds = [$user->getCollectivity()->getId()->__toString(), null];
98                }
99                $qb->innerJoin('o.collectivity', 'c');
100                $qb->andWhere(
101                    $qb->expr()->in('c.id', ':ids')
102                );
103                $qb->setParameter('ids', $collectivityIds);
104            }
105        } else {
106            $qb->addSelect('u');
107            $qb->leftJoin('o.notificationUsers', 'u')
108                ->where('u.active = 1')
109                ->where('u.user = :user')
110                ->setParameter('user', $user)
111            ;
112        }
113
114        return $qb;
115    }
116
117    public function findAll(array $order = ['createdAt' => 'DESC']): array
118    {
119        $qb = $this->getQueryBuilder($order);
120
121        foreach ($order as $field => $direction) {
122            $qb->addOrderBy(new OrderBy('o.' . $field, $direction));
123        }
124
125        return $qb->getQuery()->getResult();
126    }
127
128    public function findAllUnread(array $order = ['createdAt' => 'DESC']): array
129    {
130        // If notifications are not active, delete all notifications
131        if (false === $this->notificationsActive) {
132            $em = $this->getManager();
133            $em->createQuery('DELETE FROM App\Domain\Notification\Model\NotificationUser')->execute();
134            $em->createQuery('DELETE FROM App\Domain\Notification\Model\Notification')->execute();
135
136            return [];
137        }
138
139        // If notifications are active, delete old notifications
140
141        $qb = $this->getManager()->createQueryBuilder();
142        $qb->where('o.createdAt < :date');
143        $prevDate = new \DateTime();
144        $prevDate->modify('-' . $this->saveNotificationsDuration);
145        $qb->setParameter('date', $prevDate);
146        $qb->delete('App\Domain\Notification\Model\NotificationUser as o');
147        $qb->getQuery()->execute();
148
149        $qb = $this->getManager()->createQueryBuilder();
150        $qb->where('o.createdAt < :date');
151        $prevDate = new \DateTime();
152        $prevDate->modify('-' . $this->saveNotificationsDuration);
153        $qb->setParameter('date', $prevDate);
154        $qb->delete('App\Domain\Notification\Model\Notification as o');
155        $qb->getQuery()->execute();
156
157        $qb = $this->getQueryBuilder($order);
158
159        foreach ($order as $field => $direction) {
160            $qb->addOrderBy(new OrderBy('o.' . $field, $direction));
161        }
162
163        $this->addTableSearches($qb, ['state' => 'unread']);
164
165        return $qb->getQuery()->getResult();
166    }
167
168    public function persist($object)
169    {
170        $this->getManager()->persist($object);
171    }
172
173    public function findOneBy(array $criteria)
174    {
175        $notifs = $this->registry
176            ->getManager()
177            ->getRepository($this->getModelClass())
178            ->findBy($criteria)
179        ;
180        if (count($notifs) > 0) {
181            return $notifs[0];
182        }
183    }
184
185    public function count(array $criteria = [])
186    {
187        $qb = $this->getQueryBuilder();
188
189        $qb->select('COUNT(o.id)');
190
191        if (isset($criteria['collectivity']) && $criteria['collectivity'] instanceof Collection) {
192            $collectivityIds = $criteria['collectivity']->toArray();
193            array_push($collectivityIds, null);
194            // $qb->innerJoin('o.collectivity', 'collectivite');
195            $qb->andWhere(
196                $qb->expr()->in('collectivity', ':collectivities')
197            )
198                ->setParameter('collectivities', $collectivityIds)
199            ;
200            unset($criteria['collectivity']);
201        }
202        //
203        //        dd($criteria);
204
205        foreach ($criteria as $key => $value) {
206            $this->addWhereClause($qb, $key, $value);
207        }
208
209        return $qb->getQuery()->getSingleScalarResult();
210    }
211
212    public function findPaginated($firstResult, $maxResults, $orderColumn, $orderDir, $searches, $criteria = [])
213    {
214        $qb = $this->getQueryBuilder();
215
216        //        if (isset($criteria['collectivity']) && $criteria['collectivity'] instanceof Collection) {
217        //            $collectivityIds = $criteria['collectivity']->map(function($c) {
218        //                if ($c instanceof Collectivity) {
219        //                    return $c->getId()->__toString();
220        //                }
221        //                return $c;
222        //            })->toArray();
223        //            //dd($collectivityIds);
224        //            array_push($collectivityIds, null);
225        //            //$qb->innerJoin('o.collectivity', 'collectivite');
226        //            $qb->andWhere(
227        //                $qb->expr()->in('collectivity', ':collectivities')
228        //            )
229        //                ->setParameter('collectivities', $collectivityIds)
230        //            ;
231        //
232        //            //dd($qb->getQuery()->getParameters());
233        unset($criteria['collectivity']);
234        //        }
235
236        foreach ($criteria as $key => $value) {
237            $this->addWhereClause($qb, $key, $value);
238        }
239
240        $this->addTableOrder($qb, $orderColumn, $orderDir);
241        $this->addTableSearches($qb, $searches);
242
243        $qb = $qb->getQuery();
244        $qb->setFirstResult($firstResult);
245        $qb->setMaxResults($maxResults);
246
247        return new Paginator($qb);
248    }
249
250    private function addTableSearches(QueryBuilder $queryBuilder, $searches)
251    {
252        /** @var User $user */
253        $user = $this->security->getUser();
254        foreach ($searches as $columnName => $search) {
255            switch ($columnName) {
256                case 'state':
257                    if ('read' === $search) {
258                        if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_REFERENT') || in_array(UserMoreInfoDictionary::MOREINFO_DPD, $user->getMoreInfos())) {
259                            $queryBuilder->andWhere('o.readAt IS NOT NULL');
260                        } else {
261                            $queryBuilder->leftJoin('o.notificationUsers', 'nu');
262                            $queryBuilder->andWhere('nu.user=:user');
263                            $queryBuilder->andWhere('nu.readAt IS NOT NULL');
264                            $queryBuilder->setParameter('user', $user);
265                        }
266                    } else {
267                        if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_REFERENT') || in_array(UserMoreInfoDictionary::MOREINFO_DPD, $user->getMoreInfos())) {
268                            $queryBuilder->andWhere('o.readAt IS NULL');
269                        } else {
270                            $queryBuilder->leftJoin('o.notificationUsers', 'nu');
271                            $queryBuilder->andWhere('nu.user=:user');
272                            $queryBuilder->andWhere('nu.readAt IS NULL');
273                            $queryBuilder->setParameter('user', $user);
274                        }
275                    }
276
277                    break;
278                case 'collectivity':
279                    $queryBuilder->andWhere('collectivity.name LIKE :nom')
280                        ->setParameter('nom', '%' . $search . '%');
281                    break;
282                case 'name':
283                    $queryBuilder->andWhere('o.name LIKE :name')
284                        ->setParameter('name', '%' . $search . '%');
285                    break;
286                case 'action':
287                    if ('automatic' === $search) {
288                        $queryBuilder
289                            ->andWhere('o.action LIKE :a1 OR o.action LIKE :a2 OR o.action LIKE :a3')
290                            ->setParameter('a1', '%no_login')
291                            ->setParameter('a2', '%late_survey')
292                            ->setParameter('a3', '%late_action')
293                        ;
294                    } else {
295                        $queryBuilder->andWhere('o.action LIKE :action')
296                            ->setParameter('action', '%' . $search . '%');
297                    }
298
299                    break;
300                case 'module':
301                    $queryBuilder->andWhere('o.module LIKE :module')
302                        ->setParameter('module', '%' . $search . '%');
303                    break;
304                case 'object':
305                    $queryBuilder->andWhere('o.subject LIKE :object')
306                        ->setParameter('object', '%' . $search . '%');
307                    break;
308                case 'date':
309                    $queryBuilder->andWhere('o.createdAt BETWEEN :createdAt_start AND :createdAt_end')
310                        ->setParameter('createdAt_start', date_create_from_format('d/m/y', substr($search, 0, 8))->format('Y-m-d 00:00:00'))
311                        ->setParameter('createdAt_end', date_create_from_format('d/m/y', substr($search, 11, 8))->format('Y-m-d 23:59:59'));
312                    break;
313                case 'user':
314                    $queryBuilder->leftJoin('o.createdBy', 'cb');
315                    $queryBuilder->andWhere('CONCAT(cb.firstName, \' \', cb.lastName) LIKE :user')
316                        ->setParameter('user', '%' . $search . '%');
317                    break;
318                case 'read_date':
319                    if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_REFERENT')) {
320                        $queryBuilder->andWhere('o.readAt BETWEEN :readAt_start AND :readAt_end')
321                            ->setParameter('readAt_start', date_create_from_format('d/m/y', substr($search, 0, 8))->format('Y-m-d 00:00:00'))
322                            ->setParameter('readAt_end', date_create_from_format('d/m/y', substr($search, 11, 8))->format('Y-m-d 23:59:59'));
323                    } else {
324                        $queryBuilder->leftJoin('o.notificationUsers', 'nu2');
325                        $queryBuilder->andWhere('nu2.user=:user');
326                        $queryBuilder->andWhere('nu2.readAt BETWEEN :readAt_start AND :readAt_end')
327                            ->setParameter('readAt_start', date_create_from_format('d/m/y', substr($search, 0, 8))->format('Y-m-d 00:00:00'))
328                            ->setParameter('readAt_end', date_create_from_format('d/m/y', substr($search, 11, 8))->format('Y-m-d 23:59:59'));
329                        $queryBuilder->setParameter('user', $user);
330                    }
331                    break;
332                case 'updatedAt':
333                    $queryBuilder->andWhere('o.updatedAt BETWEEN :updatedAt_start AND :updatedAt_end')
334                        ->setParameter('updatedAt_start', date_create_from_format('d/m/y', substr($search, 0, 8))->format('Y-m-d 00:00:00'))
335                        ->setParameter('updatedAt_end', date_create_from_format('d/m/y', substr($search, 11, 8))->format('Y-m-d 23:59:59'));
336                    break;
337            }
338        }
339    }
340
341    private function addTableOrder(QueryBuilder $queryBuilder, $orderColumn, $orderDir)
342    {
343        switch ($orderColumn) {
344            case 'read_date':
345            case 'state':
346                if ($this->security->isGranted('ROLE_ADMIN') || $this->security->isGranted('ROLE_REFERENT')) {
347                    $queryBuilder->addOrderBy('o.readAt', $orderDir);
348                } else {
349                    $user = $this->security->getUser();
350                    $queryBuilder->leftJoin('o.notificationUsers', 'nu3');
351                    $queryBuilder->andWhere('nu3.user=:user');
352                    $queryBuilder->addOrderBy('nu3.readAt');
353                    $queryBuilder->setParameter('user', $user);
354                }
355                break;
356            case 'name':
357                $queryBuilder->addOrderBy('o.name', $orderDir);
358                break;
359            case 'collectivity':
360                $queryBuilder->addOrderBy('collectivity.name', $orderDir);
361                break;
362            case 'action':
363                $queryBuilder->addOrderBy('o.action', $orderDir);
364                break;
365            case 'object':
366                $queryBuilder->addOrderBy('o.subject', $orderDir);
367                break;
368            case 'module':
369                $queryBuilder->addOrderBy('o.module', $orderDir);
370                break;
371            case 'date':
372                $queryBuilder->addOrderBy('o.createdAt', $orderDir);
373                break;
374            case 'user':
375                $queryBuilder->leftJoin('o.createdBy', 'cb');
376                $queryBuilder->addSelect('CONCAT(cb.firstName, \' \', cb.lastName)
377                    AS HIDDEN person_name')
378                    ->addOrderBy('person_name', $orderDir);
379                break;
380            case 'updatedAt':
381                $queryBuilder->addOrderBy('o.updatedAt', $orderDir);
382                break;
383        }
384    }
385
386    public function objectExists(Model\Notification $notification): bool
387    {
388        if (!$notification->getObject()) {
389            return false;
390        }
391
392        $object     = $notification->getObject();
393        $moduleName = str_replace('notification.modules.', '', $notification->getModule());
394
395        if ('action_plan' === $moduleName) {
396            $objectClass = Mesurement::class;
397        } elseif ('aipd' === $moduleName && 'notifications.actions.treatment_needs_aipd' === $notification->getAction()) {
398            $objectClass = ConformiteTraitement::class;
399        } else {
400            $objectClass = array_flip(Model\Notification::MODULES)[$moduleName];
401        }
402
403        return (bool) $this->registry->getRepository($objectClass)->find($object->id);
404    }
405}