ILIAS  Release_4_4_x_branch Revision 61816
 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  $fbt = str_replace('ass', 'ilAss', $qt).'Feedback';
1263  $question->feedbackOBJ = new $fbt(
1264  $question, $GLOBALS['ilCtrl'], $GLOBALS['ilDB'], $GLOBALS['lng']
1265  );
1266  $question->fromXML($this->item, $this->qpl_id, $this->tst_id, $this->tst_object, $this->question_counter, $this->import_mapping);
1267  break;
1268  case "material":
1269  if ($this->material)
1270  {
1271  $mat = $this->material->getMaterial(0);
1272  if ((strcmp($mat["type"], "mattext") == 0) && (strcmp($mat["material"]->getLabel(), "suggested_solution") == 0))
1273  {
1274  $this->item->addSuggestedSolution($mat["material"], $this->gap_index);
1275  }
1276  if ($this->in_objectives)
1277  {
1278  $this->objectives->addMaterial($this->material);
1279  }
1280  else if (($this->render_type != NULL) && (strcmp(strtolower($this->getParent($a_xml_parser)), "render_hotspot") == 0))
1281  {
1282  $this->render_type->addMaterial($this->material);
1283  }
1284  else if (count($this->flow_mat) && (strcmp(strtolower($this->getParent($a_xml_parser)), "flow_mat") == 0))
1285  {
1286  $this->flow_mat[count($this->flow_mat)-1]->addMaterial($this->material);
1287  }
1288  else if ($this->itemfeedback != NULL)
1289  {
1290  $this->itemfeedback->addMaterial($this->material);
1291  }
1292  else if ($this->response_label != NULL)
1293  {
1294  $this->response_label->addMaterial($this->material);
1295  }
1296  else if ($this->response != NULL)
1297  {
1298  if ($this->response->hasRendering())
1299  {
1300  $this->response->setMaterial2($this->material);
1301  }
1302  else
1303  {
1304  $this->response->setMaterial1($this->material);
1305  }
1306  }
1307  elseif (($this->in_presentation) && (!$this->in_response))
1308  {
1309  if (!is_object($this->item->getQuestiontext()))
1310  {
1311  $this->item->setQuestiontext($this->material);
1312  }
1313  $this->presentation->addMaterial($this->material);
1314  }
1315  else if ($this->presentation != NULL)
1316  {
1317  $this->presentation->addMaterial($this->material);
1318  if ($this->item != NULL)
1319  {
1320  $this->item->addPresentationitem($this->material);
1321  }
1322  }
1323  }
1324  $this->material = NULL;
1325  break;
1326  case "matimage";
1327  if ($this->material != NULL)
1328  {
1329  if ($this->matimage != NULL)
1330  {
1331  $this->material->addMatimage($this->matimage);
1332  }
1333  }
1334  $this->matimage = NULL;
1335  break;
1336  // add support for matbreak element
1337  case "matbreak":
1338  $this->mattext = new ilQTIMattext();
1339  $this->mattext->setContent('<br />');
1340  $this->material->addMattext($this->mattext);
1341  $this->mattext = NULL;
1342  break;
1343  case "resprocessing":
1344  if ($this->item != NULL)
1345  {
1346  $this->item->addResprocessing($this->resprocessing);
1347  }
1348  $this->resprocessing = NULL;
1349  break;
1350  case "mattext":
1351  if ($this->material != NULL)
1352  {
1353  $this->material->addMattext($this->mattext);
1354  }
1355  $this->mattext = NULL;
1356  break;
1357  case "matapplet":
1358  if ($this->material != NULL)
1359  {
1360  $this->material->addMatapplet($this->matapplet);
1361  }
1362  $this->matapplet = NULL;
1363  break;
1364  }
1365  $this->depth[$a_xml_parser]--;
1366  }
1367 
1371  function handlerCharacterData($a_xml_parser,$a_data)
1372  {
1373  switch ($this->parser_mode)
1374  {
1375  case IL_MO_PARSE_QTI:
1376  $this->handlerParseCharacterData($a_xml_parser, $a_data);
1377  break;
1378  case IL_MO_VERIFY_QTI:
1379  $this->handlerVerifyCharacterData($a_xml_parser, $a_data);
1380  break;
1381  }
1382  }
1383 
1387  function handlerParseCharacterData($a_xml_parser,$a_data)
1388  {
1389  if ($this->do_nothing) return;
1390  $this->characterbuffer .= $a_data;
1391  $a_data = $this->characterbuffer;
1392  switch ($this->qti_element)
1393  {
1394  case "fieldlabel":
1395  $this->metadata["label"] = $a_data;
1396  break;
1397  case "fieldentry":
1398  $this->metadata["entry"] = $a_data;
1399  break;
1400  case "response_label":
1401  if ($this->response_label != NULL)
1402  {
1403  $this->response_label->setContent($a_data);
1404  }
1405  break;
1406  case "setvar":
1407  if ($this->setvar != NULL)
1408  {
1409  $this->setvar->setContent($a_data);
1410  }
1411  break;
1412  case "displayfeedback":
1413  if ($this->displayfeedback != NULL)
1414  {
1415  $this->displayfeedback->setContent($a_data);
1416  }
1417  break;
1418  case "varequal":
1419  case "varlt":
1420  case "varlte":
1421  case "vargt":
1422  case "vargte":
1423  case "varsubset":
1424  case "varinside":
1425  case "varsubstring":
1426  if ($this->responsevar != NULL)
1427  {
1428  $this->responsevar->setContent($a_data);
1429  }
1430  break;
1431  case "decvar":
1432  if (strlen($a_data))
1433  {
1434  if ($this->decvar != NULL)
1435  {
1436  $this->decvar->setContent($a_data);
1437  }
1438  }
1439  break;
1440  case "mattext":
1441  if ($this->mattext != NULL)
1442  {
1443  $this->mattext->setContent($a_data);
1444  }
1445  break;
1446  case "matapplet":
1447  if ($this->matapplet != NULL)
1448  {
1449  $this->matapplet->setContent($a_data);
1450  }
1451  break;
1452  case "matimage":
1453  if ($this->matimage != NULL)
1454  {
1455  $this->matimage->setContent($a_data);
1456  }
1457  break;
1458  case "duration":
1459  switch ($this->getParent($a_xml_parser))
1460  {
1461  case "assessment":
1462  // to be done
1463  break;
1464  case "section":
1465  // to be done
1466  break;
1467  case "item":
1468  $this->item->setDuration($a_data);
1469  break;
1470  }
1471  break;
1472  case "qticomment":
1473  switch ($this->getParent($a_xml_parser))
1474  {
1475  case "item":
1476  $this->item->setComment($a_data);
1477  break;
1478  case "assessment":
1479  $this->assessment->setComment($a_data);
1480  break;
1481  default:
1482  break;
1483  }
1484  break;
1485  }
1486  $this->sametag = TRUE;
1487  }
1488 
1492  function handlerVerifyBeginTag($a_xml_parser,$a_name,$a_attribs)
1493  {
1494  switch (strtolower($a_name))
1495  {
1496  case "questestinterop":
1497  $this->verifyroot = true;
1498  break;
1499  case "qtimetadatafield":
1500  $this->verifymetadatafield = 1;
1501  break;
1502  case "fieldlabel":
1503  $this->verifyfieldlabeltext = "";
1504  if ($this->verifymetadatafield == 1) $this->verifyfieldlabel = 1;
1505  break;
1506  case "fieldentry":
1507  $this->verifyfieldentrytext = "";
1508  if ($this->verifymetadatafield == 1) $this->verifyfieldentry = 1;
1509  break;
1510  case "item":
1511  $title = "";
1512  if (is_array($a_attribs))
1513  {
1514  foreach ($a_attribs as $attribute => $value)
1515  {
1516  switch (strtolower($attribute))
1517  {
1518  case "title":
1519  $title = $value;
1520  break;
1521  }
1522  }
1523  }
1524  array_push($this->founditems, array("title" => "$title", "type" => "", "ident" => $a_attribs["ident"]));
1525  break;
1526  case "response_lid":
1527  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1528  {
1529  // test for non ILIAS generated question types
1530  if (is_array($a_attribs))
1531  {
1532  foreach ($a_attribs as $attribute => $value)
1533  {
1534  switch (strtolower($attribute))
1535  {
1536  case "rcardinality":
1537  include_once "./Services/QTI/classes/class.ilQTIItem.php";
1538  switch (strtolower($value))
1539  {
1540  case "single":
1541  $this->founditems[count($this->founditems)-1]["type"] = QT_MULTIPLE_CHOICE_SR;
1542  break;
1543  case "multiple":
1544  $this->founditems[count($this->founditems)-1]["type"] = QT_MULTIPLE_CHOICE_MR;
1545  break;
1546  case "ordered":
1547  $this->founditems[count($this->founditems)-1]["type"] = QT_ORDERING;
1548  break;
1549  }
1550  break;
1551  }
1552  }
1553  }
1554  }
1555  break;
1556  case "response_str":
1557  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1558  {
1559  // test for non ILIAS generated question types
1560  if (is_array($a_attribs))
1561  {
1562  foreach ($a_attribs as $attribute => $value)
1563  {
1564  switch (strtolower($attribute))
1565  {
1566  case "rcardinality":
1567  include_once "./Services/QTI/classes/class.ilQTIItem.php";
1568  switch (strtolower($value))
1569  {
1570  case "single":
1571  $this->founditems[count($this->founditems)-1]["type"] = QT_CLOZE;
1572  break;
1573  case "ordered":
1574  $this->founditems[count($this->founditems)-1]["type"] = QT_TEXT;
1575  break;
1576  }
1577  break;
1578  }
1579  }
1580  }
1581  }
1582  break;
1583  case "response_xy":
1584  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1585  {
1586  $this->founditems[count($this->founditems)-1]["type"] = QT_IMAGEMAP;
1587  }
1588  break;
1589  case "response_num":
1590  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1591  {
1592  $this->founditems[count($this->founditems)-1]["type"] = QT_NUMERIC;
1593  }
1594  break;
1595  case "response_grp":
1596  if (strlen($this->founditems[count($this->founditems)-1]["type"]) == 0)
1597  {
1598  $this->founditems[count($this->founditems)-1]["type"] = QT_MATCHING;
1599  }
1600  break;
1601  case "qticomment":
1602  // check for "old" ILIAS qti format (not well formed)
1603  $this->verifyqticomment = 1;
1604  break;
1605  case "presentation":
1606  if (is_array($a_attribs))
1607  {
1608  foreach ($a_attribs as $attribute => $value)
1609  {
1610  switch (strtolower($attribute))
1611  {
1612  case "label":
1613  $this->founditems[count($this->founditems)-1]["title"] = $value;
1614  break;
1615  }
1616  }
1617  }
1618  break;
1619  }
1620  }
1621 
1625  function handlerVerifyEndTag($a_xml_parser,$a_name)
1626  {
1627  switch (strtolower($a_name))
1628  {
1629  case "qticomment":
1630  // check for "old" ILIAS qti format (not well formed)
1631  $this->verifyqticomment = 0;
1632  break;
1633  case "qtimetadatafield":
1634  $this->verifymetadatafield = 0;
1635  if (strcmp($this->verifyfieldlabeltext, "QUESTIONTYPE") == 0)
1636  {
1637  $this->founditems[count($this->founditems)-1]["type"] = $this->verifyfieldentrytext;
1638  }
1639  break;
1640  case "fieldlabel":
1641  $this->verifyfieldlabel = 0;
1642  break;
1643  case "fieldentry":
1644  $this->verifyfieldentry = 0;
1645  break;
1646  }
1647  }
1648 
1652  function handlerVerifyCharacterData($a_xml_parser,$a_data)
1653  {
1654  if ($this->verifyqticomment == 1)
1655  {
1656  if (preg_match("/Questiontype\=(.*)/", $a_data, $matches))
1657  {
1658  if (count($this->founditems))
1659  {
1660  $this->founditems[count($this->founditems)-1]["type"] = $matches[1];
1661  }
1662  }
1663  }
1664  else if ($this->verifyfieldlabel == 1)
1665  {
1666  $this->verifyfieldlabeltext = $a_data;
1667  }
1668  else if ($this->verifyfieldentry == 1)
1669  {
1670  $this->verifyfieldentrytext = $a_data;
1671  }
1672  }
1673 
1674  function &getFoundItems()
1675  {
1676  return $this->founditems;
1677  }
1678 
1683  function getImportMapping()
1684  {
1685  if (!is_array($this->import_mapping))
1686  {
1687  return array();
1688  }
1689  else
1690  {
1691  return $this->import_mapping;
1692  }
1693  }
1694 
1695  function setXMLContent($a_xml_content)
1696  {
1697  $a_xml_content = $this->cleanInvalidXmlChars($a_xml_content);
1698 
1699  return parent::setXMLContent($a_xml_content);
1700  }
1701 
1702  function openXMLFile()
1703  {
1704  $xmlContent = file_get_contents($this->xml_file);
1705  $xmlContent = $this->cleanInvalidXmlChars($xmlContent);
1706  file_put_contents($this->xml_file, $xmlContent);
1707 
1708  return parent::openXMLFile();
1709  }
1710 
1711  protected function cleanInvalidXmlChars($xmlContent)
1712  {
1713  // http://www.w3.org/TR/xml/#charsets
1714 
1715  // DOES ACTUALLY KILL CONTENT, SHOULD CLEAN NON ESCAPED ILLEGAL CHARS, DON'T KNOW
1716  //$reg = '/[^\x09\x0A\x0D\x20-\uD7FF\uE000-\uFFFD\u10000-\u10FFFF]/';
1717  //$xmlContent = preg_replace($reg, '', $xmlContent);
1718 
1719  // remove illegal chars escaped to html entities
1720  $needles = array();
1721  for($i = 0x00, $max = 0x08; $i <= $max; $i += 0x01)
1722  {
1723  $needles[] = "&#{$i};";
1724  }
1725  for($i = 0x0b, $max = 0x0c; $i <= $max; $i += 0x01)
1726  {
1727  $needles[] = "&#{$i};";
1728  }
1729  for($i = 0x0e, $max = 0x1f; $i <= $max; $i += 0x01)
1730  {
1731  $needles[] = "&#{$i};";
1732  }
1733  for($i = 0xd800, $max = 0xdfff; $i <= $max; $i += 0x0001)
1734  {
1735  $needles[] = "&#{$i};";
1736  }
1737  for($i = 0xfffe, $max = 0xffff; $i <= $max; $i += 0x0001)
1738  {
1739  $needles[] = "&#{$i};";
1740  }
1741  $reg = '/('.implode('|', $needles).')/';
1742  $xmlContent = preg_replace($reg, '', $xmlContent);
1743 
1744  return $xmlContent;
1745  }
1746 }
1747 ?>