Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
10.45% covered (danger)
10.45%
23 / 220
27.78% covered (danger)
27.78%
5 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
CollectivityController
10.45% covered (danger)
10.45%
23 / 220
27.78% covered (danger)
27.78%
5 / 18
4005.82
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
12 / 12
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
 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 / 24
0.00% covered (danger)
0.00%
0 / 1
56
 getActionCellsContent
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
6
 getLabelAndKeysArray
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 getRequestCriteria
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 showAction
24.14% covered (danger)
24.14%
7 / 29
0.00% covered (danger)
0.00%
0 / 1
74.87
 editAction
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
 deleteAction
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
156
 formPrePersistData
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
30
 deleteConfirmationAction
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 clonedFromOnNullAction
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
 deleteRelatedObjectsAction
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
182
 deleteCollectivityAction
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
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\Interfaces\CollectivityRelated;
28use App\Application\Symfony\Security\UserProvider;
29use App\Application\Traits\ServersideDatatablesTrait;
30use App\Domain\Registry\Model\Mesurement;
31use App\Domain\Registry\Model\Treatment;
32use App\Domain\Registry\Repository as RegistryRepository;
33use App\Domain\User\Dictionary\CollectivityTypeDictionary;
34use App\Domain\User\Dictionary\UserRoleDictionary;
35use App\Domain\User\Form\Type\CollectivityType;
36use App\Domain\User\Form\Type\ReviewDataType;
37use App\Domain\User\Model;
38use App\Domain\User\Model\Collectivity;
39use App\Domain\User\Model\User;
40use App\Domain\User\Repository;
41use Doctrine\ORM\EntityManagerInterface;
42use Gaufrette\FilesystemInterface;
43use Knp\Snappy\Pdf;
44use Symfony\Component\Form\Form;
45use Symfony\Component\HttpFoundation\File\UploadedFile;
46use Symfony\Component\HttpFoundation\JsonResponse;
47use Symfony\Component\HttpFoundation\Request;
48use Symfony\Component\HttpFoundation\Response;
49use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
50use Symfony\Component\Routing\RouterInterface;
51use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
52use Symfony\Component\Security\Core\Security;
53use Symfony\Component\String\Slugger\SluggerInterface;
54use Symfony\Contracts\Translation\TranslatorInterface;
55
56/**
57 * @property Repository\Collectivity $repository
58 */
59class CollectivityController extends CRUDController
60{
61    use ServersideDatatablesTrait;
62
63    /**
64     * @var RouterInterface
65     */
66    protected $router;
67
68    /**
69     * @var FilesystemInterface
70     */
71    protected $logoFilesystem;
72
73    /**
74     * @var SluggerInterface
75     */
76    protected $slugger;
77
78    /**
79     * @var Security
80     */
81    protected $security;
82
83    protected RegistryRepository\Treatment $treatmentRepository;
84
85    protected Repository\User $userRepository;
86
87    protected RegistryRepository\Proof $proofRepository;
88
89    protected RegistryRepository\Contractor $contractorRepository;
90
91    protected RegistryRepository\Mesurement $mesurementRepository;
92
93    public function __construct(
94        EntityManagerInterface $entityManager,
95        TranslatorInterface $translator,
96        Repository\Collectivity $repository,
97        Pdf $pdf,
98        RouterInterface $router,
99        Security $security,
100        RegistryRepository\Treatment $treatmentRepository,
101        RegistryRepository\Contractor $contractorRepository,
102        RegistryRepository\Proof $proofRepository,
103        RegistryRepository\Mesurement $mesurementRepository,
104        Repository\User $userRepository,
105        UserProvider $userProvider,
106        FilesystemInterface $logoFilesystem,
107        SluggerInterface $slugger,
108        AuthorizationCheckerInterface $authorizationChecker,
109    ) {
110        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
111        $this->router               = $router;
112        $this->security             = $security;
113        $this->treatmentRepository  = $treatmentRepository;
114        $this->contractorRepository = $contractorRepository;
115        $this->proofRepository      = $proofRepository;
116        $this->userRepository       = $userRepository;
117        $this->mesurementRepository = $mesurementRepository;
118        $this->userProvider         = $userProvider;
119        $this->logoFilesystem       = $logoFilesystem;
120        $this->slugger              = $slugger;
121        $this->authorizationChecker = $authorizationChecker;
122    }
123
124    protected function getDomain(): string
125    {
126        return 'user';
127    }
128
129    protected function getModel(): string
130    {
131        return 'collectivity';
132    }
133
134    protected function getModelClass(): string
135    {
136        return Collectivity::class;
137    }
138
139    protected function getFormType(): string
140    {
141        return CollectivityType::class;
142    }
143
144    public function listAction(): Response
145    {
146        $criteria = $this->getRequestCriteria();
147
148        return $this->render($this->getTemplatingBasePath('list'), [
149            'totalItem' => $this->repository->count($criteria),
150            'route'     => $this->router->generate('user_collectivity_list_datatables'),
151        ]);
152    }
153
154    public function listDataTables(Request $request): JsonResponse
155    {
156        $criteria       = $this->getRequestCriteria();
157        $collectivities = $this->getResults($request, $criteria);
158        $reponse        = $this->getBaseDataTablesResponse($request, $collectivities, $criteria);
159
160        $active   = '<span class="badge bg-green">' . $this->translator->trans('global.label.active') . '</span>';
161        $inactive = '<span class="badge bg-red">' . $this->translator->trans('global.label.inactive') . '</span>';
162        /** @var Collectivity $collectivity */
163        foreach ($collectivities as $collectivity) {
164            $reponse['data'][] = [
165                'nom'                          => '<a href="' . $this->router->generate('user_collectivity_show', ['id' => $collectivity->getId()]) . '">' . $collectivity->getName() . '</a>',
166                'nom_court'                    => $collectivity->getShortName(),
167                'type'                         => !\is_null($collectivity->getType()) ? CollectivityTypeDictionary::getTypes()[$collectivity->getType()] ?? $collectivity->getType() : null,
168                'informations_complementaires' => !\is_null($collectivity->getInformationsComplementaires()) ? nl2br($collectivity->getInformationsComplementaires()) : null,
169                'siren'                        => $collectivity->getSiren(),
170                'statut'                       => $collectivity->isActive() ? $active : $inactive,
171                'nbr_cnil'                     => $collectivity->getNbrCnil(),
172                'nbr_agents'                   => $collectivity->getNbrAgents(),
173                'population'                   => $collectivity->getPopulation(),
174                // 'tel_referent_rgpd'            => !\is_null($collectivity->getDpo()) ? ($collectivity->getDpo())->getPhoneNumber() : null,
175                'createdAt' => !\is_null($collectivity->getCreatedAt()) ? $collectivity->getCreatedAt()->format('d-m-Y H:i') : null,
176                'updatedAt' => !\is_null($collectivity->getUpdatedAt()) ? $collectivity->getUpdatedAt()->format('d-m-Y H:i') : null,
177                'actions'   => $this->getActionCellsContent($collectivity),
178                'id'        => $collectivity->getId(),
179            ];
180        }
181
182        $jsonResponse = new JsonResponse();
183        $jsonResponse->setJson(\json_encode($reponse));
184
185        return $jsonResponse;
186    }
187
188    private function getActionCellsContent(Collectivity $collectivity)
189    {
190        if (!$this->security->isGranted('ROLE_ADMIN')) {
191            return;
192        }
193
194        $cellContent = '<a href="' . $this->router->generate('user_collectivity_edit', ['id' => $collectivity->getId()]) . '">
195            <i aria-hidden="true" class="fa fa-pencil"></i> ' .
196            $this->translator->trans('global.action.edit') .
197        '</a>';
198
199        $cellContent .= '<a href="' . $this->router->generate('user_collectivity_delete', ['id' => $collectivity->getId()]) . '">
200            <i aria-hidden="true" class="fa fa-trash"></i> ' .
201            $this->translator->trans('global.action.delete') .
202        '</a>';
203
204        return $cellContent;
205    }
206
207    protected function getLabelAndKeysArray(): array
208    {
209        return [
210            'nom',
211            'nom_court',
212            'type',
213            'informations_complementaires',
214            'siren',
215            'statut',
216            'nbr_cnil',
217            'nbr_agents',
218            'population',
219            'createdAt',
220            'updatedAt',
221            'actions',
222            'id',
223        ];
224    }
225
226    private function getRequestCriteria()
227    {
228        $criteria = [];
229
230        if (!$this->security->isGranted('ROLE_ADMIN') && $this->security->isGranted('ROLE_REFERENT')) {
231            /** @var User $user */
232            $user                              = $this->security->getUser();
233            $criteria['collectivitesReferees'] = $user->getCollectivitesReferees();
234        }
235        if (!$this->security->isGranted('ROLE_REFERENT')) {
236            /** @var User $user */
237            $user           = $this->security->getUser();
238            $criteria['id'] = $user->getCollectivity()->getid();
239        }
240
241        return $criteria;
242    }
243
244    public function showAction(string $id): Response
245    {
246        /** @var User $user */
247        $user = $this->security->getUser();
248        if (\in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) {
249            $collectivities = \array_filter(\iterable_to_array($user->getCollectivitesReferees()), function (Collectivity $collectivity) use ($id) {
250                return $collectivity->getId()->toString() === $id;
251            });
252
253            if (empty($collectivities)) {
254                throw $this->createAccessDeniedException();
255            }
256        }
257
258        $object = $this->repository->findOneById($id);
259        if (!$object) {
260            throw new NotFoundHttpException("No object found with ID '{$id}'");
261        }
262        /**
263         * @var User $user
264         */
265        $user = $this->getUser();
266        if (!$user->hasAccessTo($object, false)) {
267            throw $this->createAccessDeniedException();
268        }
269        $serviceEnabled = false;
270        if ($object instanceof Collectivity) {
271            $serviceEnabled = $object->getIsServicesEnabled();
272        } elseif ($object instanceof CollectivityRelated && $object->getCollectivity()) {
273            $serviceEnabled = $object->getCollectivity()->getIsServicesEnabled();
274        }
275
276        $actionEnabled = true;
277
278        if ($object instanceof CollectivityRelated && !$this->authorizationChecker->isGranted('ROLE_ADMIN') && !$user->getServices()->isEmpty()) {
279            $actionEnabled = $object->isInUserServices($this->userProvider->getAuthenticatedUser());
280        }
281
282        if (!$this->isGranted('ROLE_USER')) {
283            $actionEnabled = false;
284        }
285
286        return $this->render($this->getTemplatingBasePath('show'), [
287            'object'         => $object,
288            'actionEnabled'  => $actionEnabled,
289            'serviceEnabled' => $serviceEnabled,
290            'sections'       => ReviewDataType::getSections(),
291        ]);
292    }
293
294    public function editAction(Request $request, string $id): Response
295    {
296        //        /** @var CollectivityRelated $object */
297        $object = $this->repository->findOneById($id);
298        if (!$object) {
299            throw new NotFoundHttpException("No object found with ID '{$id}'");
300        }
301
302        /** @var User $user */
303        $user = $this->getUser();
304        if (!$user->hasAccessTo($object) || !$this->security->isGranted('ROLE_REFERENT')) {
305            return $this->redirectToRoute($this->getRouteName('list'));
306        }
307
308        return parent::editAction($request, $id);
309    }
310
311    /**
312     * The delete action view
313     * Display a confirmation message to confirm data deletion.
314     *
315     * @Override
316     */
317    public function deleteAction(string $id): Response
318    {
319        $object = $this->repository->findOneById($id);
320        if (!$object) {
321            throw new NotFoundHttpException("No object found with ID '{$id}'");
322        }
323
324        /** @var User $user */
325        $user = $this->getUser();
326        if (!$user->hasAccessTo($object) || !$this->security->isGranted('ROLE_REFERENT')) {
327            return $this->redirectToRoute($this->getRouteName('list'));
328        }
329
330        $stringObjects = [];
331
332        $deletedTreaments = $this->treatmentRepository->findBy(['collectivity' => $object]);
333        foreach ($deletedTreaments as $deletedTreament) {
334            $stringObjects[] = 'Traitement - ' . $deletedTreament->getName();
335            /**
336             * @var Treatment
337             */
338            $aipds = $deletedTreament->getConformiteTraitement() ? $deletedTreament->getConformiteTraitement()->getAnalyseImpacts() : [];
339            foreach ($aipds as $aipd) {
340                $stringObjects[] = 'AIPD - ' . $aipd->__toString();
341            }
342        }
343
344        $deletedContractors = $this->contractorRepository->findBy(['collectivity' => $object]);
345        foreach ($deletedContractors as $deletedContractor) {
346            $stringObjects[] = 'Sous-traitant - ' . $deletedContractor->getName();
347        }
348
349        $deletedProofs = $this->proofRepository->findBy(['collectivity' => $object]);
350        foreach ($deletedProofs as $deletedProof) {
351            $stringObjects[] = 'Preuve - ' . $deletedProof->getName();
352        }
353
354        $deletedUsers = $this->userRepository->findBy(['collectivity' => $object]);
355        foreach ($deletedUsers as $deletedUser) {
356            $stringObjects[] = 'Utilisateur - ' . $deletedUser->getFirstname() . ' ' . $deletedUser->getLastname();
357        }
358
359        $deletedMesurements = $this->mesurementRepository->findBy(['collectivity' => $object]);
360        foreach ($deletedMesurements as $deletedMesurement) {
361            /*
362             * @var Mesurement
363             */
364            if ($deletedMesurement->getClonedFrom()) {
365                $stringObjects[] = 'Action de protection - ' . $deletedMesurement->getClonedFrom()->getName();
366            }
367            $stringObjects[] = 'Action de protection - ' . $deletedMesurement->getName();
368        }
369
370        return $this->render($this->getTemplatingBasePath('delete'), [
371            'object'             => $object,
372            'deletedObjects'     => $stringObjects,
373            'deletedTreatments'  => $deletedTreaments,
374            'deletedContractors' => $deletedContractors,
375        ]);
376    }
377
378    public function formPrePersistData($object, $form = null)
379    {
380        /** @var Form $reviewDataForm */
381        $reviewDataForm = $form->get('reviewData');
382        if ($reviewDataForm) {
383            /** @var UploadedFile $logoFile */
384            $logoFile = $reviewDataForm->get('logo')->getData();
385            if ($logoFile) {
386                $originalFilename = pathinfo($logoFile->getClientOriginalName(), PATHINFO_FILENAME);
387                // this is needed to safely include the file name as part of the URL
388                $safeFilename = $this->slugger->slug($originalFilename);
389                $newFilename  = $safeFilename . '-' . uniqid() . '.' . $logoFile->guessExtension();
390                $this->logoFilesystem->write($newFilename, \fopen($logoFile->getRealPath(), 'r'));
391
392                // updates the 'brochureFilename' property to store the PDF file name
393                // instead of its contents
394                /** @var Model\ReviewData $reviewData */
395                $reviewData = $object->getReviewData();
396                $reviewData->setLogo('/uploads/collectivity/logos/' . $newFilename);
397                $object->setReviewData($reviewData);
398            }
399
400            if ($reviewDataForm->has('deleteLogo')) {
401                $deleteLogo = $reviewDataForm->get('deleteLogo')->getData();
402                if ($deleteLogo) {
403                    /** @var Model\ReviewData $reviewData */
404                    $reviewData = $object->getReviewData();
405                    $reviewData->setLogo(null);
406                    $object->setReviewData($reviewData);
407                }
408            }
409        }
410    }
411
412    public function deleteConfirmationAction(string $id): Response
413    {
414        $object = $this->repository->findOneById($id);
415        if (!$object) {
416            throw new NotFoundHttpException("No object found with ID '{$id}'");
417        }
418
419        /** @var User $user */
420        $user = $this->getUser();
421        if (!$user->hasAccessTo($object)) {
422            return $this->redirectToRoute($this->getRouteName('list'));
423        }
424
425        return $this->render($this->getTemplatingBasePath('delete_processing'), [
426            'object' => $object,
427        ]);
428    }
429
430    public function clonedFromOnNullAction(string $id)
431    {
432        $object = $this->repository->findOneById($id);
433        if (!$object) {
434            throw new NotFoundHttpException("No object found with ID '{$id}'");
435        }
436
437        $this->treatmentRepository->resetClonedFromCollectivity($object);
438        $this->mesurementRepository->resetClonedFromCollectivity($object);
439        $this->contractorRepository->resetClonedFromCollectivity($object);
440
441        $this->entityManager->flush();
442
443        return new JsonResponse();
444    }
445
446    public function deleteRelatedObjectsAction($id, string $objectType)
447    {
448        $object = $this->repository->findOneById($id);
449        if (!$object) {
450            throw new NotFoundHttpException("No object found with ID '{$id}'");
451        }
452
453        /** @var User $user */
454        $user = $this->getUser();
455        if (!$user->hasAccessTo($object)) {
456            return new JsonResponse();
457        }
458
459        switch ($objectType) {
460            case 'treatments':
461                foreach ($this->treatmentRepository->findAllByCollectivity($object) as $treatment) {
462                    $this->entityManager->remove($treatment);
463                }
464                break;
465            case 'mesurements':
466                foreach ($this->mesurementRepository->findAllByCollectivity($object) as $mesurement) {
467                    $this->entityManager->remove($mesurement);
468                }
469                break;
470            case 'contractors':
471                foreach ($this->contractorRepository->findAllByCollectivity($object) as $mesurement) {
472                    $this->entityManager->remove($mesurement);
473                }
474                break;
475            case 'users':
476                foreach ($this->userRepository->findBy(['collectivity' => $object]) as $user) {
477                    $this->entityManager->remove($user);
478                }
479                break;
480            case 'proofs':
481                foreach ($this->proofRepository->findBy(['collectivity' => $object]) as $proof) {
482                    $this->entityManager->remove($proof);
483                }
484                break;
485        }
486        $this->entityManager->flush();
487
488        return new JsonResponse();
489    }
490
491    public function deleteCollectivityAction(string $id)
492    {
493        $object = $this->repository->findOneById($id);
494        if (!$object) {
495            throw new NotFoundHttpException("No object found with ID '{$id}'");
496        }
497
498        /** @var User $user */
499        $user = $this->getUser();
500        if (!$user->hasAccessTo($object)) {
501            return $this->redirectToRoute($this->getRouteName('list'));
502        }
503
504        $this->entityManager->remove($object);
505        $this->entityManager->flush();
506
507        $this->addFlash('success', $this->getFlashbagMessage('success', 'delete', $object));
508
509        return $this->redirectToRoute($this->getRouteName('list'));
510    }
511}