Passed
Push — master ( b34284...653853 )
by Angel Fernando Quiroz
12:36
created

DownloadSelectedDocumentsAction::__invoke()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 58
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 31
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 58
rs 9.1128

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