Passed
Pull Request — master (#6667)
by
unknown
09:38 queued 01:39
created

AssetRepository::update()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/* For licensing terms, see /license.txt */
6
7
namespace Chamilo\CoreBundle\Repository;
8
9
use Chamilo\CoreBundle\Entity\Asset;
10
use Chamilo\CoreBundle\Helpers\CreateUploadedFileHelper;
11
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
12
use Doctrine\Persistence\ManagerRegistry;
13
use Exception;
14
use League\Flysystem\FilesystemOperator;
15
use PhpZip\ZipFile;
16
use Symfony\Component\HttpFoundation\File\UploadedFile;
17
use Symfony\Component\Routing\RouterInterface;
18
use Vich\UploaderBundle\Storage\FlysystemStorage;
19
use Vich\UploaderBundle\Templating\Helper\UploaderHelper;
20
21
class AssetRepository extends ServiceEntityRepository
22
{
23
    protected RouterInterface $router;
24
    protected UploaderHelper $uploaderHelper;
25
    protected FilesystemOperator $filesystem;
26
    protected FlysystemStorage $storage;
27
28
    public function __construct(ManagerRegistry $registry, RouterInterface $router, UploaderHelper $uploaderHelper, FilesystemOperator $assetFilesystem, FlysystemStorage $storage)
29
    {
30
        parent::__construct($registry, Asset::class);
31
        $this->router = $router;
32
        $this->uploaderHelper = $uploaderHelper;
33
        // Flysystem mount name is saved in config/packages/oneup_flysystem.yaml
34
        $this->filesystem = $assetFilesystem;
35
        $this->storage = $storage;
36
    }
37
38
    public function getStorage(): FlysystemStorage
39
    {
40
        return $this->storage;
41
    }
42
43
    public function getFileSystem(): FilesystemOperator
44
    {
45
        return $this->filesystem;
46
    }
47
48
    public function unZipFile(Asset $asset, string $addFolder = ''): void
49
    {
50
        $folder = '/'.$asset->getCategory().'/'.$asset->getTitle();
51
52
        if (!empty($addFolder)) {
53
            $folder .= '/'.$addFolder;
54
        }
55
56
        $fs = $this->getFileSystem();
57
        $file = $this->getStorage()->resolveUri($asset);
58
59
        if (!$fs->fileExists($file)) {
60
            throw new Exception('file not found');
61
        }
62
63
        $stream = $fs->readStream($file);
64
        $zipFile = new ZipFile();
65
        $zipFile->openFromStream($stream);
66
67
        $list = $zipFile->getEntries();
68
        foreach ($list as $item) {
69
            $name = $item->getName();
70
            if ($fs->fileExists($folder.'/'.$name)) {
71
                continue;
72
            }
73
74
            if ($item->isDirectory()) {
75
                $fs->createDirectory($folder.'/'.$name);
76
77
                continue;
78
            }
79
80
            $content = $zipFile->getEntryContents($name);
81
            $fs->write($folder.'/'.$name, $content);
82
        }
83
    }
84
85
    public function getAssetContent(Asset $asset): string
86
    {
87
        if (!$asset->hasFile()) {
88
            return '';
89
        }
90
91
        $fs = $this->getFileSystem();
92
        $file = $this->getStorage()->resolveUri($asset);
93
94
        if (!$fs->fileExists($file)) {
95
            return '';
96
        }
97
98
        return $this->getFileSystem()->read($file);
99
    }
100
101
    public function getFolder(Asset $asset): ?string
102
    {
103
        if ($asset->hasFile()) {
104
            $file = $asset->getTitle();
105
106
            return '/'.$asset->getCategory().'/'.$file.'/';
107
        }
108
109
        return null;
110
    }
111
112
    public function getAssetUrl(Asset $asset): string
113
    {
114
        if (Asset::SCORM === $asset->getCategory()) {
115
            $params = [
116
                'category' => $asset->getCategory(),
117
                'path' => $asset->getTitle(),
118
            ];
119
120
            return $this->router->generate('chamilo_core_asset_showfile', $params);
121
        }
122
123
        // Classic.
124
        $helper = $this->uploaderHelper;
125
126
        $cropFilter = '';
127
        $crop = $asset->getCrop();
128
        if (!empty($crop)) {
129
            $cropFilter = '?crop='.$crop;
130
        }
131
132
        return '/assets'.$helper->asset($asset).$cropFilter;
133
    }
134
135
    public function createFromRequest(Asset $asset, array $file): Asset
136
    {
137
        if (isset($file['tmp_name']) && !empty($file['tmp_name'])) {
138
            $mimeType = mime_content_type($file['tmp_name']);
139
            $file = new UploadedFile($file['tmp_name'], $asset->getTitle(), $mimeType, null, true);
140
            $asset->setFile($file);
141
            $this->getEntityManager()->persist($asset);
142
            $this->getEntityManager()->flush();
143
        }
144
145
        return $asset;
146
    }
147
148
    public function createFromString(Asset $asset, string $mimeType, string $content): Asset
149
    {
150
        $file = CreateUploadedFileHelper::fromString($asset->getTitle(), $mimeType, $content);
151
        $asset->setFile($file);
152
        $this->getEntityManager()->persist($asset);
153
        $this->getEntityManager()->flush();
154
155
        return $asset;
156
    }
157
158
    public function update(Asset $asset): void
159
    {
160
        $this->getEntityManager()->persist($asset);
161
        $this->getEntityManager()->flush();
162
    }
163
164
    /**
165
     * Deletes an Asset from the database.
166
     * If it is a SCORM package, first removes its extracted folder on disk.
167
     */
168
    public function delete(?Asset $asset = null): void
169
    {
170
        if (null === $asset) {
171
            return;
172
        }
173
174
        // If it is a SCORM package, try to remove its on-disk content (folder or ZIP)
175
        if (Asset::SCORM === $asset->getCategory()) {
176
            $path = $this->getFolder($asset); // may be an extracted folder or a .zip file
177
178
            if ($path) {
179
                try {
180
                    if ($this->filesystem->directoryExists($path)) {
181
                        $this->filesystem->deleteDirectory($path);
182
                    } elseif ($this->filesystem->fileExists($path)) {
183
                        $this->filesystem->delete($path);
184
                    } else {
185
                        // Local filesystem fallbacks (log only on true failure)
186
                        if (@is_dir($path)) {
187
                            $it = new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);
188
                            $ri = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
189
                            foreach ($ri as $file) {
190
                                $ok = $file->isDir()
191
                                    ? @rmdir($file->getPathname())
192
                                    : @unlink($file->getPathname());
193
                                if (!$ok) {
194
                                    error_log('[AssetRepository::delete] Failed to remove path: '.$file->getPathname());
195
                                }
196
                            }
197
                            if (!@rmdir($path)) {
198
                                error_log('[AssetRepository::delete] Failed to remove directory: '.$path);
199
                            }
200
                        } elseif (@is_file($path)) {
201
                            if (!@unlink($path)) {
202
                                error_log('[AssetRepository::delete] Failed to remove file: '.$path);
203
                            }
204
                        }
205
                    }
206
                } catch (\Throwable $e) {
207
                    error_log('[AssetRepository::delete] Exception while removing SCORM path '.$path.' - '.$e->getMessage());
208
                }
209
            }
210
        }
211
212
        // Remove the asset record from the database
213
        $em = $this->getEntityManager();
214
        $em->remove($asset);
215
        $em->flush();
216
    }
217
}
218