Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 119
0.00% covered (danger)
0.00%
0 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
taoQtiTest_actions_Creator
0.00% covered (danger)
0.00%
0 / 119
0.00% covered (danger)
0.00%
0 / 11
1122
0.00% covered (danger)
0.00%
0 / 1
 index
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
42
 getTest
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 saveTest
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
132
 getIdentifier
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 getCurrentTest
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getRuntimeConfig
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getRemoteListService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isScaleEnabled
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRemoteScaleListService
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getScalePresets
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
20
 getScaleProcessor
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) 2013-2024 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 */
20
21use oat\generis\model\OntologyAwareTrait;
22use oat\taoBackOffice\model\lists\ListService;
23use oat\taoQtiItem\model\qti\metadata\exporter\scale\ScalePreprocessor;
24use oat\taoQtiItem\model\QtiCreator\Scales\RemoteScaleListService;
25use oat\taoQtiTest\models\TestCategoryPresetProvider;
26use oat\taoQtiTest\models\TestModelService;
27use oat\generis\model\data\event\ResourceUpdated;
28use oat\oatbox\event\EventManager;
29
30use function GuzzleHttp\Psr7\stream_for;
31
32/**
33 *  QTI test Creator Controller.
34 *
35 * @author Bertrand Chevrier <bertrand@taotesting.com>
36 * @package taoQtiTest
37 * @license GPLv2  http://www.opensource.org/licenses/gpl-2.0.php
38 */
39class taoQtiTest_actions_Creator extends tao_actions_CommonModule
40{
41    use OntologyAwareTrait;
42
43    /**
44     * Render the creator base view
45     */
46    public function index()
47    {
48        $labels = [];
49        $testUri = $this->getRequestParameter('uri');
50        $testModel = $this->getServiceManager()->get(TestModelService::SERVICE_ID);
51
52        // Add support for translation and side-by-side view
53        $originResourceUri = $this->getRequestParameter('originResourceUri');
54        $this->setData('translation', $this->getRequestParameter('translation') ?? "false");
55        $this->setData('originResourceUri', json_encode($originResourceUri));
56
57        $items = $testModel->getItems(new core_kernel_classes_Resource(tao_helpers_Uri::decode($testUri)));
58        foreach ($items as $item) {
59            $labels[$item->getUri()] = $item->getLabel();
60        }
61        $this->setData(
62            'labels',
63            json_encode(tao_helpers_Uri::encodeArray($labels, tao_helpers_Uri::ENCODE_ARRAY_KEYS))
64        );
65
66        $runtimeConfig = $this->getRuntimeConfig();
67        $categoriesPresetService = $this->getServiceManager()->get(TestCategoryPresetProvider::SERVICE_ID);
68        $this->setData('categoriesPresets', json_encode($categoriesPresetService->getAvailablePresets($runtimeConfig)));
69
70        $this->setData('loadUrl', _url('getTest', null, null, ['uri' => $testUri]));
71        $this->setData('loadOriginUrl', _url('getTest', null, null, ['uri' => $originResourceUri]));
72        $this->setData('saveUrl', _url('saveTest', null, null, ['uri' => $testUri]));
73
74        if (common_ext_ExtensionsManager::singleton()->isInstalled('taoBlueprints')) {
75            $this->setData(
76                'blueprintsByIdUrl',
77                _url(
78                    'getBlueprintsByIdentifier',
79                    'Blueprints',
80                    'taoBlueprints'
81                )
82            );
83            $this->setData(
84                'blueprintsByTestSectionUrl',
85                _url(
86                    'getBlueprintsByTestSection',
87                    'Blueprints',
88                    'taoBlueprints',
89                    ['test' => $testUri]
90                )
91            );
92        }
93
94        $this->setData('identifierUrl', _url('getIdentifier', null, null, ['uri' => $testUri]));
95
96        if ($this->isScaleEnabled()) {
97            $this->setData(
98                'scalesPresets',
99                $this->getScalePresets()
100            );
101        } else {
102            $this->setData('scalesPresets', json_encode([]));
103        }
104
105        $guidedNavigation = false;
106        if (is_array($runtimeConfig) && isset($runtimeConfig['guidedNavigation'])) {
107            $guidedNavigation = $runtimeConfig['guidedNavigation'];
108        }
109        $this->setData('guidedNavigation', json_encode($guidedNavigation == true));
110
111        $this->setView('creator.tpl');
112    }
113
114    /**
115     * Get json's test content, the uri of the test must be provided in parameter
116     */
117    public function getTest()
118    {
119        $test = $this->getCurrentTest();
120        $qtiTestService = taoQtiTest_models_classes_QtiTestService::singleton();
121
122        $this->setContentHeader('application/json', 'UTF-8');
123
124        $this->response = $this->getPsrResponse()->withBody(stream_for($qtiTestService->getJsonTest($test)));
125    }
126
127    /**
128     * Save a test, test uri and
129     * The request must use the POST method and contains
130     * the test uri and a json string that represents the QTI Test in the model parameter.
131     */
132    public function saveTest()
133    {
134        $saved = false;
135        if ($this->isRequestPost() && $this->getRequest()->accept('application/json')) {
136            if ($this->hasRequestParameter('model')) {
137                $parameters = $this->getRequest()->getRawParameters();
138
139                $test = $this->getCurrentTest();
140                $qtiTestService = taoQtiTest_models_classes_QtiTestService::singleton();
141
142                $saved = $qtiTestService->saveJsonTest($test, $parameters['model']);
143
144                $eventManager = $this->getServiceManager()->get(EventManager::SERVICE_ID);
145                $eventManager->trigger(new ResourceUpdated($test));
146
147                //save the blueprint if the extension is installed
148                if (common_ext_ExtensionsManager::singleton()->isInstalled('taoBlueprints')) {
149                    $testSectionLinkService = $this->getServiceManager()->get(
150                        \oat\taoBlueprints\model\TestSectionLinkService::SERVICE_ID
151                    );
152                    $model = json_decode($parameters['model'], true);
153                    if (isset($model['testParts'])) {
154                        foreach ($model['testParts'] as $testPart) {
155                            if (isset($testPart['assessmentSections'])) {
156                                foreach ($testPart['assessmentSections'] as $section) {
157                                    if (isset($section['blueprint'])) {
158                                        if (!empty($section['blueprint'])) {
159                                            $testSectionLinkService->setBlueprintForTestSection(
160                                                $test,
161                                                $section['identifier'],
162                                                $section['blueprint']
163                                            );
164                                        } else {
165                                            $testSectionLinkService->removeBlueprintForTestSection(
166                                                $test,
167                                                $section['identifier']
168                                            );
169                                        }
170                                    }
171                                }
172                            }
173                        }
174                    }
175                }
176            }
177        }
178        $this->setContentHeader('application/json', 'UTF-8');
179
180        $this->response = $this->getPsrResponse()->withBody(stream_for(json_encode(['saved' => $saved])));
181    }
182
183
184    public function getIdentifier()
185    {
186        $response = [];
187        if ($this->getRequest()->accept('application/json')) {
188            //we need the model as well to keep consistency with the client
189            if ($this->hasRequestParameter('model') && $this->hasRequestParameter('qti-type')) {
190                $parameters = $this->getRequest()->getRawParameters();
191
192                $qtiTestService = taoQtiTest_models_classes_QtiTestService::singleton();
193                $doc = $qtiTestService->fromJson($parameters['model']);
194
195                $identifier = $qtiTestService->getIdentifierFor($doc, $this->getRequestParameter('qti-type'));
196
197                $response = ['identifier' => $identifier];
198            }
199        }
200        $this->setContentHeader('application/json', 'UTF-8');
201
202        $this->response = $this->getPsrResponse()->withBody(stream_for(json_encode($response)));
203    }
204
205
206    /**
207     * Returns the test that is being authored
208     *
209     * @return core_kernel_classes_Resource
210     * @throws tao_models_classes_MissingRequestParameterException
211     */
212    protected function getCurrentTest()
213    {
214        if (!$this->hasRequestParameter('uri')) {
215            throw new tao_models_classes_MissingRequestParameterException('uri');
216        }
217        return new core_kernel_classes_Resource(tao_helpers_Uri::decode($this->getRequestParameter('uri')));
218    }
219
220    /**
221     * Get the runtime config
222     * @return array the configuration
223     */
224    protected function getRuntimeConfig()
225    {
226        $extension = $this
227            ->getServiceLocator()
228            ->get(\common_ext_ExtensionsManager::SERVICE_ID)
229            ->getExtensionById('taoQtiTest');
230        return $extension->getConfig('testRunner');
231    }
232
233    private function getRemoteListService(): ListService
234    {
235        return $this->getServiceManager()->getContainer()->get(ListService::class);
236    }
237
238    private function isScaleEnabled(): bool
239    {
240        return $this->getRemoteScaleListService()->isRemoteListEnabled();
241    }
242
243    private function getRemoteScaleListService(): RemoteScaleListService
244    {
245        return $this->getServiceManager()->getContainer()->get(RemoteScaleListService::class);
246    }
247
248    private function getScalePresets(): string
249    {
250        $listElements = $this->getScaleProcessor()->getScaleRemoteList();
251
252        if (!is_iterable($listElements)) {
253            throw new InvalidArgumentException('List elements should be iterable');
254        }
255
256        $listElements = iterator_to_array(
257            $listElements
258        );
259
260        if (empty($listElements)) {
261            return json_encode([]);
262        }
263
264        $json = json_encode($listElements);
265        if ($json === false) {
266            $this->logWarning('List of elements could not be encoded to JSON');
267            return json_encode([]);
268        }
269
270        return $json;
271    }
272
273    private function getScaleProcessor(): ScalePreprocessor
274    {
275        return $this->getServiceManager()->getContainer()->get(ScalePreprocessor::class);
276    }
277}