Passed
Push — master ( 577984...690f51 )
by Angel Fernando Quiroz
08:47
created

DownloadSelectedDocumentsAction::__invoke()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 62
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 33
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 62
rs 8.7697

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
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Controller\Api;
8
9
use Chamilo\CoreBundle\Entity\ResourceNode;
10
use Chamilo\CoreBundle\Helpers\ResourceFileHelper;
11
use Chamilo\CoreBundle\Repository\ResourceNodeRepository;
12
use Chamilo\CoreBundle\Traits\ControllerTrait;
13
use Chamilo\CourseBundle\Repository\CDocumentRepository;
14
use Exception;
15
use Symfony\Component\HttpFoundation\Request;
16
use Symfony\Component\HttpFoundation\Response;
17
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
18
use Symfony\Component\HttpFoundation\StreamedResponse;
19
use Symfony\Component\HttpKernel\KernelInterface;
20
use ZipStream\Option\Archive;
21
use ZipStream\ZipStream;
22
23
class DownloadSelectedDocumentsAction
24
{
25
    use ControllerTrait;
0 ignored issues
show
Bug introduced by
The trait Chamilo\CoreBundle\Traits\ControllerTrait requires the property $headers which is not provided by Chamilo\CoreBundle\Contr...SelectedDocumentsAction.
Loading history...
26
27
    public const CONTENT_TYPE = 'application/zip';
28
29
    public function __construct(
30
        private readonly KernelInterface $kernel,
31
        private readonly ResourceNodeRepository $resourceNodeRepository,
32
        private readonly CDocumentRepository $documentRepo,
33
        private readonly ResourceFileHelper $resourceFileHelper,
34
    ) {}
35
36
    /**
37
     * @throws Exception
38
     */
39
    public function __invoke(Request $request): Response
40
    {
41
        ini_set('max_execution_time', '300');
42
        ini_set('memory_limit', '512M');
43
44
        $data = json_decode($request->getContent(), true);
45
        $documentIds = $data['ids'] ?? [];
46
47
        if (empty($documentIds)) {
48
            return new Response('No items selected.', Response::HTTP_BAD_REQUEST);
49
        }
50
51
        $documents = $this->documentRepo->findBy(['iid' => $documentIds]);
52
53
        if (empty($documents)) {
54
            return new Response('No documents found.', Response::HTTP_NOT_FOUND);
55
        }
56
57
        $zipName = 'selected_documents.zip';
58
59
        $response = new StreamedResponse(
60
            function () use ($documents, $zipName): void {
61
                // Creates a ZIP file containing the specified documents.
62
                $options = new Archive();
63
                $options->setSendHttpHeaders(false);
64
                $options->setContentType(self::CONTENT_TYPE);
65
66
                $zip = new ZipStream($zipName, $options);
67
68
                foreach ($documents as $document) {
69
                    $node = $document->getResourceNode();
70
71
                    if (!$node) {
72
                        error_log('ResourceNode not found for document ID: '.$document->getIid());
73
74
                        continue;
75
                    }
76
77
                    $this->addNodeToZip($zip, $node);
78
                }
79
80
                if (0 === count($zip->files)) {
81
                    $zip->addFile('.empty', '');
82
                }
83
84
                $zip->finish();
85
            },
86
            Response::HTTP_CREATED
87
        );
88
89
        // Convert the file name to ASCII using iconv
90
        $zipName = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $zipName);
91
92
        $disposition = $response->headers->makeDisposition(
93
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
94
            $zipName
95
        );
96
97
        $response->headers->set('Content-Disposition', $disposition);
98
        $response->headers->set('Content-Type', self::CONTENT_TYPE);
99
100
        return $response;
101
    }
102
103
    /**
104
     * Adds a resource node and its files or children to the ZIP archive.
105
     */
106
    private function addNodeToZip(ZipStream $zip, ResourceNode $node, string $currentPath = ''): void
107
    {
108
        if ($node->getChildren()->count() > 0) {
109
            $relativePath = $currentPath.$node->getTitle().'/';
110
111
            $zip->addFile($relativePath, '');
112
113
            foreach ($node->getChildren() as $childNode) {
114
                $this->addNodeToZip($zip, $childNode, $relativePath);
115
            }
116
117
            return;
118
        }
119
120
        $resourceFile = $this->resourceFileHelper->resolveResourceFileByAccessUrl($node);
121
122
        if ($resourceFile) {
123
            $fileName = $currentPath.$resourceFile->getOriginalName();
124
            $stream = $this->resourceNodeRepository->getResourceNodeFileStream($node, $resourceFile);
125
126
            $zip->addFileFromStream($fileName, $stream);
127
        }
128
    }
129
}
130