Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
24.35% covered (danger)
24.35%
56 / 230
40.00% covered (danger)
40.00%
18 / 45
CRAP
0.00% covered (danger)
0.00%
0 / 1
Item
24.35% covered (danger)
24.35%
56 / 230
40.00% covered (danger)
40.00%
18 / 45
4429.76
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 addNamespace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespaces
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getNamespace
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addSchemaLocation
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSchemaLocations
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getSchemaLocation
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setApipAccessibility
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getApipAccessibility
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getUsedAttributes
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 getBody
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addInteraction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 removeInteraction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getInteractions
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getObjects
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRubricBlocks
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRelatedItem
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getResponseProcessing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setResponseProcessing
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setOutcomes
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 addOutcome
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getOutcomes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getOutcome
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 removeOutcome
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 addResponse
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getResponses
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 addModalFeedback
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 removeModalFeedback
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getModalFeedbacks
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getModalFeedback
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 getStylesheets
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 addStylesheet
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 removeStylesheet
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 removeResponse
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
20
 getIdentifiedElements
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 toXHTML
0.00% covered (danger)
0.00%
0 / 33
0.00% covered (danger)
0.00%
0 / 1
240
 getUserScripts
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 getTemplateQti
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getTemplateQtiVariables
74.19% covered (warning)
74.19%
23 / 31
0.00% covered (danger)
0.00%
0 / 1
10.39
 toXML
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
90
 toArray
0.00% covered (danger)
0.00%
0 / 26
0.00% covered (danger)
0.00%
0 / 1
2
 getDataForDelivery
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 toForm
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 validateOutcomes
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
110
 isEmpty
0.00% covered (danger)
0.00%
0 / 1
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) 2013 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT).
19 */
20
21namespace oat\taoQtiItem\model\qti;
22
23use oat\taoQtiItem\model\qti\container\FlowContainer;
24use oat\taoQtiItem\model\qti\container\ContainerItemBody;
25use oat\taoQtiItem\model\qti\interaction\Interaction;
26use oat\taoQtiItem\model\qti\feedback\ModalFeedback;
27use oat\taoQtiItem\model\qti\response\TemplatesDriven;
28use oat\taoQtiItem\model\qti\exception\QtiModelException;
29use common_Serializable;
30use common_Logger;
31use common_ext_ExtensionsManager;
32use taoItems_models_classes_TemplateRenderer;
33use DOMDocument;
34use 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 */
46class 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}