Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
24.07% covered (danger)
24.07%
26 / 108
33.33% covered (danger)
33.33%
5 / 15
CRAP
0.00% covered (danger)
0.00%
0 / 1
DocumentController
24.07% covered (danger)
24.07%
26 / 108
33.33% covered (danger)
33.33%
5 / 15
603.25
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
 getRouteName
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 getListData
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 indexAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 downloadAction
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 deleteConfirmationAction
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
42
 listAction
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 gridAction
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 formPrePersistData
58.33% covered (warning)
58.33%
14 / 24
0.00% covered (danger)
0.00%
0 / 1
14.86
 shareAction
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 favoriteAction
0.00% covered (danger)
0.00%
0 / 16
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\Documentation\Controller;
25
26use App\Application\Controller\CRUDController;
27use App\Application\Symfony\Security\UserProvider;
28use App\Domain\Documentation\Form\Type\DocumentType;
29use App\Domain\Documentation\Model;
30use App\Domain\Documentation\Repository;
31use App\Domain\User\Model\User;
32use Doctrine\ORM\EntityManagerInterface;
33use Gaufrette\Exception\FileNotFound;
34use Gaufrette\FilesystemInterface;
35use Knp\Snappy\Pdf;
36use Ramsey\Uuid\Uuid;
37use Symfony\Component\HttpFoundation\BinaryFileResponse;
38use Symfony\Component\HttpFoundation\Request;
39use Symfony\Component\HttpFoundation\RequestStack;
40use Symfony\Component\HttpFoundation\Response;
41use Symfony\Component\HttpFoundation\ResponseHeaderBag;
42use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
43use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
44use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
45use Symfony\Contracts\Translation\TranslatorInterface;
46
47/**
48 * @property Repository\Document $repository
49 */
50class DocumentController extends CRUDController
51{
52    /**
53     * @var AuthorizationCheckerInterface
54     */
55    protected $authorizationChecker;
56
57    /**
58     * @var UserProvider
59     */
60    protected $userProvider;
61
62    /**
63     * @var FilesystemInterface
64     */
65    protected $documentFilesystem;
66
67    /**
68     * @var FilesystemInterface
69     */
70    protected $thumbFilesystem;
71
72    /**
73     * @var RequestStack
74     */
75    protected $requestStack;
76
77    /**
78     * @var Repository\Category
79     */
80    protected $categoryRepository;
81
82    public function __construct(
83        EntityManagerInterface $entityManager,
84        TranslatorInterface $translator,
85        Repository\Document $repository,
86        Repository\Category $categoryRepository,
87        AuthorizationCheckerInterface $authorizationChecker,
88        UserProvider $userProvider,
89        FilesystemInterface $documentFilesystem,
90        FilesystemInterface $thumbFilesystem,
91        Pdf $pdf,
92        RequestStack $requestStack,
93    ) {
94        parent::__construct($entityManager, $translator, $repository, $pdf, $userProvider, $authorizationChecker);
95        $this->authorizationChecker = $authorizationChecker;
96        $this->documentFilesystem   = $documentFilesystem;
97        $this->thumbFilesystem      = $thumbFilesystem;
98        $this->requestStack         = $requestStack;
99        $this->categoryRepository   = $categoryRepository;
100    }
101
102    protected function getDomain(): string
103    {
104        return 'documentation';
105    }
106
107    protected function getModel(): string
108    {
109        return 'document';
110    }
111
112    protected function getModelClass(): string
113    {
114        return Model\Document::class;
115    }
116
117    protected function getFormType(): string
118    {
119        return DocumentType::class;
120    }
121
122    protected function getRouteName(?string $template = null): string
123    {
124        if ('list' === $template) {
125            return "{$this->getDomain()}_{$this->getModel()}_index";
126        }
127
128        return "{$this->getDomain()}_{$this->getModel()}_{$template}";
129    }
130
131    protected function getListData()
132    {
133        $order = [
134            'pinned'    => 'DESC',
135            'createdAt' => 'DESC',
136        ];
137
138        // Everybody can access all documents
139        return $this->repository->findAll($order);
140    }
141
142    public function indexAction()
143    {
144        /**
145         * @var User $user
146         */
147        $user = $this->getUser();
148        if ($user->isDocumentView()) {
149            return $this->gridAction();
150        }
151
152        return $this->listAction();
153    }
154
155    /**
156     * Trigger document file download.
157     *
158     * @return BinaryFileResponse
159     */
160    public function downloadAction(string $name)
161    {
162        $doc = $this->repository->findOneByName($name);
163
164        if (!$doc) {
165            $doc = $this->repository->findOneById($name);
166        }
167
168        if (!$doc) {
169            throw new NotFoundHttpException('Document introuvable');
170        }
171
172        $fileStream = sprintf('gaufrette://documentation_document/%s', $doc->getFile());
173
174        $response = new BinaryFileResponse($fileStream);
175        $mimeType = $this->documentFilesystem->mimeType($doc->getFile());
176        $ext      = pathinfo($doc->getFile(), PATHINFO_EXTENSION);
177        $response->headers->set('Content-Type', $mimeType);
178        $response->setContentDisposition(
179            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
180            $doc->getName() . '.' . $ext
181        );
182
183        return $response;
184    }
185
186    public function deleteConfirmationAction(string $id): Response
187    {
188        /** @var Model\Document $doc */
189        $doc = $this->repository->findOneByID($id);
190        if (!$doc) {
191            throw new NotFoundHttpException('Document introuvable');
192        }
193        // check if user is creator or admin
194        if (!$this->authorizationChecker->isGranted('ROLE_ADMIN') && $this->userProvider->getAuthenticatedUser() !== $doc->getCreator()) {
195            throw new NotFoundHttpException('Document introuvable');
196        }
197
198        if ($doc->getFile()) {
199            try {
200                $this->documentFilesystem->delete($doc->getFile());
201            } catch (FileNotFound $e) {
202                // File does not exist, do nothing
203            }
204        }
205
206        return parent::deleteConfirmationAction($id); // TODO: Change the autogenerated stub
207    }
208
209    /*
210     * The list action view
211     * Get data & display them.
212     */
213    public function listAction(): Response
214    {
215        // Set default view to list for current user
216        /**
217         * @var User $user
218         */
219        $user = $this->getUser();
220        $user->setDocumentView(false);
221        $this->entityManager->flush();
222
223        $categories = $this->categoryRepository->findAll(['name' => 'ASC']);
224
225        return $this->render($this->getTemplatingBasePath('list'), [
226            'objects'    => $this->getListData(),
227            'categories' => $categories,
228        ]);
229    }
230
231    public function gridAction(): Response
232    {
233        // Set default view to list for current user
234        /**
235         * @var User $user
236         */
237        $user = $this->getUser();
238        $user->setDocumentView(true);
239        $this->entityManager->flush();
240
241        $categories = $this->categoryRepository->findAll(['name' => 'ASC']);
242
243        return $this->render($this->getTemplatingBasePath('grid'), [
244            'objects'    => $this->getListData(),
245            'categories' => $categories,
246        ]);
247    }
248
249    /**
250     * @param Model\Document $object
251     */
252    public function formPrePersistData($object, $form = null)
253    {
254        if (!$object->getId() && !$this->authorizationChecker->isGranted('ROLE_ADMIN') && $this->userProvider->getAuthenticatedUser() !== $object->getCreator()) {
255            throw new NotFoundHttpException('Document introuvable');
256        }
257        if (false === $object->getIsLink() && null !== $file = $object->getUploadedFile()) {
258            $filename = Uuid::uuid4()->toString() . '.' . $file->getClientOriginalExtension();
259
260            $this->documentFilesystem->write($filename, \fopen($file->getRealPath(), 'r'));
261            $size = $this->documentFilesystem->size($filename);
262
263            $object->setSize($size);
264
265            $object->setFile($filename);
266            $object->setUploadedFile(null);
267
268            $url = $this->generateUrl('documentation_document_download', [
269                'name' => $filename,
270            ], UrlGeneratorInterface::NETWORK_PATH);
271
272            $object->setUrl($url);
273        } elseif (true === $object->getIsLink()) {
274            $object->setFile('');
275            $object->setSize(0);
276        }
277        if (null !== $thumb = $object->getThumbUploadedFile()) {
278            $filename = Uuid::uuid4()->toString() . '.' . $thumb->getClientOriginalExtension();
279            $this->thumbFilesystem->write($filename, \fopen($thumb->getRealPath(), 'r'));
280
281            $object->setThumbUploadedFile(null);
282            $object->setThumbUrl('/uploads/documentation/vignettes/' . $filename);
283        }
284
285        if ($object->getRemoveThumb()) {
286            $object->setThumbUploadedFile(null);
287            $object->setThumbUrl(null);
288        }
289    }
290
291    /**
292     * Trigger download or redirect to link when a user open a share link.
293     */
294    public function shareAction(string $id)
295    {
296        $doc = $this->repository->findOneByID($id);
297
298        if (!$doc) {
299            throw new NotFoundHttpException('Document introuvable');
300        }
301
302        if ($doc->getIsLink()) {
303            return $this->redirect($doc->getUrl());
304        }
305
306        return $this->downloadAction($doc->getFile());
307    }
308
309    /**
310     * Mark this document as favorite for the current user.
311     */
312    public function favoriteAction(Request $request, string $id)
313    {
314        /**
315         * @var Model\Document
316         */
317        $doc = $this->repository->findOneByID($id);
318        /**
319         * @var User
320         */
321        $user = $this->getUser();
322        if (!$doc) {
323            throw new NotFoundHttpException('Document introuvable');
324        }
325
326        $favorited = $user->getFavoriteDocuments();
327
328        // Is the current document already favorited ?
329        if ($favorited->contains($doc)) {
330            // If so, remove it
331            $favorited->removeElement($doc);
332            $doc->removeFavoritedUser($user);
333        } else {
334            // Other wise, add it
335            $favorited->add($doc);
336            $doc->addFavoritedUser($user);
337        }
338
339        $user->setFavoriteDocuments($favorited);
340
341        $this->entityManager->persist($user);
342        $this->entityManager->persist($doc);
343        $this->entityManager->flush();
344
345        $this->getDoctrine()->getManagerForClass(User::class)->flush();
346
347        return $this->redirect($request->get('back'));
348    }
349}