Passed
Push — master ( 18b10d...ddaeb4 )
by Julito
08:31 queued 10s
created

ResourceController::diskSpaceAction()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 68
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 36
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 68
rs 9.344

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Controller;
8
9
use Chamilo\CoreBundle\Entity\AbstractResource;
10
use Chamilo\CoreBundle\Entity\Course;
11
use Chamilo\CoreBundle\Entity\ResourceInterface;
12
use Chamilo\CoreBundle\Entity\ResourceLink;
13
use Chamilo\CoreBundle\Entity\ResourceNode;
14
use Chamilo\CoreBundle\Form\Type\ResourceCommentType;
15
use Chamilo\CoreBundle\Repository\Node\IllustrationRepository;
16
use Chamilo\CoreBundle\Repository\ResourceWithLinkInterface;
17
use Chamilo\CoreBundle\Security\Authorization\Voter\ResourceNodeVoter;
18
use Chamilo\CoreBundle\Traits\ControllerTrait;
19
use Chamilo\CoreBundle\Traits\CourseControllerTrait;
20
use Chamilo\CoreBundle\Traits\ResourceControllerTrait;
21
use Chamilo\CourseBundle\Controller\CourseControllerInterface;
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\Common\Collections\Criteria;
24
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
25
use Symfony\Component\HttpFoundation\JsonResponse;
26
use Symfony\Component\HttpFoundation\RedirectResponse;
27
use Symfony\Component\HttpFoundation\Request;
28
use Symfony\Component\HttpFoundation\Response;
29
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
30
use Symfony\Component\HttpFoundation\StreamedResponse;
31
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
32
use Symfony\Component\Routing\Annotation\Route;
33
use Symfony\Component\Routing\RouterInterface;
34
use ZipStream\Option\Archive;
35
use ZipStream\ZipStream;
36
37
/**
38
 * Class ResourceController.
39
 *
40
 * @Route("/r")
41
 *
42
 * @author Julio Montoya <[email protected]>.
43
 */
