Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
StimulusHandler
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 10
342
0.00% covered (danger)
0.00%
0 / 1
 isApplicable
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 handle
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 safePath
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 encodeStimulusImages
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
42
 finalize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setItemSource
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setQtiItem
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getItemSource
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getQtiItem
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 assertAttribute
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
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) 2018 (original work) Open Assessment Technologies SA;
19 *
20 */
21
22namespace oat\taoQtiItem\model\qti\asset\handler;
23
24use oat\taoItems\model\media\LocalItemSource;
25use oat\taoQtiItem\model\qti\Element;
26use oat\taoQtiItem\model\qti\Item;
27
28/**
29 * Class StimulusHandler
30 *
31 * The purpose of this handler is to extract xml stimulus from item to copy them
32 * If the included stimulus includes images, they should be encoded to avoid file dependencies
33 *
34 * @package oat\taoQtiItem\model\qti\asset\handler
35 */
36class StimulusHandler implements AssetHandler
37{
38    /**
39     * The object representation of qti item
40     *
41     * @var Item
42     */
43    protected $qtiItem;
44
45    /**
46     * The destination of the item directory
47     *
48     * @var LocalItemSource
49     */
50    protected $itemSource;
51
52    /**
53     * Applicable to all items that contain <xinclude> tag
54     *
55     * @param $relativePath
56     * @return bool
57     * @throws \common_exception_MissingParameter
58     */
59    public function isApplicable($relativePath)
60    {
61        $xincluded = [];
62        /** @var Element $xincludeElement */
63        foreach ($this->getQtiItem()->getComposingElements('oat\taoQtiItem\model\qti\Xinclude') as $xincludeElement) {
64            $xincluded[] = $xincludeElement->attr('href');
65            \common_Logger::i("Xinclude component found in resource '" .
66                $this->getQtiItem()->getIdentifier() . "' with href '" . $xincludeElement->attr('href') . "'.");
67        }
68
69        return in_array($relativePath, $xincluded);
70    }
71
72    /**
73     * Store locally, in a safe directory
74     * Encoded all stimulus images to avoid file dependencies
75     *
76     * @param $absolutePath
77     * @param $relativePath
78     * @return array|mixed
79     * @throws \common_Exception
80     */
81    public function handle($absolutePath, $relativePath)
82    {
83        $safePath = $this->safePath($relativePath);
84        $this->encodeStimulusImages($absolutePath);
85
86        $info = $this->getItemSource()->add($absolutePath, basename($absolutePath), $safePath);
87        \common_Logger::i('Stimulus file \'' . $absolutePath . '\' copied.');
88
89        return $info;
90    }
91
92    /**
93     * Remove ../ to secure path
94     *
95     * @param $path
96     * @return string
97     */
98    protected function safePath($path)
99    {
100        $safePath = '';
101        if (dirname($path) !== '.') {
102            $safePath = str_replace('../', '', dirname($path)) . '/';
103        }
104        return $safePath;
105    }
106
107    /**
108     * Walk into stimulus file to transform images from path to base64encoded data:image
109     *
110     * @param $absolutePath
111     * @throws \common_Exception
112     */
113    protected function encodeStimulusImages($absolutePath)
114    {
115        if (!is_readable($absolutePath) || !is_writable($absolutePath)) {
116            throw new \common_Exception('Stimulus cannot be imported, asset file is not readable/writable.');
117        }
118        $dom = new \DOMDocument('1.0', 'UTF-8');
119        $dom->loadXML(file_get_contents($absolutePath));
120        $images = $dom->getElementsByTagName('img');
121
122        for ($i = 0; $i < $images->length; $i++) {
123            $imageFile = dirname($absolutePath) . DIRECTORY_SEPARATOR
124                . ltrim($images->item($i)->getAttribute('src'), DIRECTORY_SEPARATOR);
125
126            if (is_readable($imageFile)) {
127                $encodedSrc = 'data:image/png;base64,' . base64_encode(file_get_contents($imageFile));
128                $images->item($i)->setAttribute('src', $encodedSrc);
129            }
130        }
131        if (isset($encodedSrc)) {
132            file_put_contents($absolutePath, $dom->saveXML());
133        }
134    }
135
136    public function finalize()
137    {
138        // Nothing to do
139    }
140
141    /**
142     * @param LocalItemSource $itemSource
143     * @return $this
144     */
145    public function setItemSource(LocalItemSource $itemSource)
146    {
147        $this->itemSource = $itemSource;
148        return $this;
149    }
150
151    /**
152     * @param Item $qtiItem
153     * @return $this
154     */
155    public function setQtiItem(Item $qtiItem)
156    {
157        $this->qtiItem = $qtiItem;
158        return $this;
159    }
160
161    /**
162     * @return LocalItemSource
163     * @throws \common_exception_MissingParameter
164     */
165    protected function getItemSource()
166    {
167        $this->assertAttribute('itemSource');
168        return $this->itemSource;
169    }
170
171    /**
172     * @return Item
173     * @throws \common_exception_MissingParameter
174     */
175    protected function getQtiItem()
176    {
177        $this->assertAttribute('qtiItem');
178        return $this->qtiItem;
179    }
180
181    /**
182     * @param $attribute
183     * @throws \common_exception_MissingParameter
184     */
185    private function assertAttribute($attribute)
186    {
187        if (!isset($this->$attribute)) {
188            throw new \common_exception_MissingParameter($attribute, 'stimulusImporter');
189        }
190    }
191}