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