Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
7.59% covered (danger)
7.59%
12 / 158
40.00% covered (danger)
40.00%
6 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserController
7.59% covered (danger)
7.59%
12 / 158
40.00% covered (danger)
40.00%
6 / 15
1642.76
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getDomain
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModel
100.00% covered (success)
100.00%
1 / 1
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
 getFormType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSoftDelete
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 unarchiveAction
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 unarchiveConfirmationAction
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 listAction
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 listDataTables
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
156
 getLabelAndKeysArray
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
2
 getRequestCriteria
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getActionCellsContent
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
72
 getServicesContent
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
30
 getRolesColor
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
30
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\Domain\User\Controller;
25
26use App\Application\Controller\CRUDController;
27use App\Application\Symfony\Security\UserProvider;
28use App\Application\Traits\ServersideDatatablesTrait;
29use App\Domain\User\Dictionary\UserMoreInfoDictionary;
30use App\Domain\User\Dictionary\UserRoleDictionary;
31use App\Domain\User\Form\Type\UserType;
32use App\Domain\User\Model\Collectivity;
33use App\Domain\User\Model\Service;
34use App\Domain\User\Model\User;
35use App\Domain\User\Repository;
36use Doctrine\ORM\EntityManagerInterface;
37use Knp\Snappy\Pdf;
38use Symfony\Component\HttpFoundation\JsonResponse;
39use Symfony\Component\HttpFoundation\Request;
40use Symfony\Component\HttpFoundation\RequestStack;
41use Symfony\Component\HttpFoundation\Response;
42use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
43use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
44use Symfony\Component\Routing\RouterInterface;
45use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
46use Symfony\Component\Security\Core\Security;
47use Symfony\Contracts\Translation\TranslatorInterface;
48use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
49
50/**
51 * @property Repository\User $repository
52 */
53class UserController extends CRUDController
54{
55    use ServersideDatatablesTrait;
56
57    private PasswordHasherFactoryInterface $passwordHasherFactory;
58
59    /**
60     * @var RequestStack
61     */
62    private $requestStack;
63
64    /**
65     * @var RouterInterface
66     */
67    protected $router;
68
69    /**
70     * @var Security
71     */
72    protected $security;
73
74    /**
75     * @var UserProvider
76     */
77    protected $userProvider;
78
79    public function __construct(
80        EntityManagerInterface $entityManager,
81        TranslatorInterface $translator,
82        Repository\User $repository,
83        RequestStack $requestStack,
84        PasswordHasherFactoryInterface $passwordHasherFactory,
85        Pdf $pdf,
86        RouterInterface $router,
87        Security $security,
88        UserProvider $userProvider,
89        AuthorizationCheckerInterface $authorizationChecker,
90    ) {
91        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
92        $this->requestStack          = $requestStack;
93        $this->passwordHasherFactory = $passwordHasherFactory;
94        $this->router                = $router;
95        $this->security              = $security;
96        $this->userProvider          = $userProvider;
97        $this->authorizationChecker  = $authorizationChecker;
98    }
99
100    protected function getDomain(): string
101    {
102        return 'user';
103    }
104
105    protected function getModel(): string
106    {
107        return 'user';
108    }
109
110    protected function getModelClass(): string
111    {
112        return User::class;
113    }
114
115    protected function getFormType(): string
116    {
117        return UserType::class;
118    }
119
120    protected function isSoftDelete(): bool
121    {
122        return true;
123    }
124
125    /**
126     * The unarchive action view
127     * Display a confirmation message to confirm data un-archivage.
128     */
129    public function unarchiveAction(string $id): Response
130    {
131        $object = $this->repository->findOneById($id);
132        if (!$object) {
133            throw new NotFoundHttpException("No object found with ID '{$id}'");
134        }
135
136        return $this->render($this->getTemplatingBasePath('unarchive'), [
137            'object' => $object,
138        ]);
139    }
140
141    /**
142     * The unarchive action
143     * Unarchive the data.
144     *
145     * @throws \Exception
146     */
147    public function unarchiveConfirmationAction(string $id): Response
148    {
149        $object = $this->repository->findOneById($id);
150        if (!$object) {
151            throw new NotFoundHttpException("No object found with ID '{$id}'");
152        }
153
154        if (!\method_exists($object, 'setDeletedAt')) {
155            throw new MethodNotImplementedException('setDeletedAt');
156        }
157        $object->setDeletedAt(null);
158        $this->repository->update($object);
159
160        $this->addFlash('success', $this->getFlashbagMessage('success', 'unarchive', $object));
161
162        return $this->redirectToRoute($this->getRouteName('list'));
163    }
164
165    public function listAction(): Response
166    {
167        $criteria = $this->getRequestCriteria();
168
169        return $this->render($this->getTemplatingBasePath('list'), [
170            'totalItem' => $this->repository->count($criteria),
171            'route'     => $this->router->generate('user_user_list_datatables', ['archive' => $criteria['archive']]),
172        ]);
173    }
174
175    public function listDataTables(Request $request): JsonResponse
176    {
177        $criteria = $this->getRequestCriteria();
178        $users    = $this->getResults($request, $criteria);
179        $reponse  = $this->getBaseDataTablesResponse($request, $users, $criteria);
180
181        /** @var User $user */
182        foreach ($users as $user) {
183            $roles = '';
184            foreach ($user->getRoles() as $role) {
185                $span = '<span class="badge ' . $this->getRolesColor($role) . '">' . UserRoleDictionary::getFullRoles()[$role] . '</span>';
186                $roles .= $span;
187            }
188
189            $infos = '';
190            foreach ($user->getMoreInfos() as $info) {
191                $span = '<span class="badge">' . UserMoreInfoDictionary::getMoreInfos()[$info] . '</span>';
192                $infos .= $span;
193            }
194
195            $userActifBgColor = 'bg-green';
196            if (!$user->isEnabled()) {
197                $userActifBgColor = 'bg-red';
198            }
199
200            $collectivityActifBgColor = 'bg-green';
201            if (!$user->getCollectivity() || !$user->getCollectivity()->isActive()) {
202                $collectivityActifBgColor = 'bg-red';
203            }
204
205            $actif = '
206                <span class="badge ' . $userActifBgColor . '">' . $this->translator->trans('user.user.list.user') . '</span>
207                <span class="badge ' . $collectivityActifBgColor . '">' . $this->translator->trans('global.label.organization') . '</span>'
208            ;
209
210            $services = '<ul>';
211            foreach ($user->getServices() as $service) {
212                $services .= '<li>' . $service->getName() . '</li>';
213            }
214            $services .= '</ul>';
215
216            $europeTimezone    = new \DateTimeZone('Europe/Paris');
217            $reponse['data'][] = [
218                'prenom'       => $user->getFirstName(),
219                'nom'          => $user->getLastName(),
220                'email'        => $user->getEmail(),
221                'collectivite' => $user->getCollectivity() ? $user->getCollectivity()->getName() : '[Structure supprimée]',
222                'roles'        => $roles,
223                'moreInfos'    => $infos,
224                'actif'        => $actif,
225                'connexion'    => !\is_null($user->getLastLogin()) ? $user->getLastLogin()->setTimezone($europeTimezone)->format('Y-m-d H:i') : null,
226                'services'     => $services,
227                'createdAt'    => $user->getCreatedAt() ? date_format($user->getCreatedAt(), 'd-m-Y H:i') : null,
228                'updatedAt'    => $user->getUpdatedAt() ? date_format($user->getUpdatedAt(), 'd-m-Y H:i') : null,
229                'actions'      => $this->getActionCellsContent($user),
230            ];
231        }
232
233        $jsonResponse = new JsonResponse();
234        $jsonResponse->setJson(\json_encode($reponse));
235
236        return $jsonResponse;
237    }
238
239    protected function getLabelAndKeysArray(): array
240    {
241        return [
242            'prenom',
243            'nom',
244            'email',
245            'collectivite',
246            'roles',
247            'moreInfos',
248            'actif',
249            'connexion',
250            'services',
251            'createdAt',
252            'updatedAt',
253            'actions',
254        ];
255    }
256
257    private function getRequestCriteria()
258    {
259        $criteria            = [];
260        $request             = $this->requestStack->getMainRequest();
261        $criteria['archive'] = $request->query->getBoolean('archive');
262
263        if (!$this->security->isGranted('ROLE_ADMIN')) {
264            /** @var User $user */
265            $user                              = $this->security->getUser();
266            $criteria['collectivitesReferees'] = $user->getCollectivitesReferees();
267        }
268
269        return $criteria;
270    }
271
272    private function getActionCellsContent(User $user)
273    {
274        $cellContent = '';
275        if ($this->security->getUser() !== $user && \is_null($user->getDeletedAt()) && !$this->isGranted('IS_IMPERSONATOR')) {
276            $cellContent .=
277                '<a href="' . $this->router->generate('reporting_dashboard_index', ['_switch_user' => $user->getUsername()]) . '">
278                    <i aria-hidden="true" class="fa fa-user-lock"></i> ' .
279                $this->translator->trans('global.action.impersonate') .
280                '</a> ';
281        }
282
283        if ($this->security->isGranted('ROLE_ADMIN')) {
284            if (\is_null($user->getDeletedAt())) {
285                $cellContent .=
286                    '<a href="' . $this->router->generate('user_user_edit', ['id' => $user->getId()]) . '">
287                        <i aria-hidden="true" class="fa fa-pencil"></i> ' .
288                    $this->translator->trans('global.action.edit') .
289                    '</a> ';
290            }
291
292            if ($this->security->getUser() !== $user) {
293                if (\is_null($user->getDeletedAt())) {
294                    $cellContent .=
295                        '<a href="' . $this->router->generate('user_user_delete', ['id' => $user->getId()]) . '">
296                        <i aria-hidden="true" class="fa fa-archive"></i> ' .
297                        $this->translator->trans('global.action.archive') .
298                        '</a> ';
299                } else {
300                    $cellContent .=
301                        '<a href="' . $this->router->generate('user_user_unarchive', ['id' => $user->getId()]) . '">
302                        <i aria-hidden="true" class="fa fa-archive"></i> ' .
303                        $this->translator->trans('global.action.unarchive') .
304                        '</a> ';
305                }
306
307                // $cellContent .=
308                // '<a href="' . $this->router->generate('user_user_delete', ['id' => $user->getId()]) . '">
309                //     <i aria-hidden="true" class="fa fa-trash"></i> ' .
310                // $this->translator->trans('global.action.delete') .
311                // '</a> ';
312            }
313        }
314
315        return $cellContent;
316    }
317
318    public function getServicesContent(string $collectivityId, string $userId): Response
319    {
320        $collectivity = $this->entityManager->getRepository(Collectivity::class)->findOneBy(['id' => $collectivityId]);
321        if (null === $collectivity) {
322            throw new NotFoundHttpException('Can\'t find collectivity for id ' . $collectivityId);
323        }
324
325        $services = $this
326        ->entityManager
327        ->getRepository(Service::class)
328        ->findBy(
329            ['collectivity' => $collectivity],
330            ['name' => 'ASC']
331        );
332
333        $serviceIdsSelected = [];
334
335        if ('creer' !== $userId) {
336            $user = $this
337            ->entityManager
338            ->getRepository(User::class)
339            ->find($userId);
340
341            $servicesAlreadySelected = $user->getServices()->getValues();
342
343            foreach ($servicesAlreadySelected as $service) {
344                $serviceIdsSelected[] = $service->getId();
345            }
346        }
347
348        $responseData = [];
349
350        foreach ($services as $service) {
351            $responseData[] = [
352                'value'    => $service->getId()->toString(),
353                'text'     => $service->__toString(),
354                'selected' => in_array($service->getId(), $serviceIdsSelected),
355            ];
356        }
357
358        return new JsonResponse($responseData);
359    }
360
361    private function getRolesColor(string $role)
362    {
363        switch ($role) {
364            case UserRoleDictionary::ROLE_ADMIN:
365                return 'bg-red';
366            case UserRoleDictionary::ROLE_USER:
367                return 'bg-blue';
368            case UserRoleDictionary::ROLE_API:
369                return 'bg-green';
370            default:
371                return 'bg-red';
372        }
373    }
374}