Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
24.35% |
56 / 230 |
|
40.00% |
18 / 45 |
CRAP | |
0.00% |
0 / 1 |
Item | |
24.35% |
56 / 230 |
|
40.00% |
18 / 45 |
4429.76 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
addNamespace | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNamespaces | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getNamespace | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addSchemaLocation | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSchemaLocations | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getSchemaLocation | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
setApipAccessibility | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getApipAccessibility | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getUsedAttributes | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
1 | |||
getBody | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addInteraction | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
removeInteraction | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getInteractions | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getObjects | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRubricBlocks | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getRelatedItem | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getResponseProcessing | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setResponseProcessing | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setOutcomes | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
addOutcome | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getOutcomes | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getOutcome | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
12 | |||
removeOutcome | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
addResponse | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getResponses | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addModalFeedback | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
removeModalFeedback | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getModalFeedbacks | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getModalFeedback | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getStylesheets | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
addStylesheet | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
removeStylesheet | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
removeResponse | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
getIdentifiedElements | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
1 | |||
toXHTML | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
240 | |||
getUserScripts | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
getTemplateQti | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getTemplateQtiVariables | |
74.19% |
23 / 31 |
|
0.00% |
0 / 1 |
10.39 | |||
toXML | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
90 | |||
toArray | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
2 | |||
getDataForDelivery | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
toForm | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
validateOutcomes | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
110 | |||
isEmpty | |
0.00% |
0 / 1 |
|
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) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT). |
19 | */ |
20 | |
21 | namespace oat\taoQtiItem\model\qti; |
22 | |
23 | use oat\taoQtiItem\model\qti\container\FlowContainer; |
24 | use oat\taoQtiItem\model\qti\container\ContainerItemBody; |
25 | use oat\taoQtiItem\model\qti\interaction\Interaction; |
26 | use oat\taoQtiItem\model\qti\feedback\ModalFeedback; |
27 | use oat\taoQtiItem\model\qti\response\TemplatesDriven; |
28 | use oat\taoQtiItem\model\qti\exception\QtiModelException; |
29 | use common_Serializable; |
30 | use common_Logger; |
31 | use common_ext_ExtensionsManager; |
32 | use taoItems_models_classes_TemplateRenderer; |
33 | use DOMDocument; |
34 | use oat\tao\helpers\Template; |
35 | |
36 | /** |
37 | * The QTI_Item object represent the assessmentItem. |
38 | * It's the main QTI object, it contains all the other objects and |
39 | * is the main point to render a complete item. |
40 | * |
41 | * @access public |
42 | * @author Sam, <sam@taotesting.com> |
43 | * @package taoQTI |
44 | * @see http://www.imsglobal.org/question/qti_v2p0/imsqti_infov2p0.html#section10042 |
45 | */ |
46 | class Item extends IdentifiedElement implements FlowContainer, IdentifiedElementContainer, common_Serializable |
47 | { |
48 | /** |
49 | * the QTI tag name as defined in QTI standard |
50 | * |
51 | * @access protected |
52 | * @var string |
53 | */ |
54 | protected static $qtiTagName = 'assessmentItem'; |
55 | |
56 | /** |
57 | * Item's body content |
58 | * |
59 | * @var \oat\taoQtiItem\model\qti\container\ContainerItemBody |
60 | */ |
61 | protected $body = null; |
62 | |
63 | /** |
64 | * Item's response processing |
65 | * |
66 | * @access protected |
67 | * @var array |
68 | */ |
69 | protected $responses = []; |
70 | |
71 | /** |
72 | * Item's response processing |
73 | * |
74 | * @access protected |
75 | * @var ResponseProcessing |
76 | */ |
77 | protected $responseProcessing = null; |
78 | |
79 | /** |
80 | * Item's outcomes |
81 | * |
82 | * @access protected |
83 | * @var array |
84 | */ |
85 | protected $outcomes = []; |
86 | |
87 | /** |
88 | * Item's stylesheets |
89 | * |
90 | * @access protected |
91 | * @var array |
92 | */ |
93 | protected $stylesheets = []; |
94 | |
95 | /** |
96 | * Rubric blocks |
97 | * |
98 | * @access protected |
99 | * @var array |
100 | */ |
101 | protected $modalFeedbacks = []; |
102 | |
103 | /** |
104 | * The namespaces defined in the original qti.xml file, |
105 | * others that the standard included ones |
106 | * |
107 | * @var array |
108 | */ |
109 | protected $namespaces = []; |
110 | |
111 | /** |
112 | * The schema locations defined in the original qti.xml file, |
113 | * |
114 | * @var array |
115 | */ |
116 | protected $schemaLocations = []; |
117 | |
118 | /** |
119 | * The information on apip accessibility. |
120 | * It is currently stored as an xml string. |
121 | * This opens opprtunity to manage APIP accessibility more thouroughly in the future |
122 | * |
123 | * @var type |
124 | */ |
125 | protected $apipAccessibility = ''; |
126 | |
127 | /** |
128 | * Short description of method __construct |
129 | * |
130 | * @access public |
131 | * @author Sam, <sam@taotesting.com> |
132 | * @param array $attributes |
133 | * @return mixed |
134 | */ |
135 | public function __construct($attributes = []) |
136 | { |
137 | // override the tool options ! |
138 | $attributes['toolName'] = PRODUCT_NAME; |
139 | $attributes['toolVersion'] = \tao_models_classes_TaoService::singleton()->getPlatformVersion(); |
140 | |
141 | // create container |
142 | $this->body = new ContainerItemBody('', $this); |
143 | |
144 | parent::__construct($attributes); |
145 | } |
146 | |
147 | public function addNamespace($name, $uri) |
148 | { |
149 | $this->namespaces[$name] = $uri; |
150 | } |
151 | |
152 | public function getNamespaces() |
153 | { |
154 | return $this->namespaces; |
155 | } |
156 | |
157 | public function getNamespace($uri) |
158 | { |
159 | return array_search($uri, $this->namespaces); |
160 | } |
161 | |
162 | public function addSchemaLocation($uri, $url) |
163 | { |
164 | $this->schemaLocations[$uri] = $url; |
165 | } |
166 | |
167 | public function getSchemaLocations() |
168 | { |
169 | return $this->schemaLocations; |
170 | } |
171 | |
172 | public function getSchemaLocation($uri) |
173 | { |
174 | return $this->schemaLocations[$uri]; |
175 | } |
176 | |
177 | public function setApipAccessibility($apipXml) |
178 | { |
179 | $this->apipAccessibility = $apipXml; |
180 | } |
181 | |
182 | public function getApipAccessibility() |
183 | { |
184 | return $this->apipAccessibility; |
185 | } |
186 | |
187 | protected function getUsedAttributes() |
188 | { |
189 | return [ |
190 | 'oat\\taoQtiItem\\model\\qti\\attribute\\Title', |
191 | 'oat\\taoQtiItem\\model\\qti\\attribute\\Label', |
192 | 'oat\\taoQtiItem\\model\\qti\\attribute\\Lang', |
193 | 'oat\\taoQtiItem\\model\\qti\\attribute\\Adaptive', |
194 | 'oat\\taoQtiItem\\model\\qti\\attribute\\TimeDependent', |
195 | 'oat\\taoQtiItem\\model\\qti\\attribute\\ToolName', |
196 | 'oat\\taoQtiItem\\model\\qti\\attribute\\ToolVersion' |
197 | ]; |
198 | } |
199 | |
200 | public function getBody() |
201 | { |
202 | return $this->body; |
203 | } |
204 | |
205 | /** |
206 | * Short description of method addInteraction |
207 | * |
208 | * @access public |
209 | * @author Sam, <sam@taotesting.com> |
210 | * @param Interaction $interaction |
211 | * @param $body |
212 | * @return bool |
213 | */ |
214 | public function addInteraction(Interaction $interaction, $body) |
215 | { |
216 | $returnValue = false; |
217 | |
218 | if (!is_null($interaction)) { |
219 | $returnValue = $this->getBody()->setElement($interaction, $body); |
220 | } |
221 | |
222 | return (bool) $returnValue; |
223 | } |
224 | |
225 | /** |
226 | * Short description of method removeInteraction |
227 | * |
228 | * @access public |
229 | * @author Sam, <sam@taotesting.com> |
230 | * @param Interaction $interaction |
231 | * @return bool |
232 | */ |
233 | public function removeInteraction(Interaction $interaction) |
234 | { |
235 | $returnValue = false; |
236 | |
237 | if (!is_null($interaction)) { |
238 | $returnValue = $this->getBody()->removeElement($interaction); |
239 | } |
240 | |
241 | return (bool) $returnValue; |
242 | } |
243 | |
244 | public function getInteractions() |
245 | { |
246 | return $this->getComposingElements('oat\taoQtiItem\model\qti\interaction\Interaction'); |
247 | } |
248 | |
249 | public function getObjects() |
250 | { |
251 | return $this->body->getElements(\oat\taoQtiItem\model\qti\QtiObject::class); |
252 | } |
253 | |
254 | public function getRubricBlocks() |
255 | { |
256 | return $this->body->getElements('oat\\taoQtiItem\\model\\qti\\RubricBlock'); |
257 | } |
258 | |
259 | public function getRelatedItem() |
260 | { |
261 | return $this; // the related item of an item is itself! |
262 | } |
263 | |
264 | /** |
265 | * Short description of method getResponseProcessing |
266 | * |
267 | * @access public |
268 | * @author Sam, <sam@taotesting.com> |
269 | * @return \oat\taoQtiItem\model\qti\response\ResponseProcessing |
270 | */ |
271 | public function getResponseProcessing() |
272 | { |
273 | return $this->responseProcessing; |
274 | } |
275 | |
276 | /** |
277 | * Short description of method setResponseProcessing |
278 | * |
279 | * @access public |
280 | * @author Sam, <sam@taotesting.com> |
281 | * @param $rprocessing |
282 | * @return mixed |
283 | */ |
284 | public function setResponseProcessing($rprocessing) |
285 | { |
286 | $this->responseProcessing = $rprocessing; |
287 | } |
288 | |
289 | /** |
290 | * Short description of method setOutcomes |
291 | * |
292 | * @access public |
293 | * @author Sam, <sam@taotesting.com> |
294 | * @param |
295 | * array outcomes |
296 | * @return mixed |
297 | * @throws InvalidArgumentException |
298 | */ |
299 | public function setOutcomes($outcomes) |
300 | { |
301 | $this->outcomes = []; |
302 | foreach ($outcomes as $outcome) { |
303 | if (!$outcome instanceof OutcomeDeclaration) { |
304 | throw new \InvalidArgumentException("wrong entry in outcomes list"); |
305 | } |
306 | $this->addOutcome($outcome); |
307 | } |
308 | } |
309 | |
310 | /** |
311 | * add an outcome declaration to the item |
312 | * |
313 | * @param \oat\taoQtiItem\model\qti\OutcomeDeclaration $outcome |
314 | */ |
315 | public function addOutcome(OutcomeDeclaration $outcome) |
316 | { |
317 | $this->outcomes[$outcome->getSerial()] = $outcome; |
318 | $outcome->setRelatedItem($this); |
319 | } |
320 | |
321 | /** |
322 | * Short description of method getOutcomes |
323 | * |
324 | * @access public |
325 | * @author Sam, <sam@taotesting.com> |
326 | * @return array |
327 | */ |
328 | public function getOutcomes() |
329 | { |
330 | return $this->outcomes; |
331 | } |
332 | |
333 | /** |
334 | * Short description of method getOutcome |
335 | * |
336 | * @access public |
337 | * @author Sam, <sam@taotesting.com> |
338 | * @param |
339 | * string serial |
340 | * @return \oat\taoQtiItem\model\qti\OutcomeDeclaration |
341 | */ |
342 | public function getOutcome($serial) |
343 | { |
344 | $returnValue = null; |
345 | |
346 | if (!empty($serial)) { |
347 | if (isset($this->outcomes[$serial])) { |
348 | $returnValue = $this->outcomes[$serial]; |
349 | } |
350 | } |
351 | |
352 | return $returnValue; |
353 | } |
354 | |
355 | /** |
356 | * Short description of method removeOutcome |
357 | * |
358 | * @access public |
359 | * @author Sam, <sam@taotesting.com> |
360 | * @param OutcomeDeclaration $outcome |
361 | * @return bool |
362 | */ |
363 | public function removeOutcome(OutcomeDeclaration $outcome) |
364 | { |
365 | $returnValue = (bool) false; |
366 | |
367 | if (!is_null($outcome)) { |
368 | if (isset($this->outcomes[$outcome->getSerial()])) { |
369 | unset($this->outcomes[$outcome->getSerial()]); |
370 | $returnValue = true; |
371 | } |
372 | } else { |
373 | common_Logger::w('Tried to remove null outcome'); |
374 | } |
375 | |
376 | if (!$returnValue) { |
377 | common_Logger::w('outcome not found ' . $outcome->getSerial()); |
378 | } |
379 | |
380 | return (bool) $returnValue; |
381 | } |
382 | |
383 | public function addResponse(ResponseDeclaration $response) |
384 | { |
385 | $this->responses[$response->getSerial()] = $response; |
386 | $response->setRelatedItem($this); |
387 | } |
388 | |
389 | public function getResponses() |
390 | { |
391 | return $this->responses; |
392 | } |
393 | |
394 | public function addModalFeedback(ModalFeedback $modalFeedback) |
395 | { |
396 | $this->modalFeedbacks[$modalFeedback->getSerial()] = $modalFeedback; |
397 | $modalFeedback->setRelatedItem($this); |
398 | } |
399 | |
400 | public function removeModalFeedback(ModalFeedback $modalFeedback) |
401 | { |
402 | unset($this->modalFeedbacks[$modalFeedback->getSerial()]); |
403 | } |
404 | |
405 | /** |
406 | * Get the modal feedbacks of the item |
407 | * |
408 | * @access public |
409 | * @author Sam, <sam@taotesting.com> |
410 | * @return array |
411 | */ |
412 | public function getModalFeedbacks() |
413 | { |
414 | return $this->modalFeedbacks; |
415 | } |
416 | |
417 | public function getModalFeedback($serial) |
418 | { |
419 | $returnValue = null; |
420 | if (isset($this->modalFeedbacks[$serial])) { |
421 | $returnValue = $this->modalFeedbacks[$serial]; |
422 | } |
423 | |
424 | return $returnValue; |
425 | } |
426 | |
427 | /** |
428 | * Get the stylesheets of the item |
429 | * |
430 | * @access public |
431 | * @author Sam, <sam@taotesting.com> |
432 | * @return array |
433 | */ |
434 | public function getStylesheets() |
435 | { |
436 | return (array) $this->stylesheets; |
437 | } |
438 | |
439 | public function addStylesheet(Stylesheet $stylesheet) |
440 | { |
441 | // @todo : validate style sheet before adding: |
442 | $this->stylesheets[$stylesheet->getSerial()] = $stylesheet; |
443 | $stylesheet->setRelatedItem($this); |
444 | } |
445 | |
446 | public function removeStylesheet(Stylesheet $stylesheet) |
447 | { |
448 | unset($this->stylesheets[$stylesheet->getSerial()]); |
449 | } |
450 | |
451 | public function removeResponse($response) |
452 | { |
453 | |
454 | $serial = ''; |
455 | if ($response instanceof ResponseDeclaration) { |
456 | $serial = $response->getSerial(); |
457 | } elseif (is_string($response)) { |
458 | $serial = $response; |
459 | } else { |
460 | throw new \InvalidArgumentException( |
461 | 'the argument must be an instance of taoQTI_models_classes_QTI_ResponseDeclaration or a string serial' |
462 | ); |
463 | } |
464 | |
465 | if (!empty($serial)) { |
466 | unset($this->responses[$serial]); |
467 | } |
468 | } |
469 | |
470 | /** |
471 | * Get recursively all included identified QTI elements in the object (identifier => Object) |
472 | * |
473 | * @return array |
474 | */ |
475 | public function getIdentifiedElements() |
476 | { |
477 | $returnValue = $this->getBody()->getIdentifiedElements(); |
478 | $returnValue->addMultiple($this->getOutcomes()); |
479 | $returnValue->addMultiple($this->getResponses()); |
480 | $returnValue->addMultiple($this->getModalFeedbacks()); |
481 | |
482 | return $returnValue; |
483 | } |
484 | |
485 | /** |
486 | * Short description of method toXHTML |
487 | * |
488 | * @access public |
489 | * @author Sam, <sam@taotesting.com> |
490 | * @param array $options |
491 | * @param array $filtered |
492 | * @return string |
493 | */ |
494 | public function toXHTML($options = [], &$filtered = []) |
495 | { |
496 | |
497 | $template = static::getTemplatePath() . '/xhtml.item.tpl.php'; |
498 | |
499 | // get the variables to use in the template |
500 | $variables = $this->getAttributeValues(); |
501 | $variables['stylesheets'] = []; |
502 | foreach ($this->getStylesheets() as $stylesheet) { |
503 | $variables['stylesheets'][] = $stylesheet->getAttributeValues(); |
504 | } |
505 | //additional css: |
506 | if (isset($options['css'])) { |
507 | foreach ($options['css'] as $css) { |
508 | $variables['stylesheets'][] = ['href' => $css, 'media' => 'all']; |
509 | } |
510 | } |
511 | |
512 | //additional js: |
513 | $variables['javascripts'] = []; |
514 | $variables['js_variables'] = []; |
515 | if (isset($options['js'])) { |
516 | foreach ($options['js'] as $js) { |
517 | $variables['javascripts'][] = ['src' => $js]; |
518 | } |
519 | } |
520 | if (isset($options['js_var'])) { |
521 | foreach ($options['js_var'] as $name => $value) { |
522 | $variables['js_variables'][$name] = $value; |
523 | } |
524 | } |
525 | |
526 | //user scripts |
527 | $variables['user_scripts'] = $this->getUserScripts(); |
528 | |
529 | $dataForDelivery = $this->getDataForDelivery(); |
530 | $variables['itemData'] = $dataForDelivery['core']; |
531 | |
532 | //copy all variable data into filtered array |
533 | foreach ($dataForDelivery['variable'] as $serial => $data) { |
534 | $filtered[$serial] = $data; |
535 | } |
536 | |
537 | $variables['contentVariableElements'] = isset($options['contentVariableElements']) |
538 | && is_array($options['contentVariableElements']) |
539 | ? $options['contentVariableElements'] |
540 | : []; |
541 | $variables['tao_lib_path'] = isset($options['path'], $options['path']['tao']) ? $options['path']['tao'] : ''; |
542 | $variables['taoQtiItem_lib_path'] = isset($options['path']) && isset($options['path']['taoQtiItem']) |
543 | ? $options['path']['taoQtiItem'] |
544 | : ''; |
545 | $variables['client_config_url'] = isset($options['client_config_url']) ? $options['client_config_url'] : ''; |
546 | |
547 | $tplRenderer = new taoItems_models_classes_TemplateRenderer($template, $variables); |
548 | |
549 | $returnValue = $tplRenderer->render(); |
550 | |
551 | return (string) $returnValue; |
552 | } |
553 | |
554 | protected function getUserScripts() |
555 | { |
556 | $userScripts = []; |
557 | |
558 | $userScriptConfig = \common_ext_ExtensionsManager::singleton() |
559 | ->getExtensionById('taoQtiItem') |
560 | ->getConfig('userScripts'); |
561 | |
562 | if (is_array($userScriptConfig)) { |
563 | foreach ($userScriptConfig as $data) { |
564 | $userScripts[] = Template::js($data['src'], $data['extension']); |
565 | } |
566 | } |
567 | return $userScripts; |
568 | } |
569 | |
570 | public static function getTemplateQti() |
571 | { |
572 | return static::getTemplatePath() . '/qti.item.tpl.php'; |
573 | } |
574 | |
575 | protected function getTemplateQtiVariables() |
576 | { |
577 | $variables = parent::getTemplateQtiVariables(); |
578 | |
579 | $variables['stylesheets'] = ''; |
580 | foreach ($this->stylesheets as $stylesheet) { |
581 | $variables['stylesheets'] .= $stylesheet->toQTI(); |
582 | } |
583 | |
584 | $variables['responses'] = ''; |
585 | foreach ($this->responses as $response) { |
586 | $variables['responses'] .= $response->toQTI(); |
587 | } |
588 | |
589 | $variables['outcomes'] = ''; |
590 | foreach ($this->outcomes as $outcome) { |
591 | $variables['outcomes'] .= $outcome->toQTI(); |
592 | } |
593 | |
594 | $variables['feedbacks'] = ''; |
595 | foreach ($this->modalFeedbacks as $feedback) { |
596 | $variables['feedbacks'] .= $feedback->toQTI(); |
597 | } |
598 | |
599 | $variables['namespaces'] = $this->getNamespaces(); |
600 | $schemaLocations = ''; |
601 | foreach ($this->getSchemaLocations() as $uri => $url) { |
602 | $schemaLocations .= $uri . ' ' . $url . ' '; |
603 | } |
604 | $variables['schemaLocations'] = trim($schemaLocations); |
605 | $nsXsi = $this->getNamespace('http://www.w3.org/2001/XMLSchema-instance'); |
606 | $variables['xsi'] = $nsXsi ? $nsXsi . ':' : 'xsi:'; |
607 | |
608 | // render the responseProcessing |
609 | $renderedResponseProcessing = ''; |
610 | $responseProcessing = $this->getResponseProcessing(); |
611 | if (isset($responseProcessing)) { |
612 | if ($responseProcessing instanceof TemplatesDriven) { |
613 | $renderedResponseProcessing = $responseProcessing->buildQTI(); |
614 | } else { |
615 | $renderedResponseProcessing = $responseProcessing->toQTI(); |
616 | } |
617 | } |
618 | |
619 | // move item CSS class to itemBody |
620 | $variables['class'] = $this->getAttributeValue('class'); |
621 | unset($variables['attributes']['class']); |
622 | |
623 | $variables['renderedResponseProcessing'] = $renderedResponseProcessing; |
624 | |
625 | $variables['apipAccessibility'] = $this->getApipAccessibility(); |
626 | |
627 | return $variables; |
628 | } |
629 | |
630 | /** |
631 | * Short description of method toQTI |
632 | * |
633 | * @access public |
634 | * @author Sam, <sam@taotesting.com> |
635 | * @param boolean $validate (optional) Validate the XML output against QTI Specification (XML Schema). |
636 | * Default is false. |
637 | * @return string |
638 | * @throws exception\QtiModelException |
639 | */ |
640 | public function toXML($validate = false) |
641 | { |
642 | |
643 | $returnValue = ''; |
644 | |
645 | $qti = $this->toQTI(); |
646 | |
647 | // render and clean the xml |
648 | $dom = new DOMDocument('1.0', 'UTF-8'); |
649 | |
650 | $domDocumentConfig = \common_ext_ExtensionsManager::singleton() |
651 | ->getExtensionById('taoQtiItem') |
652 | ->getConfig('XMLParser'); |
653 | |
654 | if (is_array($domDocumentConfig) && !empty($domDocumentConfig)) { |
655 | foreach ($domDocumentConfig as $param => $value) { |
656 | if (property_exists($dom, $param)) { |
657 | $dom->$param = $value; |
658 | } |
659 | } |
660 | } else { |
661 | $dom->formatOutput = true; |
662 | $dom->preserveWhiteSpace = false; |
663 | $dom->validateOnParse = false; |
664 | } |
665 | |
666 | if ($dom->loadXML($qti)) { |
667 | $returnValue = $dom->saveXML(); |
668 | |
669 | //in debug mode, systematically check if the save QTI is standard compliant |
670 | if ($validate) { |
671 | $parserValidator = new Parser($returnValue); |
672 | $parserValidator->validate(); |
673 | if (!$parserValidator->isValid()) { |
674 | common_Logger::w('Invalid QTI output: ' . PHP_EOL . ' ' . $parserValidator->displayErrors()); |
675 | } |
676 | } |
677 | } else { |
678 | $parserValidator = new Parser($qti); |
679 | $parserValidator->validate(); |
680 | if (!$parserValidator->isValid()) { |
681 | throw new QtiModelException('Wrong QTI item output format'); |
682 | } |
683 | } |
684 | |
685 | return (string) $returnValue; |
686 | } |
687 | |
688 | /** |
689 | * Serialize item object into json format, handy to be used in js |
690 | */ |
691 | public function toArray($filterVariableContent = false, &$filtered = []) |
692 | { |
693 | $data = parent::toArray($filterVariableContent, $filtered); |
694 | $data['namespaces'] = $this->getNamespaces(); |
695 | $data['schemaLocations'] = $this->getSchemaLocations(); |
696 | $data['stylesheets'] = $this->getArraySerializedElementCollection( |
697 | $this->getStylesheets(), |
698 | $filterVariableContent, |
699 | $filtered |
700 | ); |
701 | $data['outcomes'] = $this->getArraySerializedElementCollection( |
702 | $this->getOutcomes(), |
703 | $filterVariableContent, |
704 | $filtered |
705 | ); |
706 | $data['responses'] = $this->getArraySerializedElementCollection( |
707 | $this->getResponses(), |
708 | $filterVariableContent, |
709 | $filtered |
710 | ); |
711 | $data['feedbacks'] = $this->getArraySerializedElementCollection( |
712 | $this->getModalFeedbacks(), |
713 | $filterVariableContent, |
714 | $filtered |
715 | ); |
716 | $data['responseProcessing'] = $this->responseProcessing->toArray($filterVariableContent, $filtered); |
717 | $data['apipAccessibility'] = $this->getApipAccessibility(); |
718 | return $data; |
719 | } |
720 | |
721 | public function getDataForDelivery() |
722 | { |
723 | |
724 | $filtered = []; |
725 | $itemData = $this->toArray(true, $filtered); |
726 | unset($itemData['responseProcessing']); |
727 | |
728 | return ['core' => $itemData, 'variable' => $filtered]; |
729 | } |
730 | |
731 | /** |
732 | * @return mixed |
733 | */ |
734 | public function toForm() |
735 | { |
736 | |
737 | $formContainer = new AssessmentItem($this); |
738 | $returnValue = $formContainer->getForm(); |
739 | |
740 | return $returnValue; |
741 | } |
742 | |
743 | public function validateOutcomes() |
744 | { |
745 | // Validate during publishing and exporting |
746 | $isExternalScore = false; |
747 | $isExternalSomeOutcome = false; |
748 | foreach ($this->getOutcomes() as $outcome) { |
749 | $externalScored = $outcome->getAttributeValue('externalScored'); |
750 | if ($outcome->getIdentifier() === 'SCORE' && $externalScored && $externalScored !== 'none') { |
751 | $isExternalScore = true; |
752 | } |
753 | if ($outcome->getIdentifier() !== 'SCORE' && $externalScored && $externalScored !== 'none') { |
754 | $isExternalSomeOutcome = true; |
755 | } |
756 | } |
757 | |
758 | if ($isExternalScore && $isExternalSomeOutcome) { |
759 | throw new \Exception('ExternalScored attribute is not allowed for multiple outcomes in item'); |
760 | } |
761 | } |
762 | |
763 | public function isEmpty(): bool |
764 | { |
765 | return empty($this->getBody()) || strpos((string)$this->getBody(), '<div class="empty"') !== false; |
766 | } |
767 | } |