Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
RestQtiItem
0.00% covered (danger)
0.00%
0 / 131
0.00% covered (danger)
0.00%
0 / 11
1260
0.00% covered (danger)
0.00%
0 / 1
 getAcceptableMimeTypes
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
2
 getDestinationClass
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 index
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 import
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
72
 getTaskName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 importDeferred
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
20
 addExtraReturnData
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getUploadedPackage
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 createQtiItem
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 export
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
30
 createClass
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
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 */
19
20namespace oat\taoQtiItem\controller;
21
22use oat\tao\model\TaoOntology;
23use oat\tao\model\taskQueue\TaskLog\Entity\EntityInterface;
24use oat\tao\model\taskQueue\TaskLogInterface;
25use Request;
26use oat\taoQtiItem\model\qti\ImportService;
27use oat\taoQtiItem\model\ItemModel;
28use oat\generis\model\OntologyAwareTrait;
29use oat\taoQtiItem\model\qti\exception\ExtractException;
30use oat\taoQtiItem\model\qti\exception\ParsingException;
31use oat\taoQtiItem\model\Export\QTIPackedItemExporter;
32use oat\taoQtiItem\model\tasks\ImportQtiItem;
33
34/**
35 * End point of Rest item API
36 *
37 * @author Absar Gilani, <absar.gilani6@gmail.com>
38 * @author Gyula Szucs <gyula@taotesting.com>
39 */
40class RestQtiItem extends AbstractRestQti
41{
42    use OntologyAwareTrait;
43
44    public const RESTITEM_PACKAGE_NAME = 'content';
45
46    /**
47     * @inherit
48     */
49    protected function getAcceptableMimeTypes()
50    {
51        return
52            [
53                "application/json",
54                "text/xml",
55                "application/xml",
56                "application/rdf+xml" ,
57                "application/zip",
58            ];
59    }
60
61    /**
62     * Class items will be created in
63     *
64     * @return \core_kernel_classes_Class
65     */
66    protected function getDestinationClass()
67    {
68        return $this->getClassFromRequest(new \core_kernel_classes_Class(TaoOntology::ITEM_CLASS_URI));
69    }
70
71    /**
72     * Only import method is available, so index return failure response
73     */
74    public function index()
75    {
76        $this->returnFailure(new \common_exception_NotImplemented('This API does not support this call.'));
77    }
78
79    /**
80     * Import file entry point by using $this->service
81     * Check POST method & get valid uploaded file
82     */
83    public function import()
84    {
85        try {
86            if ($this->getRequestMethod() != Request::HTTP_POST) {
87                throw new \common_exception_NotImplemented('Only post method is accepted to import Qti package.');
88            }
89
90            // Get valid package parameter
91            $package = $this->getUploadedPackage();
92
93            // Call service to import package
94            \helpers_TimeOutHelper::setTimeOutLimit(\helpers_TimeOutHelper::LONG);
95            $report = ImportService::singleton()->importQTIPACKFile(
96                $package,
97                $this->getDestinationClass(),
98                true,
99                true,
100                true,
101                $this->isMetadataGuardiansEnabled(),
102                $this->isMetadataValidatorsEnabled(),
103                $this->isItemMustExistEnabled(),
104                $this->isItemMustBeOverwrittenEnabled()
105            );
106            \helpers_TimeOutHelper::reset();
107
108            \tao_helpers_File::remove($package);
109            if ($report->getType() !== \common_report_Report::TYPE_SUCCESS) {
110                $message = __("An unexpected error occurred during the import of the IMS QTI Item Package. ");
111                //get message of first error report
112                if (!empty($report->getErrors())) {
113                    $message .= $report->getErrors()[0]->getMessage();
114                }
115                $this->returnFailure(new \common_Exception($message));
116            } else {
117                $itemIds = [];
118                /** @var \common_report_Report $subReport */
119                foreach ($report as $subReport) {
120                    $itemIds[] = $subReport->getData()->getUri();
121                }
122                $this->returnSuccess(['items' => $itemIds]);
123            }
124        } catch (ExtractException $e) {
125            $this->returnFailure(
126                new \common_Exception(
127                    __('The ZIP archive containing the IMS QTI Item cannot be extracted.')
128                )
129            );
130        } catch (ParsingException $e) {
131            $this->returnFailure(
132                new \common_Exception(
133                    __('The ZIP archive does not contain an imsmanifest.xml file or is an invalid ZIP archive.')
134                )
135            );
136        } catch (\Exception $e) {
137            $this->returnFailure($e);
138        }
139    }
140
141    /**
142     * @inheritdoc
143     */
144    protected function getTaskName()
145    {
146        return ImportQtiItem::class;
147    }
148
149    /**
150     * Import item package through the task queue.
151     */
152    public function importDeferred()
153    {
154        try {
155            if ($this->getRequestMethod() != Request::HTTP_POST) {
156                throw new \common_exception_NotImplemented('Only post method is accepted to import Qti package.');
157            }
158
159            $task = ImportQtiItem::createTask(
160                $this->getUploadedPackage(),
161                $this->getDestinationClass(),
162                $this->getServiceLocator(),
163                $this->isMetadataGuardiansEnabled(),
164                $this->isMetadataValidatorsEnabled(),
165                $this->isItemMustExistEnabled(),
166                $this->isItemMustBeOverwrittenEnabled()
167            );
168
169            $result = [
170                'reference_id' => $task->getId()
171            ];
172
173            /** @var TaskLogInterface $taskLog */
174            $taskLog = $this->getServiceManager()->get(TaskLogInterface::SERVICE_ID);
175
176            if ($report = $taskLog->getReport($task->getId())) {
177                $result['report'] = $report->toArray();
178            }
179
180            return $this->returnSuccess($result);
181        } catch (\Exception $e) {
182            return $this->returnFailure($e);
183        }
184    }
185
186    /**
187     * Add extra values to the JSON returned.
188     *
189     * @param EntityInterface $taskLogEntity
190     * @return array
191     */
192    protected function addExtraReturnData(EntityInterface $taskLogEntity)
193    {
194        $data = [];
195
196        if ($taskLogEntity->getReport()) {
197            $plainReport = $this->getPlainReport($taskLogEntity->getReport());
198
199            //the third report is report of import test
200            $itemsReport = array_slice($plainReport, 2);
201            foreach ($itemsReport as $itemReport) {
202                if (isset($itemReport->getData()['uriResource'])) {
203                    $data['itemIds'][] = $itemReport->getData()['uriResource'];
204                }
205            }
206        }
207
208        return $data;
209    }
210
211    /**
212     * Return a valid uploaded file
213     *
214     * @return string
215     * @throws \common_Exception
216     * @throws \common_exception_Error
217     * @throws \common_exception_MissingParameter
218     * @throws \common_exception_BadRequest
219     * @throws \oat\tao\helpers\FileUploadException
220     */
221    protected function getUploadedPackage()
222    {
223        if (!$this->hasRequestParameter(self::RESTITEM_PACKAGE_NAME)) {
224            throw new \common_exception_MissingParameter(self::RESTITEM_PACKAGE_NAME, __CLASS__);
225        }
226
227        $file = \tao_helpers_Http::getUploadedFile(self::RESTITEM_PACKAGE_NAME);
228
229        if (!in_array($file['type'], self::$accepted_types)) {
230            throw new \common_exception_BadRequest('Uploaded file has to be a valid archive.');
231        }
232
233        $pathinfo = pathinfo($file['tmp_name']);
234        $destination = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . $file['name'];
235        \tao_helpers_File::move($file['tmp_name'], $destination);
236
237        return $destination;
238    }
239
240    /**
241     * Create an empty item
242     */
243    public function createQtiItem()
244    {
245        try {
246            // Check if it's post method
247            if ($this->getRequestMethod() != Request::HTTP_POST) {
248                throw new \common_exception_NotImplemented('Only post method is accepted to create empty item.');
249            }
250
251            $label = $this->hasRequestParameter('label') ? $this->getRequestParameter('label') : '';
252            // Call service to import package
253            $item = $this->getDestinationClass()->createInstance($label);
254
255            //set the QTI type
256            $itemService = \taoItems_models_classes_ItemsService::singleton();
257            $itemService->setItemModel($item, $this->getResource(ItemModel::MODEL_URI));
258
259            $this->returnSuccess($item->getUri());
260        } catch (\Exception $e) {
261            $this->returnFailure($e);
262        }
263    }
264
265    /**
266     * render an item as a Qti zip package
267     * @author christophe GARCIA <christopheg@taotesting.com>
268     */
269    public function export()
270    {
271
272        try {
273            if ($this->getRequestMethod() != Request::HTTP_GET) {
274                throw new \common_exception_NotImplemented('Only GET method is accepted to export QIT Item.');
275            }
276
277            if (!$this->hasRequestParameter('id')) {
278                $this->returnFailure(new \common_exception_MissingParameter('required parameter `id` is missing'));
279            }
280
281            $id = $this->getRequestParameter('id');
282
283            $item = new \core_kernel_classes_Resource($id);
284
285            $itemService = \taoItems_models_classes_ItemsService::singleton();
286
287            if ($itemService->hasItemModel($item, [ItemModel::MODEL_URI])) {
288                $path = \tao_helpers_Export::getExportFile();
289                $tmpZip = new \ZipArchive();
290                $tmpZip->open($path, \ZipArchive::CREATE);
291
292                $exporter = new QTIPackedItemExporter($item, $tmpZip);
293                $exporter->export(['apip' => false]);
294
295                $exporter->getZip()->close();
296
297                header('Content-Type: application/zip');
298                \tao_helpers_Http::returnFile($path, false);
299
300                return;
301            } else {
302                $this->returnFailure(new \common_exception_NotFound('item can\'t be found'));
303            }
304        } catch (\Exception $e) {
305            $this->returnFailure($e);
306        }
307    }
308
309    /**
310     * Create an Item Class
311     *
312     * Label parameter is mandatory
313     * If parent class parameter is an uri of valid test class, new class will be created under it
314     * If not parent class parameter is provided, class will be created under root class
315     * Comment parameter is not mandatory, used to describe new created class
316     *
317     * @return \core_kernel_classes_Class
318     */
319    public function createClass()
320    {
321        try {
322            $class = $this->createSubClass(new \core_kernel_classes_Class(TaoOntology::ITEM_CLASS_URI));
323
324            $result = [
325                'message' => __('Class successfully created.'),
326                'class-uri' => $class->getUri(),
327            ];
328
329            $this->returnSuccess($result);
330        } catch (\common_exception_ClassAlreadyExists $e) {
331            $result = [
332                'message' => $e->getMessage(),
333                'class-uri' => $e->getClass()->getUri(),
334            ];
335            $this->returnSuccess($result);
336        } catch (\Exception $e) {
337            $this->returnFailure($e);
338        }
339    }
340}