1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle 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 Moodle.  If not, see <http://www.gnu.org/licenses/>.
16/**
17 * @package    backup-convert
18 * @copyright  2011 Darko Miletic <dmiletic@moodlerooms.com>
19 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
20 */
21
22defined('MOODLE_INTERNAL') or die('Direct access to this script is forbidden.');
23
24require_once('cc_utils.php');
25require_once('cc_general.php');
26
27abstract class cc_xml_namespace {
28    const xml = 'http://www.w3.org/XML/1998/namespace';
29}
30
31abstract class cc_qti_metadata {
32    // Assessment.
33    const qmd_assessmenttype       = 'qmd_assessmenttype';
34    const qmd_scoretype            = 'qmd_scoretype';
35    const qmd_feedbackpermitted    = 'qmd_feedbackpermitted';
36    const qmd_hintspermitted       = 'qmd_hintspermitted';
37    const qmd_solutionspermitted   = 'qmd_solutionspermitted';
38    const qmd_timelimit            = 'qmd_timelimit';
39    const cc_allow_late_submission = 'cc_allow_late_submission';
40    const cc_maxattempts           = 'cc_maxattempts';
41    const cc_profile               = 'cc_profile';
42
43    // Items.
44    const cc_weighting         = 'cc_weighting';
45    const qmd_scoringpermitted = 'qmd_scoringpermitted';
46    const qmd_computerscored   = 'qmd_computerscored';
47    const cc_question_category = 'cc_question_category';
48}
49
50abstract class cc_qti_profiletype {
51    const multiple_choice   = 'cc.multiple_choice.v0p1';
52    const multiple_response = 'cc.multiple_response.v0p1';
53    const true_false        = 'cc.true_false.v0p1';
54    const field_entry       = 'cc.fib.v0p1';
55    const pattern_match     = 'cc.pattern_match.v0p1';
56    const essay             = 'cc.essay.v0p1';
57
58    /**
59     *
60     * validates a profile value
61     * @param string $value
62     * @return boolean
63     */
64    public static function valid($value) {
65        static $verification_values = array( self::essay,
66                                             self::field_entry,
67                                             self::multiple_choice,
68                                             self::multiple_response,
69                                             self::pattern_match,
70                                             self::true_false
71                                           );
72        return in_array($value, $verification_values);
73    }
74
75}
76
77abstract class cc_qti_values {
78    const exam_profile = 'cc.exam.v0p1';
79    const Yes          = 'Yes';
80    const No           = 'No';
81    const Response     = 'Response';
82    const Solution     = 'Solution';
83    const Hint         = 'Hint';
84    const Examination  = 'Examination';
85    const Percentage   = 'Percentage';
86    const unlimited    = 'unlimited';
87    const Single       = 'Single';
88    const Multiple     = 'Multiple';
89    const Ordered      = 'Ordered';
90    const Asterisk     = 'Asterisk';
91    const Box          = 'Box';
92    const Dashline     = 'Dashline';
93    const Underline    = 'Underline';
94    const Decimal      = 'Decimal';
95    const Integer      = 'Integer';
96    const Scientific   = 'Scientific';
97    const String       = 'String';
98    const SCORE        = 'SCORE';
99    const Set          = 'Set';
100    const Complete     = 'Complete';
101    const texttype     = 'text/plain';
102    const htmltype     = 'text/html';
103}
104
105abstract class cc_qti_tags {
106    const questestinterop = 'questestinterop';
107    const assessment = 'assessment';
108    const qtimetadata = 'qtimetadata';
109    const qtimetadatafield = 'qtimetadatafield';
110    const fieldlabel = 'fieldlabel';
111    const fieldentry = 'fieldentry';
112    const section = 'section';
113    const ident = 'ident';
114    const item = 'item';
115    const title = 'title';
116    const itemmetadata = 'itemmetadata';
117    const presentation = 'presentation';
118    const material = 'material';
119    const mattext = 'mattext';
120    const matref = 'matref';
121    const matbreak = 'matbreak';
122    const texttype = 'texttype';
123    const response_lid = 'response_lid';
124    const render_choice = 'render_choice';
125    const response_label = 'response_label';
126    const resprocessing = 'resprocessing';
127    const outcomes = 'outcomes';
128    const decvar = 'decvar';
129    const respcondition = 'respcondition';
130    const conditionvar = 'conditionvar';
131    const other = 'other';
132    const displayfeedback = 'displayfeedback';
133    const maxvalue = 'maxvalue';
134    const minvalue = 'minvalue';
135    const varname = 'varname';
136    const vartype = 'vartype';
137    const continue_ = 'continue';
138    const feedbacktype = 'feedbacktype';
139    const linkrefid = 'linkrefid';
140    const varequal = 'varequal';
141    const respident = 'respident';
142    const itemfeedback = 'itemfeedback';
143    const flow_mat = 'flow_mat';
144    const rcardinality = 'rcardinality';
145    const charset = 'charset';
146    const label = 'label';
147    const uri = 'uri';
148    const width = 'width';
149    const height = 'height';
150    const x0 = 'x0';
151    const y0 = 'y0';
152    const xml_lang = 'lang';
153    const xml_space = 'space';
154    const rubric = 'rubric';
155    const altmaterial = 'altmaterial';
156    const presentation_material = 'presentation_material';
157    const t_class = 'class';
158    const material_ref = 'material_ref';
159    const rtiming = 'rtiming';
160    const render_fib = 'render_fib';
161    const shuffle = 'shuffle';
162    const minnumber = 'minnumber';
163    const maxnumber = 'maxnumber';
164    const encoding = 'encoding';
165    const maxchars = 'maxchars';
166    const prompt = 'prompt';
167    const fibtype = 'fibtype';
168    const rows = 'rows';
169    const columns = 'columns';
170    const labelrefid = 'labelrefid';
171    const rshuffle = 'rshuffle';
172    const match_group = 'match_group';
173    const match_max = 'match_max';
174    const flow = 'flow';
175    const response_str = 'response_str';
176    const flow_label = 'flow_label';
177    const setvar = 'setvar';
178    const action = 'action';
179    const and_ = 'and';
180    const not_ = 'not';
181    const case_ = 'case';
182    const varsubstring = 'varsubstring';
183    const hint = 'hint';
184    const solution = 'solution';
185    const feedbackstyle = 'feedbackstyle';
186    const solutionmaterial = 'solutionmaterial';
187    const hintmaterial = 'hintmaterial';
188}
189
190class cc_question_metadata_base {
191    /**
192     * @var array
193     */
194    protected $metadata = array();
195
196    /**
197     * @param string $setting
198     * @param mixed $value
199     */
200    protected function set_setting($setting, $value = null) {
201        $this->metadata[$setting] = $value;
202    }
203
204    /**
205     * @param string $setting
206     * @return mixed
207     */
208    protected function get_setting($setting) {
209        $result = null;
210        if (array_key_exists($setting, $this->metadata)) {
211            $result = $this->metadata[$setting];
212        }
213        return $result;
214    }
215
216    /**
217     * @param string $setting
218     * @param string $namespace
219     * @param string $value
220     */
221    protected function set_setting_wns($setting, $namespace, $value = null) {
222        $this->metadata[$setting] = array($namespace => $value);
223    }
224
225    /**
226     * @param string $setting
227     * @param boolean $value
228     */
229    protected function enable_setting_yesno($setting, $value = true) {
230        $svalue = $value ? cc_qti_values::Yes : cc_qti_values::No;
231        $this->set_setting($setting, $svalue);
232    }
233
234    /**
235     * @param XMLGenericDocument $doc
236     * @param DOMNode $item
237     * @param string $namespace
238     */
239    public function generate_attributes(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
240        foreach ($this->metadata as $attribute => $value) {
241            if (!is_null($value)) {
242                if (!is_array($value)) {
243                    $doc->append_new_attribute_ns($item, $namespace, $attribute, $value);
244                } else {
245                    $ns = key($value);
246                    $nval = current($value);
247                    if (!is_null($nval)) {
248                        $doc->append_new_attribute_ns($item, $ns, $attribute, $nval);
249                    }
250                }
251            }
252        }
253    }
254
255    /**
256     * @param XMLGenericDocument $doc
257     * @param DOMNode $item
258     * @param string $namespace
259     */
260    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
261        $qtimetadata = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::qtimetadata);
262        foreach ($this->metadata as $label => $entry) {
263            if (!is_null($entry)) {
264                $qtimetadatafield = $doc->append_new_element_ns($qtimetadata, $namespace, cc_qti_tags::qtimetadatafield);
265                $doc->append_new_element_ns($qtimetadatafield, $namespace, cc_qti_tags::fieldlabel, $label);
266                $doc->append_new_element_ns($qtimetadatafield, $namespace, cc_qti_tags::fieldentry, $entry);
267            }
268        }
269    }
270}
271
272class cc_question_metadata extends cc_question_metadata_base {
273
274    public function set_category($value) {
275        $this->set_setting(cc_qti_metadata::cc_question_category, $value);
276    }
277
278    public function set_weighting($value) {
279        $this->set_setting(cc_qti_metadata::cc_weighting, $value);
280    }
281
282    public function enable_scoringpermitted($value = true) {
283        $this->enable_setting_yesno(cc_qti_metadata::qmd_scoringpermitted, $value);
284    }
285
286    public function enable_computerscored($value = true) {
287        $this->enable_setting_yesno(cc_qti_metadata::qmd_computerscored, $value);
288    }
289
290    /**
291     *
292     * Constructs metadata
293     * @param string $profile
294     * @throws InvalidArgumentException
295     */
296    public function __construct($profile) {
297        if (!cc_qti_profiletype::valid($profile)) {
298            throw new InvalidArgumentException('Invalid profile type!');
299        }
300        $this->set_setting(cc_qti_metadata::cc_profile, $profile);
301        $this->set_setting(cc_qti_metadata::cc_question_category);
302        $this->set_setting(cc_qti_metadata::cc_weighting        );
303        $this->set_setting(cc_qti_metadata::qmd_scoringpermitted);
304        $this->set_setting(cc_qti_metadata::qmd_computerscored  );
305    }
306}
307
308
309class cc_assesment_metadata extends cc_question_metadata_base {
310
311    public function enable_hints($value = true) {
312        $this->enable_setting_yesno(cc_qti_metadata::qmd_hintspermitted, $value);
313    }
314
315    public function enable_solutions($value = true) {
316        $this->enable_setting_yesno(cc_qti_metadata::qmd_solutionspermitted, $value);
317    }
318
319    public function enable_latesubmissions($value = true) {
320        $this->enable_setting_yesno(cc_qti_metadata::cc_allow_late_submission, $value);
321    }
322
323    public function enable_feedback($value = true) {
324        $this->enable_setting_yesno(cc_qti_metadata::qmd_feedbackpermitted, $value);
325    }
326
327    public function set_timelimit($value) {
328        $ivalue = (int)$value;
329        if (($ivalue < 0) || ($ivalue > 527401)) {
330            throw new OutOfRangeException('Time limit value out of permitted range!');
331        }
332
333        $this->set_setting(cc_qti_metadata::qmd_timelimit, $value);
334    }
335
336    public function set_maxattempts($value) {
337        $valid_values = array(cc_qti_values::Examination, cc_qti_values::unlimited, 1, 2, 3, 4, 5);
338        if (!in_array($value, $valid_values)) {
339            throw new OutOfRangeException('Max attempts has invalid value');
340        }
341
342        $this->set_setting(cc_qti_metadata::cc_maxattempts, $value);
343    }
344
345    public function __construct() {
346        //prepared default values
347        $this->set_setting(cc_qti_metadata::cc_profile        , cc_qti_values::exam_profile);
348        $this->set_setting(cc_qti_metadata::qmd_assessmenttype, cc_qti_values::Examination );
349        $this->set_setting(cc_qti_metadata::qmd_scoretype     , cc_qti_values::Percentage  );
350        //optional empty values
351        $this->set_setting(cc_qti_metadata::qmd_feedbackpermitted   );
352        $this->set_setting(cc_qti_metadata::qmd_hintspermitted      );
353        $this->set_setting(cc_qti_metadata::qmd_solutionspermitted  );
354        $this->set_setting(cc_qti_metadata::qmd_timelimit           );
355        $this->set_setting(cc_qti_metadata::cc_allow_late_submission);
356        $this->set_setting(cc_qti_metadata::cc_maxattempts          );
357    }
358
359}
360
361class cc_assesment_mattext extends cc_question_metadata_base {
362    protected $value = null;
363
364    public function __construct($value = null) {
365        $this->set_setting(cc_qti_tags::texttype, cc_qti_values::texttype);
366        $this->set_setting(cc_qti_tags::charset);//, 'ascii-us');
367        $this->set_setting(cc_qti_tags::label);
368        $this->set_setting(cc_qti_tags::uri);
369        $this->set_setting(cc_qti_tags::width);
370        $this->set_setting(cc_qti_tags::height);
371        $this->set_setting(cc_qti_tags::x0);
372        $this->set_setting(cc_qti_tags::y0);
373        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml);
374        $this->set_setting_wns(cc_qti_tags::xml_space, cc_xml_namespace::xml);//, 'default');
375        $this->value = $value;
376    }
377
378    public function set_label($value) {
379        $this->set_setting(cc_qti_tags::label, $value);
380    }
381
382    public function set_uri($value) {
383        $this->set_setting(cc_qti_tags::uri, $value);
384    }
385
386    public function set_width_height($width = null, $height = null) {
387        $this->set_setting(cc_qti_tags::width, $width);
388        $this->set_setting(cc_qti_tags::height, $height);
389    }
390
391    public function set_coor($x = null, $y = null) {
392        $this->set_setting(cc_qti_tags::x0, $x);
393        $this->set_setting(cc_qti_tags::y0, $y);
394    }
395
396    public function set_lang($lang = null) {
397        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml, $lang);
398    }
399
400    public function set_content($content, $type = cc_qti_values::texttype, $charset = null) {
401        $this->value = $content;
402        $this->set_setting(cc_qti_tags::texttype, $type);
403        $this->set_setting(cc_qti_tags::charset, $charset);
404    }
405
406    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
407        $mattext = $doc->append_new_element_ns_cdata($item, $namespace, cc_qti_tags::mattext, $this->value);
408        $this->generate_attributes($doc, $mattext, $namespace);
409    }
410}
411
412class cc_assesment_matref {
413    protected $linkref = null;
414
415    public function __construct($linkref) {
416        $this->linkref = $linkref;
417    }
418
419    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
420        $doc->append_new_element_ns($item, $namespace, cc_qti_tags::matref, $this->linkref);
421        $doc->append_new_attribute_ns($node, $namespace, cc_qti_tags::linkrefid, $this->linkref);
422    }
423}
424
425class cc_assesment_response_matref extends cc_assesment_matref {
426    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
427        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::material_ref);
428        $doc->append_new_attribute_ns($node, $namespace, cc_qti_tags::linkrefid, $this->linkref);
429    }
430}
431
432class cc_assesment_matbreak {
433    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
434        $doc->append_new_element_ns($item, $namespace, cc_qti_tags::matbreak);
435    }
436}
437
438abstract class cc_assesment_material_base extends cc_question_metadata_base {
439    /**
440    * @var mixed
441    */
442    protected $mattag = null;
443    protected $tagname   = null;
444
445    protected function set_tag_value($object) {
446        $this->mattag  = $object;
447    }
448
449    public function set_mattext(cc_assesment_mattext $object) {
450        $this->set_tag_value($object);
451    }
452
453    public function set_matref(cc_assesment_matref $object) {
454        $this->set_tag_value($object);
455    }
456
457    public function set_matbreak(cc_assesment_matbreak $object) {
458        $this->set_tag_value($object);
459    }
460
461    public function set_lang($value) {
462        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml, $value);
463    }
464
465    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
466        $material = $doc->append_new_element_ns($item, $namespace, $this->tagname);
467        $this->generate_attributes($doc, $material, $namespace);
468        if (!empty($this->mattag)) {
469            $this->mattag->generate($doc, $material, $namespace);
470        }
471        return $material;
472    }
473}
474
475class cc_assesment_altmaterial extends cc_assesment_material_base {
476    public function __construct($value = null) {
477        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml);
478        $this->tagname = cc_qti_tags::altmaterial;
479    }
480}
481
482class cc_assesment_material extends cc_assesment_material_base {
483
484    protected $altmaterial = null;
485
486    public function __construct($value = null) {
487        $this->set_setting(cc_qti_tags::label);
488        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml);
489        $this->tagname = cc_qti_tags::material;
490    }
491
492    public function set_label($value) {
493        $this->set_setting(cc_qti_tags::label, $value);
494    }
495
496    public function set_altmaterial(cc_assesment_altmaterial $object) {
497        $this->altmaterial = $object;
498    }
499
500    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
501        $material = parent::generate($doc, $item, $namespace);
502        if (!empty($this->altmaterial)) {
503            $this->altmaterial->generate($doc, $material, $namespace);
504        }
505    }
506}
507
508class cc_assesment_rubric_base extends cc_question_metadata_base {
509
510    protected $material = null;
511
512    public function set_material($object) {
513        $this->material = $object;
514    }
515
516    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
517        $rubric = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::rubric);
518        if (!empty($this->material)) {
519            $this->material->generate($doc, $rubric, $namespace);
520        }
521    }
522}
523
524class cc_assesment_presentation_material_base extends cc_question_metadata_base {
525    protected $flowmats = array();
526
527    public function add_flow_mat($object) {
528        $this->flowmats[] = $object;
529    }
530
531    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
532        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::presentation_material);
533        if (!empty($this->flowmats)) {
534            foreach ($this->flowmats as $flow_mat) {
535                $flow_mat->generate($doc, $node, $namespace);
536            }
537        }
538    }
539}
540
541class cc_assesment_flow_mat_base extends cc_question_metadata_base {
542
543    protected $mattag = null;
544
545    protected function set_tag_value($object) {
546        $this->mattag  = $object;
547    }
548
549    public function set_flow_mat(cc_assesment_flow_mat_base $object) {
550        $this->set_tag_value($object);
551    }
552
553    public function set_material(cc_assesment_material $object) {
554        $this->set_tag_value($object);
555    }
556
557    public function set_material_ref(cc_assesment_matref $object) {
558        $this->set_tag_value($object);
559    }
560
561    public function __construct($value = null) {
562        $this->set_setting(cc_qti_tags::t_class);
563    }
564
565    public function set_class($value) {
566        $this->set_setting(cc_qti_tags::t_class, $value);
567    }
568
569    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
570        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::flow_mat);
571        $this->generate_attributes($doc, $node, $namespace);
572        if (!empty($this->mattag)) {
573            $this->mattag->generate($doc, $node, $namespace);
574        }
575    }
576
577}
578
579class cc_assesment_section extends cc_question_metadata_base {
580    /**
581     * @var array
582     */
583    protected $items = array();
584
585    public function __construct() {
586        $this->set_setting(cc_qti_tags::ident, cc_helpers::uuidgen('I_'));
587        $this->set_setting(cc_qti_tags::title);
588        $this->set_setting_wns(cc_qti_tags::xml_lang, cc_xml_namespace::xml);
589    }
590
591    public function set_ident($value) {
592        $this->set_setting(cc_qti_tags::ident, $value);
593    }
594
595    public function set_title($value) {
596        $this->set_setting(cc_qti_tags::title, $value);
597    }
598
599    public function set_lang($value) {
600        $this->set_setting_wns(cc_qti_tags::xml_lang, cc_xml_namespace::xml, $value);
601    }
602
603    public function add_item(cc_assesment_section_item $object) {
604        $this->items[] = $object;
605    }
606
607    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
608        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::section);
609        $this->generate_attributes($doc, $node, $namespace);
610        if (!empty($this->items)) {
611            foreach ($this->items as $item) {
612                $item->generate($doc, $node, $namespace);
613            }
614        }
615    }
616}
617
618class cc_assesment_itemmetadata extends cc_question_metadata_base {
619    public function add_metadata($object) {
620        $this->metadata[] = $object;
621    }
622
623    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
624        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::itemmetadata);
625        if (!empty($this->metadata)) {
626            foreach ($this->metadata as $metaitem) {
627                $metaitem->generate($doc, $node, $namespace);
628            }
629        }
630    }
631}
632
633class cc_assesment_decvartype extends cc_question_metadata_base {
634
635    public function __construct() {
636        $this->set_setting(cc_qti_tags::varname, cc_qti_values::SCORE);
637        $this->set_setting(cc_qti_tags::vartype, cc_qti_values::Integer);
638        $this->set_setting(cc_qti_tags::minvalue);
639        $this->set_setting(cc_qti_tags::maxvalue);
640    }
641
642    public function set_vartype($value) {
643        $this->set_setting(cc_qti_tags::vartype, $value);
644    }
645
646    public function set_limits($min = null, $max = null) {
647        $this->set_setting(cc_qti_tags::minvalue, $min);
648        $this->set_setting(cc_qti_tags::maxvalue, $max);
649    }
650
651    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
652        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::decvar);
653        $this->generate_attributes($doc, $node, $namespace);
654    }
655}
656
657
658class cc_assignment_conditionvar_othertype extends cc_question_metadata_base {
659    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
660        $doc->append_new_element_ns($item, $namespace, cc_qti_tags::other);
661    }
662}
663
664class cc_assignment_conditionvar_varequaltype extends cc_question_metadata_base {
665    protected $tagname = null;
666    protected $answerid = null;
667
668    public function __construct($value = null) {
669        if (is_null($value)) {
670            throw new InvalidArgumentException('Must not pass null!');
671        }
672        $this->answerid = $value;
673        $this->set_setting(cc_qti_tags::respident);
674        $this->set_setting(cc_qti_tags::case_);//, cc_qti_values::No  );
675        $this->tagname = cc_qti_tags::varequal;
676    }
677
678    public function set_respident($value) {
679        $this->set_setting(cc_qti_tags::respident, $value);
680    }
681
682    public function enable_case($value = true) {
683        $this->enable_setting_yesno(cc_qti_tags::case_, $value);
684    }
685
686    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
687        $node = $doc->append_new_element_ns($item, $namespace, $this->tagname, $this->answerid);
688        $this->generate_attributes($doc, $node, $namespace);
689    }
690}
691
692class cc_assignment_conditionvar_varsubstringtype extends cc_assignment_conditionvar_varequaltype {
693    public function __construct($value) {
694        parent::__construct($value);
695        $this->tagname = cc_qti_tags::varsubstring;
696    }
697}
698
699
700class cc_assignment_conditionvar_andtype extends cc_question_metadata_base {
701    protected $nots = array();
702    protected $varequals = array();
703
704    public function set_not(cc_assignment_conditionvar_varequaltype $object) {
705        $this->nots[] = $object;
706    }
707
708    public function set_varequal(cc_assignment_conditionvar_varequaltype $object) {
709        $this->varequals[] = $object;
710    }
711
712    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
713        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::and_);
714        if (!empty($this->nots)) {
715            foreach ($this->nots as $notv) {
716                $not = $doc->append_new_element_ns($node, $namespace, cc_qti_tags::not_);
717                $notv->generate($doc, $not, $namespace);
718            }
719        }
720
721        if (!empty($this->varequals)) {
722            foreach ($this->varequals as $varequal) {
723                $varequal->generate($doc, $node, $namespace);
724            }
725        }
726    }
727}
728
729class cc_assignment_conditionvar extends cc_question_metadata_base {
730
731    /**
732     * @var cc_assignment_conditionvar_andtype
733     */
734    protected $and = null;
735    /**
736     * @var cc_assignment_conditionvar_othertype
737     */
738    protected $other = null;
739    /**
740     * @var array
741     */
742    protected $varequal = array();
743    /**
744     * @var cc_assignment_conditionvar_varsubstringtype
745     */
746    protected $varsubstring = null;
747
748    public function set_and(cc_assignment_conditionvar_andtype $object) {
749        $this->and = $object;
750    }
751
752    public function set_other(cc_assignment_conditionvar_othertype $object) {
753        $this->other = $object;
754    }
755
756    public function set_varequal(cc_assignment_conditionvar_varequaltype $object) {
757        $this->varequal[] = $object;
758    }
759
760    public function set_varsubstring(cc_assignment_conditionvar_varsubstringtype $object) {
761        $this->varsubstring = $object;
762    }
763
764    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
765        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::conditionvar);
766
767        if (!empty($this->and)) {
768            $this->and->generate($doc, $node, $namespace);
769        }
770
771        if (!empty($this->other)) {
772            $this->other->generate($doc, $node, $namespace);
773        }
774
775        if (!empty($this->varequal)) {
776            foreach ($this->varequal as $varequal) {
777                $varequal->generate($doc, $node, $namespace);
778            }
779        }
780
781        if (!empty($this->varsubstring)) {
782            $this->varsubstring->generate($doc, $node, $namespace);
783        }
784    }
785}
786
787class cc_assignment_displayfeedbacktype extends cc_question_metadata_base {
788    public function __construct() {
789        $this->set_setting(cc_qti_tags::feedbacktype);
790        $this->set_setting(cc_qti_tags::linkrefid);
791    }
792
793    public function set_feedbacktype($value) {
794        $this->set_setting(cc_qti_tags::feedbacktype, $value);
795    }
796
797    public function set_linkrefid($value) {
798        $this->set_setting(cc_qti_tags::linkrefid, $value);
799    }
800
801    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
802        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::displayfeedback);
803        $this->generate_attributes($doc, $node, $namespace);
804    }
805}
806
807
808class cc_assignment_setvartype extends cc_question_metadata_base {
809    /**
810     * @var integer
811     */
812    protected $tagvalue = null;
813
814    public function __construct($tagvalue = 100) {
815        $this->set_setting(cc_qti_tags::varname, cc_qti_values::SCORE);
816        $this->set_setting(cc_qti_tags::action , cc_qti_values::Set  );
817        $this->tagvalue = $tagvalue;
818    }
819
820    public function set_varname($value) {
821        $this->set_setting(cc_qti_tags::varname, $value);
822    }
823
824    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
825        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::setvar, $this->tagvalue);
826        $this->generate_attributes($doc, $node, $namespace);
827    }
828}
829
830class cc_assesment_respconditiontype extends cc_question_metadata_base {
831    /**
832     * @var cc_assignment_conditionvar
833     */
834    protected $conditionvar = null;
835    protected $setvar = array();
836    protected $displayfeedback = array();
837
838    public function __construct() {
839        $this->set_setting(cc_qti_tags::title);
840        $this->set_setting(cc_qti_tags::continue_, cc_qti_values::No);
841    }
842
843    public function set_title($value) {
844        $this->set_setting(cc_qti_tags::title, $value);
845    }
846
847    public function enable_continue($value = true) {
848        $this->enable_setting_yesno(cc_qti_tags::continue_, $value);
849    }
850
851    public function set_conditionvar(cc_assignment_conditionvar $object) {
852        $this->conditionvar = $object;
853    }
854
855    public function add_setvar(cc_assignment_setvartype $object) {
856        $this->setvar[] = $object;
857    }
858
859    public function add_displayfeedback(cc_assignment_displayfeedbacktype $object) {
860        $this->displayfeedback[] = $object;
861    }
862
863    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
864        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::respcondition);
865        $this->generate_attributes($doc, $node, $namespace);
866
867        if (!empty($this->conditionvar)) {
868            $this->conditionvar->generate($doc, $node, $namespace);
869        }
870
871        if (!empty($this->setvar)) {
872            foreach ($this->setvar as $setvar) {
873                $setvar->generate($doc, $node, $namespace);
874            }
875        }
876
877        if (!empty($this->displayfeedback)) {
878            foreach ($this->displayfeedback as $displayfeedback) {
879                $displayfeedback->generate($doc, $node, $namespace);
880            }
881        }
882    }
883}
884
885
886class cc_assesment_resprocessingtype extends cc_question_metadata_base {
887    /**
888     * @var cc_assesment_decvartype
889     */
890    protected $decvar = null;
891    /**
892     * @var array
893     */
894    protected $respconditions = array();
895
896    public function set_decvar(cc_assesment_decvartype $object) {
897        $this->decvar = $object;
898    }
899
900    public function add_respcondition(cc_assesment_respconditiontype $object) {
901        $this->respconditions[] = $object;
902    }
903
904    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
905        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::resprocessing);
906        $outcomes = $doc->append_new_element_ns($node, $namespace, cc_qti_tags::outcomes);
907        if (!empty($this->decvar)) {
908            $this->decvar->generate($doc, $outcomes, $namespace);
909        }
910        if (!empty($this->respconditions)) {
911            foreach ($this->respconditions as $rcond) {
912                $rcond->generate($doc, $node, $namespace);
913            }
914        }
915    }
916}
917
918
919class cc_assesment_itemfeedback_shintmaterial_base extends cc_question_metadata_base {
920    /**
921     * @var string
922     */
923    protected $tagname = null;
924    /**
925     * @var array
926     */
927    protected $flow_mats = array();
928    /**
929     * @var array
930     */
931    protected $materials = array();
932
933    /**
934     * @param cc_assesment_flow_mattype $object
935     */
936    public function add_flow_mat(cc_assesment_flow_mattype $object) {
937        $this->flow_mats[] = $object;
938    }
939
940    /**
941     * @param cc_assesment_material $object
942     */
943    public function add_material(cc_assesment_material $object) {
944        $this->materials[] = $object;
945    }
946
947    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
948        $node = $doc->append_new_element_ns($item, $namespace, $this->tagname);
949
950        if (!empty($this->flow_mats)) {
951            foreach ($this->flow_mats as $flow_mat) {
952                $flow_mat->generate($doc, $node, $namespace);
953            }
954        }
955
956        if (!empty($this->materials)) {
957            foreach ($this->materials as $material) {
958                $material->generate($doc, $node, $namespace);
959            }
960        }
961    }
962}
963
964class cc_assesment_itemfeedback_hintmaterial extends cc_assesment_itemfeedback_shintmaterial_base {
965    public function __construct() {
966        $this->tagname = cc_qti_tags::hint;
967    }
968}
969
970class cc_assesment_itemfeedback_solutionmaterial extends cc_assesment_itemfeedback_shintmaterial_base {
971    public function __construct() {
972        $this->tagname = cc_qti_tags::solutionmaterial;
973    }
974}
975
976class cc_assesment_itemfeedback_shintype_base extends cc_question_metadata_base {
977    /**
978     * @var string
979     */
980    protected $tagname = null;
981    /**
982     * @var array
983    */
984    protected $items = array();
985
986    public function __construct() {
987        $this->set_setting(cc_qti_tags::feedbackstyle, cc_qti_values::Complete);
988    }
989
990    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
991        $node = $doc->append_new_element_ns($item, $namespace, $this->tagname);
992        $this->generate_attributes($doc, $node, $namespace);
993
994        if (!empty($this->items)) {
995            foreach ($this->items as $telement) {
996                $telement->generate($doc, $node, $namespace);
997            }
998        }
999    }
1000}
1001
1002class cc_assesment_itemfeedback_solutiontype extends cc_assesment_itemfeedback_shintype_base {
1003    public function __construct() {
1004        parent::__construct();
1005        $this->tagname = cc_qti_tags::solution;
1006    }
1007
1008    /**
1009     * @param cc_assesment_itemfeedback_solutionmaterial $object
1010     */
1011    public function add_solutionmaterial(cc_assesment_itemfeedback_solutionmaterial $object) {
1012        $this->items[] = $object;
1013    }
1014}
1015
1016class cc_assesment_itemfeedbac_hinttype extends cc_assesment_itemfeedback_shintype_base {
1017    public function __construct() {
1018        parent::__construct();
1019        $this->tagname = cc_qti_tags::hint;
1020    }
1021
1022    /**
1023     * @param cc_assesment_itemfeedback_hintmaterial $object
1024     */
1025    public function add_hintmaterial(cc_assesment_itemfeedback_hintmaterial $object) {
1026        $this->items[] = $object;
1027    }
1028}
1029
1030class cc_assesment_itemfeedbacktype extends cc_question_metadata_base {
1031    /**
1032     * @var cc_assesment_flow_mattype
1033     */
1034    protected $flow_mat = null;
1035    /**
1036     * @var cc_assesment_material
1037     */
1038    protected $material = null;
1039    /**
1040     * @var cc_assesment_itemfeedback_solutiontype
1041     */
1042    protected $solution = null;
1043    protected $hint = null;
1044
1045    public function __construct() {
1046        $this->set_setting(cc_qti_tags::ident, cc_helpers::uuidgen('I_'));
1047        $this->set_setting(cc_qti_tags::title);
1048    }
1049
1050    /**
1051     * @param string $value
1052     */
1053    public function set_ident($value) {
1054        $this->set_setting(cc_qti_tags::ident, $value);
1055    }
1056
1057    /**
1058     * @param string $value
1059     */
1060    public function set_title($value) {
1061        $this->set_setting(cc_qti_tags::title, $value);
1062    }
1063
1064    /**
1065     * @param cc_assesment_flow_mattype $object
1066     */
1067    public function set_flow_mat(cc_assesment_flow_mattype $object) {
1068        $this->flow_mat = $object;
1069    }
1070
1071    /**
1072     * @param cc_assesment_material $object
1073     */
1074    public function set_material(cc_assesment_material $object) {
1075        $this->material = $object;
1076    }
1077
1078    /**
1079     * @param cc_assesment_itemfeedback_solutiontype $object
1080     */
1081    public function set_solution(cc_assesment_itemfeedback_solutiontype $object) {
1082        $this->solution = $object;
1083    }
1084
1085    public function set_hint($object) {
1086        $this->hint = $object;
1087    }
1088
1089    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1090        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::itemfeedback);
1091        $this->generate_attributes($doc, $node, $namespace);
1092
1093        if (!empty($this->flow_mat) && empty($this->material)) {
1094            $this->flow_mat->generate($doc, $node, $namespace);
1095        }
1096
1097        if (!empty($this->material) && empty($this->flow_mat)) {
1098            $this->material->generate($doc, $node, $namespace);
1099        }
1100
1101        if (!empty($this->solution)) {
1102            $this->solution->generate($doc, $node, $namespace);
1103        }
1104
1105        if (!empty($this->itemfeedback)) {
1106            $this->itemfeedback->generate($doc, $node, $namespace);
1107        }
1108    }
1109}
1110
1111class cc_assesment_section_item extends cc_assesment_section {
1112
1113    /**
1114     * @var cc_assesment_itemmetadata
1115     */
1116    protected $itemmetadata = null;
1117    /**
1118     * @var cc_assesment_presentation
1119     */
1120    protected $presentation = null;
1121    protected $resprocessing = array();
1122    protected $itemfeedback = array();
1123
1124    public function set_itemmetadata(cc_assesment_itemmetadata $object) {
1125        $this->itemmetadata = $object;
1126    }
1127
1128    public function set_presentation(cc_assesment_presentation $object) {
1129        $this->presentation = $object;
1130    }
1131
1132    public function add_resprocessing(cc_assesment_resprocessingtype $object) {
1133        $this->resprocessing[] = $object;
1134    }
1135
1136    public function add_itemfeedback(cc_assesment_itemfeedbacktype $object) {
1137        $this->itemfeedback[] = $object;
1138    }
1139
1140    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1141        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::item);
1142        $this->generate_attributes($doc, $node, $namespace);
1143
1144        if (!empty($this->itemmetadata)) {
1145            $this->itemmetadata->generate($doc, $node, $namespace);
1146        }
1147
1148        if (!empty($this->presentation)) {
1149            $this->presentation->generate($doc, $node, $namespace);
1150        }
1151
1152        if (!empty($this->resprocessing)) {
1153            foreach ($this->resprocessing as $resprocessing) {
1154                $resprocessing->generate($doc, $node, $namespace);
1155            }
1156        }
1157
1158        if (!empty($this->itemfeedback)) {
1159            foreach ($this->itemfeedback as $itemfeedback) {
1160                $itemfeedback->generate($doc, $node, $namespace);
1161            }
1162        }
1163    }
1164}
1165
1166class cc_assesment_render_choicetype extends cc_question_metadata_base {
1167    /**
1168     * @var array
1169     */
1170    protected $materials = array();
1171
1172    /**
1173     * @var array
1174     */
1175    protected $material_refs = array();
1176
1177    /**
1178     * @var array
1179     */
1180    protected $response_labels = array();
1181    /**
1182     * @var array
1183     */
1184    protected $flow_labels = array();
1185
1186    public function __construct() {
1187        $this->set_setting(cc_qti_tags::shuffle, cc_qti_values::No);
1188        $this->set_setting(cc_qti_tags::minnumber);
1189        $this->set_setting(cc_qti_tags::maxnumber);
1190    }
1191
1192    public function add_material(cc_assesment_material $object) {
1193        $this->materials[] = $object;
1194    }
1195
1196    public function add_material_ref(cc_assesment_response_matref $object) {
1197        $this->material_refs[] = $object;
1198    }
1199
1200    public function add_response_label(cc_assesment_response_labeltype $object) {
1201        $this->response_labels[] = $object;
1202    }
1203
1204    public function add_flow_label($object) {
1205        $this->flow_labels[] = $object;
1206    }
1207
1208    public function enable_shuffle($value = true) {
1209        $this->enable_setting_yesno(cc_qti_tags::shuffle, $value);
1210    }
1211
1212    public function set_limits($min = null, $max = null) {
1213        $this->set_setting(cc_qti_tags::minnumber, $min);
1214        $this->set_setting(cc_qti_tags::maxnumber, $max);
1215    }
1216
1217    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1218        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::render_choice);
1219        $this->generate_attributes($doc, $node, $namespace);
1220
1221        if (!empty($this->materials)) {
1222            foreach ($this->materials as $mattag) {
1223                $mattag->generate($doc, $node, $namespace);
1224            }
1225        }
1226
1227        if (!empty($this->material_refs)) {
1228            foreach ($this->material_refs as $matreftag) {
1229                $matreftag->generate($doc, $node, $namespace);
1230            }
1231        }
1232
1233        if (!empty($this->response_labels)) {
1234            foreach ($this->response_labels as $resplabtag) {
1235                $resplabtag->generate($doc, $node, $namespace);
1236            }
1237        }
1238
1239        if (!empty($this->flow_labels)) {
1240            foreach ($this->flow_labels as $flowlabtag) {
1241                $flowlabtag->generate($doc, $node, $namespace);
1242            }
1243        }
1244    }
1245
1246}
1247
1248class cc_assesment_flow_mattype extends cc_question_metadata_base {
1249
1250    /**
1251     * @var cc_assesment_material
1252     */
1253    protected $material = null;
1254    /**
1255     * @var cc_assesment_response_matref
1256     */
1257    protected $material_ref = null;
1258    /**
1259     * @var cc_assesment_flow_mattype
1260     */
1261    protected $flow_mat = null;
1262
1263    public function __construct() {
1264        $this->set_setting(cc_qti_tags::t_class);
1265    }
1266
1267    public function set_class($value) {
1268        $this->set_setting(cc_qti_tags::t_class, $value);
1269    }
1270
1271    public function set_material(cc_assesment_material $object) {
1272        $this->material = $object;
1273    }
1274
1275    public function set_material_ref(cc_assesment_response_matref $object) {
1276        $this->material_ref = $object;
1277    }
1278
1279    public function set_flow_mat(cc_assesment_flow_mattype $object) {
1280        $this->flow_mat = $object;
1281    }
1282
1283    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1284        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::flow_mat);
1285        $this->generate_attributes($doc, $node, $namespace);
1286
1287        if (!empty($this->flow_mat)) {
1288            $this->flow_mat->generate($doc, $node, $namespace);
1289        }
1290
1291        if (!empty($this->material)) {
1292            $this->material->generate($doc, $node, $namespace);
1293        }
1294
1295        if (!empty($this->material_ref)) {
1296            $this->material_ref->generate($doc, $node, $namespace);
1297        }
1298    }
1299}
1300
1301class cc_assesment_response_labeltype extends cc_question_metadata_base {
1302    /**
1303     * @var cc_assesment_material
1304     */
1305    protected $material = null;
1306
1307    /**
1308     * @var cc_assesment_response_matref
1309     */
1310    protected $material_ref = null;
1311
1312    /**
1313     * @var cc_assesment_flow_mattype
1314     */
1315    protected $flow_mat = null;
1316
1317    public function __construct() {
1318        $this->set_setting(cc_qti_tags::ident, cc_helpers::uuidgen('I_'));
1319        $this->set_setting(cc_qti_tags::labelrefid);
1320        $this->set_setting(cc_qti_tags::rshuffle);
1321        $this->set_setting(cc_qti_tags::match_group);
1322        $this->set_setting(cc_qti_tags::match_max);
1323    }
1324
1325    public function set_ident($value) {
1326        $this->set_setting(cc_qti_tags::ident, $value);
1327    }
1328
1329    public function get_ident() {
1330        return $this->get_setting(cc_qti_tags::ident);
1331    }
1332
1333    public function set_labelrefid($value) {
1334        $this->set_setting(cc_qti_tags::labelrefid, $value);
1335    }
1336
1337    public function enable_rshuffle($value = true) {
1338        $this->enable_setting_yesno(cc_qti_tags::rshuffle, $value);
1339    }
1340
1341    public function set_match_group($value) {
1342        $this->set_setting(cc_qti_tags::match_group, $value);
1343    }
1344
1345    public function set_match_max($value) {
1346        $this->set_setting(cc_qti_tags::match_max, $value);
1347    }
1348
1349    public function set_material(cc_assesment_material $object) {
1350        $this->material = $object;
1351    }
1352
1353    public function set_material_ref(cc_assesment_response_matref $object) {
1354        $this->material_ref = $object;
1355    }
1356
1357    public function set_flow_mat(cc_assesment_flow_mattype $object) {
1358        $this->flow_mat = $object;
1359    }
1360
1361    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1362        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::response_label);
1363        $this->generate_attributes($doc, $node, $namespace);
1364
1365        if (!empty($this->material)) {
1366            $this->material->generate($doc, $node, $namespace);
1367        }
1368
1369        if (!empty($this->material_ref)) {
1370            $this->material_ref->generate($doc, $node, $namespace);
1371        }
1372
1373        if (!empty($this->flow_mat)) {
1374            $this->flow_mat->generate($doc, $node, $namespace);
1375        }
1376    }
1377}
1378
1379class cc_assesment_flow_labeltype extends cc_question_metadata_base {
1380    /**
1381     * @var cc_assesment_flow_labeltype
1382     */
1383    protected $flow_label = null;
1384    /**
1385     * @var cc_assesment_response_labeltype
1386     */
1387    protected $response_label = null;
1388
1389    public function __construct() {
1390        $this->set_setting(cc_qti_tags::t_class);
1391    }
1392
1393    public function set_class($value) {
1394        $this->set_setting(cc_qti_tags::t_class, $value);
1395    }
1396
1397    public function set_flow_label(cc_assesment_flow_labeltype $object) {
1398        $this->flow_label = $object;
1399    }
1400
1401    public function set_response_label(cc_assesment_response_labeltype $object) {
1402        $this->response_label = $object;
1403    }
1404
1405    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1406        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::flow_label);
1407        $this->generate_attributes($doc, $node, $namespace);
1408
1409        if (!empty($this->material)) {
1410            $this->material->generate($doc, $node, $namespace);
1411        }
1412
1413        if (!empty($this->material_ref)) {
1414            $this->material_ref->generate($doc, $node, $namespace);
1415        }
1416
1417        if (!empty($this->response_label)) {
1418            $this->response_label->generate($doc, $node, $namespace);
1419        }
1420
1421        if (!empty($this->flow_label)) {
1422            $this->flow_label->generate($doc, $node, $namespace);
1423        }
1424    }
1425
1426}
1427
1428
1429class cc_assesment_render_fibtype extends cc_question_metadata_base {
1430    /**
1431     * @var cc_assesment_material
1432     */
1433    protected $material = null;
1434
1435    /**
1436     * @var cc_assesment_response_matref
1437     */
1438    protected $material_ref = null;
1439    /**
1440     * @var cc_assesment_response_labeltype
1441     */
1442    protected $response_label = null;
1443    /**
1444     *
1445     * Enter description here ...
1446     * @var unknown_type
1447     */
1448    protected $flow_label = null;
1449
1450
1451    public function __construct() {
1452        $this->set_setting(cc_qti_tags::encoding );
1453        $this->set_setting(cc_qti_tags::charset  );
1454        $this->set_setting(cc_qti_tags::rows     );
1455        $this->set_setting(cc_qti_tags::columns  );
1456        $this->set_setting(cc_qti_tags::maxchars );
1457        $this->set_setting(cc_qti_tags::minnumber);
1458        $this->set_setting(cc_qti_tags::maxnumber);
1459        $this->set_setting(cc_qti_tags::prompt, cc_qti_values::Box);
1460        $this->set_setting(cc_qti_tags::fibtype, cc_qti_values::String);
1461    }
1462
1463    public function set_encoding($value) {
1464        $this->set_setting(cc_qti_tags::encoding, $value);
1465    }
1466    public function set_charset($value) {
1467        $this->set_setting(cc_qti_tags::charset, $value);
1468    }
1469
1470    public function set_rows($value) {
1471        $this->set_setting(cc_qti_tags::rows, $value);
1472    }
1473
1474    public function set_columns($value) {
1475        $this->set_setting(cc_qti_tags::columns, $value);
1476    }
1477
1478    public function set_maxchars($value) {
1479        $this->set_setting(cc_qti_tags::columns, $value);
1480    }
1481
1482    public function set_limits($min = null, $max = null) {
1483        $this->set_setting(cc_qti_tags::minnumber, $min);
1484        $this->set_setting(cc_qti_tags::maxnumber, $max);
1485    }
1486
1487    public function set_prompt($value) {
1488        $this->set_setting(cc_qti_tags::prompt, $value);
1489    }
1490
1491    public function set_fibtype($value) {
1492        $this->set_setting(cc_qti_tags::fibtype, $value);
1493    }
1494
1495    public function set_material(cc_assesment_material $object) {
1496        $this->material = $object;
1497    }
1498
1499    public function set_material_ref(cc_assesment_response_matref $object) {
1500        $this->material_ref = $object;
1501    }
1502
1503    public function set_response_label(cc_assesment_response_labeltype $object) {
1504        $this->response_label = $object;
1505    }
1506
1507    public function set_flow_label($object) {
1508        $this->flow_label = $object;
1509    }
1510
1511    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1512        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::render_fib);
1513        $this->generate_attributes($doc, $node, $namespace);
1514
1515        if (!empty($this->material) && empty($this->material_ref)) {
1516            $this->material->generate($doc, $node, $namespace);
1517        }
1518
1519        if (!empty($this->material_ref) && empty($this->material)) {
1520            $this->material_ref->generate($doc, $node, $namespace);
1521        }
1522
1523        if (!empty($this->response_label)) {
1524            $this->response_label->generate($doc, $node, $namespace);
1525        }
1526
1527        if (!empty($this->flow_label)) {
1528            $this->flow_label->generate($doc, $node, $namespace);
1529        }
1530    }
1531}
1532
1533class cc_response_lidtype extends cc_question_metadata_base {
1534    /**
1535     * @var string
1536     */
1537    protected $tagname = null;
1538    /**
1539     * @var cc_assesment_material
1540     */
1541    protected $material = null;
1542
1543    /**
1544     * @var cc_assesment_response_matref
1545     */
1546    protected $material_ref = null;
1547
1548    /**
1549     * @var cc_assesment_render_choicetype
1550     */
1551    protected $render_choice = null;
1552
1553    /**
1554     * @var cc_assesment_render_fibtype
1555     */
1556    protected $render_fib = null;
1557
1558    public function __construct() {
1559        $this->set_setting(cc_qti_tags::rcardinality, cc_qti_values::Single);
1560        $this->set_setting(cc_qti_tags::rtiming);
1561        $this->set_setting(cc_qti_tags::ident, cc_helpers::uuidgen('I_'));
1562        $this->tagname = cc_qti_tags::response_lid;
1563    }
1564
1565    public function set_rcardinality($value) {
1566        $this->set_setting(cc_qti_tags::rcardinality, $value);
1567    }
1568
1569    public function enable_rtiming($value = true) {
1570        $this->enable_setting_yesno(cc_qti_tags::rtiming, $value);
1571    }
1572
1573    public function set_ident($value) {
1574        $this->set_setting(cc_qti_tags::ident, $value);
1575    }
1576
1577    public function get_ident() {
1578        return $this->get_setting(cc_qti_tags::ident);
1579    }
1580
1581    public function set_material_ref(cc_assesment_response_matref $object) {
1582        $this->material_ref = $object;
1583    }
1584
1585    public function set_material(cc_assesment_material $object) {
1586        $this->material = $object;
1587    }
1588
1589    public function set_render_choice(cc_assesment_render_choicetype $object) {
1590        $this->render_choice = $object;
1591    }
1592
1593    public function set_render_fib(cc_assesment_render_fibtype $object) {
1594        $this->render_fib = $object;
1595    }
1596
1597    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1598        $node = $doc->append_new_element_ns($item, $namespace, $this->tagname);
1599        $this->generate_attributes($doc, $node, $namespace);
1600
1601        if (!empty($this->material) && empty($this->material_ref)) {
1602            $this->material->generate($doc, $node, $namespace);
1603        }
1604
1605        if (!empty($this->material_ref) && empty($this->material)) {
1606            $this->material_ref->generate($doc, $node, $namespace);
1607        }
1608
1609        if (!empty($this->render_choice) && empty($this->render_fib)) {
1610            $this->render_choice->generate($doc, $node, $namespace);
1611        }
1612
1613        if (!empty($this->render_fib) && empty($this->render_choice)) {
1614            $this->render_fib->generate($doc, $node, $namespace);
1615        }
1616    }
1617}
1618
1619class cc_assesment_response_strtype extends cc_response_lidtype {
1620    public function __construct() {
1621        $rtt = parent::__construct();
1622        $this->tagname = cc_qti_tags::response_str;
1623    }
1624}
1625
1626class cc_assesment_flowtype extends cc_question_metadata_base {
1627    /**
1628     * @var cc_assesment_flowtype
1629     */
1630    protected $flow = null;
1631    /**
1632     * @var cc_assesment_material
1633     */
1634    protected $material = null;
1635    /**
1636     * @var cc_assesment_response_matref
1637     */
1638    protected $material_ref = null;
1639    /**
1640     * @var cc_response_lidtype
1641     */
1642    protected $response_lid = null;
1643    /**
1644     * @var cc_assesment_response_strtype
1645     */
1646    protected $response_str = null;
1647
1648    public function __construct() {
1649        $this->set_setting(cc_qti_tags::t_class);
1650    }
1651
1652    public function set_class($value) {
1653        $this->set_setting(cc_qti_tags::t_class, $value);
1654    }
1655
1656    public function set_flow(cc_assesment_flowtype $object) {
1657        $this->flow = $object;
1658    }
1659
1660    public function set_material(cc_assesment_material $object) {
1661        $this->material = $object;
1662    }
1663
1664    public function set_material_ref(cc_assesment_response_matref $object) {
1665        $this->material_ref = $object;
1666    }
1667
1668    public function set_response_lid(cc_response_lidtype $object) {
1669        $this->response_lid = $object;
1670    }
1671
1672    public function set_response_str(cc_assesment_response_strtype $object) {
1673        $this->response_str = $object;
1674    }
1675
1676    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1677        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::flow);
1678        $this->generate_attributes($doc, $node, $namespace);
1679
1680        if (!empty($this->flow)) {
1681            $this->flow->generate($doc, $node, $namespace);
1682        }
1683
1684        if (!empty($this->material)) {
1685            $this->material->generate($doc, $node, $namespace);
1686        }
1687
1688        if (!empty($this->response_lid)) {
1689            $this->response_lid->generate($doc, $node, $namespace);
1690        }
1691
1692        if (!empty($this->response_str)) {
1693            $this->response_str->generate($doc, $node, $namespace);
1694        }
1695    }
1696}
1697
1698class cc_assesment_presentation extends cc_question_metadata_base {
1699    /**
1700     * @var cc_assesment_flowtype
1701     */
1702    protected $flow         = null;
1703    /**
1704     * @var cc_assesment_material
1705     */
1706    protected $material     = null;
1707    /**
1708     * @var cc_response_lidtype
1709     */
1710    protected $response_lid = null;
1711    /**
1712     * @var cc_assesment_response_strtype
1713     */
1714    protected $response_str = null;
1715
1716    public function __construct() {
1717        $this->set_setting(cc_qti_tags::label);
1718        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml);
1719        $this->set_setting(cc_qti_tags::x0);
1720        $this->set_setting(cc_qti_tags::y0);
1721        $this->set_setting(cc_qti_tags::width);
1722        $this->set_setting(cc_qti_tags::height);
1723    }
1724
1725    public function set_label($value) {
1726        $this->set_setting(cc_qti_tags::label, $value);
1727    }
1728
1729    public function set_lang($value) {
1730        $this->set_setting_wns(cc_qti_tags::xml_lang , cc_xml_namespace::xml, $value);
1731    }
1732
1733    public function set_coor($x = null, $y = null) {
1734        $this->set_setting(cc_qti_tags::x0, $x);
1735        $this->set_setting(cc_qti_tags::y0, $y);
1736    }
1737
1738    public function set_size($width = null, $height = null) {
1739        $this->set_setting(cc_qti_tags::width, $width);
1740        $this->set_setting(cc_qti_tags::height, $height);
1741    }
1742
1743    public function set_flow(cc_assesment_flowtype $object) {
1744        $this->flow = $object;
1745    }
1746
1747    public function set_material(cc_assesment_material $object) {
1748        $this->material = $object;
1749    }
1750
1751    public function set_response_lid(cc_response_lidtype $object) {
1752        $this->response_lid = $object;
1753    }
1754
1755    public function set_response_str(cc_assesment_response_strtype $object) {
1756        $this->response_str = $object;
1757    }
1758
1759    public function generate(XMLGenericDocument &$doc, DOMNode &$item, $namespace) {
1760        $node = $doc->append_new_element_ns($item, $namespace, cc_qti_tags::presentation);
1761        $this->generate_attributes($doc, $node, $namespace);
1762
1763        if (!empty($this->flow)) {
1764            $this->flow->generate($doc, $node, $namespace);
1765        }
1766
1767        if (!empty($this->material) && empty($this->flow)) {
1768            $this->material->generate($doc, $node, $namespace);
1769        }
1770
1771        if (!empty($this->response_lid) && empty($this->flow)) {
1772            $this->response_lid->generate($doc, $node, $namespace);
1773        }
1774
1775        if (!empty($this->response_str) && empty($this->flow)) {
1776            $this->response_str->generate($doc, $node, $namespace);
1777        }
1778    }
1779}
1780
1781class assesment1_resurce_file extends general_cc_file {
1782    const deafultname = 'assesment.xml';
1783
1784    protected $rootns   = 'xmlns';
1785    protected $rootname = cc_qti_tags::questestinterop;
1786    protected $ccnamespaces = array('xmlns' => 'http://www.imsglobal.org/xsd/ims_qtiasiv1p2',
1787                                    'xsi'   => 'http://www.w3.org/2001/XMLSchema-instance');
1788    protected $ccnsnames = array('xmlns' => 'http://www.imsglobal.org/profile/cc/ccv1p0/derived_schema/domainProfile_4/ims_qtiasiv1p2_localised.xsd');
1789
1790    /**
1791     * @var string
1792     */
1793    protected $assessment_title = 'Untitled';
1794    /**
1795     * @var cc_assesment_metadata
1796     */
1797    protected $metadata = null;
1798    /**
1799     * @var cc_assesment_rubric_base
1800     */
1801    protected $rubric = null;
1802
1803    /**
1804     * @var cc_assesment_presentation_material_base
1805     */
1806    protected $presentation_material = null;
1807
1808    /**
1809     * @var cc_assesment_section
1810     */
1811    protected $section = null;
1812
1813    public function set_metadata(cc_assesment_metadata $object) {
1814        $this->metadata = $object;
1815    }
1816
1817    public function set_rubric(cc_assesment_rubric_base $object) {
1818        $this->rubric = $object;
1819    }
1820
1821    public function set_presentation_material(cc_assesment_presentation_material_base $object) {
1822        $this->presentation_material = $object;
1823    }
1824
1825    public function set_section(cc_assesment_section $object) {
1826        $this->section = $object;
1827    }
1828
1829    public function set_title($value) {
1830        $this->assessment_title = self::safexml($value);
1831    }
1832
1833    protected function on_save() {
1834        $rns = $this->ccnamespaces[$this->rootns];
1835        //root assesment element - required
1836        $assessment = $this->append_new_element_ns($this->root, $rns, cc_qti_tags::assessment);
1837        $this->append_new_attribute_ns($assessment, $rns, cc_qti_tags::ident, cc_helpers::uuidgen('QDB_'));
1838        $this->append_new_attribute_ns($assessment, $rns, cc_qti_tags::title, $this->assessment_title);
1839
1840        //metadata - optional
1841        if (!empty($this->metadata)) {
1842            $this->metadata->generate($this, $assessment, $rns);
1843        }
1844
1845        //rubric - optional
1846        if (!empty($this->rubric)) {
1847            $this->rubric->generate($this, $assessment, $rns);
1848        }
1849
1850        //presentation_material - optional
1851        if (!empty($this->presentation_material)) {
1852            $this->presentation_material->generate($this, $assessment, $rns);
1853        }
1854
1855        //section - required
1856        if (!empty($this->section)) {
1857            $this->section->generate($this, $assessment, $rns);
1858        }
1859
1860        return true;
1861    }
1862}
1863
1864
1865class assesment11_resurce_file extends assesment1_resurce_file {
1866    protected $ccnsnames = array('xmlns' => 'http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_qtiasiv1p2p1_v1p0.xsd');
1867}
1868
1869abstract class cc_assesment_helper {
1870
1871    public static $correct_fb = null;
1872    public static $incorrect_fb = null;
1873
1874    public static function add_feedback($qitem, $content, $content_type, $ident) {
1875        if (empty($content)) {
1876            return false;
1877        }
1878        $qitemfeedback = new cc_assesment_itemfeedbacktype();
1879        $qitem->add_itemfeedback($qitemfeedback);
1880        if (!empty($ident)) {
1881            $qitemfeedback->set_ident($ident);
1882        }
1883        $qflowmat = new cc_assesment_flow_mattype();
1884        $qitemfeedback->set_flow_mat($qflowmat);
1885        $qmaterialfb = new cc_assesment_material();
1886        $qflowmat->set_material($qmaterialfb);
1887        $qmattext = new cc_assesment_mattext();
1888        $qmaterialfb->set_mattext($qmattext);
1889        $qmattext->set_content($content, $content_type);
1890        return true;
1891    }
1892
1893    public static function add_answer($qresponse_choice, $content, $content_type) {
1894        $qresponse_label = new cc_assesment_response_labeltype();
1895        $qresponse_choice->add_response_label($qresponse_label);
1896        $qrespmaterial = new cc_assesment_material();
1897        $qresponse_label->set_material($qrespmaterial);
1898        $qrespmattext = new cc_assesment_mattext();
1899        $qrespmaterial->set_mattext($qrespmattext);
1900        $qrespmattext->set_content($content, $content_type);
1901        return $qresponse_label;
1902    }
1903
1904    public static function add_response_condition($node, $title, $ident, $feedback_refid, $respident) {
1905        $qrespcondition = new cc_assesment_respconditiontype();
1906        $node->add_respcondition($qrespcondition);
1907        //define rest of the conditions
1908        $qconditionvar = new cc_assignment_conditionvar();
1909        $qrespcondition->set_conditionvar($qconditionvar);
1910        $qvarequal = new cc_assignment_conditionvar_varequaltype($ident);
1911        $qvarequal->enable_case();
1912        $qconditionvar->set_varequal($qvarequal);
1913        $qvarequal->set_respident($respident);
1914        $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
1915        $qrespcondition->add_displayfeedback($qdisplayfeedback);
1916        $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
1917        $qdisplayfeedback->set_linkrefid($feedback_refid);
1918    }
1919
1920    public static function add_assesment_description($rt, $content, $contenttype) {
1921        if (empty($rt) || empty($content)) {
1922            return;
1923        }
1924        $activity_rubric = new cc_assesment_rubric_base();
1925        $rubric_material = new cc_assesment_material();
1926        $activity_rubric->set_material($rubric_material);
1927        $rubric_mattext = new cc_assesment_mattext();
1928        $rubric_material->set_label('Summary');
1929        $rubric_material->set_mattext($rubric_mattext);
1930        $rubric_mattext->set_content($content, $contenttype);
1931        $rt->set_rubric($activity_rubric);
1932    }
1933
1934    public static function add_respcondition($node, $title, $feedback_refid, $grade_value = null, $continue = false ) {
1935        $qrespcondition = new cc_assesment_respconditiontype();
1936        $qrespcondition->set_title($title);
1937        $node->add_respcondition($qrespcondition);
1938        $qrespcondition->enable_continue($continue);
1939        //Add setvar if grade present
1940        if ($grade_value !== null) {
1941            $qsetvar = new cc_assignment_setvartype($grade_value);
1942            $qrespcondition->add_setvar($qsetvar);
1943        }
1944        //define the condition for success
1945        $qconditionvar = new cc_assignment_conditionvar();
1946        $qrespcondition->set_conditionvar($qconditionvar);
1947        $qother = new cc_assignment_conditionvar_othertype();
1948        $qconditionvar->set_other($qother);
1949        $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
1950        $qrespcondition->add_displayfeedback($qdisplayfeedback);
1951        $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
1952        $qdisplayfeedback->set_linkrefid($feedback_refid);
1953    }
1954
1955    /**
1956     *
1957     * Enter description here ...
1958     * @param XMLGenericDocument $qdoc
1959     * @param unknown_type $manifest
1960     * @param cc_assesment_section $section
1961     * @param unknown_type $rootpath
1962     * @param unknown_type $contextid
1963     * @param unknown_type $outdir
1964     */
1965    public static function process_questions(&$qdoc, &$manifest, cc_assesment_section &$section, $rootpath, $contextid, $outdir) {
1966        $question_file = $rootpath . DIRECTORY_SEPARATOR . 'questions.xml';
1967        //load questions file
1968        $questions = new XMLGenericDocument();
1969        if (!$questions->load($question_file)) {
1970            return false;
1971        }
1972
1973        pkg_resource_dependencies::instance()->reset();
1974        $questioncount = 0;
1975        $questionforexport = 0;
1976        $qids = $qdoc->nodeList('//question_instances//questionid');
1977        foreach ($qids as $qid) {
1978            /** @var DOMNode $qid */
1979            $value = $qid->nodeValue;
1980            if (intval($value) == 0) {
1981                continue;
1982            }
1983            $question_node = $questions->node("//question_category/questions/question[@id='{$value}']");
1984            if (empty($question_node)) {
1985                continue;
1986            }
1987            ++$questionforexport;
1988            //process question
1989            //question type
1990            $qtype = $questions->nodeValue('qtype', $question_node);
1991            $question_processor = null;
1992            switch ($qtype) {
1993                case 'multichoice':
1994                    $single_correct_answer = (int)$questions->nodeValue('plugin_qtype_multichoice_question/multichoice/single', $question_node) > 0;
1995                    //TODO: Add checking for the nunmber of valid responses
1996                    //If question is marked as multi response but contains only one valid answer it
1997                    //should be handle as single response - classic multichoice
1998                    if ($single_correct_answer) {
1999                        $question_processor = new cc_assesment_question_multichoice($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2000                    } else {
2001                        $question_processor = new cc_assesment_question_multichoice_multiresponse($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2002                    }
2003                    $question_processor->generate();
2004                    ++$questioncount;
2005                break;
2006                case 'truefalse':
2007                    $question_processor = new cc_assesment_question_truefalse($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2008                    $question_processor->generate();
2009                    ++$questioncount;
2010                break;
2011                case 'essay':
2012                    $question_processor = new cc_assesment_question_essay($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2013                    $question_processor->generate();
2014                    ++$questioncount;
2015                break;
2016                case 'shortanswer':
2017                    //This is rather ambiguos since shortanswer supports partial pattern match
2018                    //In order to detect pattern match we need to scan for all the responses
2019                    //if at least one of the responses uses wildcards it should be treated as
2020                    //pattern match, otherwise it should be simple fill in the blank
2021                    if (self::has_matching_element($questions, $question_node)) {
2022                        //$question_processor = new cc_assesment_question_patternmatch($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2023                        $questionforexport--;
2024                    } else {
2025                        $question_processor = new cc_assesment_question_sfib($qdoc, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2026                    }
2027                    if (!empty($question_processor)) {
2028                        $question_processor->generate();
2029                        ++$questioncount;
2030                    }
2031                break;
2032                default:
2033                    ;
2034                break;
2035            }
2036
2037        }
2038
2039        //return dependencies
2040        return ($questioncount == 0) || ($questioncount != $questionforexport)?
2041               false: pkg_resource_dependencies::instance()->get_deps();
2042    }
2043
2044    /**
2045     *
2046     * Checks if question has matching element
2047     * @param XMLGenericDocument $questions
2048     * @param object $question_node
2049     * @return bool
2050     */
2051    public static function has_matching_element(XMLGenericDocument $questions, $question_node) {
2052        $answers = $questions->nodeList('plugin_qtype_shortanswer_question//answertext', $question_node);
2053        $result = false;
2054        foreach ($answers as $answer) {
2055            $prepare = str_replace('\*', '\#', $answer->nodeValue);
2056            $result = (strpos($prepare, '*') !== false);
2057            if ($result) {
2058                break;
2059            }
2060        }
2061        return $result;
2062    }
2063
2064}
2065
2066class cc_assesment_question_proc_base {
2067    /**
2068     * @var XMLGenericDocument
2069     */
2070    protected $quiz = null;
2071
2072    /**
2073     * @var XMLGenericDocument
2074     */
2075    protected $questions = null;
2076
2077    /**
2078    * @var cc_manifest
2079    */
2080    protected $manifest = null;
2081
2082    /**
2083     * @var cc_assesment_section
2084     */
2085    protected $section = null;
2086
2087    /**
2088     * @var DOMElement
2089     */
2090    protected $question_node = null;
2091
2092    /**
2093     * @var string
2094     */
2095    protected $rootpath = null;
2096
2097    /**
2098     * @var string
2099     */
2100    protected $contextid = null;
2101
2102    /**
2103     * @var string
2104     */
2105    protected $outdir = null;
2106
2107    /**
2108     * @var string
2109     */
2110    protected $qtype = null;
2111
2112    /**
2113     * @var cc_question_metadata
2114     */
2115    protected $qmetadata = null;
2116
2117    /**
2118     * @var cc_assesment_section_item
2119     */
2120    protected $qitem = null;
2121
2122    /**
2123     * @var cc_assesment_presentation
2124     */
2125    protected $qpresentation = null;
2126
2127    /**
2128     * @var cc_response_lidtype
2129     */
2130    protected $qresponse_lid = null;
2131
2132    protected $qresprocessing = null;
2133
2134    protected $correct_grade_value = null;
2135    protected $correct_answer_node_id = null;
2136    protected $correct_answer_ident = null;
2137
2138    protected $total_grade_value = null;
2139
2140    protected $answerlist = null;
2141
2142    protected $general_feedback = null;
2143    protected $correct_feedbacks = array();
2144    protected $incorrect_feedbacks = array();
2145
2146    /**
2147     * @param XMLGenericDocument $questions
2148     * @param cc_manifest $manifest
2149     * @param cc_assesment_section $section
2150     * @param DOMElement $question_node
2151     * @param string $rootpath
2152     * @param string $contextid
2153     * @param string $outdir
2154     */
2155    public function __construct(XMLGenericDocument &$quiz, XMLGenericDocument &$questions, cc_manifest &$manifest, cc_assesment_section &$section, &$question_node, $rootpath, $contextid, $outdir) {
2156        $this->quiz = $quiz;
2157        $this->questions = $questions;
2158        $this->manifest = $manifest;
2159        $this->section = $section;
2160        $this->question_node = $question_node;
2161        $this->rootpath = $rootpath;
2162        $this->contextid = $contextid;
2163        $this->outdir = $outdir;
2164
2165        //
2166        $qitem = new cc_assesment_section_item();
2167        $this->section->add_item($qitem);
2168        $qitem->set_title($this->questions->nodeValue('name', $this->question_node));
2169        $this->qitem = $qitem;
2170    }
2171
2172    public function on_generate_metadata() {
2173        if (empty($this->qmetadata)) {
2174            $this->qmetadata = new cc_question_metadata($this->qtype);
2175            //Get weighting value
2176            $weighting_value = (int)$this->questions->nodeValue('defaultmark', $this->question_node);
2177            if ($weighting_value > 1) {
2178                $this->qmetadata->set_weighting($weighting_value);
2179            }
2180            //Get category
2181            $question_category = $this->questions->nodeValue('../../name', $this->question_node);
2182            if (!empty($question_category)) {
2183                $this->qmetadata->set_category($question_category);
2184            }
2185            $rts = new cc_assesment_itemmetadata();
2186            $rts->add_metadata($this->qmetadata);
2187            $this->qitem->set_itemmetadata($rts);
2188        }
2189    }
2190
2191    public function on_generate_presentation() {
2192        if (empty($this->qpresentation)) {
2193            $qpresentation = new cc_assesment_presentation();
2194            $this->qitem->set_presentation($qpresentation);
2195            //add question text
2196            $qmaterial = new cc_assesment_material();
2197            $qmattext = new cc_assesment_mattext();
2198            $question_text = $this->questions->nodeValue('questiontext', $this->question_node);
2199            $result = cc_helpers::process_linked_files( $question_text,
2200                                                        $this->manifest,
2201                                                        $this->rootpath,
2202                                                        $this->contextid,
2203                                                        $this->outdir);
2204            $qmattext->set_content($result[0], cc_qti_values::htmltype);
2205            $qmaterial->set_mattext($qmattext);
2206            $qpresentation->set_material($qmaterial);
2207            $this->qpresentation = $qpresentation;
2208            pkg_resource_dependencies::instance()->add($result[1]);
2209        }
2210    }
2211
2212    public function on_generate_answers() {}
2213    public function on_generate_feedbacks() {
2214        $general_question_feedback = $this->questions->nodeValue('generalfeedback', $this->question_node);
2215        if (empty($general_question_feedback)) {
2216            return;
2217        }
2218        $name = 'general_fb';
2219        //Add question general feedback - the one that should be always displayed
2220        $result = cc_helpers::process_linked_files( $general_question_feedback,
2221                                                    $this->manifest,
2222                                                    $this->rootpath,
2223                                                    $this->contextid,
2224                                                    $this->outdir);
2225
2226        cc_assesment_helper::add_feedback($this->qitem,
2227                                          $result[0],
2228                                          cc_qti_values::htmltype,
2229                                          $name);
2230        pkg_resource_dependencies::instance()->add($result[1]);
2231        $this->general_feedback = $name;
2232    }
2233
2234    public function on_generate_response_processing() {
2235
2236        $qresprocessing = new cc_assesment_resprocessingtype();
2237        $this->qitem->add_resprocessing($qresprocessing);
2238        $qdecvar = new cc_assesment_decvartype();
2239        $qresprocessing->set_decvar($qdecvar);
2240        //according to the Common Cartridge 1.1 Profile: Implementation document
2241        //this should always be set to 0, 100 in case of question type that is not essay
2242        $qdecvar->set_limits(0,100);
2243        $qdecvar->set_vartype(cc_qti_values::Decimal);
2244
2245        $this->qresprocessing = $qresprocessing;
2246
2247    }
2248
2249    public function generate() {
2250        $this->on_generate_metadata();
2251
2252        $this->on_generate_presentation();
2253
2254        $this->on_generate_answers();
2255
2256        $this->on_generate_feedbacks();
2257
2258        $this->on_generate_response_processing();
2259    }
2260
2261}
2262
2263class cc_assesment_question_multichoice extends cc_assesment_question_proc_base {
2264    public function __construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir) {
2265        parent::__construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2266        $this->qtype = cc_qti_profiletype::multiple_choice;
2267
2268        /**
2269         *
2270         * What is needed is a maximum grade value taken from the answer fraction
2271         * It is supposed to always be between 1 and 0 in decimal representation,
2272         * however that is not always the case so a change in test was needed
2273         * but since we support here one correct answer type
2274         * correct answer would always have to be 1
2275         */
2276        $correct_answer_node = $this->questions->node("plugin_qtype_multichoice_question/answers/answer[fraction > 0]", $this->question_node);
2277        if (empty($correct_answer_node)) {
2278            throw new RuntimeException('No correct answer!');
2279        }
2280        $this->correct_answer_node_id = $this->questions->nodeValue('@id', $correct_answer_node);
2281        $maximum_quiz_grade = (int)$this->quiz->nodeValue('/activity/quiz/grade');
2282        $this->total_grade_value = ($maximum_quiz_grade + 1).'.0000000';
2283    }
2284
2285    public function on_generate_answers() {
2286        //add responses holder
2287        $qresponse_lid = new cc_response_lidtype();
2288        $this->qresponse_lid = $qresponse_lid;
2289        $this->qpresentation->set_response_lid($qresponse_lid);
2290        $qresponse_choice = new cc_assesment_render_choicetype();
2291        $qresponse_lid->set_render_choice($qresponse_choice);
2292        //Mark that question has only one correct answer -
2293        //which applies for multiple choice and yes/no questions
2294        $qresponse_lid->set_rcardinality(cc_qti_values::Single);
2295        //are we to shuffle the responses?
2296        $shuffle_answers = (int)$this->quiz->nodeValue('/activity/quiz/shuffleanswers') > 0;
2297        $qresponse_choice->enable_shuffle($shuffle_answers);
2298        $answerlist = array();
2299        $qa_responses = $this->questions->nodeList('plugin_qtype_multichoice_question/answers/answer', $this->question_node);
2300        foreach ($qa_responses as $node) {
2301            $answer_content = $this->questions->nodeValue('answertext', $node);
2302            $id = ((int)$this->questions->nodeValue('@id', $node) == $this->correct_answer_node_id);
2303            $result = cc_helpers::process_linked_files( $answer_content,
2304                                                        $this->manifest,
2305                                                        $this->rootpath,
2306                                                        $this->contextid,
2307                                                        $this->outdir);
2308            $qresponse_label = cc_assesment_helper::add_answer( $qresponse_choice,
2309                                                                $result[0],
2310                                                                cc_qti_values::htmltype);
2311            pkg_resource_dependencies::instance()->add($result[1]);
2312            $answer_ident = $qresponse_label->get_ident();
2313            $feedback_ident = $answer_ident.'_fb';
2314            if (empty($this->correct_answer_ident) && $id) {
2315                $this->correct_answer_ident = $answer_ident;
2316            }
2317            //add answer specific feedbacks if not empty
2318            $content = $this->questions->nodeValue('feedback', $node);
2319            if (!empty($content)) {
2320                $result = cc_helpers::process_linked_files( $content,
2321                                                            $this->manifest,
2322                                                            $this->rootpath,
2323                                                            $this->contextid,
2324                                                            $this->outdir);
2325
2326
2327                cc_assesment_helper::add_feedback( $this->qitem,
2328                                                   $result[0],
2329                                                   cc_qti_values::htmltype,
2330                                                   $feedback_ident);
2331
2332                pkg_resource_dependencies::instance()->add($result[1]);
2333
2334                $answerlist[$answer_ident] = $feedback_ident;
2335            }
2336        }
2337
2338        $this->answerlist = $answerlist;
2339
2340    }
2341
2342    public function on_generate_feedbacks() {
2343        parent::on_generate_feedbacks();
2344        //Question combined feedbacks
2345        $correct_question_fb = $this->questions->nodeValue('plugin_qtype_multichoice_question/multichoice/correctfeedback', $this->question_node);
2346        $incorrect_question_fb = $this->questions->nodeValue('plugin_qtype_multichoice_question/multichoice/incorrectfeedback', $this->question_node);
2347        $proc = array('correct_fb' => $correct_question_fb, 'general_incorrect_fb' => $incorrect_question_fb);
2348        foreach ($proc as $ident => $content) {
2349            if (empty($content)) {
2350                continue;
2351            }
2352            $result = cc_helpers::process_linked_files( $content,
2353                                                        $this->manifest,
2354                                                        $this->rootpath,
2355                                                        $this->contextid,
2356                                                        $this->outdir);
2357
2358            cc_assesment_helper::add_feedback( $this->qitem,
2359                                               $result[0],
2360                                               cc_qti_values::htmltype,
2361                                               $ident);
2362
2363            pkg_resource_dependencies::instance()->add($result[1]);
2364            if ($ident == 'correct_fb') {
2365                $this->correct_feedbacks[] = $ident;
2366            } else {
2367                $this->incorrect_feedbacks[] = $ident;
2368            }
2369        }
2370
2371    }
2372
2373    public function on_generate_response_processing() {
2374        parent::on_generate_response_processing();
2375
2376        //respconditions
2377        /**
2378         * General unconditional feedback must be added as a first respcondition
2379         * without any condition and just displayfeedback (if exists)
2380         */
2381        if (!empty($this->general_feedback)) {
2382            $qrespcondition = new cc_assesment_respconditiontype();
2383            $qrespcondition->set_title('General feedback');
2384            $this->qresprocessing->add_respcondition($qrespcondition);
2385            $qrespcondition->enable_continue();
2386            //define the condition for success
2387            $qconditionvar = new cc_assignment_conditionvar();
2388            $qrespcondition->set_conditionvar($qconditionvar);
2389            $qother = new cc_assignment_conditionvar_othertype();
2390            $qconditionvar->set_other($qother);
2391            $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2392            $qrespcondition->add_displayfeedback($qdisplayfeedback);
2393            $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2394            $qdisplayfeedback->set_linkrefid('general_fb');
2395        }
2396
2397        //success condition
2398        /**
2399         * For all question types outside of the Essay question, scoring is done in a
2400         * single <respcondition> with a continue flag set to No. The outcome is always
2401         * a variable named SCORE which value must be set to 100 in case of correct answer.
2402         * Partial scores (not 0 or 100) are not supported.
2403         */
2404        $qrespcondition = new cc_assesment_respconditiontype();
2405        $qrespcondition->set_title('Correct');
2406        $this->qresprocessing->add_respcondition($qrespcondition);
2407        $qrespcondition->enable_continue(false);
2408        $qsetvar = new cc_assignment_setvartype(100);
2409        $qrespcondition->add_setvar($qsetvar);
2410        //define the condition for success
2411        $qconditionvar = new cc_assignment_conditionvar();
2412        $qrespcondition->set_conditionvar($qconditionvar);
2413        $qvarequal = new cc_assignment_conditionvar_varequaltype($this->correct_answer_ident);
2414        $qconditionvar->set_varequal($qvarequal);
2415        $qvarequal->set_respident($this->qresponse_lid->get_ident());
2416
2417        if (array_key_exists($this->correct_answer_ident, $this->answerlist)) {
2418            $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2419            $qrespcondition->add_displayfeedback($qdisplayfeedback);
2420            $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2421            $qdisplayfeedback->set_linkrefid($this->answerlist[$this->correct_answer_ident]);
2422        }
2423
2424        foreach ($this->correct_feedbacks as $ident) {
2425            $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2426            $qrespcondition->add_displayfeedback($qdisplayfeedback);
2427            $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2428            $qdisplayfeedback->set_linkrefid($ident);
2429        }
2430
2431        //rest of the conditions
2432        foreach ($this->answerlist as $ident => $refid) {
2433            if ($ident == $this->correct_answer_ident) {
2434                continue;
2435            }
2436
2437            $qrespcondition = new cc_assesment_respconditiontype();
2438            $this->qresprocessing->add_respcondition($qrespcondition);
2439            $qsetvar = new cc_assignment_setvartype(0);
2440            $qrespcondition->add_setvar($qsetvar);
2441            //define the condition for fail
2442            $qconditionvar = new cc_assignment_conditionvar();
2443            $qrespcondition->set_conditionvar($qconditionvar);
2444            $qvarequal = new cc_assignment_conditionvar_varequaltype($ident);
2445            $qconditionvar->set_varequal($qvarequal);
2446            $qvarequal->set_respident($this->qresponse_lid->get_ident());
2447
2448            $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2449            $qrespcondition->add_displayfeedback($qdisplayfeedback);
2450            $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2451            $qdisplayfeedback->set_linkrefid($refid);
2452
2453            foreach ($this->incorrect_feedbacks as $ident) {
2454                $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2455                $qrespcondition->add_displayfeedback($qdisplayfeedback);
2456                $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2457                $qdisplayfeedback->set_linkrefid($ident);
2458            }
2459        }
2460    }
2461}
2462
2463class cc_assesment_question_multichoice_multiresponse extends cc_assesment_question_proc_base {
2464    /**
2465     * @var DOMNodeList
2466     */
2467    protected $correct_answers = null;
2468
2469    public function __construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir) {
2470        parent::__construct($quiz, $questions, $manifest, $section, $question_node, $rootpath, $contextid, $outdir);
2471        $this->qtype = cc_qti_profiletype::multiple_response;
2472
2473        $correct_answer_nodes = $this->questions->nodeList("plugin_qtype_multichoice_question/answers/answer[fraction > 0]", $this->question_node);
2474        if ($correct_answer_nodes->length == 0) {
2475            throw new RuntimeException('No correct answer!');
2476        }
2477        $this->correct_answers = $correct_answer_nodes;
2478        //$this->correct_answer_node_id = $this->questions->nodeValue('@id', $correct_answer_node);
2479        $maximum_quiz_grade = (int)$this->quiz->nodeValue('/activity/quiz/grade');
2480        $this->total_grade_value = ($maximum_quiz_grade + 1).'.0000000';
2481    }
2482
2483    public function on_generate_answers() {
2484        //add responses holder
2485        $qresponse_lid = new cc_response_lidtype();
2486        $this->qresponse_lid = $qresponse_lid;
2487        $this->qpresentation->set_response_lid($qresponse_lid);
2488        $qresponse_choice = new cc_assesment_render_choicetype();
2489        $qresponse_lid->set_render_choice($qresponse_choice);
2490        //Mark that question has more than one correct answer
2491        $qresponse_lid->set_rcardinality(cc_qti_values::Multiple);
2492        //are we to shuffle the responses?
2493        $shuffle_answers = (int)$this->quiz->nodeValue('/activity/quiz/shuffleanswers') > 0;
2494        $qresponse_choice->enable_shuffle($shuffle_answers);
2495        $answerlist = array();
2496        $qa_responses = $this->questions->nodeList('plugin_qtype_multichoice_question/answers/answer', $this->question_node);
2497        foreach ($qa_responses as $node) {
2498            $answer_content = $this->questions->nodeValue('answertext', $node);
2499            $answer_grade_fraction = (float)$this->questions->nodeValue('fraction', $node);
2500            $result = cc_helpers::process_linked_files( $answer_content,
2501                                                        $this->manifest,
2502                                                        $this->rootpath,
2503                                                        $this->contextid,
2504                                                        $this->outdir);
2505            $qresponse_label = cc_assesment_helper::add_answer( $qresponse_choice,
2506                                                                $result[0],
2507                                                                cc_qti_values::htmltype);
2508            pkg_resource_dependencies::instance()->add($result[1]);
2509            $answer_ident = $qresponse_label->get_ident();
2510            $feedback_ident = $answer_ident.'_fb';
2511            //add answer specific feedbacks if not empty
2512            $content = $this->questions->nodeValue('feedback', $node);
2513            if (!empty($content)) {
2514                $result = cc_helpers::process_linked_files( $content,
2515                                                            $this->manifest,
2516                                                            $this->rootpath,
2517                                                            $this->contextid,
2518                                                            $this->outdir);
2519
2520
2521                cc_assesment_helper::add_feedback( $this->qitem,
2522                                                    $result[0],
2523                                                    cc_qti_values::htmltype,
2524                                                    $feedback_ident);
2525
2526                pkg_resource_dependencies::instance()->add($result[1]);
2527
2528            }
2529            $answerlist[$answer_ident] = array($feedback_ident, ($answer_grade_fraction > 0));
2530        }
2531
2532        $this->answerlist = $answerlist;
2533
2534    }
2535
2536    public function on_generate_feedbacks() {
2537        parent::on_generate_feedbacks();
2538        //Question combined feedbacks
2539        $correct_question_fb = $this->questions->nodeValue('plugin_qtype_multichoice_question/multichoice/correctfeedback', $this->question_node);
2540        $incorrect_question_fb = $this->questions->nodeValue('plugin_qtype_multichoice_question/multichoice/incorrectfeedback', $this->question_node);
2541        if (empty($correct_question_fb)) {
2542            //Hardcode some text for now
2543            $correct_question_fb = 'Well done!';
2544        }
2545        if (empty($incorrect_question_fb)) {
2546            //Hardcode some text for now
2547            $incorrect_question_fb = 'Better luck next time!';
2548        }
2549
2550        $proc = array('correct_fb' => $correct_question_fb, 'incorrect_fb' => $incorrect_question_fb);
2551        foreach ($proc as $ident => $content) {
2552            if (empty($content)) {
2553                continue;
2554            }
2555            $result = cc_helpers::process_linked_files( $content,
2556                                                        $this->manifest,
2557                                                        $this->rootpath,
2558                                                        $this->contextid,
2559                                                        $this->outdir);
2560
2561            cc_assesment_helper::add_feedback( $this->qitem,
2562                                                $result[0],
2563                                                cc_qti_values::htmltype,
2564                                                $ident);
2565
2566            pkg_resource_dependencies::instance()->add($result[1]);
2567            if ($ident == 'correct_fb') {
2568                $this->correct_feedbacks[$ident] = $ident;
2569            } else {
2570                $this->incorrect_feedbacks[$ident] = $ident;
2571            }
2572        }
2573
2574    }
2575
2576    public function on_generate_response_processing() {
2577        parent::on_generate_response_processing();
2578
2579        //respconditions
2580        /**
2581        * General unconditional feedback must be added as a first respcondition
2582        * without any condition and just displayfeedback (if exists)
2583        */
2584        cc_assesment_helper::add_respcondition( $this->qresprocessing,
2585                                                'General feedback',
2586                                                $this->general_feedback,
2587                                                null,
2588                                                true
2589                                               );
2590
2591        //success condition
2592        /**
2593        * For all question types outside of the Essay question, scoring is done in a
2594        * single <respcondition> with a continue flag set to No. The outcome is always
2595        * a variable named SCORE which value must be set to 100 in case of correct answer.
2596        * Partial scores (not 0 or 100) are not supported.
2597        */
2598        $qrespcondition = new cc_assesment_respconditiontype();
2599        $qrespcondition->set_title('Correct');
2600        $this->qresprocessing->add_respcondition($qrespcondition);
2601        $qrespcondition->enable_continue(false);
2602        $qsetvar = new cc_assignment_setvartype(100);
2603        $qrespcondition->add_setvar($qsetvar);
2604        //define the condition for success
2605        $qconditionvar = new cc_assignment_conditionvar();
2606        $qrespcondition->set_conditionvar($qconditionvar);
2607        //create root and condition
2608        $qandcondition = new cc_assignment_conditionvar_andtype();
2609        $qconditionvar->set_and($qandcondition);
2610        foreach ($this->answerlist as $ident => $refid) {
2611            $qvarequal = new cc_assignment_conditionvar_varequaltype($ident);
2612            $qvarequal->enable_case();
2613            if ($refid[1]) {
2614                $qandcondition->set_varequal($qvarequal);
2615            } else {
2616                $qandcondition->set_not($qvarequal);
2617            }
2618            $qvarequal->set_respident($this->qresponse_lid->get_ident());
2619        }
2620
2621        $qdisplayfeedback = new cc_assignment_displayfeedbacktype();
2622        $qrespcondition->add_displayfeedback($qdisplayfeedback);
2623        $qdisplayfeedback->set_feedbacktype(cc_qti_values::Response);
2624        //TODO: this needs to be fixed
2625        reset($this->correct_feedbacks);
2626        $ident = key($this->correct_feedbacks);
2627        $qdisplayfeedback->set_linkrefid($ident);
2628
2629
2630        //rest of the conditions
2631        foreach ($this->answerlist as $ident => $refid) {
2632            cc_assesment_helper::add_response_condition( $this->qresprocessing,
2633                                                         'Incorrect feedback',
2634                                                         $refid[0],
2635                                                         $this->general_feedback,
2636                                                         $this->qresponse_lid->get_ident()
2637                                                       );
2638        }
2639
2640        //Final element for incorrect feedback
2641        reset($this->incorrect_feedbacks);
2642        $ident = key($this->incorrect_feedbacks);
2643        cc_assesment_helper::add_respcondition( $this->qresprocessing,
2644                                                'Incorrect feedback',
2645                                                $ident,
2646                                                0
2647                                              );
2648
2649    }
2650
2651}
2652