Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
14.72% |
39 / 265 |
|
36.84% |
7 / 19 |
CRAP | |
0.00% |
0 / 1 |
MesurementController | |
14.72% |
39 / 265 |
|
36.84% |
7 / 19 |
1862.74 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
getDomain | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getModel | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getModelClass | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getFormType | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getListData | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 | |||
listAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
reportAction | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
reportActionPlan | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
6 | |||
actionPlanAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
apiGetMesurementsByCollectivity | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
20 | |||
createFromJsonAction | |
75.00% |
12 / 16 |
|
0.00% |
0 / 1 |
4.25 | |||
showMesurementAction | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
72 | |||
getRequestCriteria | |
70.00% |
7 / 10 |
|
0.00% |
0 / 1 |
4.43 | |||
getLabelAndKeysArray | |
0.00% |
0 / 91 |
|
0.00% |
0 / 1 |
42 | |||
listDataTables | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
56 | |||
generateShowLink | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
generateActionCell | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
12 | |||
deleteConfirmationAction | |
0.00% |
0 / 17 |
|
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 | |
22 | declare(strict_types=1); |
23 | |
24 | namespace App\Domain\Registry\Controller; |
25 | |
26 | use App\Application\Controller\CRUDController; |
27 | use App\Application\Interfaces\CollectivityRelated; |
28 | use App\Application\Symfony\Security\UserProvider; |
29 | use App\Application\Traits\ServersideDatatablesTrait; |
30 | use App\Domain\Documentation\Model\Category; |
31 | use App\Domain\Registry\Dictionary\MesurementPriorityDictionary; |
32 | use App\Domain\Registry\Dictionary\MesurementStatusDictionary; |
33 | use App\Domain\Registry\Form\Type\MesurementType; |
34 | use App\Domain\Registry\Model; |
35 | use App\Domain\Registry\Repository; |
36 | use App\Domain\Reporting\Handler\WordHandler; |
37 | use App\Domain\User\Dictionary\UserRoleDictionary; |
38 | use App\Domain\User\Model as UserModel; |
39 | use App\Domain\User\Model\User; |
40 | use App\Domain\User\Repository as UserRepository; |
41 | use Doctrine\ORM\EntityManagerInterface; |
42 | use Knp\Snappy\Pdf; |
43 | use Symfony\Component\Form\FormFactoryInterface; |
44 | use Symfony\Component\HttpFoundation\JsonResponse; |
45 | use Symfony\Component\HttpFoundation\Request; |
46 | use Symfony\Component\HttpFoundation\RequestStack; |
47 | use Symfony\Component\HttpFoundation\Response; |
48 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
49 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
50 | use Symfony\Component\Routing\RouterInterface; |
51 | use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; |
52 | use Symfony\Contracts\Translation\TranslatorInterface; |
53 | use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException; |
54 | |
55 | /** |
56 | * @property Repository\Mesurement $repository |
57 | */ |
58 | class MesurementController extends CRUDController |
59 | { |
60 | use ServersideDatatablesTrait; |
61 | |
62 | /** |
63 | * @var UserRepository\Collectivity |
64 | */ |
65 | protected $collectivityRepository; |
66 | |
67 | /** |
68 | * @var WordHandler |
69 | */ |
70 | protected $wordHandler; |
71 | |
72 | /** |
73 | * @var AuthorizationCheckerInterface |
74 | */ |
75 | protected $authorizationChecker; |
76 | |
77 | /** |
78 | * @var UserProvider |
79 | */ |
80 | protected $userProvider; |
81 | |
82 | /** |
83 | * @var FormFactoryInterface |
84 | */ |
85 | protected $formFactory; |
86 | |
87 | /** |
88 | * @var RouterInterface |
89 | */ |
90 | protected $router; |
91 | |
92 | /** |
93 | * @var RequestStack |
94 | */ |
95 | private $requestStack; |
96 | |
97 | public function __construct( |
98 | EntityManagerInterface $entityManager, |
99 | TranslatorInterface $translator, |
100 | Repository\Mesurement $repository, |
101 | UserRepository\Collectivity $collectivityRepository, |
102 | WordHandler $wordHandler, |
103 | AuthorizationCheckerInterface $authorizationChecker, |
104 | UserProvider $userProvider, |
105 | FormFactoryInterface $formFactory, |
106 | RouterInterface $router, |
107 | Pdf $pdf, |
108 | RequestStack $requestStack, |
109 | ) { |
110 | parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker); |
111 | $this->collectivityRepository = $collectivityRepository; |
112 | $this->wordHandler = $wordHandler; |
113 | $this->authorizationChecker = $authorizationChecker; |
114 | $this->userProvider = $userProvider; |
115 | $this->formFactory = $formFactory; |
116 | $this->router = $router; |
117 | $this->requestStack = $requestStack; |
118 | } |
119 | |
120 | protected function getDomain(): string |
121 | { |
122 | return 'registry'; |
123 | } |
124 | |
125 | protected function getModel(): string |
126 | { |
127 | return 'mesurement'; |
128 | } |
129 | |
130 | protected function getModelClass(): string |
131 | { |
132 | return Model\Mesurement::class; |
133 | } |
134 | |
135 | protected function getFormType(): string |
136 | { |
137 | return MesurementType::class; |
138 | } |
139 | |
140 | protected function getListData() |
141 | { |
142 | $request = $this->requestStack->getCurrentRequest(); |
143 | $criteria = $this->getRequestCriteria($request); |
144 | |
145 | return $this->repository->findBy($criteria); |
146 | } |
147 | |
148 | public function listAction(): Response |
149 | { |
150 | $request = $this->requestStack->getCurrentRequest(); |
151 | |
152 | $category = $this->entityManager->getRepository(Category::class)->findOneBy([ |
153 | 'name' => 'Action de protection', |
154 | ]); |
155 | |
156 | return $this->render($this->getTemplatingBasePath('list'), [ |
157 | 'totalItem' => $this->repository->count($this->getRequestCriteria($request)), |
158 | 'category' => $category, |
159 | 'route' => $this->router->generate('registry_mesurement_list_datatables'), |
160 | ]); |
161 | } |
162 | |
163 | /** |
164 | * Generate a word report of contractors. |
165 | * |
166 | * @throws \PhpOffice\PhpWord\Exception\Exception |
167 | */ |
168 | public function reportAction(): Response |
169 | { |
170 | $objects = $this->repository->findAllByCollectivity( |
171 | $this->userProvider->getAuthenticatedUser()->getCollectivity(), |
172 | ['name' => 'asc'] |
173 | ); |
174 | |
175 | return $this->wordHandler->generateRegistryMesurementReport($objects); |
176 | } |
177 | |
178 | /** |
179 | * Generate a word report of contractors. |
180 | * |
181 | * @throws \PhpOffice\PhpWord\Exception\Exception |
182 | */ |
183 | public function reportActionPlan(): Response |
184 | { |
185 | $objects = $this->repository->findAllByCollectivity( |
186 | $this->userProvider->getAuthenticatedUser()->getCollectivity(), |
187 | ['name' => 'asc'] |
188 | ); |
189 | |
190 | $filteredObjects = array_filter($objects, function ($object) { |
191 | return null !== $object->getPlanificationDate() && 'not-applicable' !== $object->getStatus(); |
192 | }); |
193 | |
194 | return $this->wordHandler->generateRegistryMesurementReport($filteredObjects, true); |
195 | } |
196 | |
197 | /** |
198 | * Display list of action plan |
199 | * Action plan are mesurement planified which are not yet applied. |
200 | * |
201 | * @return Response |
202 | */ |
203 | public function actionPlanAction() |
204 | { |
205 | $request = $this->requestStack->getCurrentRequest(); |
206 | |
207 | $category = $this->entityManager->getRepository(Category::class)->findOneBy([ |
208 | 'name' => "Plan d'action", |
209 | ]); |
210 | |
211 | return $this->render('Registry/Mesurement/action_plan.html.twig', [ |
212 | 'totalItem' => $this->repository->count($this->getRequestCriteria($request)), |
213 | 'category' => $category, |
214 | 'route' => $this->router->generate('registry_mesurement_list_datatables', ['action_plan' => true]), |
215 | ]); |
216 | } |
217 | |
218 | /** |
219 | * Get all active treatments of a collectivity and return their id/name as JSON. |
220 | */ |
221 | public function apiGetMesurementsByCollectivity(string $collectivityId): Response |
222 | { |
223 | if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { |
224 | throw new AccessDeniedHttpException('You can\'t access to a collectivity mesurement data'); |
225 | } |
226 | |
227 | /** @var UserModel\Collectivity|null $collectivity */ |
228 | $collectivity = $this->collectivityRepository->findOneById($collectivityId); |
229 | if (null === $collectivity) { |
230 | throw new NotFoundHttpException('Can\'t find collectivity for id ' . $collectivityId); |
231 | } |
232 | |
233 | $mesurements = $this->repository->findAllByCollectivity( |
234 | $collectivity, |
235 | [ |
236 | 'name' => 'ASC', |
237 | ] |
238 | ); |
239 | $responseData = []; |
240 | |
241 | /** @var Model\Mesurement $mesurement */ |
242 | foreach ($mesurements as $mesurement) { |
243 | $responseData[] = [ |
244 | 'value' => $mesurement->getId()->toString(), |
245 | 'text' => $mesurement->__toString(), |
246 | ]; |
247 | } |
248 | |
249 | return new JsonResponse($responseData); |
250 | } |
251 | |
252 | /** |
253 | * Route to create an not applied mesurement with the modal. |
254 | * |
255 | * @return JsonResponse |
256 | */ |
257 | public function createFromJsonAction(Request $request) |
258 | { |
259 | $form = $this->formFactory->create($this->getFormType(), null, ['csrf_protection' => false]); |
260 | $form->handleRequest($request); |
261 | |
262 | if (!$form->isSubmitted() || !$form->isValid()) { |
263 | $errors = []; |
264 | foreach ($form->getErrors(true) as $error) { |
265 | $errors[$error->getOrigin()->getName()] = $error->getMessage(); |
266 | } |
267 | |
268 | return new JsonResponse($errors, Response::HTTP_BAD_REQUEST); |
269 | } |
270 | |
271 | /** @var Model\Mesurement $object */ |
272 | $object = $form->getData(); |
273 | $object->setStatus(MesurementStatusDictionary::STATUS_NOT_APPLIED); |
274 | |
275 | $this->entityManager->persist($object); |
276 | $this->entityManager->flush(); |
277 | |
278 | $dataToSerialize = [ |
279 | 'id' => $object->getId()->toString(), |
280 | 'name' => $object->getName(), |
281 | ]; |
282 | |
283 | return new JsonResponse($dataToSerialize, Response::HTTP_CREATED); |
284 | } |
285 | |
286 | public function showMesurementAction(Request $request, string $id): Response |
287 | { |
288 | /* We get the referer to know if we come from MesurementListe or from PlanActionListe to return to the corresponding list */ |
289 | $referer = filter_var($request->headers->get('referer'), FILTER_SANITIZE_URL); |
290 | if (null === $referer) { |
291 | $this->router->generate('registry_mesurement_list'); |
292 | } |
293 | |
294 | $object = $this->repository->findOneById($id); |
295 | if (!$object) { |
296 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
297 | } |
298 | /** @var User $user */ |
299 | $user = $this->getUser(); |
300 | if (!$user->hasAccessTo($object, false)) { |
301 | return $this->redirectToRoute($this->getRouteName('list')); |
302 | } |
303 | |
304 | $actionEnabled = true; |
305 | |
306 | if ($object instanceof CollectivityRelated && !$this->authorizationChecker->isGranted('ROLE_ADMIN') && !$user->getServices()->isEmpty()) { |
307 | $actionEnabled = $object->isInUserServices($this->userProvider->getAuthenticatedUser()); |
308 | } |
309 | |
310 | if (!$this->isGranted('ROLE_USER')) { |
311 | $actionEnabled = false; |
312 | } |
313 | |
314 | return $this->render($this->getTemplatingBasePath('show'), [ |
315 | 'object' => $object, |
316 | 'referer' => $referer, |
317 | 'actionEnabled' => $actionEnabled, |
318 | ]); |
319 | } |
320 | |
321 | private function getRequestCriteria(Request $request) |
322 | { |
323 | $criteria = []; |
324 | $user = $this->userProvider->getAuthenticatedUser(); |
325 | |
326 | if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { |
327 | $criteria['collectivity'] = $user->getCollectivity(); |
328 | } |
329 | |
330 | if (\in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) { |
331 | $criteria['collectivity'] = $user->getCollectivitesReferees(); |
332 | } |
333 | |
334 | if ($request->query->getBoolean('action_plan')) { |
335 | // Since we have to display planified & not-applied mesurement, filter |
336 | $criteria['planificationDate'] = 'null'; |
337 | $criteria['status'] = MesurementStatusDictionary::STATUS_NOT_APPLIED; |
338 | } |
339 | |
340 | return $criteria; |
341 | } |
342 | |
343 | protected function getLabelAndKeysArray(): array |
344 | { |
345 | $request = $this->requestStack->getCurrentRequest(); |
346 | $isActionPlan = $request->query->getBoolean('action_plan'); |
347 | |
348 | if ($isActionPlan) { |
349 | if ($this->isGranted('ROLE_REFERENT')) { |
350 | return [ |
351 | 'nom', |
352 | 'collectivite', |
353 | 'service', |
354 | 'date_planification', |
355 | 'cout', |
356 | 'charge', |
357 | 'priorite', |
358 | 'responsable_action', |
359 | 'description', |
360 | 'observation', |
361 | 'createdAt', |
362 | 'updatedAt', |
363 | 'actions', |
364 | ]; |
365 | } elseif ($this->userProvider->getAuthenticatedUser()->hasServices()) { |
366 | return [ |
367 | 'nom', |
368 | 'service', |
369 | 'date_planification', |
370 | 'cout', |
371 | 'charge', |
372 | 'priorite', |
373 | 'responsable_action', |
374 | 'description', |
375 | 'observation', |
376 | 'createdAt', |
377 | 'updatedAt', |
378 | 'actions', |
379 | ]; |
380 | } |
381 | |
382 | return [ |
383 | 'nom', |
384 | 'date_planification', |
385 | 'cout', |
386 | 'charge', |
387 | 'priorite', |
388 | 'responsable_action', |
389 | 'description', |
390 | 'observation', |
391 | 'createdAt', |
392 | 'updatedAt', |
393 | 'actions', |
394 | ]; |
395 | } |
396 | if ($this->isGranted('ROLE_REFERENT')) { |
397 | return [ |
398 | 'nom', |
399 | 'collectivite', |
400 | 'service', |
401 | 'statut', |
402 | 'cout', |
403 | 'charge', |
404 | 'priorite', |
405 | 'responsable_action', |
406 | 'description', |
407 | 'observation', |
408 | 'createdAt', |
409 | 'updatedAt', |
410 | 'actions', |
411 | ]; |
412 | } elseif ($this->userProvider->getAuthenticatedUser()->hasServices()) { |
413 | return [ |
414 | 'nom', |
415 | 'service', |
416 | 'statut', |
417 | 'cout', |
418 | 'charge', |
419 | 'priorite', |
420 | 'responsable_action', |
421 | 'description', |
422 | 'observation', |
423 | 'createdAt', |
424 | 'updatedAt', |
425 | 'actions', |
426 | ]; |
427 | } |
428 | |
429 | return [ |
430 | 'nom', |
431 | 'statut', |
432 | 'cout', |
433 | 'charge', |
434 | 'priorite', |
435 | 'responsable_action', |
436 | 'description', |
437 | 'observation', |
438 | 'createdAt', |
439 | 'updatedAt', |
440 | 'actions', |
441 | ]; |
442 | } |
443 | |
444 | public function listDataTables(Request $request): JsonResponse |
445 | { |
446 | $criteria = $this->getRequestCriteria($request); |
447 | $actions = $this->getResults($request, $criteria); |
448 | $reponse = $this->getBaseDataTablesResponse($request, $actions, $criteria); |
449 | |
450 | $request = $this->requestStack->getCurrentRequest(); |
451 | $isActionPlan = $request->query->getBoolean('action_plan'); |
452 | |
453 | /** @var Model\Mesurement $action */ |
454 | foreach ($actions as $action) { |
455 | $reponse['data'][] = [ |
456 | 'id' => $action->getId(), |
457 | 'nom' => $this->generateShowLink($action), |
458 | 'collectivite' => $this->authorizationChecker->isGranted('ROLE_REFERENT') && $action->getCollectivity() ? $action->getCollectivity()->getName() : '', |
459 | 'service' => $action->getService() ? $action->getService()->getName() : '', |
460 | 'statut' => MesurementStatusDictionary::getStatus()[$action->getStatus()], |
461 | 'cout' => $action->getCost(), |
462 | 'charge' => $action->getCharge(), |
463 | 'priorite' => !\is_null($action->getPriority()) ? MesurementPriorityDictionary::getPriorities()[$action->getPriority()] : null, |
464 | 'date_planification' => !\is_null($action->getPlanificationDate()) ? \date_format($action->getPlanificationDate(), 'd/m/Y') : null, |
465 | 'responsable_action' => $action->getManager(), |
466 | 'description' => $action->getDescription(), |
467 | 'observation' => $action->getComment(), |
468 | 'createdAt' => \date_format($action->getCreatedAt(), 'd/m/Y H:i'), |
469 | 'updatedAt' => \date_format($action->getUpdatedAt(), 'd/m/Y H:i'), |
470 | 'actions' => $this->generateActionCell($action, $isActionPlan), |
471 | ]; |
472 | } |
473 | |
474 | $jsonResponse = new JsonResponse(); |
475 | $jsonResponse->setJson(json_encode($reponse)); |
476 | |
477 | return $jsonResponse; |
478 | } |
479 | |
480 | private function generateShowLink(Model\Mesurement $mesurement) |
481 | { |
482 | return '<a href="' . |
483 | $this->router->generate('registry_mesurement_show', ['id' => $mesurement->getId()]) . |
484 | '">' . \htmlspecialchars($mesurement->getName()) . '</a>'; |
485 | } |
486 | |
487 | private function generateActionCell(Model\Mesurement $mesurement, bool $isActionPlan = false) |
488 | { |
489 | $user = $this->userProvider->getAuthenticatedUser(); |
490 | if ($user->hasAccessTo($mesurement)) { |
491 | if ($isActionPlan) { |
492 | return '<a href="' . |
493 | $this->router->generate('registry_mesurement_edit', ['id' => $mesurement->getId()]) . '"> |
494 | <i aria-hidden="true" class="fa fa-pencil"></i> ' . |
495 | $this->translator->trans('global.action.edit') |
496 | . '</a>'; |
497 | } |
498 | |
499 | return '<a href="' . |
500 | $this->router->generate('registry_mesurement_edit', ['id' => $mesurement->getId()]) . '"> |
501 | <i aria-hidden="true" class="fa fa-pencil"></i> ' . |
502 | $this->translator->trans('global.action.edit') |
503 | . '</a> |
504 | |
505 | <a href="' . |
506 | $this->router->generate('registry_mesurement_delete', ['id' => $mesurement->getId()]) . |
507 | '"><i aria-hidden="true" class="fa fa-trash"></i> ' . |
508 | $this->translator->trans('global.action.delete') |
509 | . '</a>'; |
510 | } |
511 | |
512 | return ''; |
513 | } |
514 | |
515 | /** |
516 | * The deletion action |
517 | * Delete the data. |
518 | * OVERRIDE of the CRUDController to manage clone id. |
519 | * |
520 | * @throws \Exception |
521 | */ |
522 | public function deleteConfirmationAction(string $id): Response |
523 | { |
524 | $object = $this->repository->findOneById($id); |
525 | if (!$object) { |
526 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
527 | } |
528 | |
529 | /** @var User $user */ |
530 | $user = $this->getUser(); |
531 | if (!$user->hasAccessTo($object)) { |
532 | return $this->redirectToRoute($this->getRouteName('list')); |
533 | } |
534 | |
535 | if ($this->isSoftDelete()) { |
536 | if (!\method_exists($object, 'setDeletedAt')) { |
537 | throw new MethodNotImplementedException('setDeletedAt'); |
538 | } |
539 | $object->setDeletedAt(new \DateTimeImmutable()); |
540 | $this->repository->update($object); |
541 | } else { |
542 | /* Delete clonedFrom id from clone to prevent SQL error on foreign key */ |
543 | foreach ($this->repository->findBy(['clonedFrom' => $id]) as $clone) { |
544 | $clone->setClonedFrom(null); |
545 | } |
546 | $this->entityManager->remove($object); |
547 | $this->entityManager->flush(); |
548 | } |
549 | |
550 | $this->addFlash('success', $this->getFlashbagMessage('success', 'delete', $object)); |
551 | |
552 | return $this->redirectToRoute($this->getRouteName('list')); |
553 | } |
554 | } |