Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
6.88% covered (danger)
6.88%
15 / 218
35.29% covered (danger)
35.29%
6 / 17
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReferentielController
6.88% covered (danger)
6.88%
15 / 218
35.29% covered (danger)
35.29%
6 / 17
1680.10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
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
 createAction
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 editAction
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 duplicateAction
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
42
 formPrePersistData
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
110
 listAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 listDataTables
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 generateActionCellContent
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 getLabelAndKeysArray
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 rightsAction
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
20
 exportAction
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 importAction
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
20
 formatToFileCompliant
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
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\Maturity\Controller;
25
26use App\Application\Controller\CRUDController;
27use App\Application\Symfony\Security\UserProvider;
28use App\Application\Traits\ServersideDatatablesTrait;
29use App\Domain\Maturity\Form\Type\ImportModeleType;
30use App\Domain\Maturity\Form\Type\ModeleReferentielRightsType;
31use App\Domain\Maturity\Form\Type\ReferentielType;
32use App\Domain\Maturity\Model;
33use App\Domain\Maturity\Repository;
34use Doctrine\ORM\EntityManagerInterface;
35use JMS\Serializer\SerializerBuilder;
36use Knp\Snappy\Pdf;
37use Symfony\Component\HttpFoundation\JsonResponse;
38use Symfony\Component\HttpFoundation\Request;
39use Symfony\Component\HttpFoundation\Response;
40use Symfony\Component\HttpFoundation\ResponseHeaderBag;
41use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
42use Symfony\Component\Routing\RouterInterface;
43use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
44use Symfony\Contracts\Translation\TranslatorInterface;
45
46/**
47 * @property Repository\Referentiel $repository
48 */
49class ReferentielController extends CRUDController
50{
51    use ServersideDatatablesTrait;
52
53    /**
54     * @var AuthorizationCheckerInterface
55     */
56    protected $authorizationChecker;
57
58    /**
59     * @var UserProvider
60     */
61    protected $userProvider;
62
63    protected RouterInterface $router;
64
65    public function __construct(
66        EntityManagerInterface $entityManager,
67        TranslatorInterface $translator,
68        Repository\Referentiel $repository,
69        AuthorizationCheckerInterface $authorizationChecker,
70        UserProvider $userProvider,
71        RouterInterface $router,
72        Pdf $pdf,
73    ) {
74        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
75        $this->authorizationChecker = $authorizationChecker;
76        $this->userProvider         = $userProvider;
77        $this->router               = $router;
78    }
79
80    protected function getDomain(): string
81    {
82        return 'maturity';
83    }
84
85    protected function getModel(): string
86    {
87        return 'referentiel';
88    }
89
90    protected function getModelClass(): string
91    {
92        return Model\Referentiel::class;
93    }
94
95    protected function getFormType(): string
96    {
97        return ReferentielType::class;
98    }
99
100    /**
101     * {@inheritdoc}
102     * Override method in order to hydrate survey answers.
103     */
104    public function createAction(Request $request): Response
105    {
106        $modelClass = $this->getModelClass();
107        /** @var Model\Referentiel $object */
108        $object = new $modelClass();
109
110        $form = $this->createForm($this->getFormType(), $object);
111
112        $form->handleRequest($request);
113        if ($form->isSubmitted() && $form->isValid()) {
114            $this->formPrePersistData($object);
115            $this->entityManager->persist($object);
116            $this->entityManager->flush();
117
118            $this->addFlash('success', $this->getFlashbagMessage('success', 'create', $object->getName()));
119
120            return $this->redirectToRoute($this->getRouteName('list'));
121        }
122
123        return $this->render($this->getTemplatingBasePath('create'), [
124            'form' => $form->createView(),
125        ]);
126    }
127
128    public function editAction(Request $request, string $id): Response
129    {
130        /** @var Model\Referentiel $object */
131        $object = $this->repository->findOneById($id);
132        if (!$object) {
133            throw new NotFoundHttpException("No object found with ID '{$id}'");
134        }
135
136        $sections = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]);
137
138        $form = $this->createForm($this->getFormType(), $object, ['validation_groups' => ['default', $this->getModel(), 'edit']]);
139
140        $form->handleRequest($request);
141        if ($form->isSubmitted() && $form->isValid()) {
142            $this->formPrePersistData($object);
143            $this->entityManager->persist($object);
144            $this->entityManager->flush();
145
146            $this->addFlash('success', $this->getFlashbagMessage('success', 'edit', $object));
147
148            return $this->redirectToRoute($this->getRouteName('list'));
149        }
150
151        return $this->render($this->getTemplatingBasePath('edit'), [
152            'form' => $form->createView(),
153        ]);
154    }
155
156    public function duplicateAction(Request $request, string $id): Response
157    {
158        /** @var Model\Referentiel $object */
159        $object = $this->repository->findOneById($id);
160        if (!$object) {
161            throw new NotFoundHttpException("No object found with ID '{$id}'");
162        }
163
164        $newRef = new Model\Referentiel();
165
166        $newRef->setName($object->getName());
167        $newRef->setDescription($object->getDescription());
168
169        $newRef->setAuthorizedCollectivities($object->getAuthorizedCollectivities());
170
171        $newRef->setAuthorizedCollectivityTypes($object->getAuthorizedCollectivityTypes());
172        $newRef->setOptionRightSelection($newRef->getOptionRightSelection());
173
174        /** @var Model\Domain $domain */
175        foreach ($object->getDomains() as $domain) {
176            $d = new Model\Domain();
177            $d->setName($domain->getName());
178            $d->setDescription($domain->getDescription());
179            $d->setReferentiel($newRef);
180            $d->setColor($domain->getColor());
181            $d->setPosition($domain->getPosition());
182
183            $this->entityManager->persist($d);
184            /** @var Model\Question $q */
185            foreach ($domain->getQuestions() as $q) {
186                $newQ = new Model\Question();
187                $newQ->setPosition($q->getPosition());
188                $newQ->setName($q->getName());
189                $newQ->setOptional($q->getOptional());
190                if ($q->getOptional()) {
191                    $newQ->setOptionReason($q->getOptionReason());
192                }
193                $newQ->setWeight($q->getWeight());
194                $newQ->setDomain($d);
195                $this->entityManager->persist($newQ);
196                /** @var Model\Answer $a */
197                foreach ($q->getAnswers() as $a) {
198                    $newA = new Model\Answer();
199                    $newA->setName($a->getName());
200                    $newA->setPosition($a->getPosition());
201                    $newA->setRecommendation($a->getRecommendation());
202                    $newA->setResponse($a->getResponse());
203                    $newA->setQuestion($newQ);
204                    $this->entityManager->persist($newA);
205                }
206            }
207        }
208
209        $this->entityManager->persist($newRef);
210        $this->entityManager->flush();
211
212        $this->addFlash('success', $this->getFlashbagMessage('success', 'duplicate', $object));
213
214        return $this->redirectToRoute($this->getRouteName('edit'), ['id' => $newRef->getId()->toString()]);
215    }
216
217    public function formPrePersistData($object, $form = null)
218    {
219        $domains = [];
220
221        $colors = [
222            'info',
223            'success',
224            'primary',
225            'warning',
226        ];
227
228        // get all existing domains
229        $toRemove = $this->entityManager->getRepository(Model\Domain::class)->findBy(['referentiel' => $object]);
230
231        foreach ($object->getDomains() as $k => $domain) {
232            $key = array_search($domain, $toRemove);
233            if (false !== $key) {
234                unset($toRemove[$key]);
235            }
236            /* @var Model\Domain $domain */
237            // $domain->setPosition($k);
238            $domain->setColor($colors[$k % 4]);
239
240            // get all existing questions
241            $toRemoveQuestions = $this->entityManager->getRepository(Model\Question::class)->findBy(['domain' => $domain]);
242
243            $questions = [];
244            foreach ($domain->getQuestions() as $n => $question) {
245                /** @var Model\Question $question */
246                $key = array_search($question, $toRemoveQuestions);
247                if (false !== $key) {
248                    unset($toRemoveQuestions[$key]);
249                }
250
251                // $question->setPosition($n);
252                $question->setDomain($domain);
253                // get all existing Answers
254                $toRemoveAnswers = $this->entityManager->getRepository(Model\Answer::class)->findBy(['question' => $question]);
255                $answers         = [];
256                foreach ($question->getAnswers() as $l => $answer) {
257                    /** @var Model\Answer $answer */
258                    $key = array_search($answer, $toRemoveAnswers);
259                    if (false !== $key) {
260                        unset($toRemoveAnswers[$key]);
261                    }
262                    // $answer->setPosition($l);
263                    $answer->setQuestion($question);
264                    $answers[] = $answer;
265                }
266
267                foreach ($toRemoveAnswers as $removing) {
268                    $this->entityManager->remove($removing);
269                }
270
271                $question->setAnswers($answers);
272
273                $questions[] = $question;
274            }
275            foreach ($toRemoveQuestions as $removing) {
276                $this->entityManager->remove($removing);
277            }
278            $domain->setQuestions($questions);
279            $domain->setReferentiel($object);
280
281            $domains[] = $domain;
282        }
283        // Remove deleted domains
284        foreach ($toRemove as $removing) {
285            $this->entityManager->remove($removing);
286        }
287
288        $object->setDomains($domains);
289    }
290
291    /**
292     * The list action view
293     * Get data & display them.
294     */
295    public function listAction(): Response
296    {
297        return $this->render('Maturity/Referentiel/list.html.twig', [
298            'totalItem' => $this->repository->count(),
299            'route'     => $this->router->generate('maturity_referentiel_list_datatables'),
300        ]);
301    }
302
303    public function listDataTables(Request $request): JsonResponse
304    {
305        $referentiels = $this->getResults($request);
306        $reponse      = $this->getBaseDataTablesResponse($request, $referentiels);
307
308        foreach ($referentiels as $referentiel) {
309            $reponse['data'][] = [
310                'name'        => $referentiel->getName(),
311                'description' => $referentiel->getDescription(),
312                'createdAt'   => date_format($referentiel->getCreatedAt(), 'd-m-Y H:i'),
313                'updatedAt'   => date_format($referentiel->getUpdatedAt(), 'd-m-Y H:i'),
314                'actions'     => $this->generateActionCellContent($referentiel),
315            ];
316        }
317
318        $reponse['recordsTotal']    = count($reponse['data']);
319        $reponse['recordsFiltered'] = count($reponse['data']);
320
321        return new JsonResponse($reponse);
322    }
323
324    private function generateActionCellContent(Model\Referentiel $referentiel)
325    {
326        $id                  = $referentiel->getId();
327        $htmltoReturnIfAdmin = '';
328
329        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
330            $htmltoReturnIfAdmin = '<a href="' . $this->router->generate('maturity_referentiel_rights', ['id' => $id]) . '">
331                <i aria-hidden="true" class="fa fa-user-shield"></i> '
332                . $this->translator->trans('global.action.rights') .
333                '</a>';
334        }
335
336        return
337            '<a href="' . $this->router->generate('maturity_referentiel_edit', ['id' => $id]) . '">
338                <i aria-hidden="true" class="fa fa-pencil"></i> '
339            . $this->translator->trans('global.action.edit') .
340            '</a>'
341            . $htmltoReturnIfAdmin .
342            '<a href="' . $this->router->generate('maturity_referentiel_duplicate', ['id' => $id]) . '">
343                <i aria-hidden="true" class="fa fa-clone"></i> ' .
344            $this->translator->trans('global.action.duplicate') .
345            '</a>' .
346            '<a href="' . $this->router->generate('maturity_referentiel_export', ['id' => $id]) . '">
347                <i aria-hidden="true" class="fa fa-file-code"></i> ' .
348            $this->translator->trans('global.action.export') .
349            '</a>' .
350            '<a href="' . $this->router->generate('maturity_referentiel_delete', ['id' => $id]) . '">
351                <i aria-hidden="true" class="fa fa-trash"></i> ' .
352            $this->translator->trans('global.action.delete') .
353            '</a>';
354    }
355
356    protected function getLabelAndKeysArray(): array
357    {
358        return [
359            '0' => 'name',
360            '1' => 'description',
361            '2' => 'createdAt',
362            '3' => 'updatedAt',
363            '4' => 'actions',
364        ];
365    }
366
367    public function rightsAction(Request $request, string $id): Response
368    {
369        $object = $this->repository->findOneById($id);
370        if (!$object) {
371            throw new NotFoundHttpException("No object found with ID '{$id}'");
372        }
373        $form = $this->createForm(ModeleReferentielRightsType::class, $object);
374
375        $form->handleRequest($request);
376
377        if ($form->isSubmitted() && $form->isValid()) {
378            $this->formPrePersistData($object);
379            $this->entityManager->persist($object);
380            $this->entityManager->flush();
381
382            $this->addFlash('success', $this->getFlashbagMessage('success', 'rights', $object));
383
384            return $this->redirectToRoute($this->getRouteName('list'));
385        }
386
387        return $this->render('Maturity/Referentiel/rights.html.twig', [
388            'form' => $form->createView(),
389        ]);
390    }
391
392    public function exportAction(string $id)
393    {
394        $object = $this->repository->findOneById($id);
395        if (!$object) {
396            throw new NotFoundHttpException("No object found with ID '{$id}'");
397        }
398        /** @var Model\Referentiel $toExport */
399        $toExport = clone $object;
400
401        $serializer = SerializerBuilder::create()->build();
402        $xml        = $serializer->serialize($toExport, 'xml');
403
404        $response    = new Response($xml);
405        $disposition = $response->headers->makeDisposition(
406            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
407            self::formatToFileCompliant($object->getName()) . '.xml'
408        );
409
410        $response->headers->set('Content-Disposition', $disposition);
411
412        return $response;
413    }
414
415    public function importAction(Request $request)
416    {
417        $form = $this->createForm(ImportModeleType::class);
418        $form->handleRequest($request);
419        if ($form->isSubmitted() && $form->isValid()) {
420            $content    = file_get_contents($form->getData()['file']->getPathname());
421            $serializer = SerializerBuilder::create()->build();
422            /* @var Model\Referentiel $object */
423
424            try {
425                $object = $serializer->deserialize($content, Model\Referentiel::class, 'xml');
426                $object->deserialize();
427            } catch (\Exception $e) {
428                $this->addFlash('danger', "Impossible d'importer ce fichier");
429
430                return $this->redirectToRoute($this->getRouteName('list'));
431            }
432            // $object->setDomains($domain);
433
434            $object->setCreatedAt(new \DateTimeImmutable());
435            $object->setName('(import) ' . $object->getName());
436            $this->entityManager->persist($object);
437            $this->entityManager->flush();
438            $this->addFlash('success', $this->getFlashbagMessage('success', 'import', $object));
439
440            return $this->redirectToRoute($this->getRouteName('list'));
441        }
442
443        return $this->render($this->getTemplatingBasePath('import'), [
444            'form' => $form->createView(),
445        ]);
446    }
447
448    private static function formatToFileCompliant(string $string)
449    {
450        $unwanted_array = [
451            'Š' => 'S', 'š' => 's', 'Ž' => 'Z', 'ž' => 'z', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'A', 'Ç' => 'C', 'È' => 'E', 'É' => 'E',
452            'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ø' => 'O', 'Ù' => 'U',
453            'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ý' => 'Y', 'Þ' => 'B', 'ß' => 'Ss', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'a', 'ç' => 'c',
454            'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ð' => 'o', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o',
455            'ö' => 'o', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ý' => 'y', 'þ' => 'b', 'ÿ' => 'y',
456        ];
457
458        return strtr($string, $unwanted_array);
459    }
460}