Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
5.45% |
11 / 202 |
|
27.78% |
5 / 18 |
CRAP | |
0.00% |
0 / 1 |
ProofController | |
5.45% |
11 / 202 |
|
27.78% |
5 / 18 |
2519.09 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
7 / 7 |
|
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 | |||
formPrePersistData | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
archiveAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
archiveConfirmationAction | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
12 | |||
unarchiveAction | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
unarchiveConfirmationAction | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 | |||
deleteConfirmationAction | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
downloadAction | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
12 | |||
downloadAll | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
110 | |||
listAction | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
listDataTables | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
42 | |||
getLabelAndKeysArray | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
12 | |||
getRequestCriteria | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
getActionCellsContent | |
0.00% |
0 / 25 |
|
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 | |
22 | declare(strict_types=1); |
23 | |
24 | namespace App\Domain\Registry\Controller; |
25 | |
26 | use App\Application\Controller\CRUDController; |
27 | use App\Application\Symfony\Security\UserProvider; |
28 | use App\Application\Traits\ServersideDatatablesTrait; |
29 | use App\Domain\Documentation\Model\Category; |
30 | use App\Domain\Registry\Dictionary\ProofTypeDictionary; |
31 | use App\Domain\Registry\Form\Type\ProofType; |
32 | use App\Domain\Registry\Model; |
33 | use App\Domain\Registry\Repository; |
34 | use App\Domain\Reporting\Handler\WordHandler; |
35 | use App\Domain\Tools\ChainManipulator; |
36 | use App\Domain\User\Dictionary\UserRoleDictionary; |
37 | use App\Domain\User\Model\User; |
38 | use Doctrine\ORM\EntityManagerInterface; |
39 | use Gaufrette\FilesystemInterface; |
40 | use Knp\Snappy\Pdf; |
41 | use PhpOffice\PhpWord\Shared\ZipArchive; |
42 | use Ramsey\Uuid\Uuid; |
43 | use Symfony\Component\HttpFoundation\BinaryFileResponse; |
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\HttpFoundation\ResponseHeaderBag; |
49 | use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; |
50 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
51 | use Symfony\Component\Routing\RouterInterface; |
52 | use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; |
53 | use Symfony\Contracts\Translation\TranslatorInterface; |
54 | use Symfony\Polyfill\Intl\Icu\Exception\MethodNotImplementedException; |
55 | |
56 | /** |
57 | * @property Repository\Proof $repository |
58 | */ |
59 | class ProofController extends CRUDController |
60 | { |
61 | use ServersideDatatablesTrait; |
62 | |
63 | /** |
64 | * @var RequestStack |
65 | */ |
66 | protected $requestStack; |
67 | |
68 | /** |
69 | * @var WordHandler |
70 | */ |
71 | protected $wordHandler; |
72 | |
73 | /** |
74 | * @var AuthorizationCheckerInterface |
75 | */ |
76 | protected $authorizationChecker; |
77 | |
78 | /** |
79 | * @var UserProvider |
80 | */ |
81 | protected $userProvider; |
82 | |
83 | /** |
84 | * @var FilesystemInterface |
85 | */ |
86 | protected $documentFilesystem; |
87 | |
88 | /** |
89 | * @var RouterInterface |
90 | */ |
91 | protected $router; |
92 | |
93 | public function __construct( |
94 | EntityManagerInterface $entityManager, |
95 | TranslatorInterface $translator, |
96 | Repository\Proof $repository, |
97 | RequestStack $requestStack, |
98 | WordHandler $wordHandler, |
99 | AuthorizationCheckerInterface $authorizationChecker, |
100 | UserProvider $userProvider, |
101 | FilesystemInterface $documentFilesystem, |
102 | Pdf $pdf, |
103 | RouterInterface $router, |
104 | ) { |
105 | parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker); |
106 | $this->requestStack = $requestStack; |
107 | $this->wordHandler = $wordHandler; |
108 | $this->authorizationChecker = $authorizationChecker; |
109 | $this->userProvider = $userProvider; |
110 | $this->documentFilesystem = $documentFilesystem; |
111 | $this->router = $router; |
112 | } |
113 | |
114 | protected function getDomain(): string |
115 | { |
116 | return 'registry'; |
117 | } |
118 | |
119 | protected function getModel(): string |
120 | { |
121 | return 'proof'; |
122 | } |
123 | |
124 | protected function getModelClass(): string |
125 | { |
126 | return Model\Proof::class; |
127 | } |
128 | |
129 | protected function getFormType(): string |
130 | { |
131 | return ProofType::class; |
132 | } |
133 | |
134 | /** |
135 | * {@inheritdoc} |
136 | * - Upload documentFile before object persistence in database. |
137 | * |
138 | * @throws \Exception |
139 | */ |
140 | public function formPrePersistData($object, $form = null) |
141 | { |
142 | if (!$object instanceof Model\Proof) { |
143 | throw new \RuntimeException('You must persist a ' . Model\Proof::class . ' object class with your form'); |
144 | } |
145 | |
146 | $file = $object->getDocumentFile(); |
147 | |
148 | if ($file) { |
149 | $filename = Uuid::uuid4()->toString() . '.' . $file->getClientOriginalExtension(); |
150 | $this->documentFilesystem->write($filename, \fopen($file->getRealPath(), 'r')); |
151 | $object->setDocument($filename); |
152 | $object->setDocumentFile(null); |
153 | } |
154 | } |
155 | |
156 | /** |
157 | * The archive action view |
158 | * Display a confirmation message to confirm data archivage. |
159 | */ |
160 | public function archiveAction(string $id): Response |
161 | { |
162 | /** @var Model\Proof $object */ |
163 | $object = $this->repository->findOneById($id); |
164 | if (!$object) { |
165 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
166 | } |
167 | |
168 | /** @var User $user */ |
169 | $user = $this->getUser(); |
170 | if (!$user->hasAccessTo($object)) { |
171 | return $this->redirectToRoute($this->getRouteName('list')); |
172 | } |
173 | |
174 | return $this->render($this->getTemplatingBasePath('archive'), [ |
175 | 'object' => $object, |
176 | ]); |
177 | } |
178 | |
179 | /** |
180 | * The archive action |
181 | * Archive the data. |
182 | * |
183 | * @throws \Exception |
184 | */ |
185 | public function archiveConfirmationAction(string $id): Response |
186 | { |
187 | /** @var Model\Proof|null $object */ |
188 | $object = $this->repository->findOneById($id); |
189 | if (!$object) { |
190 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
191 | } |
192 | |
193 | /** @var User $user */ |
194 | $user = $this->getUser(); |
195 | if (!$user->hasAccessTo($object)) { |
196 | return $this->redirectToRoute($this->getRouteName('list')); |
197 | } |
198 | |
199 | $object->setDeletedAt(new \DateTimeImmutable()); |
200 | $this->repository->update($object); |
201 | |
202 | $this->addFlash('success', $this->getFlashbagMessage('success', 'archive', $object)); |
203 | |
204 | return $this->redirectToRoute($this->getRouteName('list')); |
205 | } |
206 | |
207 | /** |
208 | * The archive action view |
209 | * Display a confirmation message to confirm data archivage. |
210 | */ |
211 | public function unarchiveAction(string $id): Response |
212 | { |
213 | $object = $this->repository->findOneById($id); |
214 | if (!$object) { |
215 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
216 | } |
217 | |
218 | return $this->render($this->getTemplatingBasePath('unarchive'), [ |
219 | 'object' => $object, |
220 | ]); |
221 | } |
222 | |
223 | /** |
224 | * The archive action |
225 | * Archive the data. |
226 | * |
227 | * @throws \Exception |
228 | */ |
229 | public function unarchiveConfirmationAction(string $id): Response |
230 | { |
231 | /** @var Model\Proof|null $object */ |
232 | $object = $this->repository->findOneById($id); |
233 | if (!$object) { |
234 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
235 | } |
236 | |
237 | $object->setDeletedAt(null); |
238 | $this->repository->update($object); |
239 | |
240 | $this->addFlash('success', $this->getFlashbagMessage('success', 'unarchive', $object)); |
241 | |
242 | return $this->redirectToRoute($this->getRouteName('list')); |
243 | } |
244 | |
245 | /** |
246 | * {@inheritdoc} |
247 | * OVERRIDE METHOD. |
248 | * Override deletion in order to delete Proof into server. |
249 | */ |
250 | public function deleteConfirmationAction(string $id): Response |
251 | { |
252 | /** @var Model\Proof|null $object */ |
253 | $object = $this->repository->findOneById($id); |
254 | if (!$object) { |
255 | throw new NotFoundHttpException("No object found with ID '{$id}'"); |
256 | } |
257 | |
258 | /** @var User $user */ |
259 | $user = $this->getUser(); |
260 | if (!$user->hasAccessTo($object)) { |
261 | return $this->redirectToRoute($this->getRouteName('list')); |
262 | } |
263 | |
264 | if ($this->isSoftDelete()) { |
265 | if (!\method_exists($object, 'setDeletedAt')) { |
266 | throw new MethodNotImplementedException('setDeletedAt'); |
267 | } |
268 | $object->setDeletedAt(new \DateTimeImmutable()); |
269 | $this->repository->update($object); |
270 | } else { |
271 | $filename = $object->getDocument(); |
272 | |
273 | $this->entityManager->remove($object); |
274 | $this->entityManager->flush(); |
275 | |
276 | // TODO: Log error if deletion fail |
277 | $this->documentFilesystem->delete($filename); |
278 | } |
279 | |
280 | $this->addFlash('success', $this->getFlashbagMessage('success', 'delete', $object)); |
281 | |
282 | return $this->redirectToRoute($this->getRouteName('list')); |
283 | } |
284 | |
285 | /** |
286 | * Download uploaded document which belongs to provided object id. |
287 | * |
288 | * @throws \Exception |
289 | */ |
290 | public function downloadAction(string $id): Response |
291 | { |
292 | /** @var Model\Proof|null $object */ |
293 | $object = $this->repository->findOneById($id); |
294 | |
295 | if (!$object) { |
296 | throw new NotFoundHttpException("No object exists with id '{$id}'"); |
297 | } |
298 | /** @var User $user */ |
299 | $user = $this->getUser(); |
300 | |
301 | // Can only download if we belong to the collectivity or if we are admin |
302 | if (!$user->hasAccessTo($object, false)) { |
303 | throw new AccessDeniedHttpException('Vous ne pouvez pas télécharger cette preuve'); |
304 | } |
305 | |
306 | $extension = \pathinfo($object->getDocument(), PATHINFO_EXTENSION); |
307 | $response = new BinaryFileResponse('gaufrette://registry_proof_document/' . $object->getDocument()); |
308 | |
309 | $filenameWithoutAccent = ChainManipulator::removeAccents($object->getName()); |
310 | $filename = ChainManipulator::removeAllNonAlphaNumericChar($filenameWithoutAccent); |
311 | |
312 | $response->setContentDisposition( |
313 | ResponseHeaderBag::DISPOSITION_ATTACHMENT, |
314 | "{$filename}.{$extension}" |
315 | ); |
316 | |
317 | return $response; |
318 | } |
319 | |
320 | public function downloadAll() |
321 | { |
322 | $objects = []; |
323 | if ('ROLE_ADMIN' === $this->userProvider->getAuthenticatedUser()->getRoles()[0]) { |
324 | $objects = $this->repository->findAll(); |
325 | } |
326 | |
327 | if ('ROLE_REFERENT' === $this->userProvider->getAuthenticatedUser()->getRoles()[0]) { |
328 | $collectivities = \iterable_to_array($this->userProvider->getAuthenticatedUser()->getCollectivitesReferees()); |
329 | |
330 | foreach ($collectivities as $collectivity) { |
331 | $objects = array_merge($objects, $this->repository->findAllByCollectivity($collectivity)); |
332 | } |
333 | } |
334 | |
335 | if (('ROLE_USER' == $this->userProvider->getAuthenticatedUser()->getRoles()[0]) || ('ROLE_PREVIEW' == $this->userProvider->getAuthenticatedUser()->getRoles()[0])) { |
336 | $collectivity = $this->userProvider->getAuthenticatedUser()->getCollectivity(); |
337 | $objects = $this->repository->findAllByCollectivity($collectivity); |
338 | } |
339 | |
340 | $files = []; |
341 | foreach ($objects as $object) { |
342 | /** @var Model\Proof|null $object */ |
343 | if (!$object->getDeletedAt()) { |
344 | $fileName = str_replace(' ', '_', $object->getName()) . '-' . $object->getDocument(); |
345 | $files[] = [$object->getDocument(), $fileName]; |
346 | } |
347 | } |
348 | $zip = new ZipArchive(); |
349 | |
350 | $dir = $this->getParameter('kernel.project_dir') . '/public/uploads/registry/proof/zip/'; |
351 | |
352 | if (!is_dir($dir)) { |
353 | mkdir($dir, 0777, true); |
354 | } |
355 | |
356 | $filename = $dir . 'test.zip'; |
357 | |
358 | $zip->open($filename, ZipArchive::CREATE | ZipArchive::OVERWRITE); |
359 | |
360 | foreach ($files as $file) { |
361 | $zip->addFile('./uploads/registry/proof/document/' . $file[0], $file[1]); |
362 | } |
363 | |
364 | $zip->close(); |
365 | |
366 | $date = date('dmY'); |
367 | $response = new Response(file_get_contents($filename)); |
368 | $response->headers->set('Content-Type', 'application/zip'); |
369 | $response->headers->set('Content-Disposition', 'attachment;filename="Documents' . $date . '.zip"'); |
370 | $response->headers->set('Content-length', filesize($filename)); |
371 | |
372 | return $response; |
373 | } |
374 | |
375 | public function listAction(): Response |
376 | { |
377 | $criteria = $this->getRequestCriteria(); |
378 | |
379 | $category = $this->entityManager->getRepository(Category::class)->findOneBy([ |
380 | 'name' => 'Preuves', |
381 | ]); |
382 | |
383 | return $this->render($this->getTemplatingBasePath('list'), [ |
384 | 'totalItem' => $this->repository->count($criteria), |
385 | 'category' => $category, |
386 | 'route' => $this->router->generate('registry_proof_list_datatables', ['archive' => $criteria['archive']]), |
387 | ]); |
388 | } |
389 | |
390 | public function listDataTables(Request $request): JsonResponse |
391 | { |
392 | $criteria = $this->getRequestCriteria(); |
393 | $users = $this->getResults($request, $criteria); |
394 | $reponse = $this->getBaseDataTablesResponse($request, $users, $criteria); |
395 | |
396 | /** @var Model\Proof $proof */ |
397 | foreach ($users as $proof) { |
398 | $reponse['data'][] = [ |
399 | 'nom' => $proof->getName(), |
400 | 'collectivite' => $this->isGranted('ROLE_REFERENT') && $proof->getCollectivity() ? $proof->getCollectivity()->getName() : '', |
401 | 'service' => $proof->getService() ? $proof->getService()->getName() : '', |
402 | 'type' => !\is_null($proof->getType()) ? ProofTypeDictionary::getTypes()[$proof->getType()] : null, |
403 | 'commentaire' => $proof->getComment(), |
404 | 'date' => \date_format($proof->getCreatedAt(), 'd/m/Y H:i'), |
405 | 'updatedAt' => \date_format($proof->getUpdatedAt(), 'd/m/Y H:i'), |
406 | 'actions' => $this->getActionCellsContent($proof), |
407 | ]; |
408 | } |
409 | |
410 | $jsonResponse = new JsonResponse(); |
411 | $jsonResponse->setJson(\json_encode($reponse)); |
412 | |
413 | return $jsonResponse; |
414 | } |
415 | |
416 | protected function getLabelAndKeysArray(): array |
417 | { |
418 | if ($this->isGranted('ROLE_REFERENT')) { |
419 | return [ |
420 | 'nom', |
421 | 'collectivite', |
422 | 'service', |
423 | 'type', |
424 | 'commentaire', |
425 | 'date', |
426 | 'updatedAt', |
427 | 'actions', |
428 | ]; |
429 | } |
430 | if ($this->userProvider->getAuthenticatedUser()->hasServices()) { |
431 | return [ |
432 | 'nom', |
433 | 'service', |
434 | 'type', |
435 | 'commentaire', |
436 | 'date', |
437 | 'updatedAt', |
438 | 'actions', |
439 | ]; |
440 | } |
441 | |
442 | return [ |
443 | 'nom', |
444 | 'type', |
445 | 'commentaire', |
446 | 'date', |
447 | 'updatedAt', |
448 | 'actions', |
449 | ]; |
450 | } |
451 | |
452 | private function getRequestCriteria() |
453 | { |
454 | $criteria = []; |
455 | $request = $this->requestStack->getMasterRequest(); |
456 | $criteria['archive'] = $request->query->getBoolean('archive'); |
457 | $user = $this->userProvider->getAuthenticatedUser(); |
458 | |
459 | if (!$this->authorizationChecker->isGranted('ROLE_ADMIN')) { |
460 | $criteria['collectivity'] = $user->getCollectivity(); |
461 | } |
462 | |
463 | if (\in_array(UserRoleDictionary::ROLE_REFERENT, $user->getRoles())) { |
464 | $criteria['collectivity'] = $user->getCollectivitesReferees(); |
465 | } |
466 | |
467 | return $criteria; |
468 | } |
469 | |
470 | private function getActionCellsContent(Model\Proof $proof) |
471 | { |
472 | $cellContent = ''; |
473 | $user = $this->userProvider->getAuthenticatedUser(); |
474 | if ($this->authorizationChecker->isGranted('ROLE_PREVIEW')) { |
475 | $cellContent .= '<a href="' . $this->router->generate('registry_proof_download', ['id' => $proof->getId()]) . '"> |
476 | <i aria-hidden="true" class="fa fa-download"></i> ' . |
477 | $this->translator->trans('global.action.download') . ' |
478 | </a>'; |
479 | } |
480 | |
481 | if ($this->authorizationChecker->isGranted('ROLE_USER') && $user->hasAccessTo($proof)) { |
482 | if (\is_null($proof->getDeletedAt())) { |
483 | $cellContent .= '<a href="' . $this->router->generate('registry_proof_edit', ['id' => $proof->getId()]) . '"> |
484 | <i aria-hidden="true" class="fa fa-pencil"></i> ' . |
485 | $this->translator->trans('global.action.edit') . ' |
486 | </a> |
487 | <a href="' . $this->router->generate('registry_proof_archive', ['id' => $proof->getId()]) . '"> |
488 | <i aria-hidden="true" class="fa fa-archive"></i> ' . |
489 | $this->translator->trans('global.action.archive') . ' |
490 | </a>'; |
491 | } else { |
492 | $cellContent .= '<a href="' . $this->router->generate('registry_proof_unarchive', ['id' => $proof->getId()]) . '"> |
493 | <i aria-hidden="true" class="fa fa-archive"></i> ' . |
494 | $this->translator->trans('global.action.unarchive') . ' |
495 | </a>'; |
496 | } |
497 | $cellContent .= '<a href="' . $this->router->generate('registry_proof_delete', ['id' => $proof->getId()]) . '"> |
498 | <i aria-hidden="true" class="fa fa-trash"></i> ' . |
499 | $this->translator->trans('global.action.delete') . ' |
500 | </a>'; |
501 | } |
502 | |
503 | return $cellContent; |
504 | } |
505 | } |