Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
SharedStimulusPackageImporter
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 11
650
0.00% covered (danger)
0.00%
0 / 1
 import
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
12
 edit
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 getSharedStimulusFile
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 getSharedStimulusStylesheets
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 isFileExtension
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 storeSharedStimulus
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
 replaceSharedStimulus
0.00% covered (danger)
0.00%
0 / 27
0.00% covered (danger)
0.00%
0 / 1
12
 getDecodedUri
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 getMediaService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSharedStimulusStoreService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSharedStimulusMediaEncoderService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 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_Error;
27use common_exception_UserReadableException;
28use oat\oatbox\reporting\Report as Report;
29use core_kernel_classes_Class;
30use core_kernel_classes_Resource as Resource;
31use Exception;
32use oat\taoMediaManager\model\sharedStimulus\encoder\SharedStimulusMediaEncoder;
33use oat\taoMediaManager\model\sharedStimulus\encoder\SharedStimulusMediaEncoderInterface;
34use oat\taoMediaManager\model\sharedStimulus\service\StoreService;
35use qtism\data\storage\xml\XmlStorageException;
36use RecursiveDirectoryIterator;
37use RecursiveIteratorIterator;
38use SplFileInfo;
39use tao_helpers_File;
40use tao_helpers_form_Form as Form;
41use tao_helpers_Uri;
42
43/**
44 * Service methods to manage the Media
45 *
46 * @access public
47 * @package taoMediaManager
48 */
49class SharedStimulusPackageImporter extends ZipImporter
50{
51    /**
52     * Starts the import based on the form
53     *
54     * @param core_kernel_classes_Class $class
55     * @param Form|array $form
56     * @param string|null $userId owner of the resource
57     * @return Report
58     */
59    public function import($class, $form, $userId = null): Report
60    {
61        try {
62            $uploadedFile = $this->fetchUploadedFile($form);
63            $extractPath = $this->extractArchive($uploadedFile);
64
65            $xmlFile = $this->getSharedStimulusFile($extractPath);
66            $cssFiles = $this->getSharedStimulusStylesheets($extractPath);
67
68            $this->getUploadService()->remove($uploadedFile);
69
70            // throws an exception of invalid
71            SharedStimulusImporter::isValidSharedStimulus($xmlFile);
72
73            $embeddedFile = $this->getSharedStimulusMediaEncoderService()->encodeAssets($xmlFile);
74
75            $report = Report::createSuccess(__('Shared Stimulus imported successfully'));
76
77            $subReport = $this->storeSharedStimulus(
78                $class,
79                $this->getDecodedUri($form),
80                $embeddedFile,
81                $cssFiles,
82                $userId
83            );
84
85            $report->add($subReport);
86        } catch (Exception $e) {
87            $message = $e instanceof common_exception_UserReadableException
88                ? $e->getUserMessage()
89                : __('An error has occurred. Please contact your administrator.');
90            $report = Report::createFailure($message);
91            $this->logError($e->getMessage());
92        }
93
94        return $report;
95    }
96
97    /**
98     * Edit a shared stimulus package
99     *
100     * @param Resource $instance
101     * @param Form|array $form
102     * @param null|string $userId
103     * @return Report
104     */
105    public function edit(Resource $instance, $form, $userId = null)
106    {
107        try {
108            $uploadedFile = $this->fetchUploadedFile($form);
109            $extractPath = $this->extractArchive($uploadedFile);
110
111            $xmlFile = $this->getSharedStimulusFile($extractPath);
112
113            $this->getUploadService()->remove($uploadedFile);
114
115            // throws an exception of invalid
116            SharedStimulusImporter::isValidSharedStimulus($xmlFile);
117
118            $embeddedFile = $this->getSharedStimulusMediaEncoderService()->encodeAssets($xmlFile);
119
120            $report = $this->replaceSharedStimulus($instance, $this->getDecodedUri($form), $embeddedFile, $userId);
121        } catch (Exception $e) {
122            $message = $e instanceof common_exception_UserReadableException
123                ? $e->getUserMessage()
124                : __('An error has occurred. Please contact your administrator.');
125            $report = Report::createFailure($message);
126            $this->logError($e->getMessage());
127            $report->setData(['uriResource' => '']);
128        }
129
130        return $report;
131    }
132
133    /**
134     * Get the shared stimulus file with assets from the zip
135     *
136     * @return string path to the xml
137     *
138     * @throws common_Exception
139     */
140    private function getSharedStimulusFile(string $extractPath): string
141    {
142        $iterator = new RecursiveIteratorIterator(
143            new RecursiveDirectoryIterator($extractPath),
144            RecursiveIteratorIterator::LEAVES_ONLY
145        );
146
147        /** @var $file SplFileInfo */
148        foreach ($iterator as $file) {
149            //check each file to see if it can be the shared stimulus file
150            if ($this->isFileExtension($file, 'xml')) {
151                return $file->getRealPath();
152            }
153        }
154
155        throw new common_Exception('XML not found in the package');
156    }
157
158    /**
159     * Get an additional CSS stylesheet for the shared stimulus (If exists)
160     *
161     * @return array path to the CSS or false if not found
162     */
163    private function getSharedStimulusStylesheets(string $extractPath): array
164    {
165        $iterator = new RecursiveIteratorIterator(
166            new RecursiveDirectoryIterator($extractPath),
167            RecursiveIteratorIterator::LEAVES_ONLY
168        );
169
170        $cssFileInfoArray = [];
171
172        /** @var $file SplFileInfo */
173        foreach ($iterator as $file) {
174            if ($this->isFileExtension($file, 'css')) {
175                $cssFileInfoArray[] = $file->getRealPath();
176            }
177        }
178
179        return $cssFileInfoArray;
180    }
181
182    public function isFileExtension(SplFileInfo $file, string $extension): bool
183    {
184        if ($file->isFile()) {
185            return preg_match('/^[\w]/', $file->getFilename()) === 1 && $file->getExtension() === $extension;
186        }
187
188        return false;
189    }
190
191    /**
192     * Convert file linked inside and store it into media manager
193     *
194     * @throws common_exception_Error
195     */
196    private function storeSharedStimulus(
197        Resource $class,
198        string $lang,
199        string $xmlFile,
200        array $cssFiles,
201        string $userId = null
202    ): Report {
203        $stimulusFilename = basename($xmlFile);
204
205        $directory = $this->getSharedStimulusStoreService()->store(
206            $xmlFile,
207            $stimulusFilename,
208            $cssFiles
209        );
210
211        $mediaResourceUri = $this->getMediaService()->createSharedStimulusInstance(
212            $directory . DIRECTORY_SEPARATOR . $stimulusFilename,
213            $class->getUri(),
214            $lang,
215            $userId
216        );
217
218        if ($mediaResourceUri !== false) {
219            $report = Report::createSuccess(__('Imported %s', basename($xmlFile)));
220            $report->setData(['uriResource' => $mediaResourceUri]);
221        } else {
222            $report = Report::createFailure(__('Fail to import Shared Stimulus'));
223            $report->setData(['uriResource' => '']);
224        }
225
226        return $report;
227    }
228
229    /**
230     * Validate an xml file, convert file linked inside and store it into media manager
231     *
232     * @throws common_exception_Error
233     * @throws XmlStorageException
234     */
235    protected function replaceSharedStimulus(
236        Resource $instance,
237        string $lang,
238        string $xmlFile,
239        string $userId = null
240    ): Report {
241        //if the class does not belong to media classes create a new one with its name (for items)
242        $mediaClass = new core_kernel_classes_Class(
243            TaoMediaOntology::CLASS_URI_MEDIA_ROOT
244        );
245
246        if (!$instance->isInstanceOf($mediaClass)) {
247            $report = Report::createFailure(
248                'The instance ' . $instance->getUri() . ' is not a Media instance'
249            );
250            $report->setData(['uriResource' => '']);
251            return $report;
252        }
253
254        SharedStimulusImporter::isValidSharedStimulus($xmlFile);
255        $name = basename($xmlFile, '.xml');
256        $name .= '.xhtml';
257        $filepath = dirname($xmlFile) . '/' . $name;
258        tao_helpers_File::copy($xmlFile, $filepath);
259
260        if (!$this->getMediaService()->editMediaInstance($filepath, $instance->getUri(), $lang, $userId)) {
261            $report = Report::createFailure(__('Fail to edit Shared Stimulus'));
262        } else {
263            $report = Report::createSuccess(__('Shared Stimulus edited successfully'));
264            $report->add(
265                Report::createSuccess(
266                    __('Edited %s', $instance->getLabel()),
267                    [
268                        'uriResource' => $instance->getUri()
269                    ]
270                )
271            );
272        }
273
274        $report->setData(['uriResource' => $instance->getUri()]);
275
276        return $report;
277    }
278
279    /**
280     * @param array|Form $form
281     */
282    private function getDecodedUri($form): string
283    {
284        return tao_helpers_Uri::decode($form instanceof Form ? $form->getValue('lang') : $form['lang']);
285    }
286
287    private function getMediaService(): MediaService
288    {
289        return $this->getServiceLocator()->get(MediaService::class);
290    }
291
292    private function getSharedStimulusStoreService(): StoreService
293    {
294        return $this->getServiceLocator()->get(StoreService::class);
295    }
296
297    private function getSharedStimulusMediaEncoderService(): SharedStimulusMediaEncoderInterface
298    {
299        return $this->getServiceLocator()->get(SharedStimulusMediaEncoderInterface::SERVICE_ID);
300    }
301}