Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
12.24% covered (danger)
12.24%
18 / 147
46.67% covered (danger)
46.67%
7 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
ContractorController
12.24% covered (danger)
12.24%
18 / 147
46.67% covered (danger)
46.67%
7 / 15
962.17
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
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 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getListData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 reportAction
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 apiGetContractorsByCollectivity
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
20
 listDataTables
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
56
 isContractorInUserServices
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getLabelAndKeysArray
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
12
 getRequestCriteria
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
12
 getActionCellsContent
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 deleteConfirmationAction
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
42
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\Registry\Controller;
25
26use App\Application\Controller\CRUDController;
27use App\Application\Symfony\Security\UserProvider;
28use App\Application\Traits\ServersideDatatablesTrait;
29use App\Domain\Documentation\Model\Category;
30use App\Domain\Registry\Form\Type\ContractorType;
31use App\Domain\Registry\Model\Contractor;
32use App\Domain\Registry\Repository;
33use App\Domain\Reporting\Handler\WordHandler;
34use App\Domain\User\Dictionary\UserRoleDictionary;
35use App\Domain\User\Model as UserModel;
36use App\Domain\User\Model\User;
37use App\Domain\User\Repository as UserRepository;
38use Doctrine\ORM\EntityManagerInterface;
39use Knp\Snappy\Pdf;
40use Symfony\Component\HttpFoundation\JsonResponse;
41use Symfony\Component\HttpFoundation\Request;
42use Symfony\Component\HttpFoundation\Response;
43use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
44use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
45use Symfony\Component\Routing\RouterInterface;
46use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
47use Symfony\Contracts\Translation\TranslatorInterface;
48use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException;
49
50/**
51 * @property Repository\Contractor $repository
52 */
53class ContractorController extends CRUDController
54{
55    use ServersideDatatablesTrait;
56
57    /**
58     * @var UserRepository\Collectivity
59     */
60    protected $collectivityRepository;
61
62    /**
63     * @var WordHandler
64     */
65    protected $wordHandler;
66
67    /**
68     * @var AuthorizationCheckerInterface
69     */
70    protected $authorizationChecker;
71
72    /**
73     * @var UserProvider
74     */
75    protected $userProvider;
76
77    /**
78     * @var RouterInterface
79     */
80    protected $router;
81
82    public function __construct(
83        EntityManagerInterface $entityManager,
84        TranslatorInterface $translator,
85        Repository\Contractor $repository,
86        UserRepository\Collectivity $collectivityRepository,
87        WordHandler $wordHandler,
88        AuthorizationCheckerInterface $authorizationChecker,
89        UserProvider $userProvider,
90        Pdf $pdf,
91        RouterInterface $router,
92    ) {
93        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
94        $this->collectivityRepository = $collectivityRepository;
95        $this->wordHandler            = $wordHandler;
96        $this->authorizationChecker   = $authorizationChecker;
97        $this->userProvider           = $userProvider;
98        $this->router                 = $router;
99    }
100
101    protected function getDomain(): string
102    {
103        return 'registry';
104    }
105
106    protected function getModel(): string
107    {
108        return 'contractor';
109    }
110
111    protected function getModelClass(): string
112    {
113        return Contractor::class;
114    }
115
116    protected function getFormType(): string
117    {
118        return ContractorType::class;
119    }
120
121    public function listAction(): Response
122    {
123        $category = $this->entityManager->getRepository(Category::class)->findOneBy([
124            'name' => 'Sous-traitant',
125        ]);
126
127        return $this->render($this->getTemplatingBasePath('list'), [
128            'totalItem' => $this->repository->count($this->getRequestCriteria()),
129            'category'  => $category,
130            'route'     => $this->router->generate('registry_contractor_list_datatables'),
131        ]);
132    }
133
134    protected function getListData()
135    {
136        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
137            return $this->repository->findAll();
138        }
139
140        return $this->repository->findAllByCollectivity($this->userProvider->getAuthenticatedUser()->getCollectivity());
141    }
142
143    /**
144     * Generate a word report of contractors.
145     *
146     * @throws \PhpOffice\PhpWord\Exception\Exception
147     */
148    public function reportAction(): Response
149    {
150        $objects = $this->repository->findAllByCollectivity(
151            $this->userProvider->getAuthenticatedUser()->getCollectivity(),
152            ['name' => 'asc']
153        );
154
155        return $this->wordHandler->generateRegistryContractorReport($objects);
156    }
157
158    /**
159     * Get all active treatments of a collectivity and return their id/name as JSON.
160     */
161    public function apiGetContractorsByCollectivity(string $collectivityId): Response
162    {
163        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
164            throw new AccessDeniedHttpException('You can\'t access to a collectivity contractor data');
165        }
166
167        /** @var UserModel\Collectivity|null $collectivity */
168        $collectivity = $this->collectivityRepository->findOneById($collectivityId);
169        if (null === $collectivity) {
170            throw new NotFoundHttpException('Can\'t find collectivity for id ' . $collectivityId);
171        }
172
173        $contractors = $this->repository->findAllByCollectivity(
174            $collectivity,
175            [
176                'name' => 'ASC',
177            ]
178        );
179        $responseData = [];
180
181        /** @var Contractor $contractor */
182        foreach ($contractors as $contractor) {
183            $responseData[] = [
184                'value' => $contractor->getId()->toString(),
185                'text'  => $contractor->__toString(),
186            ];
187        }
188
189        return new JsonResponse($responseData);
190    }
191
192    public function listDataTables(Request $request): JsonResponse
193    {
194        $criteria    = $this->getRequestCriteria();
195        $contractors = $this->getResults($request, $criteria);
196        $reponse     = $this->getBaseDataTablesResponse($request, $contractors, $criteria);
197
198        $yes = '<span class="badge bg-green">' . $this->translator->trans('global.label.yes') . '</span>';
199        $no  = '<span class="badge bg-red">' . $this->translator->trans('global.label.no') . '</span>';
200
201        /** @var Contractor $contractor */
202        foreach ($contractors as $contractor) {
203            $contractorLink = '<a href="' . $this->router->generate('registry_contractor_show', ['id' => $contractor->getId()->toString()]) . '">
204                ' . \htmlspecialchars($contractor->getName()) . '
205            </a>';
206
207            $reponse['data'][] = [
208                'id'                     => $contractor->getId(),
209                'nom'                    => $contractorLink,
210                'collectivite'           => $contractor->getCollectivity()->getName(),
211                'service'                => $contractor->getService() ? $contractor->getService()->getName() : '',
212                'clauses_contractuelles' => $contractor->isContractualClausesVerified() ? $yes : $no,
213                'element_securite'       => $contractor->isAdoptedSecurityFeatures() ? $yes : $no,
214                'registre_traitements'   => $contractor->isMaintainsTreatmentRegister() ? $yes : $no,
215                'donnees_hors_eu'        => $contractor->isSendingDataOutsideEu() ?
216                    '<span class="badge bg-green">' . $this->translator->trans('global.label.yes') . '</span>' :
217                    '<span class="badge bg-red">' . $this->translator->trans('global.label.no') . '</span>',
218                'createdAt' => date_format($contractor->getCreatedAt(), 'd-m-Y H:i'),
219                'updatedAt' => date_format($contractor->getUpdatedAt(), 'd-m-Y H:i'),
220                'actions'   => $this->getActionCellsContent($contractor),
221            ];
222        }
223
224        $jsonResponse = new JsonResponse();
225        $jsonResponse->setJson(\json_encode($reponse));
226
227        return $jsonResponse;
228    }
229
230    private function isContractorInUserServices(Contractor $contractor): bool
231    {
232        $user = $this->userProvider->getAuthenticatedUser();
233
234        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
235            return true;
236        }
237
238        return $contractor->isInUserServices($user);
239    }
240
241    protected function getLabelAndKeysArray(): array
242    {
243        if ($this->isGranted('ROLE_REFERENT')) {
244            return [
245                'nom',
246                'collectivite',
247                'service',
248                'clauses_contractuelles',
249                'element_securite',
250                'registre_traitements',
251                'donnees_hors_eu',
252                'createdAt',
253                'updatedAt',
254                'actions',
255            ];
256        }
257        if ($this->userProvider->getAuthenticatedUser()->hasServices()) {
258            return [
259                'nom',
260                'service',
261                'clauses_contractuelles',
262                'element_securite',
263                'registre_traitements',
264                'donnees_hors_eu',
265                'createdAt',
266                'updatedAt',
267                'actions',
268            ];
269        }
270
271        return [
272            'nom',
273            'clauses_contractuelles',
274            'element_securite',
275            'registre_traitements',
276            'donnees_hors_eu',
277            'createdAt',
278            'updatedAt',
279            'actions',
280        ];
281    }
282
283    private function getRequestCriteria()
284    {
285        $criteria = [];
286        $user     = $this->userProvider->getAuthenticatedUser();
287
288        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) {
289            $criteria['collectivity'] = $user->getCollectivity();
290        }
291
292        if (\in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) {
293            $criteria['collectivity'] = $user->getCollectivitesReferees();
294        }
295
296        return $criteria;
297    }
298
299    private function getActionCellsContent(Contractor $sousTraitant)
300    {
301        $user = $this->userProvider->getAuthenticatedUser();
302        if ($user->getServices()->isEmpty() || $this->isContractorInUserServices($sousTraitant)) {
303            $cellContent = '<a href="' . $this->router->generate('registry_contractor_edit', ['id' => $sousTraitant->getId()]) . '">
304                    <i aria-hidden="true" class="fa fa-pencil"></i> ' .
305                    $this->translator->trans('global.action.edit') .
306                '</a>';
307
308            $cellContent .=
309                '<a href="' . $this->router->generate('registry_contractor_delete', ['id' => $sousTraitant->getId()]) . '">
310                    <i aria-hidden="true" class="fa fa-trash"></i> ' .
311                    $this->translator->trans('global.action.delete') .
312                '</a>';
313
314            return $cellContent;
315        }
316
317        return null;
318    }
319
320    /**
321     * The deletion action
322     * Delete the data.
323     * OVERRIDE of the CRUDController to manage clone id.
324     *
325     * @throws \Exception
326     */
327    public function deleteConfirmationAction(string $id): Response
328    {
329        $object = $this->repository->findOneById($id);
330        if (!$object) {
331            throw new NotFoundHttpException("No object found with ID '{$id}'");
332        }
333
334        /** @var User $user */
335        $user = $this->getUser();
336        if (!$user->hasAccessTo($object)) {
337            return $this->redirectToRoute($this->getRouteName('list'));
338        }
339
340        if ($this->isSoftDelete()) {
341            if (!\method_exists($object, 'setDeletedAt')) {
342                throw new MethodNotImplementedException('setDeletedAt');
343            }
344            $object->setDeletedAt(new \DateTimeImmutable());
345            $this->repository->update($object);
346        } else {
347            /* Delete clonedFrom id from clone to prevent SQL error on foreign key */
348            foreach ($this->repository->findBy(['clonedFrom' => $id]) as $clone) {
349                $clone->setClonedFrom(null);
350            }
351            $this->entityManager->remove($object);
352            $this->entityManager->flush();
353        }
354
355        $this->addFlash('success', $this->getFlashbagMessage('success', 'delete', $object));
356
357        return $this->redirectToRoute($this->getRouteName('list'));
358    }
359}