44
class ResourceController extends AbstractResourceController implements CourseControllerInterface
45
{
46
    use CourseControllerTrait;
47
    use ResourceControllerTrait;
48
    use ControllerTrait;
49
50
    private string $fileContentName = 'file_content';
51
52
    /**
53
     * @deprecated Use Vue
54
     *
55
     * @Route("/{tool}/{type}/{id}/new_folder", methods={"GET", "POST"}, name="chamilo_core_resource_new_folder")
56
     */
57
    /*public function newFolderAction(Request $request): Response
58
    {
59
        return $this->createResource($request, 'folder');
60
    }*/
61
62
    /**
63
     * @deprecated Use Vue
64
     *
65
     * @Route("/{tool}/{type}/{id}/new", methods={"GET", "POST"}, name="chamilo_core_resource_new")
66
     */
67
    /*public function newAction(Request $request): Response
68
    {
69
        return $this->createResource($request, 'file');
70
    }*/
71
72
    /**
73
     * @Route("/{tool}/{type}/{id}/disk_space", methods={"GET", "POST"}, name="chamilo_core_resource_disk_space")
74
     */
75
    public function diskSpaceAction(Request $request): Response
76
    {
77
        $nodeId = $request->get('id');
78
        $repository = $this->getRepositoryFromRequest($request);
79
80
        /** @var ResourceNode $resourceNode */
81
        $resourceNode = $repository->getResourceNodeRepository()->find($nodeId);
82
83
        $this->denyAccessUnlessGranted(
84
            ResourceNodeVoter::VIEW,
85
            $resourceNode,
86
            $this->trans('Unauthorised access to resource')
87
        );
88
89
        $this->setBreadCrumb($request, $resourceNode);
90
91
        $course = $this->getCourse();
92
        $totalSize = 0;
93
        if (null !== $course) {
94
            $totalSize = $course->getDiskQuota();
95
        }
96
97
        $size = $repository->getResourceNodeRepository()->getSize(
98
            $resourceNode,
99
            $repository->getResourceType(),
100
            $course
101
        );
102
103
        $labels[] = $course->getTitle();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$labels was never initialized. Although not strictly required by PHP, it is generally a good practice to add $labels = array(); before regardless.
Loading history...
104
        $data[] = $size;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
105
        $sessions = $course->getSessions();
106
107
        foreach ($sessions as $sessionRelCourse) {
108
            $session = $sessionRelCourse->getSession();
109
110
            $labels[] = $course->getTitle().' - '.$session->getName();
111
            $size = $repository->getResourceNodeRepository()->getSize(
112
                $resourceNode,
113
                $repository->getResourceType(),
114
                $course,
115
                $session
116
            );
117
            $data[] = $size;
118
        }
119
120
        /*$groups = $course->getGroups();
121
        foreach ($groups as $group) {
122
            $labels[] = $course->getTitle().' - '.$group->getName();
123
            $size = $repository->getResourceNodeRepository()->getSize(
124
                $resourceNode,
125
                $repository->getResourceType(),
126
                $course,
127
                null,
128
                $group
129
            );
130
            $data[] = $size;
131
        }*/
132
133
        $used = array_sum($data);
134
        $labels[] = $this->trans('Free');
135
        $data[] = $totalSize - $used;
136
137
        return $this->render(
138
            $repository->getTemplates()->getFromAction(__FUNCTION__),
139
            [
140
                'resourceNode' => $resourceNode,
141
                'labels' => $labels,
142
                'data' => $data,
143
            ]
144
        );
145
    }
146
147
    /**
148
     * @deprecated Use Vue
149
     *
150
     * @Route("/{tool}/{type}/{id}/edit", methods={"GET", "POST"})
151
     */
152
    public function editAction(Request $request, IllustrationRepository $illustrationRepo): Response
153
    {
154
        $resourceNodeId = $request->get('id');
155
156
        $repository = $this->getRepositoryFromRequest($request);
157
        $resource = $repository->getResourceFromResourceNode($resourceNodeId);
158
        $this->denyAccessUnlessValidResource($resource);
159
        $settings = $repository->getResourceSettings();
160
        $resourceNode = $resource->getResourceNode();
161
162
        $this->denyAccessUnlessGranted(
163
            ResourceNodeVoter::EDIT,
164
            $resourceNode,
165
            $this->trans('Unauthorised access to resource')
166
        );
167
168
        $this->setBreadCrumb($request, $resourceNode);
169
        $resourceNodeParentId = $resourceNode->getId();
170
171
        $routeParams = $this->getResourceParams($request);
172
        $routeParams['id'] = $resourceNodeParentId;
173
174
        $form = $repository->getForm($this->container->get('form.factory'), $resource);
175
176
        if ($resourceNode->hasEditableTextContent() && $settings->isAllowToSaveEditorToResourceFile()) {
177
            /*$form->add(
178
                $this->fileContentName,
179
                CKEditorType::class,
180
                [
181
                    'mapped' => false,
182
                    'config' => [
183
                        'filebrowserImageBrowseRoute' => 'resources_filemanager',
184
                        'filebrowserImageBrowseRouteParameters' => $routeParams,
185
                    ],
186
                ]
187
            );
188
            $content = $repository->getResourceNodeFileContent($resourceNode);
189
            $form->get($this->fileContentName)->setData($content);*/
190
        }
191
192
        $form->handleRequest($request);
193
194
        if ($form->isSubmitted() && $form->isValid()) {
195
            /** @var AbstractResource|ResourceInterface $newResource */
196
            $newResource = $form->getData();
197
198
            if ($form->has($this->fileContentName)) {
199
                $data = $form->get($this->fileContentName)->getData();
200
                $repository->updateResourceFileContent($newResource, $data);
201
            }
202
203
            $repository->updateNodeForResource($newResource);
204
205
            if ($form->has('illustration')) {
206
                $illustration = $form->get('illustration')->getData();
207
                if ($illustration) {
208
                    $illustrationRepo->addIllustration($newResource, $this->getUser(), $illustration);
209
                }
210
            }
211
212
            $this->addFlash('success', $this->trans('Updated'));
213
            $resourceNodeParentId = $newResource->getResourceNode()->getParent()->getId();
214
            $routeParams['id'] = $resourceNodeParentId;
215
216
            return $this->redirectToRoute('chamilo_core_resource_list', $routeParams);
217
        }
218
219
        return $this->render(
220
            $repository->getTemplates()->getFromAction(__FUNCTION__),
221
            [
222
                'form' => $form->createView(),
223
                'parent' => $resourceNodeParentId,
224
            ]
225
        );
226
    }
227
228
    /**
229
     * @deprecated use Vue
230
     *
231
     * Shows a resource information
232
     *
233
     * @Route("/{tool}/{type}/{id}/info", methods={"GET", "POST"}, name="chamilo_core_resource_info")
234
     */
235
    /*public function infoAction(Request $request): Response
236
    {
237
        $nodeId = $request->get('id');
238
        $repository = $this->getRepositoryFromRequest($request);
239
240
        $resource = $repository->getResourceFromResourceNode($nodeId);
241
        $this->denyAccessUnlessValidResource($resource);
242
        $resourceNode = $resource->getResourceNode();
243
244
        $this->denyAccessUnlessGranted(
245
            ResourceNodeVoter::VIEW,
246
            $resourceNode,
247
            $this->trans(sprintf('Unauthorised access to resource #%s', $nodeId))
248
        );
249
250
        $this->setBreadCrumb($request, $resourceNode);
251
252
        $tool = $request->get('tool');
253
        $type = $request->get('type');
254
255
        $form = $this->createForm(ResourceCommentType::class, null);
256
257
        $params = [
258
            'resource' => $resource,
259
            'course' => $this->getCourse(),
260
            //   'illustration' => $illustration,
261
            'tool' => $tool,
262
            'type' => $type,
263
            'comment_form' => $form->createView(),
264
        ];
265
266
        return $this->render(
267
            $repository->getTemplates()->getFromAction(__FUNCTION__, $request->isXmlHttpRequest()),
268
            $params
269
        );
270
    }*/
271
272
    /**
273
     * Preview a file. Mostly used when using a modal.
274
     *
275
     * @Route("/{tool}/{type}/{id}/preview", methods={"GET"}, name="chamilo_core_resource_preview")
276
     */
277
    /*public function previewAction(Request $request): Response
278
    {
279
        $nodeId = $request->get('id');
280
        $repository = $this->getRepositoryFromRequest($request);
281
282
        $resource = $repository->getResourceFromResourceNode($nodeId);
283
        $this->denyAccessUnlessValidResource($resource);
284
285
        $resourceNode = $resource->getResourceNode();
286
        $this->denyAccessUnlessGranted(
287
            ResourceNodeVoter::VIEW,
288
            $resourceNode,
289
            $this->trans('Unauthorised access to resource')
290
        );
291
292
        $this->setBreadCrumb($request, $resourceNode);
293
294
        $tool = $request->get('tool');
295
        $type = $request->get('type');
296
297
        $params = [
298
            'resource' => $resource,
299
            'tool' => $tool,
300
            'type' => $type,
301
        ];
302
303
        return $this->render($repository->getTemplates()->getFromAction(__FUNCTION__), $params);
304
    }*/
305
306
    /**
307
     * @deprecated use vue
308
     *
309
     * @Route("/{tool}/{type}/{id}/change_visibility", name="chamilo_core_resource_change_visibility")
310
     */
311
    public function changeVisibilityAction(Request $request): Response
312
    {
313
        $id = (int) $request->get('id');
314
315
        $repository = $this->getRepositoryFromRequest($request);
316
317
        $resource = $repository->getResourceFromResourceNode($id);
318
        $this->denyAccessUnlessValidResource($resource);
319
        /** @var AbstractResource $resource */
320
        $resourceNode = $resource->getResourceNode();
321
322
        $this->denyAccessUnlessGranted(
323
            ResourceNodeVoter::EDIT,
324
            $resourceNode,
325
            $this->trans('Unauthorised access to resource')
326
        );
327
328
        if ($this->hasCourse()) {
329
            $link = $resource->getFirstResourceLinkFromCourseSession($this->getCourse(), $this->getSession());
330
        } else {
331
            $link = $resource->getFirstResourceLink();
332
        }
333
334
        // Use repository to change settings easily.
335
        if ($link && ResourceLink::VISIBILITY_PUBLISHED === $link->getVisibility()) {
336
            $repository->setVisibilityDraft($resource);
337
        } else {
338
            $repository->setVisibilityPublished($resource);
339
        }
340
341
        $result = [
342
            'visibility' => $link->getVisibility(),
343
            'ok' => true,
344
        ];
345
346
        return new JsonResponse($result);
347
    }
348
349
    /**
350
     * @deprecated Use Vue + api platform
351
     *
352
     * @Route("/{tool}/{type}/{id}/delete", name="chamilo_core_resource_delete")
353
     */
354
    public function deleteAction(Request $request): Response
355
    {
356
        $em = $this->getDoctrine()->getManager();
357
358
        $id = $request->get('id');
359
        $resourceNode = $this->getDoctrine()->getRepository(ResourceNode::class)->find($id);
360
        $parentId = $resourceNode->getParent()->getId();
361
362
        $this->denyAccessUnlessGranted(
363
            ResourceNodeVoter::DELETE,
364
            $resourceNode,
365
            $this->trans('Unauthorised access to resource')
366
        );
367
368
        $children = $resourceNode->getChildren();
369
370
        if (!empty($children)) {
371
            /** @var ResourceNode $child */
372
            foreach ($children as $child) {
373
                $em->remove($child);
374
            }
375
        }
376
377
        $em->remove($resourceNode);
378
        $this->addFlash('success', $this->trans('Deleted').': '.$resourceNode->getSlug());
379
        $em->flush();
380
381
        $routeParams = $this->getResourceParams($request);
382
        $routeParams['id'] = $parentId;
383
384
        return $this->redirectToRoute('chamilo_core_resource_list', $routeParams);
385
    }
386
387
    /**
388
     * Shows the associated resource file.
389
     *
390
     * @deprecated use vue
391
     *
392
     * @Route("/{tool}/{type}/{id}/view_resource", methods={"GET"}, name="chamilo_core_resource_view_resource")
393
     */
394
    /*public function viewResourceAction(Request $request, RouterInterface $router): Response
395
    {
396
        $id = $request->get('id');
397
398
        $resourceNode = $this->getResourceNodeRepository()->find($id);
399
400
        if (null === $resourceNode) {
401
            throw new FileNotFoundException('Resource not found');
402
        }
403
404
        $this->denyAccessUnlessGranted(
405
            ResourceNodeVoter::VIEW,
406
            $resourceNode,
407
            $this->trans('Unauthorised access to resource')
408
        );
409
410
        $repository = $this->getRepositoryFromRequest($request);
411
412
        $resource = $repository->getResourceFromResourceNode($id);
413
414
        $tool = $request->get('tool');
415
        $type = $request->get('type');
416
        $this->setBreadCrumb($request, $resourceNode);
417
418
        $params = [
419
            'resource' => $resource,
420
            'tool' => $tool,
421
            'type' => $type,
422
        ];
423
424
        return $this->render($repository->getTemplates()->getFromAction(__FUNCTION__), $params);
425
    }*/
426
427
    /**
428
     * View file of a resource node.
429
     *
430
     * @Route("/{tool}/{type}/{id}/view", methods={"GET"}, name="chamilo_core_resource_view")
431
     */
432
    public function viewAction(Request $request): Response
433
    {
434
        $id = $request->get('id');
435
        $filter = (string) $request->get('filter'); // See filters definitions in /config/services.yml.
436
        $resourceNode = $this->getResourceNodeRepository()->find($id);
437
438
        if (null === $resourceNode) {
439
            throw new FileNotFoundException('Resource not found');
440
        }
441
442
        return $this->processFile($request, $resourceNode, 'show', $filter);
443
    }
444
445
    /**
446
     * Redirect resource to link.
447
     *
448
     * @Route("/{tool}/{type}/{id}/link", methods={"GET"}, name="chamilo_core_resource_link")
449
     *
450
     * @return RedirectResponse|void
451
     */
452
    public function linkAction(Request $request, RouterInterface $router)
453
    {
454
        $id = $request->get('id');
455
        $resourceNode = $this->getResourceNodeRepository()->find($id);
456
457
        if (null === $resourceNode) {
458
            throw new FileNotFoundException('Resource not found');
459
        }
460
461
        $repo = $this->getRepositoryFromRequest($request);
462
        if ($repo instanceof ResourceWithLinkInterface) {
463
            $resource = $repo->getResourceFromResourceNode($resourceNode->getId());
464
            $url = $repo->getLink($resource, $router, $this->getCourseUrlQueryToArray());
465
466
            return $this->redirect($url);
467
        }
468
469
        $this->abort('No redirect');
470
    }
471
472
    /**
473
     * Download file of a resource node.
474
     *
475
     * @Route("/{tool}/{type}/{id}/download", methods={"GET"}, name="chamilo_core_resource_download")
476
     *
477
     * @return RedirectResponse|StreamedResponse
478
     */
479
    public function downloadAction(Request $request)
480
    {
481
        $id = (int) $request->get('id');
482
        $resourceNode = $this->getResourceNodeRepository()->find($id);
483
484
        if (null === $resourceNode) {
485
            throw new FileNotFoundException('Resource not found');
486
        }
487
488
        $repo = $this->getRepositoryFromRequest($request);
489
490
        $this->denyAccessUnlessGranted(
491
            ResourceNodeVoter::VIEW,
492
            $resourceNode,
493
            $this->trans('Unauthorised access to resource')
494
        );
495
496
        // If resource node has a file just download it. Don't download the children.
497
        if ($resourceNode->hasResourceFile()) {
498
            // Redirect to download single file.
499
            return $this->processFile($request, $resourceNode, 'download');
500
        }
501
502
        $zipName = $resourceNode->getSlug().'.zip';
503
        //$rootNodePath = $resourceNode->getPathForDisplay();
504
        $resourceNodeRepo = $repo->getResourceNodeRepository();
505
        $type = $repo->getResourceType();
506
507
        $criteria = Criteria::create()
508
            ->where(Criteria::expr()->neq('resourceFile', null)) // must have a file
509
            ->andWhere(Criteria::expr()->eq('resourceType', $type)) // only download same type
510
        ;
511
512
        $qb = $resourceNodeRepo->getChildrenQueryBuilder($resourceNode);
513
        $qb->addCriteria($criteria);
514
        /** @var ArrayCollection|ResourceNode[] $children */
515
        $children = $qb->getQuery()->getResult();
516
        $count = \count($children);
517
        if (0 === $count) {
518
            $params = $this->getResourceParams($request);
519
            $params['id'] = $id;
520
521
            $this->addFlash('warning', $this->trans('No files'));
522
523
            return $this->redirectToRoute('chamilo_core_resource_list', $params);
524
        }
525
526
        $response = new StreamedResponse(
527
            function () use ($zipName, $children, $repo): void {
528
                // Define suitable options for ZipStream Archive.
529
                $options = new Archive();
530
                $options->setContentType('application/octet-stream');
531
                //initialise zipstream with output zip filename and options.
532
                $zip = new ZipStream($zipName, $options);
533
534
                /** @var ResourceNode $node */
535
                foreach ($children as $node) {
536
                    $stream = $repo->getResourceNodeFileStream($node);
537
                    $fileName = $node->getResourceFile()->getOriginalName();
538
                    //$fileToDisplay = basename($node->getPathForDisplay());
539
                    //$fileToDisplay = str_replace($rootNodePath, '', $node->getPathForDisplay());
540
                    //error_log($fileToDisplay);
541
                    $zip->addFileFromStream($fileName, $stream);
542
                }
543
                $zip->finish();
544
            }
545
        );
546
547
        $disposition = $response->headers->makeDisposition(
548
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
549
            $zipName //Transliterator::transliterate($zipName)
550
        );
551
        $response->headers->set('Content-Disposition', $disposition);
552
        $response->headers->set('Content-Type', 'application/octet-stream');
553
554
        return $response;
555
    }
556
557
    /**
558
     * @return mixed|StreamedResponse
559
     */
560
    private function processFile(Request $request, ResourceNode $resourceNode, string $mode = 'show', string $filter = '')
561
    {
562
        $this->denyAccessUnlessGranted(
563
            ResourceNodeVoter::VIEW,
564
            $resourceNode,
565
            $this->trans('Unauthorised view access to resource')
566
        );
567
568
        $resourceFile = $resourceNode->getResourceFile();
569
570
        if (null === $resourceFile) {
571
            throw new NotFoundHttpException($this->trans('File not found for resource'));
572
        }
573
574
        $fileName = $resourceNode->getResourceFile()->getOriginalName();
575
        $mimeType = $resourceFile->getMimeType();
576
        $resourceNodeRepo = $this->getResourceNodeRepository();
577
578
        switch ($mode) {
579
            case 'download':
580
                $forceDownload = true;
581
582
                break;
583
            case 'show':
584
            default:
585
                $forceDownload = false;
586
                // If it's an image then send it to Glide.
587
                if (str_contains($mimeType, 'image')) {
588
                    $glide = $this->getGlide();
589
                    $server = $glide->getServer();
590
                    $params = $request->query->all();
591
592
                    // The filter overwrites the params from GET.
593
                    if (!empty($filter)) {
594
                        $params = $glide->getFilters()[$filter] ?? [];
595
                    }
596
597
                    // The image was cropped manually by the user, so we force to render this version,
598
                    // no matter other crop parameters.
599
                    $crop = $resourceFile->getCrop();
600
                    if (!empty($crop)) {
601
                        $params['crop'] = $crop;
602
                    }
603
604
                    $fileName = $resourceNodeRepo->getFilename($resourceFile);
605
606
                    return $server->getImageResponse($fileName, $params);
607
                }
608
609
                // Modify the HTML content before displaying it.
610
                if (str_contains($mimeType, 'html')) {
611
                    $content = $resourceNodeRepo->getResourceNodeFileContent($resourceNode);
612
613
                    $response = new Response();
614
                    $disposition = $response->headers->makeDisposition(
615
                        ResponseHeaderBag::DISPOSITION_INLINE,
616
                        $fileName
617
                    );
618
                    $response->headers->set('Content-Disposition', $disposition);
619
                    $response->headers->set('Content-Type', 'text/html');
620
621
                    /*$crawler = new Crawler();
622
                    $crawler->addHtmlContent($content);
623
                    var_dump($crawler->filter('head')->count());
624
                    $head = $crawler->filter('head');
625
                    var_dump($head->html());exit;*/
626
627
                    // @todo move into a function/class
628
                    if ('true' === $this->getSettingsManager()->getSetting('editor.translate_html')) {
629
                        $user = $this->getUser();
630
                        if (null !== $user) {
631
                            $user = json_encode(['locale' => $user->getLocale()]);
632
                            $js = '
633
                               <script>
634
                                window.user = '.$user.'                                
635
                               </script> 
636
                               <script src="/build/runtime.js"></script>
637
                               <script src="/build/translatehtml.js"></script>';
638
                            $content = str_replace('</html>', $js.'</html>', $content);
639
                        }
640
                    }
641
642
                    $response->setContent($content);
643
                    /*$contents = $this->renderView('@ChamiloCore/Resource/view_html.twig', [
644
                        'category' => '...',
645
                    ]);*/
646
647
                    return $response;
648
                }
649
650
                break;
651
        }
652
653
        $stream = $resourceNodeRepo->getResourceNodeFileStream($resourceNode);
654
655
        $response = new StreamedResponse(
656
            function () use ($stream): void {
657
                stream_copy_to_stream($stream, fopen('php://output', 'wb'));
658
            }
659
        );
660
661
        //Transliterator::transliterate($fileName)
662
        $disposition = $response->headers->makeDisposition(
663
            $forceDownload ? ResponseHeaderBag::DISPOSITION_ATTACHMENT : ResponseHeaderBag::DISPOSITION_INLINE,
664
            $fileName
665
        );
666
        $response->headers->set('Content-Disposition', $disposition);
667
        $response->headers->set('Content-Type', $mimeType ?: 'application/octet-stream');
668
669
        return $response;
670
    }
671
}
672