ILIAS  release_5-0 Revision 5.0.0-1144-gc4397b1f870
All Data Structures Namespaces Files Functions Variables Modules Pages
class.assClozeTestExport.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 include_once "./Modules/TestQuestionPool/classes/export/qti12/class.assQuestionExport.php";
5 
16 {
26  function toXML($a_include_header = true, $a_include_binary = true, $a_shuffle = false, $test_output = false, $force_image_references = false)
27  {
28  global $ilias;
29 
30  include_once "./Services/Math/classes/class.EvalMath.php";
31  $eval = new EvalMath();
32  $eval->suppress_errors = TRUE;
33  include_once("./Services/Xml/classes/class.ilXmlWriter.php");
34  $a_xml_writer = new ilXmlWriter;
35  // set xml header
36  $a_xml_writer->xmlHeader();
37  $a_xml_writer->xmlStartTag("questestinterop");
38  $attrs = array(
39  "ident" => "il_".IL_INST_ID."_qst_".$this->object->getId(),
40  "title" => $this->object->getTitle(),
41  "maxattempts" => $this->object->getNrOfTries()
42  );
43  $a_xml_writer->xmlStartTag("item", $attrs);
44  // add question description
45  $a_xml_writer->xmlElement("qticomment", NULL, $this->object->getComment());
46  // add estimated working time
47  $workingtime = $this->object->getEstimatedWorkingTime();
48  $duration = sprintf("P0Y0M0DT%dH%dM%dS", $workingtime["h"], $workingtime["m"], $workingtime["s"]);
49  $a_xml_writer->xmlElement("duration", NULL, $duration);
50  // add ILIAS specific metadata
51  $a_xml_writer->xmlStartTag("itemmetadata");
52  $a_xml_writer->xmlStartTag("qtimetadata");
53  $a_xml_writer->xmlStartTag("qtimetadatafield");
54  $a_xml_writer->xmlElement("fieldlabel", NULL, "ILIAS_VERSION");
55  $a_xml_writer->xmlElement("fieldentry", NULL, $ilias->getSetting("ilias_version"));
56  $a_xml_writer->xmlEndTag("qtimetadatafield");
57  $a_xml_writer->xmlStartTag("qtimetadatafield");
58  $a_xml_writer->xmlElement("fieldlabel", NULL, "QUESTIONTYPE");
59  $a_xml_writer->xmlElement("fieldentry", NULL, CLOZE_TEST_IDENTIFIER);
60  $a_xml_writer->xmlEndTag("qtimetadatafield");
61  $a_xml_writer->xmlStartTag("qtimetadatafield");
62  $a_xml_writer->xmlElement("fieldlabel", NULL, "AUTHOR");
63  $a_xml_writer->xmlElement("fieldentry", NULL, $this->object->getAuthor());
64  $a_xml_writer->xmlEndTag("qtimetadatafield");
65 
66  // additional content editing information
67  $this->addAdditionalContentEditingModeInformation($a_xml_writer);
68  $this->addGeneralMetadata($a_xml_writer);
69 
70  $a_xml_writer->xmlStartTag("qtimetadatafield");
71  $a_xml_writer->xmlElement("fieldlabel", NULL, "textgaprating");
72  $a_xml_writer->xmlElement("fieldentry", NULL, $this->object->getTextgapRating());
73  $a_xml_writer->xmlEndTag("qtimetadatafield");
74 
75  $a_xml_writer->xmlStartTag("qtimetadatafield");
76  $a_xml_writer->xmlElement("fieldlabel", NULL, "fixedTextLength");
77  $a_xml_writer->xmlElement("fieldentry", NULL, $this->object->getFixedTextLength());
78  $a_xml_writer->xmlEndTag("qtimetadatafield");
79 
80  $a_xml_writer->xmlStartTag("qtimetadatafield");
81  $a_xml_writer->xmlElement("fieldlabel", NULL, "identicalScoring");
82  $a_xml_writer->xmlElement("fieldentry", NULL, $this->object->getIdenticalScoring());
83  $a_xml_writer->xmlEndTag("qtimetadatafield");
84 
85  $a_xml_writer->xmlStartTag("qtimetadatafield");
86  $a_xml_writer->xmlElement("fieldlabel", NULL, "combinations");
87  $a_xml_writer->xmlElement("fieldentry", NULL, base64_encode(json_encode($this->object->getGapCombinations())));
88  $a_xml_writer->xmlEndTag("qtimetadatafield");
89 
90  $a_xml_writer->xmlEndTag("qtimetadata");
91  $a_xml_writer->xmlEndTag("itemmetadata");
92 
93  // PART I: qti presentation
94  $attrs = array(
95  "label" => $this->object->getTitle()
96  );
97  $a_xml_writer->xmlStartTag("presentation", $attrs);
98  // add flow to presentation
99  $a_xml_writer->xmlStartTag("flow");
100 
101  $questionText = $this->object->getQuestion() ? $this->object->getQuestion() : '&nbsp;';
102  $this->object->addQTIMaterial($a_xml_writer, $questionText);
103 
104  $text_parts = preg_split("/\[gap.*?\[\/gap\]/", $this->object->getClozeText());
105 
106  // add material with question text to presentation
107  for ($i = 0; $i <= $this->object->getGapCount(); $i++)
108  {
109  $this->object->addQTIMaterial($a_xml_writer, $text_parts[$i]);
110 
111  if ($i < $this->object->getGapCount())
112  {
113  // add gap
114  $gap = $this->object->getGap($i);
115  switch ($gap->getType())
116  {
117  case CLOZE_SELECT:
118  // comboboxes
119  $attrs = array(
120  "ident" => "gap_$i",
121  "rcardinality" => "Single"
122  );
123  $a_xml_writer->xmlStartTag("response_str", $attrs);
124  $solution = $this->object->getSuggestedSolution($i);
125  if (count($solution))
126  {
127  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
128  {
129  $attrs = array(
130  "label" => "suggested_solution"
131  );
132  $a_xml_writer->xmlStartTag("material", $attrs);
133  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
134  if (strcmp($matches[1], "") != 0)
135  {
136  $intlink = $solution["internal_link"];
137  }
138  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
139  $a_xml_writer->xmlEndTag("material");
140  }
141  }
142 
143  $attrs = array("shuffle" => ($gap->getShuffle() ? "Yes" : "No"));
144  $a_xml_writer->xmlStartTag("render_choice", $attrs);
145 
146  // add answers
147  $shuffleState = $gap->getShuffle();
148  $gap->setShuffle(false);
149  $items = $gap->getItems();
150  $gap->setShuffle($shuffleState);
151  foreach ($items as $answeritem)
152  {
153  $attrs = array(
154  "ident" => $answeritem->getOrder()
155  );
156  $a_xml_writer->xmlStartTag("response_label", $attrs);
157  $a_xml_writer->xmlStartTag("material");
158  $a_xml_writer->xmlElement("mattext", NULL, $answeritem->getAnswertext());
159  $a_xml_writer->xmlEndTag("material");
160  $a_xml_writer->xmlEndTag("response_label");
161  }
162  $a_xml_writer->xmlEndTag("render_choice");
163  $a_xml_writer->xmlEndTag("response_str");
164  break;
165  case CLOZE_TEXT:
166  // text fields
167  $attrs = array(
168  "ident" => "gap_$i",
169  "rcardinality" => "Single"
170  );
171  $a_xml_writer->xmlStartTag("response_str", $attrs);
172  $solution = $this->object->getSuggestedSolution($i);
173  if (count($solution))
174  {
175  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
176  {
177  $attrs = array(
178  "label" => "suggested_solution"
179  );
180  $a_xml_writer->xmlStartTag("material", $attrs);
181  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
182  if (strcmp($matches[1], "") != 0)
183  {
184  $intlink = $solution["internal_link"];
185  }
186  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
187  $a_xml_writer->xmlEndTag("material");
188  }
189  }
190  $attrs = array(
191  "fibtype" => "String",
192  "prompt" => "Box",
193  "columns" => $gap->getMaxWidth(),
194  "maxchars" => $gap->getGapSize()
195  );
196  $a_xml_writer->xmlStartTag("render_fib", $attrs);
197  $a_xml_writer->xmlEndTag("render_fib");
198  $a_xml_writer->xmlEndTag("response_str");
199  break;
200  case CLOZE_NUMERIC:
201  // numeric fields
202  $attrs = array(
203  "ident" => "gap_$i",
204  "numtype" => "Decimal",
205  "rcardinality" => "Single"
206  );
207  $a_xml_writer->xmlStartTag("response_num", $attrs);
208  $solution = $this->object->getSuggestedSolution($i);
209  if (count($solution))
210  {
211  if (preg_match("/il_(\d*?)_(\w+)_(\d+)/", $solution["internal_link"], $matches))
212  {
213  $attrs = array(
214  "label" => "suggested_solution"
215  );
216  $a_xml_writer->xmlStartTag("material", $attrs);
217  $intlink = "il_" . IL_INST_ID . "_" . $matches[2] . "_" . $matches[3];
218  if (strcmp($matches[1], "") != 0)
219  {
220  $intlink = $solution["internal_link"];
221  }
222  $a_xml_writer->xmlElement("mattext", NULL, $intlink);
223  $a_xml_writer->xmlEndTag("material");
224  }
225  }
226  $answeritem = $gap->getItem(0);
227  $attrs = array(
228  "fibtype" => "Decimal",
229  "prompt" => "Box",
230  "columns" => $gap->getMaxWidth(),
231  "maxchars" => $gap->getGapSize()
232  );
233  if (is_object($answeritem))
234  {
235  if ($eval->e($answeritem->getLowerBound()) !== FALSE)
236  {
237  $attrs["minnumber"] = $answeritem->getLowerBound();
238  }
239  if ($eval->e($answeritem->getUpperBound()) !== FALSE)
240  {
241  $attrs["maxnumber"] = $answeritem->getUpperBound();
242  }
243  }
244  $a_xml_writer->xmlStartTag("render_fib", $attrs);
245  $a_xml_writer->xmlEndTag("render_fib");
246  $a_xml_writer->xmlEndTag("response_num");
247  break;
248  }
249  }
250  }
251  $a_xml_writer->xmlEndTag("flow");
252  $a_xml_writer->xmlEndTag("presentation");
253 
254  // PART II: qti resprocessing
255  $a_xml_writer->xmlStartTag("resprocessing");
256  $a_xml_writer->xmlStartTag("outcomes");
257  $a_xml_writer->xmlStartTag("decvar");
258  $a_xml_writer->xmlEndTag("decvar");
259  $a_xml_writer->xmlEndTag("outcomes");
260 
261  // add response conditions
262  for ($i = 0; $i < $this->object->getGapCount(); $i++)
263  {
264  $gap = $this->object->getGap($i);
265  switch ($gap->getType())
266  {
267  case CLOZE_SELECT:
268  $shuffleState = $gap->getShuffle();
269  $gap->setShuffle(false);
270  $items = $gap->getItems();
271  $gap->setShuffle($shuffleState);
272  foreach ($items as $answer)
273  {
274  $attrs = array(
275  "continue" => "Yes"
276  );
277  $a_xml_writer->xmlStartTag("respcondition", $attrs);
278  // qti conditionvar
279  $a_xml_writer->xmlStartTag("conditionvar");
280 
281  $attrs = array(
282  "respident" => "gap_$i"
283  );
284  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
285  $a_xml_writer->xmlEndTag("conditionvar");
286  // qti setvar
287  $attrs = array(
288  "action" => "Add"
289  );
290  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
291  // qti displayfeedback
292  $linkrefid = "";
293  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
294  $attrs = array(
295  "feedbacktype" => "Response",
296  "linkrefid" => $linkrefid
297  );
298  $a_xml_writer->xmlElement("displayfeedback", $attrs);
299  $a_xml_writer->xmlEndTag("respcondition");
300  }
301  break;
302  case CLOZE_TEXT:
303  foreach ($gap->getItems() as $answer)
304  {
305  $attrs = array(
306  "continue" => "Yes"
307  );
308  $a_xml_writer->xmlStartTag("respcondition", $attrs);
309  // qti conditionvar
310  $a_xml_writer->xmlStartTag("conditionvar");
311  $attrs = array(
312  "respident" => "gap_$i"
313  );
314  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
315  $a_xml_writer->xmlEndTag("conditionvar");
316  // qti setvar
317  $attrs = array(
318  "action" => "Add"
319  );
320  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
321  // qti displayfeedback
322  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
323  $attrs = array(
324  "feedbacktype" => "Response",
325  "linkrefid" => $linkrefid
326  );
327  $a_xml_writer->xmlElement("displayfeedback", $attrs);
328  $a_xml_writer->xmlEndTag("respcondition");
329  }
330  break;
331  case CLOZE_NUMERIC:
332  foreach ($gap->getItems() as $answer)
333  {
334  $attrs = array(
335  "continue" => "Yes"
336  );
337  $a_xml_writer->xmlStartTag("respcondition", $attrs);
338  // qti conditionvar
339  $a_xml_writer->xmlStartTag("conditionvar");
340  $attrs = array(
341  "respident" => "gap_$i"
342  );
343  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
344  $a_xml_writer->xmlEndTag("conditionvar");
345  // qti setvar
346  $attrs = array(
347  "action" => "Add"
348  );
349  $a_xml_writer->xmlElement("setvar", $attrs, $answer->getPoints());
350  // qti displayfeedback
351  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
352  $attrs = array(
353  "feedbacktype" => "Response",
354  "linkrefid" => $linkrefid
355  );
356  $a_xml_writer->xmlElement("displayfeedback", $attrs);
357  $a_xml_writer->xmlEndTag("respcondition");
358  }
359  break;
360  }
361  }
362 
363  $feedback_allcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
364  $this->object->getId(), true
365  );
366  if (strlen($feedback_allcorrect))
367  {
368  $attrs = array(
369  "continue" => "Yes"
370  );
371  $a_xml_writer->xmlStartTag("respcondition", $attrs);
372  // qti conditionvar
373  $a_xml_writer->xmlStartTag("conditionvar");
374 
375  for ($i = 0; $i < $this->object->getGapCount(); $i++)
376  {
377  $gap = $this->object->getGap($i);
378  $indexes = $gap->getBestSolutionIndexes();
379  if ($i > 0)
380  {
381  $a_xml_writer->xmlStartTag("and");
382  }
383  switch ($gap->getType())
384  {
385  case CLOZE_SELECT:
386  $k = 0;
387  foreach ($indexes as $key)
388  {
389  if ($k > 0)
390  {
391  $a_xml_writer->xmlStartTag("or");
392  }
393  $attrs = array(
394  "respident" => "gap_$i"
395  );
396  $answer = $gap->getItem($key);
397  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
398  if ($k > 0)
399  {
400  $a_xml_writer->xmlEndTag("or");
401  }
402  $k++;
403  }
404  break;
405  case CLOZE_TEXT:
406  $k = 0;
407  foreach ($indexes as $key)
408  {
409  if ($k > 0)
410  {
411  $a_xml_writer->xmlStartTag("or");
412  }
413  $attrs = array(
414  "respident" => "gap_$i"
415  );
416  $answer = $gap->getItem($key);
417  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
418  if ($k > 0)
419  {
420  $a_xml_writer->xmlEndTag("or");
421  }
422  $k++;
423  }
424  break;
425  case CLOZE_NUMERIC:
426  $k = 0;
427  foreach ($indexes as $key)
428  {
429  if ($k > 0)
430  {
431  $a_xml_writer->xmlStartTag("or");
432  }
433  $attrs = array(
434  "respident" => "gap_$i"
435  );
436  $answer = $gap->getItem($key);
437  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
438  if ($k > 0)
439  {
440  $a_xml_writer->xmlEndTag("or");
441  }
442  $k++;
443  }
444  break;
445  }
446  if ($i > 0)
447  {
448  $a_xml_writer->xmlEndTag("and");
449  }
450  }
451  $a_xml_writer->xmlEndTag("conditionvar");
452  // qti displayfeedback
453  $attrs = array(
454  "feedbacktype" => "Response",
455  "linkrefid" => "response_allcorrect"
456  );
457  $a_xml_writer->xmlElement("displayfeedback", $attrs);
458  $a_xml_writer->xmlEndTag("respcondition");
459  }
460  $feedback_onenotcorrect = $this->object->feedbackOBJ->getGenericFeedbackExportPresentation(
461  $this->object->getId(), false
462  );
463  if (strlen($feedback_onenotcorrect))
464  {
465  $attrs = array(
466  "continue" => "Yes"
467  );
468  $a_xml_writer->xmlStartTag("respcondition", $attrs);
469  // qti conditionvar
470  $a_xml_writer->xmlStartTag("conditionvar");
471 
472  $a_xml_writer->xmlStartTag("not");
473  for ($i = 0; $i < $this->object->getGapCount(); $i++)
474  {
475  $gap = $this->object->getGap($i);
476  $indexes = $gap->getBestSolutionIndexes();
477  if ($i > 0)
478  {
479  $a_xml_writer->xmlStartTag("and");
480  }
481  switch ($gap->getType())
482  {
483  case CLOZE_SELECT:
484  $k = 0;
485  foreach ($indexes as $key)
486  {
487  if ($k > 0)
488  {
489  $a_xml_writer->xmlStartTag("or");
490  }
491  $attrs = array(
492  "respident" => "gap_$i"
493  );
494  $answer = $gap->getItem($key);
495  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
496  if ($k > 0)
497  {
498  $a_xml_writer->xmlEndTag("or");
499  }
500  $k++;
501  }
502  break;
503  case CLOZE_TEXT:
504  $k = 0;
505  foreach ($indexes as $key)
506  {
507  if ($k > 0)
508  {
509  $a_xml_writer->xmlStartTag("or");
510  }
511  $attrs = array(
512  "respident" => "gap_$i"
513  );
514  $answer = $gap->getItem($key);
515  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
516  if ($k > 0)
517  {
518  $a_xml_writer->xmlEndTag("or");
519  }
520  $k++;
521  }
522  break;
523  case CLOZE_NUMERIC:
524  $k = 0;
525  foreach ($indexes as $key)
526  {
527  if ($k > 0)
528  {
529  $a_xml_writer->xmlStartTag("or");
530  }
531  $attrs = array(
532  "respident" => "gap_$i"
533  );
534  $answer = $gap->getItem($key);
535  $a_xml_writer->xmlElement("varequal", $attrs, $answer->getAnswertext());
536  if ($k > 0)
537  {
538  $a_xml_writer->xmlEndTag("or");
539  }
540  $k++;
541  }
542  break;
543  }
544  if ($i > 0)
545  {
546  $a_xml_writer->xmlEndTag("and");
547  }
548  }
549  $a_xml_writer->xmlEndTag("not");
550  $a_xml_writer->xmlEndTag("conditionvar");
551  // qti displayfeedback
552  $attrs = array(
553  "feedbacktype" => "Response",
554  "linkrefid" => "response_onenotcorrect"
555  );
556  $a_xml_writer->xmlElement("displayfeedback", $attrs);
557  $a_xml_writer->xmlEndTag("respcondition");
558  }
559 
560  $a_xml_writer->xmlEndTag("resprocessing");
561 
562  // PART III: qti itemfeedback
563  for ($i = 0; $i < $this->object->getGapCount(); $i++)
564  {
565  $gap = $this->object->getGap($i);
566  switch ($gap->getType())
567  {
568  case CLOZE_SELECT:
569  break;
570  case CLOZE_TEXT:
571  break;
572  case CLOZE_NUMERIC:
573  break;
574  }
575  /*foreach ($gap->getItems() as $answer)
576  {
577  $linkrefid = "$i" . "_Response_" . $answer->getOrder();
578  $attrs = array(
579  "ident" => $linkrefid,
580  "view" => "All"
581  );
582  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
583  // qti flow_mat
584  $a_xml_writer->xmlStartTag("flow_mat");
585 // $a_xml_writer->xmlStartTag("material");
586 // $a_xml_writer->xmlElement("mattext");
587 // $a_xml_writer->xmlEndTag("material");
588  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
589  $this->object->getId(), $index
590  );
591  $this->object->addQTIMaterial($a_xml_writer, $fb);
592  $a_xml_writer->xmlEndTag("flow_mat");
593  $a_xml_writer->xmlEndTag("itemfeedback");
594  }*/
595 
596  $attrs = array(
597  "ident" => $i,
598  "view" => "All"
599  );
600  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
601  // qti flow_mat
602  $a_xml_writer->xmlStartTag("flow_mat");
603  // $a_xml_writer->xmlStartTag("material");
604  // $a_xml_writer->xmlElement("mattext");
605  // $a_xml_writer->xmlEndTag("material");
606  $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackExportPresentation(
607  $this->object->getId(), $i
608  );
609  $this->object->addQTIMaterial($a_xml_writer, $fb);
610  $a_xml_writer->xmlEndTag("flow_mat");
611  $a_xml_writer->xmlEndTag("itemfeedback");
612  }
613 
614  if (strlen($feedback_allcorrect))
615  {
616  $attrs = array(
617  "ident" => "response_allcorrect",
618  "view" => "All"
619  );
620  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
621  // qti flow_mat
622  $a_xml_writer->xmlStartTag("flow_mat");
623  $this->object->addQTIMaterial($a_xml_writer, $feedback_allcorrect);
624  $a_xml_writer->xmlEndTag("flow_mat");
625  $a_xml_writer->xmlEndTag("itemfeedback");
626  }
627  if (strlen($feedback_onenotcorrect))
628  {
629  $attrs = array(
630  "ident" => "response_onenotcorrect",
631  "view" => "All"
632  );
633  $a_xml_writer->xmlStartTag("itemfeedback", $attrs);
634  // qti flow_mat
635  $a_xml_writer->xmlStartTag("flow_mat");
636  $this->object->addQTIMaterial($a_xml_writer, $feedback_onenotcorrect);
637  $a_xml_writer->xmlEndTag("flow_mat");
638  $a_xml_writer->xmlEndTag("itemfeedback");
639  }
640 
641  $a_xml_writer->xmlEndTag("item");
642  $a_xml_writer->xmlEndTag("questestinterop");
643 
644  $xml = $a_xml_writer->xmlDumpMem(FALSE);
645  if (!$a_include_header)
646  {
647  $pos = strpos($xml, "?>");
648  $xml = substr($xml, $pos + 2);
649  }
650  return $xml;
651  }
652 }
653 
654 ?>
const CLOZE_TEXT
Cloze question constants.
XML writer class.
Class for question exports.
addGeneralMetadata(ilXmlWriter $xmlwriter)
const CLOZE_SELECT
Class for cloze question exports.
xmlHeader()
Writes xml header public.
addAdditionalContentEditingModeInformation(ilXmlWriter $a_xml_writer)
adds a qti meta data field for ilias specific information of "additional content editing mode" (xml w...
const CLOZE_NUMERIC
toXML($a_include_header=true, $a_include_binary=true, $a_shuffle=false, $test_output=false, $force_image_references=false)
Returns a QTI xml representation of the question.
const CLOZE_TEST_IDENTIFIER
Question identifier constants.