ILIAS  release_4-3 Revision
 All Data Structures Namespaces Files Functions Variables Groups Pages
class.ilQTIParser.php
Go to the documentation of this file.
1 <?php
2 /*
3  +-----------------------------------------------------------------------------+
4  | ILIAS open source |
5  +-----------------------------------------------------------------------------+
6  | Copyright (c) 1998-2001 ILIAS open source, University of Cologne |
7  | |
8  | This program is free software; you can redistribute it and/or |
9  | modify it under the terms of the GNU General Public License |
10  | as published by the Free Software Foundation; either version 2 |
11  | of the License, or (at your option) any later version. |
12  | |
13  | This program is distributed in the hope that it will be useful, |
14  | but WITHOUT ANY WARRANTY; without even the implied warranty of |
15  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16  | GNU General Public License for more details. |
17  | |
18  | You should have received a copy of the GNU General Public License |
19  | along with this program; if not, write to the Free Software |
20  | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
21  +-----------------------------------------------------------------------------+
22 */
23 
24 include_once("./Services/Xml/classes/class.ilSaxParser.php");
25 
26 define ("IL_MO_PARSE_QTI", 1);
27 define ("IL_MO_VERIFY_QTI", 2);
28 
38 class ilQTIParser extends ilSaxParser
39 {
40  var $lng;
42  var $path;
43  var $items;
44  var $item;
45  var $depth;
51  var $material;
52  var $matimage;
53  var $response;
55  var $outcomes;
56  var $decvar;
58  var $setvar;
61  var $flow_mat;
62  var $flow;
64  var $mattext;
65  var $sametag;
70  var $qpl_id;
71  var $tst_id;
79  var $in_assessment = FALSE;
80  var $section;
84  var $in_objectives = FALSE;
85 
86  var $founditems = array();
87  var $verifyroot = false;
94 
102  protected $in_prensentation_material = false;
103 
111  // TODO: The following line gets me an parse error in PHP 4, but I found no hint that pass-by-reference is forbidden in PHP 4 ????
112  function ilQTIParser($a_xml_file, $a_mode = IL_MO_PARSE_QTI, $a_qpl_id = 0, $a_import_idents = "")
113  {
114  global $lng;
115 
116  $this->setParserMode($a_mode);
117 
118  parent::ilSaxParser($a_xml_file);
119 
120  $this->qpl_id = $a_qpl_id;
121  $this->import_idents = array();
122  if (is_array($a_import_idents))
123  {
124  $this->import_idents =& $a_import_idents;
125  }
126 
127  $this->lng =& $lng;
128  $this->hasRootElement = FALSE;
129  $this->import_mapping = array();
130  $this->assessments = array();
131  $this->assessment = NULL;
132  $this->section = NULL;
133  $this->path = array();
134  $this->items = array();
135  $this->item = NULL;
136  $this->depth = array();
137  $this->do_nothing = FALSE;
138  $this->qti_element = "";
139  $this->in_presentation = FALSE;
140  $this->in_objectives = FALSE;
141  $this->in_reponse = FALSE;
142  $this->render_type = NULL;
143  $this->render_hotspot = NULL;
144  $this->response_label = NULL;
145  $this->material = NULL;
146  $this->response = NULL;
147  $this->assessmentcontrol = NULL;
148  $this->objectives = NULL;
149  $this->matimage = NULL;
150  $this->resprocessing = NULL;
151  $this->outcomes = NULL;
152  $this->decvar = NULL;
153  $this->respcondition = NULL;
154  $this->setvar = NULL;
155  $this->displayfeedback = NULL;
156  $this->itemfeedback = NULL;
157  $this->flow_mat = array();
158  $this->question_counter = 1;
159  $this->flow = 0;
160  $this->gap_index = 0;
161  $this->presentation = NULL;
162  $this->mattext = NULL;
163  $this->matapplet = NULL;
164  $this->sametag = FALSE;
165  $this->in_assessment = FALSE;
166  $this->characterbuffer = "";
167  $this->metadata = array("label" => "", "entry" => "");
168  }
169 
170  function setTestObject(&$a_tst_object)
171  {
172  $this->tst_object =& $a_tst_object;
173  if (is_object($a_tst_object))
174  {
175  $this->tst_id = $this->tst_object->getId();
176  }
177  }
178 
179  function setParserMode($a_mode = IL_MO_PARSE_QTI)
180  {
181  $this->parser_mode = $a_mode;
182  $this->founditems = array();
183  $this->verifyroot = false;
184  $this->verifyqticomment = 0;
185  $this->verifymetadatafield = 0;
186  $this->verifyfieldlabel = 0;
187  $this->verifyfieldentry = 0;
188  $this->verifyfieldlabeltext = "";
189  $this->verifyfieldentrytext = "";
190  $this->question_counter = 1;
191  }
192 
198  function setHandlers($a_xml_parser)
199  {
200  xml_set_object($a_xml_parser,$this);
201  xml_set_element_handler($a_xml_parser,'handlerBeginTag','handlerEndTag');
202  xml_set_character_data_handler($a_xml_parser,'handlerCharacterData');
203  }
204 
205  function startParsing()
206  {
207  $this->question_counter = 1;
209  return FALSE;
210  }
211 
212  function getParent($a_xml_parser)
213  {
214  if ($this->depth[$a_xml_parser] > 0)
215  {
216  return $this->path[$this->depth[$a_xml_parser]-1];
217  }
218  else
219  {
220  return "";
221  }
222  }
223 
227  function handlerBeginTag($a_xml_parser,$a_name,$a_attribs)
228  {
229  switch ($this->parser_mode)
230  {
231  case IL_MO_PARSE_QTI:
232  $this->handlerParseBeginTag($a_xml_parser, $a_name, $a_attribs);
233  break;
234  case IL_MO_VERIFY_QTI:
235  $this->handlerVerifyBeginTag($a_xml_parser, $a_name, $a_attribs);
236  break;
237  }
238  }
239 
243  function handlerParseBeginTag($a_xml_parser,$a_name,$a_attribs)
244  {
245  if ($this->do_nothing) return;
246  $this->sametag = FALSE;
247  $this->characterbuffer = "";
248  $this->depth[$a_xml_parser]++;
249  $this->path[$this->depth[$a_xml_parser]] = strtolower($a_name);
250  $this->qti_element = $a_name;
251 
252  switch (strtolower($a_name))
253  {
254  case "assessment":
255  include_once ("./Services/QTI/classes/class.ilQTIAssessment.php");
256  $this->assessment =& $this->assessments[array_push($this->assessments, new ilQTIAssessment())-1];
257  $this->in_assessment = TRUE;
258  if (is_array($a_attribs))
259  {
260  foreach ($a_attribs as $attribute => $value)
261  {
262  switch (strtolower($attribute))
263  {
264  case "title":
265  $this->assessment->setTitle($value);
266  break;
267  case "ident":
268  $this->assessment->setIdent($value);
269  break;
270  }
271  }
272  }
273  break;
274  case "assessmentcontrol":
275  include_once ("./Services/QTI/classes/class.ilQTIAssessmentcontrol.php");
276  $this->assessmentcontrol = new ilQTIAssessmentcontrol();
277  if (is_array($a_attribs))
278  {
279  foreach ($a_attribs as $attribute => $value)
280  {
281  switch (strtolower($attribute))
282  {
283  case "solutionswitch":
284  $this->assessmentcontrol->setSolutionswitch($value);
285  break;
286  case "hintswitch":
287  $this->assessmentcontrol->setHintswitch($value);
288  break;
289  case "feedbackswitch":
290  $this->assessmentcontrol->setFeedbackswitch($value);
291  break;
292  }
293  }
294  }
295  break;
296  case "objectives":
297  include_once ("./Services/QTI/classes/class.ilQTIObjectives.php");
298  $this->objectives = new ilQTIObjectives();
299  $this->in_objectives = TRUE;
300  break;
301  case 'presentation_material':
302  require_once 'Services/QTI/classes/class.ilQTIPresentationMaterial.php';
303  $this->prensentation_material = new ilQTIPresentationMaterial();
304  $this->in_prensentation_material = TRUE;
305  break;
306  case "section":
307  include_once ("./Services/QTI/classes/class.ilQTISection.php");
308  $this->section = new ilQTISection();
309  break;
310  case "itemmetadata":
311  $this->in_itemmetadata = TRUE;
312  break;
313  case "qtimetadatafield":
314  $this->metadata = array("label" => "", "entry" => "");
315  break;
316  case "flow":
317  include_once ("./Services/QTI/classes/class.ilQTIFlow.php");
318  $this->flow++;
319  break;
320  case "flow_mat":
321  include_once ("./Services/QTI/classes/class.ilQTIFlowMat.php");
322  array_push($this->flow_mat, new ilQTIFlowMat());
323  break;
324  case "itemfeedback":
325  include_once ("./Services/QTI/classes/class.ilQTIItemfeedback.php");
326  $this->itemfeedback = new ilQTIItemfeedback();
327  if (is_array($a_attribs))
328  {
329  foreach ($a_attribs as $attribute => $value)
330  {
331  switch (strtolower($attribute))
332  {
333  case "ident":
334  $this->itemfeedback->setIdent($value);
335  break;
336  case "view":
337  $this->itemfeedback->setView($value);
338  break;
339  }
340  }
341  }
342  break;
343  case "displayfeedback":
344  include_once ("./Services/QTI/classes/class.ilQTIDisplayfeedback.php");
345  $this->displayfeedback = new ilQTIDisplayfeedback();
346  if (is_array($a_attribs))
347  {
348  foreach ($a_attribs as $attribute => $value)
349  {
350  switch (strtolower($attribute))
351  {
352  case "feedbacktype":
353  $this->displayfeedback->setFeedbacktype($value);
354  break;
355  case "linkrefid":
356  $this->displayfeedback->setLinkrefid($value);
357  break;
358  }
359  }
360  }
361  break;
362  case "setvar":
363  include_once ("./Services/QTI/classes/class.ilQTISetvar.php");
364  $this->setvar = new ilQTISetvar();
365  if (is_array($a_attribs))
366  {
367  foreach ($a_attribs as $attribute => $value)
368  {
369  switch (strtolower($attribute))
370  {
371  case "action":
372  $this->setvar->setAction($value);
373  break;
374  case "varname":
375  $this->setvar->setVarname($value);
376  break;
377  }
378  }
379  }
380  break;
381  case "conditionvar":
382  include_once ("./Services/QTI/classes/class.ilQTIConditionvar.php");
383  $this->conditionvar = new ilQTIConditionvar();
384  break;
385  case "not":
386  if ($this->conditionvar != NULL)
387  {
388  $this->conditionvar->addNot();
389  }
390  break;
391  case "and":
392  if ($this->conditionvar != NULL)
393  {
394  $this->conditionvar->addAnd();
395  }
396  break;
397  case "or":
398  if ($this->conditionvar != NULL)
399  {
400  $this->conditionvar->addOr();
401  }
402  break;
403  case "varequal":
404  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
405  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_EQUAL);
406  if (is_array($a_attribs))
407  {
408  foreach ($a_attribs as $attribute => $value)
409  {
410  switch (strtolower($attribute))
411  {
412  case "case":
413  $this->responsevar->setCase($value);
414  break;
415  case "respident":
416  $this->responsevar->setRespident($value);
417  break;
418  case "index":
419  $this->responsevar->setIndex($value);
420  break;
421  }
422  }
423  }
424  break;
425  case "varlt":
426  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
427  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_LT);
428  if (is_array($a_attribs))
429  {
430  foreach ($a_attribs as $attribute => $value)
431  {
432  switch (strtolower($attribute))
433  {
434  case "respident":
435  $this->responsevar->setRespident($value);
436  break;
437  case "index":
438  $this->responsevar->setIndex($value);
439  break;
440  }
441  }
442  }
443  break;
444  case "varlte":
445  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
446  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_LTE);
447  if (is_array($a_attribs))
448  {
449  foreach ($a_attribs as $attribute => $value)
450  {
451  switch (strtolower($attribute))
452  {
453  case "respident":
454  $this->responsevar->setRespident($value);
455  break;
456  case "index":
457  $this->responsevar->setIndex($value);
458  break;
459  }
460  }
461  }
462  break;
463  case "vargt":
464  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
465  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_GT);
466  if (is_array($a_attribs))
467  {
468  foreach ($a_attribs as $attribute => $value)
469  {
470  switch (strtolower($attribute))
471  {
472  case "respident":
473  $this->responsevar->setRespident($value);
474  break;
475  case "index":
476  $this->responsevar->setIndex($value);
477  break;
478  }
479  }
480  }
481  break;
482  case "vargte":
483  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
484  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_GTE);
485  if (is_array($a_attribs))
486  {
487  foreach ($a_attribs as $attribute => $value)
488  {
489  switch (strtolower($attribute))
490  {
491  case "respident":
492  $this->responsevar->setRespident($value);
493  break;
494  case "index":
495  $this->responsevar->setIndex($value);
496  break;
497  }
498  }
499  }
500  break;
501  case "varsubset":
502  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
503  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_SUBSET);
504  if (is_array($a_attribs))
505  {
506  foreach ($a_attribs as $attribute => $value)
507  {
508  switch (strtolower($attribute))
509  {
510  case "respident":
511  $this->responsevar->setRespident($value);
512  break;
513  case "setmatch":
514  $this->responsevar->setSetmatch($value);
515  break;
516  case "index":
517  $this->responsevar->setIndex($value);
518  break;
519  }
520  }
521  }
522  break;
523  case "varinside":
524  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
525  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_INSIDE);
526  if (is_array($a_attribs))
527  {
528  foreach ($a_attribs as $attribute => $value)
529  {
530  switch (strtolower($attribute))
531  {
532  case "respident":
533  $this->responsevar->setRespident($value);
534  break;
535  case "areatype":
536  $this->responsevar->setAreatype($value);
537  break;
538  case "index":
539  $this->responsevar->setIndex($value);
540  break;
541  }
542  }
543  }
544  break;
545  case "varsubstring":
546  include_once("./Services/QTI/classes/class.ilQTIResponseVar.php");
547  $this->responsevar = new ilQTIResponseVar(RESPONSEVAR_SUBSTRING);
548  if (is_array($a_attribs))
549  {
550  foreach ($a_attribs as $attribute => $value)
551  {
552  switch (strtolower($attribute))
553  {
554  case "case":
555  $this->responsevar->setCase($value);
556  break;
557  case "respident":
558  $this->responsevar->setRespident($value);
559  break;
560  case "index":
561  $this->responsevar->setIndex($value);
562  break;
563  }
564  }
565  }
566  break;
567  case "respcondition":
568  include_once("./Services/QTI/classes/class.ilQTIRespcondition.php");
569  $this->respcondition = new ilQTIRespcondition();
570  if (is_array($a_attribs))
571  {
572  foreach ($a_attribs as $attribute => $value)
573  {
574  switch (strtolower($attribute))
575  {
576  case "continue":
577  $this->respcondition->setContinue($value);
578  break;
579  case "title":
580  $this->respcondition->setTitle($value);
581  break;
582  }
583  }
584  }
585  break;
586  case "outcomes":
587  include_once("./Services/QTI/classes/class.ilQTIOutcomes.php");
588  $this->outcomes = new ilQTIOutcomes();
589  break;
590  case "decvar":
591  include_once("./Services/QTI/classes/class.ilQTIDecvar.php");
592  $this->decvar = new ilQTIDecvar();
593  if (is_array($a_attribs))
594  {
595  foreach ($a_attribs as $attribute => $value)
596  {
597  switch (strtolower($attribute))
598  {
599  case "varname":
600  $this->decvar->setVarname($value);
601  break;
602  case "vartype":
603  $this->decvar->setVartype($value);
604  break;
605  case "defaultval":
606  $this->decvar->setDefaultval($value);
607  break;
608  case "minvalue":
609  $this->decvar->setMinvalue($value);
610  break;
611  case "maxvalue":
612  $this->decvar->setMaxvalue($value);
613  break;
614  case "members":
615  $this->decvar->setMembers($value);
616  break;
617  case "cutvalue":
618  $this->decvar->setCutvalue($value);
619  break;
620  }
621  }
622  }
623  break;
624  case "matimage":
625  include_once("./Services/QTI/classes/class.ilQTIMatimage.php");
626  $this->matimage = new ilQTIMatimage();
627  if (is_array($a_attribs))
628  {
629  foreach ($a_attribs as $attribute => $value)
630  {
631  switch (strtolower($attribute))
632  {
633  case "imagtype":
634  $this->matimage->setImagetype($value);
635  break;
636  case "label":
637  $this->matimage->setLabel($value);
638  break;
639  case "height":
640  $this->matimage->setHeight($value);
641  break;
642  case "width":
643  $this->matimage->setWidth($value);
644  break;
645  case "uri":
646  $this->matimage->setUri($value);
647  break;
648  case "embedded":
649  $this->matimage->setEmbedded($value);
650  break;
651  case "x0":
652  $this->matimage->setX0($value);
653  break;
654  case "y0":
655  $this->matimage->setY0($value);
656  break;
657  case "entityref":
658  $this->matimage->setEntityref($value);
659  break;
660  }
661  }
662  }
663  break;
664  case "material":
665  include_once("./Services/QTI/classes/class.ilQTIMaterial.php");
666  $this->material = new ilQTIMaterial();
667  $this->material->setFlow($this->flow);
668  if (is_array($a_attribs))
669  {
670  foreach ($a_attribs as $attribute => $value)
671  {
672  switch (strtolower($attribute))
673  {
674  case "label":
675  $this->material->setLabel($value);
676  break;
677  }
678  }
679  }
680  break;
681  case "mattext":
682  include_once ("./Services/QTI/classes/class.ilQTIMattext.php");
683  $this->mattext = new ilQTIMattext();
684  if (is_array($a_attribs))
685  {
686  foreach ($a_attribs as $attribute => $value)
687  {
688  switch (strtolower($attribute))
689  {
690  case "texttype":
691  $this->mattext->setTexttype($value);
692  break;
693  case "label":
694  $this->mattext->setLabel($value);
695  break;
696  case "charset":
697  $this->mattext->setCharset($value);
698  break;
699  case "uri":
700  $this->mattext->setUri($value);
701  break;
702  case "xml:space":
703  $this->mattext->setXmlspace($value);
704  break;
705  case "xml:lang":
706  $this->mattext->setXmllang($value);
707  break;
708  case "entityref":
709  $this->mattext->setEntityref($value);
710  break;
711  case "height":
712  $this->mattext->setHeight($value);
713  break;
714  case "width":
715  $this->mattext->setWidth($value);
716  break;
717  case "x0":
718  $this->mattext->setX0($value);
719  break;
720  case "y0":
721  $this->mattext->setY0($value);
722  break;
723  }
724  }
725  }
726  break;
727  case "matapplet":
728  include_once ("./Services/QTI/classes/class.ilQTIMatapplet.php");
729  $this->matapplet = New ilQTIMatapplet();
730  if (is_array($a_attribs))
731  {
732  foreach ($a_attribs as $attribute => $value)
733  {
734  switch (strtolower($attribute))
735  {
736  case "label":
737  $this->matapplet->setLabel($value);
738  break;
739  case "uri":
740  $this->matapplet->setUri($value);
741  break;
742  case "y0":
743  $this->matapplet->setY0($value);
744  break;
745  case "height":
746  $this->matapplet->setHeight($value);
747  break;
748  case "width":
749  $this->matapplet->setWidth($value);
750  break;
751  case "x0":
752  $this->matapplet->setX0($value);
753  break;
754  case "embedded":
755  $this->matapplet->setEmbedded($value);
756  break;
757  case "entityref":
758  $this->matapplet->setEntityref($value);
759  break;
760  }
761  }
762  }
763  break;
764  case "questestinterop":
765  $this->hasRootElement = TRUE;
766  break;
767  case "qticomment":
768  break;
769  case "objectbank":
770  // not implemented yet
771  break;
772  case "section":
773  if ($this->assessment != NULL)
774  {
775  $this->assessment->addSection($this->section);
776  }
777  $this->section = NULL;
778  break;
779  case "presentation":
780  $this->in_presentation = TRUE;
781  include_once ("./Services/QTI/classes/class.ilQTIPresentation.php");
782  $this->presentation = new ilQTIPresentation();
783  break;
784  case "response_label":
785  if ($this->render_type != NULL)
786  {
787  include_once("./Services/QTI/classes/class.ilQTIResponseLabel.php");
788  $this->response_label = new ilQTIResponseLabel();
789  foreach ($a_attribs as $attribute => $value)
790  {
791  switch (strtolower($attribute))
792  {
793  case "rshuffle":
794  $this->response_label->setRshuffle($value);
795  break;
796  case "rarea":
797  $this->response_label->setRarea($value);
798  break;
799  case "rrange":
800  $this->response_label->setRrange($value);
801  break;
802  case "labelrefid":
803  $this->response_label->setLabelrefid($value);
804  break;
805  case "ident":
806  $this->response_label->setIdent($value);
807  break;
808  case "match_group":
809  $this->response_label->setMatchGroup($value);
810  break;
811  case "match_max":
812  $this->response_label->setMatchMax($value);
813  break;
814  }
815  }
816  }
817  break;
818  case "render_choice":
819  if ($this->in_response)
820  {
821  include_once("./Services/QTI/classes/class.ilQTIRenderChoice.php");
822  $this->render_type = new ilQTIRenderChoice();
823  foreach ($a_attribs as $attribute => $value)
824  {
825  switch (strtolower($attribute))
826  {
827  case "shuffle":
828  $this->render_type->setShuffle($value);
829  break;
830  }
831  }
832  }
833  break;
834  case "render_hotspot":
835  if ($this->in_response)
836  {
837  include_once("./Services/QTI/classes/class.ilQTIRenderHotspot.php");
838  $this->render_type = new ilQTIRenderHotspot();
839  foreach ($a_attribs as $attribute => $value)
840  {
841  switch (strtolower($attribute))
842  {
843  case "showdraw":
844  $this->render_type->setShuffle($value);
845  break;
846  case "minnumber":
847  $this->render_type->setMinnumber($value);
848  break;
849  case "maxnumber":
850  $this->render_type->setMaxnumber($value);
851  break;
852  }
853  }
854  }
855  break;
856  case "render_fib":
857  if ($this->in_response)
858  {
859  include_once("./Services/QTI/classes/class.ilQTIRenderFib.php");
860  $this->render_type = new ilQTIRenderFib();
861  foreach ($a_attribs as $attribute => $value)
862  {
863  switch (strtolower($attribute))
864  {
865  case "encoding":
866  $this->render_type->setEncoding($value);
867  break;
868  case "fibtype":
869  $this->render_type->setFibtype($value);
870  break;
871  case "rows":
872  $this->render_type->setRows($value);
873  break;
874  case "maxchars":
875  $this->render_type->setMaxchars($value);
876  break;
877  case "prompt":
878  $this->render_type->setPrompt($value);
879  break;
880  case "columns":
881  $this->render_type->setColumns($value);
882  break;
883  case "charset":
884  $this->render_type->setCharset($value);
885  break;
886  case "maxnumber":
887  $this->render_type->setMaxnumber($value);
888  break;
889  case "minnumber":
890  $this->render_type->setMinnumber($value);
891  break;
892  }
893  }
894  }
895  break;
896  case "response_lid":
897  // Ordering Terms and Definitions or
898  // Ordering Terms and Pictures or
899  // Multiple choice single response or
900  // Multiple choice multiple response
901  case "response_xy":
902  // Imagemap question
903  case "response_str":
904  // Close question
905  case "response_num":
906  case "response_grp":
907  // Matching terms and definitions
908  // Matching terms and images
909  include_once "./Services/QTI/classes/class.ilQTIResponse.php";
910  switch (strtolower($a_name))
911  {
912  case "response_lid":
913  $response_type = RT_RESPONSE_LID;
914  break;
915  case "response_xy":
916  $response_type = RT_RESPONSE_XY;
917  break;
918  case "response_str":
919  $response_type = RT_RESPONSE_STR;
920  break;
921  case "response_num":
922  $response_type = RT_RESPONSE_NUM;
923  break;
924  case "response_grp":
925  $response_type = RT_RESPONSE_GRP;
926  break;
927  }
928  $this->in_response = TRUE;
929  $this->response = new ilQTIResponse($response_type);
930  $this->response->setFlow($this->flow);
931  if (is_array($a_attribs))
932  {
933  foreach ($a_attribs as $attribute => $value)
934  {
935  switch (strtolower($attribute))
936  {
937  case "ident":
938  $this->response->setIdent($value);
939  break;
940  case "rtiming":
941  $this->response->setRTiming($value);
942  break;
943  case "rcardinality":
944  $this->response->setRCardinality($value);
945  break;
946  case "numtype":
947  $this->response->setNumtype($value);
948  break;
949  }
950  }
951  }
952  break;
953  case "item":
954  include_once("./Services/QTI/classes/class.ilQTIItem.php");
955  $this->gap_index = 0;
956  $this->item =& $this->items[array_push($this->items, new ilQTIItem())-1];
957  if (is_array($a_attribs))
958  {
959  foreach ($a_attribs as $attribute => $value)
960  {
961  switch (strtolower($attribute))
962  {
963  case "ident":
964  $this->item->setIdent($value);
965  if (count($this->import_idents) > 0)
966  {
967  if (!in_array($value, $this->import_idents))
968  {
969  $this->do_nothing = TRUE;
970  }
971  }
972  break;
973  case "title":
974  $this->item->setTitle($value);
975  break;
976  case "maxattempts":
977  $this->item->setMaxattempts($value);
978  break;
979  }
980  }
981  }
982  break;
983  case "resprocessing":
984  include_once("./Services/QTI/classes/class.ilQTIResprocessing.php");
985  $this->resprocessing = new ilQTIResprocessing();
986  if (is_array($a_attribs))
987  {
988  foreach ($a_attribs as $attribute => $value)
989  {
990  switch (strtolower($attribute))
991  {
992  case "scoremodel":
993  $this->resprocessing->setScoremodel($value);
994  break;
995  }
996  }
997  }
998  break;
999  }
1000  }
1001 
1005  function handlerEndTag($a_xml_parser,$a_name)
1006  {
1007  switch ($this->parser_mode)
1008  {
1009  case IL_MO_PARSE_QTI:
1010  $this->handlerParseEndTag($a_xml_parser, $a_name);
1011  break;
1012  case IL_MO_VERIFY_QTI:
1013  $this->handlerVerifyEndTag($a_xml_parser, $a_name);
1014  break;
1015  }
1016  }
1017 
1021  function handlerParseEndTag($a_xml_parser,$a_name)
1022  {
1023  if (($this->do_nothing) && (strcmp(strtolower($a_name), "item") != 0)) return;
1024  switch (strtolower($a_name))
1025  {
1026  case "assessment":
1027  if (is_object($this->tst_object))
1028  {
1029  $this->tst_object->fromXML($this->assessment);
1030  }
1031  $this->in_assessment = FALSE;
1032  break;
1033  case "assessmentcontrol":
1034  $this->assessment->addAssessmentcontrol($this->assessmentcontrol);
1035  $this->assessmentcontrol = NULL;
1036  break;
1037  case "objectives":
1038  if (strcmp(strtolower($this->getParent($a_xml_parser)), "assessment") == 0)
1039  {
1040  $this->assessment->addObjectives($this->objectives);
1041  }
1042  $this->in_objectives = FALSE;
1043  break;
1044  case 'presentation_material':
1045  $this->assessment->setPresentationMaterial($this->prensentation_material);
1046  $this->in_prensentation_material = FALSE;
1047  break;
1048  case "itemmetadata":
1049  $this->in_itemmetadata = FALSE;
1050  break;
1051  case "qtimetadatafield":
1052  // handle only specific ILIAS metadata
1053  switch ($this->metadata["label"])
1054  {
1055  case "ILIAS_VERSION":
1056  break;
1057  case "QUESTIONTYPE":
1058  if ($this->item != NULL)
1059  {
1060  $this->item->setQuestiontype($this->metadata["entry"]);
1061  }
1062  break;
1063  case "AUTHOR":
1064  if ($this->item != NULL)
1065  {
1066  $this->item->setAuthor($this->metadata["entry"]);
1067  }
1068  default:
1069  if ($this->item != NULL)
1070  {
1071  $this->item->addMetadata($this->metadata);
1072  }
1073  break;
1074  }
1075  if ($this->in_assessment)
1076  {
1077  $this->assessment->addQtiMetadata($this->metadata);
1078  }
1079  $this->metadata = array("label" => "", "entry" => "");
1080  break;
1081  case "flow":
1082  $this->flow--;
1083  break;
1084  case "flow_mat":
1085  if (count($this->flow_mat))
1086  {
1087  $flow_mat = array_pop($this->flow_mat);
1088  if (count($this->flow_mat))
1089  {
1090  $this->flow_mat[count($this->flow_mat)-1]->addFlow_mat($flow_mat);
1091  }
1092  else if($this->in_prensentation_material)
1093  {
1094  $this->prensentation_material->addFlowMat($flow_mat);
1095  }
1096  else if ($this->itemfeedback != NULL)
1097  {
1098  $this->itemfeedback->addFlow_mat($flow_mat);
1099  }
1100  else if ($this->response_label != NULL)
1101  {
1102  $this->response_label->addFlow_mat($flow_mat);
1103  }
1104  }
1105  break;
1106  case "itemfeedback":
1107  if ($this->item != NULL)
1108  {
1109  if ($this->itemfeedback != NULL)
1110  {
1111  $this->item->addItemfeedback($this->itemfeedback);
1112  }
1113  }
1114  $this->itemfeedback = NULL;
1115  break;
1116  case "displayfeedback":
1117  if ($this->respcondition != NULL)
1118  {
1119  if ($this->displayfeedback != NULL)
1120  {
1121  $this->respcondition->addDisplayfeedback($this->displayfeedback);
1122  }
1123  }
1124  $this->displayfeedback = NULL;
1125  break;
1126  case "setvar":
1127  if ($this->respcondition != NULL)
1128  {
1129  if ($this->setvar != NULL)
1130  {
1131  $this->respcondition->addSetvar($this->setvar);
1132  }
1133  }
1134  $this->setvar = NULL;
1135  break;
1136  case "conditionvar":
1137  if ($this->respcondition != NULL)
1138  {
1139  $this->respcondition->setConditionvar($this->conditionvar);
1140  }
1141  $this->conditionvar = NULL;
1142  break;
1143  case "varequal":
1144  case "varlt":
1145  case "varlte":
1146  case "vargt":
1147  case "vargte":
1148  case "varsubset":
1149  case "varinside":
1150  case "varsubstring":
1151  if ($this->conditionvar != NULL)
1152  {
1153  if ($this->responsevar != NULL)
1154  {
1155  $this->conditionvar->addResponseVar($this->responsevar);
1156  }
1157  }
1158  $this->responsevar = NULL;
1159  break;
1160  case "respcondition":
1161  if ($this->resprocessing != NULL)
1162  {
1163  $this->resprocessing->addRespcondition($this->respcondition);
1164  }
1165  $this->respcondition = NULL;
1166  break;
1167  case "outcomes":
1168  if ($this->resprocessing != NULL)
1169  {
1170  $this->resprocessing->setOutcomes($this->outcomes);
1171  }
1172  $this->outcomes = NULL;
1173  break;
1174  case "decvar":
1175  if ($this->outcomes != NULL)
1176  {
1177  $this->outcomes->addDecvar($this->decvar);
1178  }
1179  $this->decvar = NULL;
1180  break;
1181  case "presentation":
1182  $this->in_presentation = FALSE;
1183  if ($this->presentation != NULL)
1184  {
1185  if ($this->item != NULL)
1186  {
1187  $this->item->setPresentation($this->presentation);
1188  }
1189  }
1190  $this->presentation = NULL;
1191  break;
1192  case "response_label":
1193  if ($this->render_type != NULL)
1194  {
1195  $this->render_type->addResponseLabel($this->response_label);
1196  $this->response_label = NULL;
1197  }
1198  break;
1199  case "render_choice":
1200  case "render_hotspot":
1201  case "render_fib":
1202  if ($this->in_response)
1203  {
1204  if ($this->response != NULL)
1205  {
1206  if ($this->render_type != NULL)
1207  {
1208  $this->response->setRenderType($this->render_type);
1209  $this->render_type = NULL;
1210  }
1211  }
1212  }
1213  break;
1214  case "response_lid":
1215  case "response_xy":
1216  case "response_str":
1217  case "response_num":
1218  case "response_grp":
1219  $this->gap_index++;
1220  if ($this->presentation != NULL)
1221  {
1222  if ($this->response != NULL)
1223  {
1224  $this->presentation->addResponse($this->response);
1225  if ($this->item != NULL)
1226  {
1227  $this->item->addPresentationitem($this->response);
1228  }
1229  }
1230  }
1231  $this->response = NULL;
1232  $this->in_response = FALSE;
1233  break;
1234  case "item":
1235  if ($this->do_nothing)
1236  {
1237  $this->do_nothing = FALSE;
1238  return;
1239  }
1240  if (strlen($this->item->getQuestionType()))
1241  {
1242  // this is an ILIAS QTI question
1243  }
1244  else
1245  {
1246  // this is a QTI question which wasn't generated by ILIAS
1247  }
1248  global $ilDB;
1249  global $ilUser;
1250  // save the item directly to save memory
1251  // the database id's of the created items are exported. if the import fails
1252  // ILIAS can delete the already imported items
1253 
1254  // problems: the object id of the parent questionpool is not yet known. must be set later
1255  // the complete flag must be calculated?
1256  $qt = $this->item->determineQuestionType();
1257  $presentation = $this->item->getPresentation();
1258 
1259  include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
1261  $question = new $qt();
1262  $question->fromXML($this->item, $this->qpl_id, $this->tst_id, $this->tst_object, $this->question_counter, $this->import_mapping);
1263  break;
1264  case "material":
1265  if ($this->material)
1266  {
1267  $mat = $this->material->getMaterial(0);
1268  if ((strcmp($mat["type"], "mattext") == 0) && (strcmp($mat["material"]->getLabel(), "suggested_solution") == 0))
1269  {
1270  $this->item->addSuggestedSolution($mat["material"], $this->gap_index);
1271  }
1272  if ($this->in_objectives)
1273  {
1274  $this->objectives->addMaterial($this->material);
1275  }
1276  else if (($this->render_type != NULL) && (strcmp(strtolower($this->getParent($a_xml_parser)), "render_hotspot") == 0))
1277  {
1278  $this->render_type->addMaterial($this->material);
1279  }
1280  else if (count($this->flow_mat) && (strcmp(strtolower($this->getParent($a_xml_parser)), "flow_mat") == 0))
1281  {
1282  $this->flow_mat[count($this->flow_mat)-1]->addMaterial($this->material);
1283  }
1284  else if ($this->itemfeedback != NULL)
1285  {
1286  $this->itemfeedback->addMaterial($this->material);
1287  }
1288  else if ($this->response_label != NULL)
1289  {
1290  $this->response_label->addMaterial($this->material);
1291  }
1292  else if ($this->response != NULL)
1293  {
1294  if ($this->response->hasRendering())
1295  {
1296  $this->response->setMaterial2($this->material);
1297  }
1298  else
1299  {
1300  $this->response->setMaterial1($this->material);
1301  }
1302  }
1303  elseif (($this->in_presentation) && (!$this->in_response))
1304  {
1305  if (!is_object($this->item->getQuestiontext()))
1306  {
1307  $this->item->setQuestiontext($this->material);
1308  }
1309  $this->presentation->addMaterial($this->material);
1310  }
1311  else if ($this->presentation != NULL)
1312  {
1313  $this->presentation->addMaterial($this->material);
1314  if ($this->item != NULL)
1315  {
1316  $this->item->addPresentationitem($this->material);
1317  }
1318  }
1319  }
1320  $this->material = NULL;
1321  break;
1322  case "matimage";
1323  if ($this->material != NULL)
1324  {
1325  if ($this->matimage != NULL)
1326  {
1327  $this->material->addMatimage($this->matimage);
1328  }
1329  }
1330  $this->matimage = NULL;
1331  break;
1332  // add support for matbreak element
1333  case "matbreak":
1334  $this->mattext = new ilQTIMattext();
1335  $this->mattext->setContent('<br />');
1336  $this->material->addMattext($this->mattext);
1337  $this->mattext = NULL;
1338  break;
1339  case "resprocessing":
1340  if ($this->item != NULL)
1341  {
1342  $this->item->addResprocessing($this->resprocessing);
1343  }
1344  $this->resprocessing = NULL;
1345  break;
1346  case "mattext":
1347  if ($this->material != NULL)
1348  {
1349  $this->material->addMattext($this->mattext);
1350  }
1351  $this->mattext = NULL;
1352  break;
1353  case "matapplet":
1354  if ($this->material != NULL)
1355  {
1356  $this->material->addMatapplet($this->matapplet);
1357  }
1358  $this->matapplet = NULL;
1359  break;
1360  }
1361  $this->depth[$a_xml_parser]--;
1362  }
1363 
1367  function handlerCharacterData($a_xml_parser,$a_data)
1368  {
1369  switch ($this->parser_mode)
1370  {
1371  case IL_MO_PARSE_QTI:
1372  $this->handlerParseCharacterData($a_xml_parser, $a_data);
1373  break;
1374  case IL_MO_VERIFY_QTI:
1375  $this->handlerVerifyCharacterData($a_xml_parser, $a_data);
1376  break;
1377  }
1378  }
1379 
1383  function handlerParseCharacterData($a_xml_parser,$a_data)
1384  {
1385  if ($this->do_nothing) return;
1386  $this->characterbuffer .= $a_data;
1387  $a_data = $this->characterbuffer;
1388  switch ($this->qti_element)
1389  {
1390  case "fieldlabel":
1391  $this->metadata["label"] = $a_data;
1392  break;
1393  case "fieldentry":
1394  $this->metadata["entry"] = $a_data;
1395  break;
1396  case "response_label":
1397  if ($this->response_label != NULL)
1398  {
1399  $this->response_label->setContent($a_data);
1400  }
1401  break;
1402  case "setvar":
1403  if ($this->setvar != NULL)
1404  {
1405  $this->setvar->setContent($a_data);
1406  }
1407  break;
1408  case "displayfeedback":
1409  if ($this->displayfeedback != NULL)
1410  {
1411  $this->displayfeedback->setContent($a_data);
1412  }
1413  break;
1414  case "varequal":
1415  case "varlt":
1416  case "varlte":
1417  case "vargt":
1418  case "vargte":
1419  case "varsubset":
1420  case "varinside":
1421  case "varsubstring":
1422  if ($this->responsevar != NULL)
1423  {
1424  $this->responsevar->setContent($a_data);
1425  }
1426  break;
1427  case "decvar":
1428  if (strlen($a_data))
1429  {
1430  if ($this->decvar != NULL)
1431  {
1432  $this->decvar->setContent($a_data);
1433  }
1434  }
1435  break;
1436  case "mattext":
1437  if ($this->mattext != NULL)
1438  {
1439  $this->mattext->setContent($a_data);
1440  }
1441  break;
1442  case "matapplet":
1443  if ($this->matapplet != NULL)
1444  {
1445  $this->matapplet->setContent($a_data);
1446  }
1447  break;
1448  case "matimage":
1449  if ($this->matimage != NULL)
1450  {
1451  $this->matimage->setContent($a_data);
1452  }
1453  break;
1454  case "duration":
1455  switch ($this->getParent($a_xml_parser))
1456  {
1457  case "assessment":
1458  // to be done
1459  break;
1460  case "section":
1461  // to be done
1462  break;
1463  case "item":
1464  $this->item->setDuration($a_data);
1465  break;
1466  }
1467  break;
1468  case "qticomment":
1469  switch ($this->getParent($a_xml_parser))
1470  {
1471  case "item":
1472  $this->item->setComment($a_data);
1473  break;
1474  case "assessment":
1475  $this->assessment->setComment($a_data);
1476  break;
1477  default:
1478  break;
1479  }
1480  break;
1481  }
1482  $this->sametag = TRUE;
1483  }
1484 
1488  function handlerVerifyBeginTag($a_xml_parser,$a_name,$a_attribs)
1489  {
1490  switch (strtolower($a_name))
1491  {
1492  case "questestinterop":
1493  $this->verifyroot = true;
1494  break;
1495  case "qtimetadatafield":
1496  $this->verifymetadatafield = 1;
1497  break;
1498  case "fieldlabel":
1499  $this->verifyfieldlabeltext = "";
1500  if ($this->verifymetadatafield == 1) $this->verifyfieldlabel = 1;
1501  break;
1502  case "fieldentry":
1503  $this->verifyfieldentrytext = "";
1504  if ($this->verifymetadatafield == 1) $this->verifyfieldentry = 1;
1505  break;
1506  case "item":
1507  $title = "";
1508  if (is_array($a_attribs))
1509  {
1510  foreach ($a_attribs as $attribute => $value)
1511  {
1512  switch (strtolower($attribute))
1513  {
1514  case "title":
1515  $title = $value;
1516  break;
1517  }
1518  }
1519  }
1520  array_push($this->founditems, array("title" => "$title", "type" => "", "ident" => $a_attribs["ident"]));
1521  break;
1522  case "response_lid":
1523  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1524  {
1525  // test for non ILIAS generated question types
1526  if (is_array($a_attribs))
1527  {
1528  foreach ($a_attribs as $attribute => $value)
1529  {
1530  switch (strtolower($attribute))
1531  {
1532  case "rcardinality":
1533  include_once "./Services/QTI/classes/class.ilQTIItem.php";
1534  switch (strtolower($value))
1535  {
1536  case "single":
1537  $this->founditems[count($this->founditems)-1]["type"] = QT_MULTIPLE_CHOICE_SR;
1538  break;
1539  case "multiple":
1540  $this->founditems[count($this->founditems)-1]["type"] = QT_MULTIPLE_CHOICE_MR;
1541  break;
1542  case "ordered":
1543  $this->founditems[count($this->founditems)-1]["type"] = QT_ORDERING;
1544  break;
1545  }
1546  break;
1547  }
1548  }
1549  }
1550  }
1551  break;
1552  case "response_str":
1553  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1554  {
1555  // test for non ILIAS generated question types
1556  if (is_array($a_attribs))
1557  {
1558  foreach ($a_attribs as $attribute => $value)
1559  {
1560  switch (strtolower($attribute))
1561  {
1562  case "rcardinality":
1563  include_once "./Services/QTI/classes/class.ilQTIItem.php";
1564  switch (strtolower($value))
1565  {
1566  case "single":
1567  $this->founditems[count($this->founditems)-1]["type"] = QT_CLOZE;
1568  break;
1569  case "ordered":
1570  $this->founditems[count($this->founditems)-1]["type"] = QT_TEXT;
1571  break;
1572  }
1573  break;
1574  }
1575  }
1576  }
1577  }
1578  break;
1579  case "response_xy":
1580  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1581  {
1582  $this->founditems[count($this->founditems)-1]["type"] = QT_IMAGEMAP;
1583  }
1584  break;
1585  case "response_num":
1586  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1587  {
1588  $this->founditems[count($this->founditems)-1]["type"] = QT_NUMERIC;
1589  }
1590  break;
1591  case "response_grp":
1592  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1593  {
1594  $this->founditems[count($this->founditems)-1]["type"] = QT_MATCHING;
1595  }
1596  break;
1597  case "qticomment":
1598  // check for "old" ILIAS qti format (not well formed)
1599  $this->verifyqticomment = 1;
1600  break;
1601  case "presentation":
1602  if (is_array($a_attribs))
1603  {
1604  foreach ($a_attribs as $attribute => $value)
1605  {
1606  switch (strtolower($attribute))
1607  {
1608  case "label":
1609  $this->founditems[count($this->founditems)-1]["title"] = $value;
1610  break;
1611  }
1612  }
1613  }
1614  break;
1615  }
1616  }
1617 
1621  function handlerVerifyEndTag($a_xml_parser,$a_name)
1622  {
1623  switch (strtolower($a_name))
1624  {
1625  case "qticomment":
1626  // check for "old" ILIAS qti format (not well formed)
1627  $this->verifyqticomment = 0;
1628  break;
1629  case "qtimetadatafield":
1630  $this->verifymetadatafield = 0;
1631  if (strcmp($this->verifyfieldlabeltext, "QUESTIONTYPE") == 0)
1632  {
1633  $this->founditems[count($this->founditems)-1]["type"] = $this->verifyfieldentrytext;
1634  }
1635  break;
1636  case "fieldlabel":
1637  $this->verifyfieldlabel = 0;
1638  break;
1639  case "fieldentry":
1640  $this->verifyfieldentry = 0;
1641  break;
1642  }
1643  }
1644 
1648  function handlerVerifyCharacterData($a_xml_parser,$a_data)
1649  {
1650  if ($this->verifyqticomment == 1)
1651  {
1652  if (preg_match("/Questiontype\=(.*)/", $a_data, $matches))
1653  {
1654  if (count($this->founditems))
1655  {
1656  $this->founditems[count($this->founditems)-1]["type"] = $matches[1];
1657  }
1658  }
1659  }
1660  else if ($this->verifyfieldlabel == 1)
1661  {
1662  $this->verifyfieldlabeltext = $a_data;
1663  }
1664  else if ($this->verifyfieldentry == 1)
1665  {
1666  $this->verifyfieldentrytext = $a_data;
1667  }
1668  }
1669 
1670  function &getFoundItems()
1671  {
1672  return $this->founditems;
1673  }
1674 
1679  function getImportMapping()
1680  {
1681  if (!is_array($this->import_mapping))
1682  {
1683  return array();
1684  }
1685  else
1686  {
1687  return $this->import_mapping;
1688  }
1689  }
1690 
1691  function setXMLContent($a_xml_content)
1692  {
1693  $a_xml_content = $this->cleanInvalidXmlChars($a_xml_content);
1694 
1695  return parent::setXMLContent($a_xml_content);
1696  }
1697 
1698  function openXMLFile()
1699  {
1700  $xmlContent = file_get_contents($this->xml_file);
1701  $xmlContent = $this->cleanInvalidXmlChars($xmlContent);
1702  file_put_contents($this->xml_file, $xmlContent);
1703 
1704  return parent::openXMLFile();
1705  }
1706 
1707  protected function cleanInvalidXmlChars($xmlContent)
1708  {
1709  // http://www.w3.org/TR/xml/#charsets
1710 
1711  // DOES ACTUALLY KILL CONTENT, SHOULD CLEAN NON ESCAPED ILLEGAL CHARS, DON'T KNOW
1712  //$reg = '/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]/';
1713  //$xmlContent = preg_replace($reg, '', $xmlContent);
1714 
1715  // remove illegal chars escaped to html entities
1716  $needles = array();
1717  for($i = 0x00, $max = 0x08; $i <= $max; $i += 0x01)
1718  {
1719  $needles[] = "&#{$i};";
1720  }
1721  for($i = 0x0b, $max = 0x0c; $i <= $max; $i += 0x01)
1722  {
1723  $needles[] = "&#{$i};";
1724  }
1725  for($i = 0x0e, $max = 0x1f; $i <= $max; $i += 0x01)
1726  {
1727  $needles[] = "&#{$i};";
1728  }
1729  for($i = 0xd800, $max = 0xdfff; $i <= $max; $i += 0x0001)
1730  {
1731  $needles[] = "&#{$i};";
1732  }
1733  for($i = 0xfffe, $max = 0xffff; $i <= $max; $i += 0x0001)
1734  {
1735  $needles[] = "&#{$i};";
1736  }
1737  $reg = '/('.implode('|', $needles).')/';
1738  $xmlContent = preg_replace($reg, '', $xmlContent);
1739 
1740  return $xmlContent;
1741  }
1742 }
1743 ?>