Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
41.03% covered (danger)
41.03%
32 / 78
23.08% covered (danger)
23.08%
3 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
ZipExporter
41.03% covered (danger)
41.03%
32 / 78
23.08% covered (danger)
23.08%
3 / 13
214.60
0.00% covered (danger)
0.00%
0 / 1
 getLabel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExportForm
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 export
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 processExport
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
 normalizeClassName
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getSavePath
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 createZipFile
78.79% covered (warning)
78.79%
26 / 33
0.00% covered (danger)
0.00%
0 / 1
10.95
 getServiceManager
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFileManagement
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getMediaResourcePreparer
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSharedStimulusCSSExporter
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getClassResources
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getResourceLink
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2014-2021 (original work) Open Assessment Technologies SA;
19 */
20
21declare(strict_types=1);
22
23namespace oat\taoMediaManager\model;
24
25use common_Exception;
26use common_exception_UserReadableException;
27use common_report_Report as Report;
28use core_kernel_classes_Class;
29use core_kernel_classes_Container;
30use core_kernel_classes_EmptyProperty;
31use core_kernel_classes_Literal;
32use core_kernel_classes_Property;
33use core_kernel_classes_Resource;
34use Exception;
35use oat\oatbox\log\LoggerAwareTrait;
36use oat\oatbox\service\ServiceManager;
37use oat\taoMediaManager\model\export\service\MediaResourcePreparerInterface;
38use oat\taoMediaManager\model\export\service\SharedStimulusCSSExporter;
39use oat\taoMediaManager\model\fileManagement\FileManagement;
40use tao_helpers_Export;
41use tao_models_classes_export_ExportHandler;
42use ZipArchive;
43
44/**
45 * Service methods to manage the Media
46 *
47 * @access  public
48 * @author  Antoine Robin, <antoine.robin@vesperiagroup.com>
49 * @package taoMediaManager
50 */
51class ZipExporter implements tao_models_classes_export_ExportHandler
52{
53    use LoggerAwareTrait;
54
55    /**
56     * @inheritDoc
57     */
58    public function getLabel()
59    {
60        return __('Zip');
61    }
62
63    /**
64     * @inheritDoc
65     */
66    public function getExportForm(core_kernel_classes_Resource $resource)
67    {
68        return (new ZipExportForm(['resource' => $resource]))
69            ->getForm();
70    }
71
72    /**
73     * @inheritDoc
74     */
75    public function export($formValues, $destPath)
76    {
77        if (!isset($formValues['filename'])) {
78            return Report::createFailure('Missing filename for export using ' . __CLASS__);
79        }
80
81        if (!isset($formValues['id'])) {
82            return Report::createFailure('No id for export using ' . __CLASS__);
83        }
84
85        $report = Report::createSuccess();
86
87        $report->setData($this->processExport($formValues));
88        $report->setMessage(__('Media successfully exported.'));
89
90        return $report;
91    }
92
93    private function processExport(array $formValues): string
94    {
95        $class = new core_kernel_classes_Class($formValues['id']);
96        $exportClasses = [];
97
98        if ($class->isClass()) {
99            $exportData = [
100                $class->getLabel() => $this->getClassResources($class)
101            ];
102
103            foreach ($class->getSubClasses(true) as $subClass) {
104                $instances = $this->getClassResources($subClass);
105
106                if (count($instances) === 0) {
107                    continue;
108                }
109
110                $exportData[$subClass->getLabel()] = $instances;
111
112                $exportClasses[$subClass->getLabel()] = $this->normalizeClassName($subClass, $exportClasses);
113            }
114        } else {
115            $exportData = [$class->getLabel() => [$class]];
116        }
117
118        $safePath = $this->getSavePath($formValues['filename']);
119
120        return $this->createZipFile($safePath, $exportClasses, $exportData);
121    }
122
123    private function normalizeClassName(core_kernel_classes_Class $class, array $exportClasses): string
124    {
125        $parents = $class->getParentClasses();
126        $parent = array_shift($parents);
127
128        return array_key_exists($parent->getLabel(), $exportClasses)
129            ? $exportClasses[$parent->getLabel()] . '/' . $class->getLabel()
130            : $class->getLabel();
131    }
132
133    private function getSavePath(string $unsafePath): string
134    {
135        $pathInfo = pathinfo($unsafePath);
136        $safePath = $pathInfo['filename'];
137
138        if (array_key_exists('extension', $pathInfo)) {
139            $safePath .= '.' . $pathInfo['extension'];
140        }
141
142        return $safePath;
143    }
144
145    /**
146     * @throws common_Exception
147     * @throws Exception
148     */
149    protected function createZipFile($filename, array $exportClasses = [], array $exportFiles = []): string
150    {
151        try {
152            $errors = [];
153
154            $baseDir = tao_helpers_Export::getExportPath();
155            $path = $baseDir . '/' . $filename . '.zip';
156
157            $zip = new ZipArchive();
158            if ($zip->open($path, ZipArchive::CREATE) !== true) {
159                throw new common_Exception('Unable to create zipfile ' . $path);
160            }
161
162            if ($zip->numFiles !== 0) {
163                $zip->close();
164
165                return $path;
166            }
167
168            foreach ($exportFiles as $label => $files) {
169                $archivePath = '';
170
171                /** @var $class core_kernel_classes_Class */
172                if (array_key_exists($label, $exportClasses)) {
173                    $archivePath = $exportClasses[$label] . '/';
174
175                    $zip->addEmptyDir($archivePath);
176                }
177
178                //create the directory
179
180                /** @var core_kernel_classes_Resource $fileResource */
181                foreach ($files as $fileResource) {
182                    try {
183                        $link = $this->getResourceLink($fileResource);
184
185                        $fileContent = $this->getFileManagement()
186                            ->getFileStream($link);
187
188                        $preparedFileContent = $this->getMediaResourcePreparer()->prepare($fileResource, $fileContent);
189                        $zip->addFromString($archivePath . $fileResource->getLabel(), $preparedFileContent);
190
191                        $this->getSharedStimulusCSSExporter()->pack($fileResource, $link, $zip);
192                    } catch (common_exception_UserReadableException $exception) {
193                        $errors[] = sprintf("Error in Asset class \"%s\": %s", $label, $exception->getUserMessage());
194                    }
195                }
196            }
197
198            if (!empty($errors)) {
199                throw new ZipExporterFileErrorList($errors);
200            }
201
202            $zip->close();
203
204            return $path;
205        } catch (Exception $e) {
206            $this->getLogger()->error($e->getMessage() . $e->getTraceAsString());
207
208            $zip->close();
209
210            if (is_file($path)) {
211                unlink($path);
212            }
213
214            throw $e;
215        }
216    }
217
218    public function getServiceManager()
219    {
220        return ServiceManager::getServiceManager();
221    }
222
223    private function getFileManagement(): FileManagement
224    {
225        return $this->getServiceManager()->get(FileManagement::SERVICE_ID);
226    }
227
228    private function getMediaResourcePreparer(): MediaResourcePreparerInterface
229    {
230        return $this->getServiceManager()->get(MediaResourcePreparerInterface::SERVICE_ID);
231    }
232
233    private function getSharedStimulusCSSExporter(): SharedStimulusCSSExporter
234    {
235        return $this->getServiceManager()->get(SharedStimulusCSSExporter::class);
236    }
237
238    private function getClassResources(core_kernel_classes_Class $class): array
239    {
240        return $class->getInstances();
241    }
242
243    /**
244     * @return core_kernel_classes_Container|string
245     *
246     * @throws core_kernel_classes_EmptyProperty
247     * @throws common_Exception
248     */
249    private function getResourceLink(core_kernel_classes_Resource $resource)
250    {
251        $link = $resource->getUniquePropertyValue(
252            new core_kernel_classes_Property(TaoMediaOntology::PROPERTY_LINK)
253        );
254
255        return $link instanceof core_kernel_classes_Literal ? $link->literal : $link;
256    }
257}