ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
class.assMatchingQuestionGUI.php
Go to the documentation of this file.
1<?php
2/* Copyright (c) 1998-2013 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4require_once './Modules/TestQuestionPool/classes/class.assQuestionGUI.php';
5require_once './Modules/TestQuestionPool/interfaces/interface.ilGuiQuestionScoringAdjustable.php';
6require_once './Modules/TestQuestionPool/interfaces/interface.ilGuiAnswerScoringAdjustable.php';
7require_once './Modules/Test/classes/inc.AssessmentConstants.php';
8
24{
35 public function __construct($id = -1)
36 {
38 include_once "./Modules/TestQuestionPool/classes/class.assMatchingQuestion.php";
39 $this->object = new assMatchingQuestion();
40 $this->setErrorMessage($this->lng->txt("msg_form_save_error"));
41 if ($id >= 0) {
42 $this->object->loadFromDb($id);
43 }
44 }
45
49 protected function writePostData($always = false)
50 {
51 $hasErrors = (!$always) ? $this->editQuestion(true) : false;
52 if (!$hasErrors) {
53 require_once 'Services/Form/classes/class.ilPropertyFormGUI.php';
58 return 0;
59 }
60 return 1;
61 }
62
64 {
65 // Delete all existing answers and create new answers from the form data
66 $this->object->flushMatchingPairs();
67 $this->object->flushTerms();
68 $this->object->flushDefinitions();
69
70 // add terms
71 require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php';
72 foreach ($_POST['terms']['answer'] as $index => $answer) {
73 $filename = $_POST['terms']['imagename'][$index];
74 if (strlen($_FILES['terms']['name']['image'][$index])) {
75 // upload the new file
76 $name = $_FILES['terms']['name']['image'][$index];
77 if ($this->object->setImageFile(
78 $_FILES['terms']['tmp_name']['image'][$index],
79 $this->object->getEncryptedFilename($name)
80 )
81 ) {
82 $filename = $this->object->getEncryptedFilename($name);
83 } else {
84 $filename = "";
85 }
86 }
87 $this->object->addTerm(
88 new assAnswerMatchingTerm($answer, $filename, $_POST['terms']['identifier'][$index])
89 );
90 }
91 // add definitions
92 require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php';
93 foreach ($_POST['definitions']['answer'] as $index => $answer) {
94 $filename = $_POST['definitions']['imagename'][$index];
95 if (strlen($_FILES['definitions']['name']['image'][$index])) {
96 // upload the new file
97 $name = $_FILES['definitions']['name']['image'][$index];
98 if ($this->object->setImageFile(
99 $_FILES['definitions']['tmp_name']['image'][$index],
100 $this->object->getEncryptedFilename($name)
101 )
102 ) {
103 $filename = $this->object->getEncryptedFilename($name);
104 } else {
105 $filename = "";
106 }
107 }
108 $this->object->addDefinition(
109 new assAnswerMatchingDefinition($answer, $filename, $_POST['definitions']['identifier'][$index])
110 );
111 }
112
113 // add matching pairs
114 if (is_array($_POST['pairs']['points'])) {
115 require_once './Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php';
116 foreach ($_POST['pairs']['points'] as $index => $points) {
117 $term_id = $_POST['pairs']['term'][$index];
118 $definition_id = $_POST['pairs']['definition'][$index];
119 $this->object->addMatchingPair(
120 $this->object->getTermWithIdentifier($term_id),
121 $this->object->getDefinitionWithIdentifier($definition_id),
122 $points
123 );
124 }
125 }
126 }
127
129 {
130 if (!$this->object->getSelfAssessmentEditingMode()) {
131 $this->object->setShuffle($_POST["shuffle"]);
132 } else {
133 $this->object->setShuffle(1);
134 }
135 $this->object->setThumbGeometry($_POST["thumb_geometry"]);
136 $this->object->setMatchingMode($_POST['matching_mode']);
137 }
138
139 public function uploadterms()
140 {
141 $this->writePostData(true);
142 $this->editQuestion();
143 }
144
145 public function removeimageterms()
146 {
147 $this->writePostData(true);
148 $position = key($_POST['cmd']['removeimageterms']);
149 $this->object->removeTermImage($position);
150 $this->editQuestion();
151 }
152
153 public function uploaddefinitions()
154 {
155 $this->writePostData(true);
156 $this->editQuestion();
157 }
158
159 public function removeimagedefinitions()
160 {
161 $this->writePostData(true);
162 $position = key($_POST['cmd']['removeimagedefinitions']);
163 $this->object->removeDefinitionImage($position);
164 $this->editQuestion();
165 }
166
167 public function addterms()
168 {
169 $this->writePostData();
170 $position = key($_POST["cmd"]["addterms"]);
171 $this->object->insertTerm($position + 1);
172 $this->editQuestion();
173 }
174
175 public function removeterms()
176 {
177 $this->writePostData();
178 $position = key($_POST["cmd"]["removeterms"]);
179 $this->object->deleteTerm($position);
180 $this->editQuestion();
181 }
182
183 public function adddefinitions()
184 {
185 $this->writePostData();
186 $position = key($_POST["cmd"]["adddefinitions"]);
187 $this->object->insertDefinition($position + 1);
188 $this->editQuestion();
189 }
190
191 public function removedefinitions()
192 {
193 $this->writePostData();
194 $position = key($_POST["cmd"]["removedefinitions"]);
195 $this->object->deleteDefinition($position);
196 $this->editQuestion();
197 }
198
199 public function addpairs()
200 {
201 $this->writePostData();
202 $position = key($_POST["cmd"]["addpairs"]);
203 $this->object->insertMatchingPair($position + 1);
204 $this->editQuestion();
205 }
206
207 public function removepairs()
208 {
209 $this->writePostData();
210 $position = key($_POST["cmd"]["removepairs"]);
211 $this->object->deleteMatchingPair($position);
212 $this->editQuestion();
213 }
214
215 public function editQuestion($checkonly = false)
216 {
217 $save = $this->isSaveCommand();
218 $this->getQuestionTemplate();
219
220 include_once("./Services/Form/classes/class.ilPropertyFormGUI.php");
221 $form = new ilPropertyFormGUI();
222 $this->editForm = $form;
223
224 $form->setFormAction($this->ctrl->getFormAction($this));
225 $form->setTitle($this->outQuestionType());
226 $form->setMultipart(true);
227 $form->setTableWidth("100%");
228 $form->setId("matching");
229
230
231 // title, author, description, question, working time (assessment mode)
232 $this->addBasicQuestionFormProperties($form);
234 $this->populateAnswerSpecificFormPart($form);
235 $this->populateTaxonomyFormSection($form);
236 $this->addQuestionFormCommandButtons($form);
237
238 $errors = false;
239 if ($save) {
240 $form->setValuesByPost();
241 $errors = !$form->checkInput();
242 $form->setValuesByPost(); // again, because checkInput now performs the whole stripSlashes handling and we need this if we don't want to have duplication of backslashes
243 if (!$errors && !$this->isValidTermAndDefinitionAmount($form) && !$this->object->getSelfAssessmentEditingMode()) {
244 $errors = true;
245 $terms = $form->getItemByPostVar('terms');
246 $terms->setAlert($this->lng->txt("msg_number_of_terms_too_low"));
247 ilUtil::sendFailure($this->lng->txt('form_input_not_valid'));
248 }
249 if ($errors) {
250 $checkonly = false;
251 }
252 }
253
254 if (!$checkonly) {
255 $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
256 }
257 return $errors;
258 }
259
260 private function isDefImgUploadCommand()
261 {
262 return $this->ctrl->getCmd() == 'uploaddefinitions';
263 }
264
265 private function isTermImgUploadCommand()
266 {
267 return $this->ctrl->getCmd() == 'uploadterms';
268 }
269
278 {
279 $matchingMode = $form->getItemByPostVar('matching_mode')->getValue();
280
281 if ($matchingMode == assMatchingQuestion::MATCHING_MODE_N_ON_N) {
282 return true;
283 }
284
285 $numTerms = count($form->getItemByPostVar('terms')->getValues());
286 $numDefinitions = count($form->getItemByPostVar('definitions')->getValues());
287
288 if ($numTerms >= $numDefinitions) {
289 return true;
290 }
291
292 return false;
293 }
294
296 {
297 // Definitions
298 include_once "./Modules/TestQuestionPool/classes/class.ilMatchingWizardInputGUI.php";
299 $definitions = new ilMatchingWizardInputGUI($this->lng->txt("definitions"), "definitions");
300 if ($this->object->getSelfAssessmentEditingMode()) {
301 $definitions->setHideImages(true);
302 }
303
304 $definitions->setRequired(true);
305 $definitions->setQuestionObject($this->object);
306 $definitions->setTextName($this->lng->txt('definition_text'));
307 $definitions->setImageName($this->lng->txt('definition_image'));
308 include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingDefinition.php";
309 if (!count($this->object->getDefinitions())) {
310 $this->object->addDefinition(new assAnswerMatchingDefinition());
311 }
312 $definitionvalues = $this->object->getDefinitions();
313 $definitions->setValues($definitionvalues);
314 if ($this->isDefImgUploadCommand()) {
315 $definitions->checkInput();
316 }
317 $form->addItem($definitions);
318
319 // Terms
320 include_once "./Modules/TestQuestionPool/classes/class.ilMatchingWizardInputGUI.php";
321 $terms = new ilMatchingWizardInputGUI($this->lng->txt("terms"), "terms");
322 if ($this->object->getSelfAssessmentEditingMode()) {
323 $terms->setHideImages(true);
324 }
325 $terms->setRequired(true);
326 $terms->setQuestionObject($this->object);
327 $terms->setTextName($this->lng->txt('term_text'));
328 $terms->setImageName($this->lng->txt('term_image'));
329 include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingTerm.php";
330 if (!count($this->object->getTerms())) {
331 $this->object->addTerm(new assAnswerMatchingTerm());
332 }
333 $termvalues = $this->object->getTerms();
334 $terms->setValues($termvalues);
335 if ($this->isTermImgUploadCommand()) {
336 $terms->checkInput();
337 }
338 $form->addItem($terms);
339
340 // Matching Pairs
341 include_once "./Modules/TestQuestionPool/classes/class.ilMatchingPairWizardInputGUI.php";
342 $pairs = new ilMatchingPairWizardInputGUI($this->lng->txt('matching_pairs'), 'pairs');
343 $pairs->setRequired(true);
344 $pairs->setTerms($this->object->getTerms());
345 $pairs->setDefinitions($this->object->getDefinitions());
346 include_once "./Modules/TestQuestionPool/classes/class.assAnswerMatchingPair.php";
347 if (count($this->object->getMatchingPairs()) == 0) {
348 $this->object->addMatchingPair(new assAnswerMatchingPair($termvalues[0], $definitionvalues[0], 0));
349 }
350 $pairs->setPairs($this->object->getMatchingPairs());
351 $form->addItem($pairs);
352
353 return $form;
354 }
355
357 {
358 // Edit mode
359 $hidden = new ilHiddenInputGUI("matching_type");
360 $hidden->setValue($matchingtype);
361 $form->addItem($hidden);
362
363 if (!$this->object->getSelfAssessmentEditingMode()) {
364 // shuffle
365 $shuffle = new ilSelectInputGUI($this->lng->txt("shuffle_answers"), "shuffle");
366 $shuffle_options = array(
367 0 => $this->lng->txt("no"),
368 1 => $this->lng->txt("matching_shuffle_terms_definitions"),
369 2 => $this->lng->txt("matching_shuffle_terms"),
370 3 => $this->lng->txt("matching_shuffle_definitions")
371 );
372 $shuffle->setOptions($shuffle_options);
373 $shuffle->setValue($this->object->getShuffle() != null ? $this->object->getShuffle() : 1);
374 $shuffle->setRequired(false);
375 $form->addItem($shuffle);
376
377 $geometry = new ilNumberInputGUI($this->lng->txt("thumb_geometry"), "thumb_geometry");
378 $geometry->setValue($this->object->getThumbGeometry());
379 $geometry->setRequired(true);
380 $geometry->setMaxLength(6);
381 $geometry->setMinValue(20);
382 $geometry->setSize(6);
383 $geometry->setInfo($this->lng->txt("thumb_geometry_info"));
384 $form->addItem($geometry);
385 }
386
387 // Matching Mode
388 $mode = new ilRadioGroupInputGUI($this->lng->txt('qpl_qst_inp_matching_mode'), 'matching_mode');
389 $mode->setRequired(true);
390
391 $modeONEonONE = new ilRadioOption(
392 $this->lng->txt('qpl_qst_inp_matching_mode_one_on_one'),
394 );
395 $mode->addOption($modeONEonONE);
396
397 $modeALLonALL = new ilRadioOption(
398 $this->lng->txt('qpl_qst_inp_matching_mode_all_on_all'),
400 );
401 $mode->addOption($modeALLonALL);
402
403 $mode->setValue($this->object->getMatchingMode());
404
405 $form->addItem($mode);
406 }
407
414 {
415 return true;
416 }
417
431 public function getSolutionOutput(
432 $active_id,
433 $pass = null,
434 $graphicalOutput = false,
435 $result_output = false,
436 $show_question_only = true,
437 $show_feedback = false,
438 $show_correct_solution = false,
439 $show_manual_scoring = false,
440 $show_question_text = true
441 ) {
442 // generate the question output
443 include_once "./Services/UICore/classes/class.ilTemplate.php";
444 $template = new ilTemplate("tpl.il_as_qpl_matching_output_solution.html", true, true, "Modules/TestQuestionPool");
445 $solutiontemplate = new ilTemplate("tpl.il_as_tst_solution_output.html", true, true, "Modules/TestQuestionPool");
446
447 $solutions = array();
448 if (($active_id > 0) && (!$show_correct_solution)) {
449 include_once "./Modules/Test/classes/class.ilObjTest.php";
450 $solutions = $this->object->getSolutionValues($active_id, $pass, !$this->getUseIntermediateSolution());
451 $solution_script .= "";
452 } else {
453 foreach ($this->object->getMaximumScoringMatchingPairs() as $pair) {
454 $solutions[] = array(
455 "value1" => $pair->term->identifier,
456 "value2" => $pair->definition->identifier,
457 'points' => $pair->points
458 );
459 }
460 }
461
462 $i = 0;
463
464 foreach ($solutions as $solution) {
465 $definition = $this->object->getDefinitionWithIdentifier($solution['value2']);
466 $term = $this->object->getTermWithIdentifier($solution['value1']);
467 $points = $solution['points'];
468
469 if (is_object($definition)) {
470 if (strlen($definition->picture)) {
471 if (strlen($definition->text)) {
472 $template->setCurrentBlock('definition_image_text');
473 $template->setVariable("TEXT_DEFINITION", ilUtil::prepareFormOutput($definition->text));
474 $template->parseCurrentBlock();
475 }
476
477 $answerImageSrc = ilWACSignedPath::signFile(
478 $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $definition->picture
479 );
480
481 $template->setCurrentBlock('definition_image');
482 $template->setVariable('ANSWER_IMAGE_URL', $answerImageSrc);
483 $template->setVariable('ANSWER_IMAGE_ALT', (strlen($definition->text)) ? ilUtil::prepareFormOutput($definition->text) : ilUtil::prepareFormOutput($definition->picture));
484 $template->setVariable('ANSWER_IMAGE_TITLE', (strlen($definition->text)) ? ilUtil::prepareFormOutput($definition->text) : ilUtil::prepareFormOutput($definition->picture));
485 $template->setVariable('URL_PREVIEW', $this->object->getImagePathWeb() . $definition->picture);
486 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
487 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
488 $template->parseCurrentBlock();
489 } else {
490 $template->setCurrentBlock('definition_text');
491 $template->setVariable("DEFINITION", $this->object->prepareTextareaOutput($definition->text, true));
492 $template->parseCurrentBlock();
493 }
494 }
495 if (is_object($term)) {
496 if (strlen($term->picture)) {
497 if (strlen($term->text)) {
498 $template->setCurrentBlock('term_image_text');
499 $template->setVariable("TEXT_TERM", ilUtil::prepareFormOutput($term->text));
500 $template->parseCurrentBlock();
501 }
502
503 $answerImageSrc = ilWACSignedPath::signFile(
504 $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $term->picture
505 );
506
507 $template->setCurrentBlock('term_image');
508 $template->setVariable('ANSWER_IMAGE_URL', $answerImageSrc);
509 $template->setVariable('ANSWER_IMAGE_ALT', (strlen($term->text)) ? ilUtil::prepareFormOutput($term->text) : ilUtil::prepareFormOutput($term->picture));
510 $template->setVariable('ANSWER_IMAGE_TITLE', (strlen($term->text)) ? ilUtil::prepareFormOutput($term->text) : ilUtil::prepareFormOutput($term->picture));
511 $template->setVariable('URL_PREVIEW', $this->object->getImagePathWeb() . $term->picture);
512 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
513 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
514 $template->parseCurrentBlock();
515 } else {
516 $template->setCurrentBlock('term_text');
517 $template->setVariable("TERM", $this->object->prepareTextareaOutput($term->text, true));
518 $template->parseCurrentBlock();
519 }
520 $i++;
521 }
522 if (($active_id > 0) && (!$show_correct_solution)) {
523 if ($graphicalOutput) {
524 // output of ok/not ok icons for user entered solutions
525 $ok = false;
526 foreach ($this->object->getMatchingPairs() as $pair) {
527 if ($this->isCorrectMatching($pair, $definition, $term)) {
528 $ok = true;
529 }
530 }
531
532 if ($ok) {
533 $template->setCurrentBlock("icon_ok");
534 $template->setVariable("ICON_OK", ilUtil::getImagePath("icon_ok.svg"));
535 $template->setVariable("TEXT_OK", $this->lng->txt("answer_is_right"));
536 $template->parseCurrentBlock();
537 } else {
538 $template->setCurrentBlock("icon_ok");
539 $template->setVariable("ICON_NOT_OK", ilUtil::getImagePath("icon_not_ok.svg"));
540 $template->setVariable("TEXT_NOT_OK", $this->lng->txt("answer_is_wrong"));
541 $template->parseCurrentBlock();
542 }
543 }
544 }
545
546 if ($result_output) {
547 $resulttext = ($points == 1) ? "(%s " . $this->lng->txt("point") . ")" : "(%s " . $this->lng->txt("points") . ")";
548 $template->setCurrentBlock("result_output");
549 $template->setVariable("RESULT_OUTPUT", sprintf($resulttext, $points));
550 $template->parseCurrentBlock();
551 }
552
553 $template->setCurrentBlock("row");
554 $template->setVariable("TEXT_MATCHES", $this->lng->txt("matches"));
555 $template->parseCurrentBlock();
556 }
557
558 $questiontext = $this->object->getQuestion();
559 if ($show_question_text == true) {
560 $template->setVariable("QUESTIONTEXT", $this->object->prepareTextareaOutput($questiontext, true));
561 }
562
563 $questionoutput = $template->get();
564
565 $feedback = '';
566 if ($show_feedback) {
567 if (!$this->isTestPresentationContext()) {
568 $fb = $this->getGenericFeedbackOutput($active_id, $pass);
569 $feedback .= strlen($fb) ? $fb : '';
570 }
571
572 $fb = $this->getSpecificFeedbackOutput(array());
573 $feedback .= strlen($fb) ? $fb : '';
574 }
575 if (strlen($feedback)) {
576 $cssClass = (
577 $this->hasCorrectSolution($active_id, $pass) ?
579 );
580
581 $solutiontemplate->setVariable("ILC_FB_CSS_CLASS", $cssClass);
582 $solutiontemplate->setVariable("FEEDBACK", $this->object->prepareTextareaOutput($feedback, true));
583 }
584
585 $solutiontemplate->setVariable("SOLUTION_OUTPUT", $questionoutput);
586
587 $solutionoutput = $solutiontemplate->get();
588 if (!$show_question_only) {
589 // get page object output
590 $solutionoutput = $this->getILIASPage($solutionoutput);
591 }
592 return $solutionoutput;
593 }
594
595 public function getPreview($show_question_only = false, $showInlineFeedback = false)
596 {
597 $solutions = is_object($this->getPreviewSession()) ? (array) $this->getPreviewSession()->getParticipantsSolution() : array();
598
599 global $DIC; /* @var ILIAS\DI\Container $DIC */
600 if ($DIC['ilBrowser']->isMobile() || $DIC['ilBrowser']->isIpad()) {
601 require_once 'Services/jQuery/classes/class.iljQueryUtil.php';
604 $this->tpl->addJavaScript('./libs/bower/bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.min.js');
605 }
606 $this->tpl->addJavaScript('Modules/TestQuestionPool/js/ilMatchingQuestion.js');
607 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_javascript.css', 'Modules/TestQuestionPool'));
608
609 $template = new ilTemplate("tpl.il_as_qpl_matching_output.html", true, true, "Modules/TestQuestionPool");
610
611 foreach ($solutions as $defId => $terms) {
612 foreach ($terms as $termId) {
613 $template->setCurrentBlock("matching_data");
614 $template->setVariable("DEFINITION_ID", $defId);
615 $template->setVariable("TERM_ID", $termId);
616 $template->parseCurrentBlock();
617 }
618 }
619
620 // shuffle output
621 $terms = $this->object->getTerms();
622 $definitions = $this->object->getDefinitions();
623 switch ($this->object->getShuffle()) {
624 case 1:
625 $seed = $this->object->getShuffler()->getSeed();
626 $this->object->getShuffler()->setSeed($seed . '1');
627 $terms = $this->object->getShuffler()->shuffle($terms);
628 $this->object->getShuffler()->setSeed($seed . '2');
629 $definitions = $this->object->getShuffler()->shuffle($definitions);
630 $this->object->getShuffler()->setSeed($seed);
631 break;
632 case 2:
633 $terms = $this->object->getShuffler()->shuffle($terms);
634 break;
635 case 3:
636 $definitions = $this->object->getShuffler()->shuffle($definitions);
637 break;
638 }
639
640 // create definitions
641 $counter = 0;
642 foreach ($definitions as $definition) {
643 if (strlen($definition->picture)) {
644 $template->setCurrentBlock("definition_picture");
645 $template->setVariable("DEFINITION_ID", $definition->identifier);
646 $template->setVariable("IMAGE_HREF", $this->object->getImagePathWeb() . $definition->picture);
647 $thumbweb = $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $definition->picture;
648 $thumb = $this->object->getImagePath() . $this->object->getThumbPrefix() . $definition->picture;
649 if (!@file_exists($thumb)) {
650 $this->object->rebuildThumbnails();
651 }
652 $template->setVariable("THUMBNAIL_HREF", $thumbweb);
653 $template->setVariable("THUMB_ALT", $this->lng->txt("image"));
654 $template->setVariable("THUMB_TITLE", $this->lng->txt("image"));
655 $template->setVariable("TEXT_DEFINITION", (strlen($definition->text)) ? $this->object->prepareTextareaOutput($definition->text, true, true) : '');
656 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
657 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
658 $template->parseCurrentBlock();
659 } else {
660 $template->setCurrentBlock("definition_text");
661 $template->setVariable("DEFINITION", $this->object->prepareTextareaOutput($definition->text, true, true));
662 $template->parseCurrentBlock();
663 }
664
665 $template->setCurrentBlock("droparea");
666 $template->setVariable("ID_DROPAREA", $definition->identifier);
667 $template->setVariable("QUESTION_ID", $this->object->getId());
668 $template->parseCurrentBlock();
669
670 $template->setCurrentBlock("definition_data");
671 $template->setVariable("DEFINITION_ID", $definition->identifier);
672 $template->parseCurrentBlock();
673 }
674
675 // create terms
676 $counter = 0;
677 foreach ($terms as $term) {
678 if (strlen($term->picture)) {
679 $template->setCurrentBlock("term_picture");
680 $template->setVariable("TERM_ID", $term->identifier);
681 $template->setVariable("IMAGE_HREF", $this->object->getImagePathWeb() . $term->picture);
682 $thumbweb = $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $term->picture;
683 $thumb = $this->object->getImagePath() . $this->object->getThumbPrefix() . $term->picture;
684 if (!@file_exists($thumb)) {
685 $this->object->rebuildThumbnails();
686 }
687 $template->setVariable("THUMBNAIL_HREF", $thumbweb);
688 $template->setVariable("THUMB_ALT", $this->lng->txt("image"));
689 $template->setVariable("THUMB_TITLE", $this->lng->txt("image"));
690 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
691 $template->setVariable("TEXT_TERM", (strlen($term->text)) ? $this->object->prepareTextareaOutput($term->text, true, true) : '');
692 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
693 $template->parseCurrentBlock();
694 } else {
695 $template->setCurrentBlock("term_text");
696 $template->setVariable("TERM_TEXT", $this->object->prepareTextareaOutput($term->text, true, true));
697 $template->parseCurrentBlock();
698 }
699 $template->setCurrentBlock("draggable");
700 $template->setVariable("ID_DRAGGABLE", $term->identifier);
701 $template->parseCurrentBlock();
702
703 $template->setCurrentBlock("term_data");
704 $template->setVariable("TERM_ID", $term->identifier);
705 $template->parseCurrentBlock();
706 }
707
708 $template->setVariable('MATCHING_MODE', $this->object->getMatchingMode());
709
710 $template->setVariable("RESET_BUTTON", $this->lng->txt("reset_terms"));
711
712 $template->setVariable("QUESTIONTEXT", $this->object->prepareTextareaOutput($this->object->getQuestion(), true));
713
714 $questionoutput = $template->get();
715
716 if (!$show_question_only) {
717 // get page object output
718 $questionoutput = $this->getILIASPage($questionoutput);
719 }
720
721 return $questionoutput;
722 }
723
729 protected function sortDefinitionsBySolution(array $solution, array $definitions)
730 {
731 $neworder = array();
732 $handled_defintions = array();
733 foreach ($solution as $solution_values) {
734 $id = $solution_values['value2'];
735 if (!isset($handled_defintions[$id])) {
736 $neworder[] = $this->object->getDefinitionWithIdentifier($id);
737 $handled_defintions[$id] = $id;
738 }
739 }
740
741 foreach ($definitions as $definition) {
745 if (!isset($handled_defintions[$definition->identifier])) {
746 $neworder[] = $definition;
747 }
748 }
749
750 return $neworder;
751 }
752
754 {
755 global $DIC; /* @var ILIAS\DI\Container $DIC */
756
757 $files = array();
758
759 if ($DIC['ilBrowser']->isMobile() || $DIC['ilBrowser']->isIpad()) {
760 $files[] = './libs/bower/bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.min.js';
761 }
762
763 $files[] = 'Modules/TestQuestionPool/js/ilMatchingQuestion.js';
764
765 return $files;
766 }
767
768 // hey: prevPassSolutions - pass will be always available from now on
769 public function getTestOutput($active_id, $pass, $is_postponed = false, $user_post_solution = false, $inlineFeedback = false)
770 // hey.
771 {
772 global $DIC; /* @var ILIAS\DI\Container $DIC */
773 if ($DIC['ilBrowser']->isMobile() || $DIC['ilBrowser']->isIpad()) {
774 require_once 'Services/jQuery/classes/class.iljQueryUtil.php';
777 $this->tpl->addJavaScript('./libs/bower/bower_components/jqueryui-touch-punch/jquery.ui.touch-punch.min.js');
778 }
779 $this->tpl->addJavaScript('Modules/TestQuestionPool/js/ilMatchingQuestion.js');
780 $this->tpl->addCss(ilUtil::getStyleSheetLocation('output', 'test_javascript.css', 'Modules/TestQuestionPool'));
781
782 $template = new ilTemplate("tpl.il_as_qpl_matching_output.html", true, true, "Modules/TestQuestionPool");
783
784 if ($active_id) {
785 // hey: prevPassSolutions - obsolete due to central check
786 #$solutions = NULL;
787 #include_once "./Modules/Test/classes/class.ilObjTest.php";
788 #if (!ilObjTest::_getUsePreviousAnswers($active_id, true))
789 #{
790 # if (is_null($pass)) $pass = ilObjTest::_getPass($active_id);
791 #}
792 // hey.
793 if (is_array($user_post_solution)) {
794 $solutions = array();
795 foreach ($user_post_solution['matching'][$this->object->getId()] as $definition => $term) {
796 array_push($solutions, array("value1" => $term, "value2" => $definition));
797 }
798 } else {
799 // hey: prevPassSolutions - obsolete due to central check
800 $solutions = $this->object->getTestOutputSolutions($active_id, $pass);
801 // hey.
802 }
803
804 $counter = 0;
805 foreach ($solutions as $idx => $solution_value) {
806 if (($solution_value["value2"] > -1) && ($solution_value["value1"] > -1)) {
807 $template->setCurrentBlock("matching_data");
808 $template->setVariable("TERM_ID", $solution_value["value1"]);
809 $template->setVariable("DEFINITION_ID", $solution_value["value2"]);
810 $template->parseCurrentBlock();
811 }
812
813 $counter++;
814 }
815 }
816
817 $terms = $this->object->getTerms();
818 $definitions = $this->object->getDefinitions();
819 switch ($this->object->getShuffle()) {
820 case 1:
821 $seed = $this->object->getShuffler()->getSeed();
822 $this->object->getShuffler()->setSeed($seed . '1');
823 $terms = $this->object->getShuffler()->shuffle($terms);
824 if (count($solutions)) {
825 $definitions = $this->sortDefinitionsBySolution($solutions, $definitions);
826 } else {
827 $this->object->getShuffler()->setSeed($seed . '2');
828 $definitions = $this->object->getShuffler()->shuffle($definitions);
829 }
830 $this->object->getShuffler()->setSeed($seed);
831 break;
832 case 2:
833 $terms = $this->object->getShuffler()->shuffle($terms);
834 break;
835 case 3:
836 if (count($solutions)) {
837 $definitions = $this->sortDefinitionsBySolution($solutions, $definitions);
838 } else {
839 $definitions = $this->object->getShuffler()->shuffle($definitions);
840 }
841 break;
842 }
843
844 // create definitions
845 $counter = 0;
846 foreach ($definitions as $definition) {
847 if (strlen($definition->picture)) {
848 $template->setCurrentBlock("definition_picture");
849 $template->setVariable("DEFINITION_ID", $definition->identifier);
850 $template->setVariable("IMAGE_HREF", $this->object->getImagePathWeb() . $definition->picture);
851 $thumbweb = $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $definition->picture;
852 $thumb = $this->object->getImagePath() . $this->object->getThumbPrefix() . $definition->picture;
853 if (!@file_exists($thumb)) {
854 $this->object->rebuildThumbnails();
855 }
856 $template->setVariable("THUMBNAIL_HREF", $thumbweb);
857 $template->setVariable("THUMB_ALT", $this->lng->txt("image"));
858 $template->setVariable("THUMB_TITLE", $this->lng->txt("image"));
859 $template->setVariable("TEXT_DEFINITION", (strlen($definition->text)) ? $this->object->prepareTextareaOutput($definition->text, true, true) : '');
860 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
861 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
862 $template->parseCurrentBlock();
863 } else {
864 $template->setCurrentBlock("definition_text");
865 $template->setVariable("DEFINITION", $this->object->prepareTextareaOutput($definition->text, true, true));
866 $template->parseCurrentBlock();
867 }
868
869 $template->setCurrentBlock("droparea");
870 $template->setVariable("ID_DROPAREA", $definition->identifier);
871 $template->setVariable("QUESTION_ID", $this->object->getId());
872 $template->parseCurrentBlock();
873
874 $template->setCurrentBlock("definition_data");
875 $template->setVariable("DEFINITION_ID", $definition->identifier);
876 $template->parseCurrentBlock();
877 }
878
879 // create terms
880 $counter = 0;
881 foreach ($terms as $term) {
882 if (strlen($term->picture)) {
883 $template->setCurrentBlock("term_picture");
884 $template->setVariable("TERM_ID", $term->identifier);
885 $template->setVariable("IMAGE_HREF", $this->object->getImagePathWeb() . $term->picture);
886 $thumbweb = $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $term->picture;
887 $thumb = $this->object->getImagePath() . $this->object->getThumbPrefix() . $term->picture;
888 if (!@file_exists($thumb)) {
889 $this->object->rebuildThumbnails();
890 }
891 $template->setVariable("THUMBNAIL_HREF", $thumbweb);
892 $template->setVariable("THUMB_ALT", $this->lng->txt("image"));
893 $template->setVariable("THUMB_TITLE", $this->lng->txt("image"));
894 $template->setVariable("TEXT_PREVIEW", $this->lng->txt('preview'));
895 $template->setVariable("TEXT_TERM", (strlen($term->text)) ? $this->object->prepareTextareaOutput($term->text, true, true) : '');
896 $template->setVariable("IMG_PREVIEW", ilUtil::getImagePath('enlarge.svg'));
897 $template->parseCurrentBlock();
898 } else {
899 $template->setCurrentBlock("term_text");
900 $template->setVariable("TERM_TEXT", $this->object->prepareTextareaOutput($term->text, true, true));
901 $template->parseCurrentBlock();
902 }
903 $template->setCurrentBlock("draggable");
904 $template->setVariable("ID_DRAGGABLE", $term->identifier);
905 $template->parseCurrentBlock();
906
907 $template->setCurrentBlock('term_data');
908 $template->setVariable('TERM_ID', $term->identifier);
909 $template->parseCurrentBlock();
910 }
911
912 $template->setVariable('MATCHING_MODE', $this->object->getMatchingMode());
913
914 $template->setVariable("RESET_BUTTON", $this->lng->txt("reset_terms"));
915
916 $template->setVariable("QUESTIONTEXT", $this->object->prepareTextareaOutput($this->object->getQuestion(), true));
917
918 return $this->outQuestionPage("", $is_postponed, $active_id, $template->get());
919 }
920
924 public function checkInput()
925 {
926 if ((!$_POST["title"]) or (!$_POST["author"]) or (!$_POST["question"])) {
927 return false;
928 }
929 return true;
930 }
931
939 public function setQuestionTabs()
940 {
941 global $DIC;
942 $rbacsystem = $DIC['rbacsystem'];
943 $ilTabs = $DIC['ilTabs'];
944
945 $ilTabs->clearTargets();
946
947 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $_GET["q_id"]);
948 include_once "./Modules/TestQuestionPool/classes/class.assQuestion.php";
949 $q_type = $this->object->getQuestionType();
950
951 if (strlen($q_type)) {
952 $classname = $q_type . "GUI";
953 $this->ctrl->setParameterByClass(strtolower($classname), "sel_question_types", $q_type);
954 $this->ctrl->setParameterByClass(strtolower($classname), "q_id", $_GET["q_id"]);
955 }
956
957 if ($_GET["q_id"]) {
958 if ($rbacsystem->checkAccess('write', $_GET["ref_id"])) {
959 // edit page
960 $ilTabs->addTarget(
961 "edit_page",
962 $this->ctrl->getLinkTargetByClass("ilAssQuestionPageGUI", "edit"),
963 array("edit", "insert", "exec_pg"),
964 "",
965 "",
966 $force_active
967 );
968 }
969
970 $this->addTab_QuestionPreview($ilTabs);
971 }
972
973 $force_active = false;
974 if ($rbacsystem->checkAccess('write', $_GET["ref_id"])) {
975 $url = "";
976 if ($classname) {
977 $url = $this->ctrl->getLinkTargetByClass($classname, "editQuestion");
978 }
979 // edit question properties
980 $ilTabs->addTarget(
981 "edit_question",
982 $url,
983 array("editQuestion", "save", "saveEdit", "removeimageterms", "uploadterms", "removeimagedefinitions", "uploaddefinitions",
984 "addpairs", "removepairs", "addterms", "removeterms", "adddefinitions", "removedefinitions", "originalSyncForm"),
985 $classname,
986 "",
987 $force_active
988 );
989 }
990
991 // add tab for question feedback within common class assQuestionGUI
992 $this->addTab_QuestionFeedback($ilTabs);
993
994 // add tab for question hint within common class assQuestionGUI
995 $this->addTab_QuestionHints($ilTabs);
996
997 // add tab for question's suggested solution within common class assQuestionGUI
998 $this->addTab_SuggestedSolution($ilTabs, $classname);
999
1000 // Assessment of questions sub menu entry
1001 if ($_GET["q_id"]) {
1002 $ilTabs->addTarget(
1003 "statistics",
1004 $this->ctrl->getLinkTargetByClass($classname, "assessment"),
1005 array("assessment"),
1006 $classname,
1007 ""
1008 );
1009 }
1010
1011 $this->addBackTab($ilTabs);
1012 }
1013
1014 public function getSpecificFeedbackOutput($userSolution)
1015 {
1016 $matches = array_values($this->object->matchingpairs);
1017
1018 if (!$this->object->feedbackOBJ->specificAnswerFeedbackExists()) {
1019 return '';
1020 }
1021
1022 $feedback = '<table class="test_specific_feedback"><tbody>';
1023
1024 foreach ($matches as $idx => $ans) {
1025 if (!isset($userSolution[$ans->definition->identifier])) {
1026 continue;
1027 }
1028
1029 if (!is_array($userSolution[$ans->definition->identifier])) {
1030 continue;
1031 }
1032
1033 if (!in_array($ans->term->identifier, $userSolution[$ans->definition->identifier])) {
1034 continue;
1035 }
1036
1037 $fb = $this->object->feedbackOBJ->getSpecificAnswerFeedbackTestPresentation(
1038 $this->object->getId(),
1039 0,
1040 $idx
1041 );
1042 $feedback .= '<tr><td>"' . $ans->definition->text . '"&nbsp;' . $this->lng->txt("matches") . '&nbsp;"';
1043 $feedback .= $ans->term->text . '"</td><td>';
1044 $feedback .= $fb . '</td> </tr>';
1045 }
1046
1047 $feedback .= '</tbody></table>';
1048 return $this->object->prepareTextareaOutput($feedback, true);
1049 }
1050
1061 {
1062 return array();
1063 }
1064
1075 {
1076 return array();
1077 }
1078
1087 public function getAggregatedAnswersView($relevant_answers)
1088 {
1089 return ''; //print_r($relevant_answers,true);
1090 }
1091
1092 private function isCorrectMatching($pair, $definition, $term)
1093 {
1094 if (!($pair->points > 0)) {
1095 return false;
1096 }
1097
1098 if (!is_object($term)) {
1099 return false;
1100 }
1101
1102 if ($pair->definition->identifier != $definition->identifier) {
1103 return false;
1104 }
1105
1106 if ($pair->term->identifier != $term->identifier) {
1107 return false;
1108 }
1109
1110 return true;
1111 }
1112
1113 protected function getAnswerStatisticImageHtml($picture)
1114 {
1115 $thumbweb = $this->object->getImagePathWeb() . $this->object->getThumbPrefix() . $picture;
1116 return '<img src="' . $thumbweb . '" alt="' . $picture . '" title="' . $picture . '"/>';
1117 }
1118
1119 protected function getAnswerStatisticMatchingElemHtml($elem)
1120 {
1121 $html = '';
1122
1123 if (strlen($elem->text)) {
1124 $html .= $elem->text;
1125 }
1126
1127 if (strlen($elem->picture)) {
1128 $html .= $this->getAnswerStatisticImageHtml($elem->picture);
1129 }
1130
1131 return $html;
1132 }
1133
1134 public function getAnswersFrequency($relevantAnswers, $questionIndex)
1135 {
1136 $answersByActiveAndPass = array();
1137
1138 foreach ($relevantAnswers as $row) {
1139 $key = $row['active_fi'] . ':' . $row['pass'];
1140
1141 if (!isset($answersByActiveAndPass[$key])) {
1142 $answersByActiveAndPass[$key] = array();
1143 }
1144
1145 $answersByActiveAndPass[$key][$row['value1']] = $row['value2'];
1146 }
1147
1148 $answers = array();
1149
1150 foreach ($answersByActiveAndPass as $key => $matchingPairs) {
1151 foreach ($matchingPairs as $termId => $defId) {
1152 $hash = md5($termId . ':' . $defId);
1153
1154 if (!isset($answers[$hash])) {
1155 $termHtml = $this->getAnswerStatisticMatchingElemHtml(
1156 $this->object->getTermWithIdentifier($termId)
1157 );
1158
1159 $defHtml = $this->getAnswerStatisticMatchingElemHtml(
1160 $this->object->getDefinitionWithIdentifier($defId)
1161 );
1162
1163 $answers[$hash] = array(
1164 'answer' => $termHtml . $defHtml,
1165 'term' => $termHtml,
1166 'definition' => $defHtml,
1167 'frequency' => 0
1168 );
1169 }
1170
1171 $answers[$hash]['frequency']++;
1172 }
1173 }
1174
1175 return $answers;
1176 }
1177
1185 public function getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex)
1186 {
1187 require_once 'Modules/TestQuestionPool/classes/tables/class.ilMatchingQuestionAnswerFreqStatTableGUI.php';
1188
1189 $table = new ilMatchingQuestionAnswerFreqStatTableGUI($parentGui, $parentCmd, $this->object);
1190 $table->setQuestionIndex($questionIndex);
1191 $table->setData($this->getAnswersFrequency($relevantAnswers, $questionIndex));
1192 $table->initColumns();
1193
1194 return $table;
1195 }
1196
1198 {
1199 require_once 'Modules/TestQuestionPool/classes/forms/class.ilAssMatchingPairCorrectionsInputGUI.php';
1200 $pairs = new ilAssMatchingPairCorrectionsInputGUI($this->lng->txt('matching_pairs'), 'pairs');
1201 $pairs->setRequired(true);
1202 $pairs->setTerms($this->object->getTerms());
1203 $pairs->setDefinitions($this->object->getDefinitions());
1204 $pairs->setPairs($this->object->getMatchingPairs());
1205 $form->addItem($pairs);
1206 }
1207
1212 {
1213 $pairs = $form->getItemByPostVar('pairs')->getPairs();
1214
1215 foreach ($this->object->getMatchingPairs() as $idx => $matchingPair) {
1216 $matchingPair->points = (float) $pairs[$idx]->points;
1217 }
1218 }
1219}
$filename
Definition: buildRTE.php:89
$_GET["client_id"]
$_POST["username"]
An exception for terminatinating execution or to throw for unit testing.
Class for matching question definitions.
Class for matching question pairs.
Class for matching question terms.
Matching question GUI representation.
isValidTermAndDefinitionAmount(ilPropertyFormGUI $form)
for mode 1:1 terms count must not be less than definitions count for mode n:n this limitation is canc...
populateAnswerSpecificFormPart(\ilPropertyFormGUI $form)
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
writePostData($always=false)
{Evaluates a posted edit form and writes the form data in the question object.integer A positive valu...
supportsIntermediateSolutionOutput()
Question type specific support of intermediate solution output The function getSolutionOutput respect...
getAnswersFrequency($relevantAnswers, $questionIndex)
getSolutionOutput( $active_id, $pass=null, $graphicalOutput=false, $result_output=false, $show_question_only=true, $show_feedback=false, $show_correct_solution=false, $show_manual_scoring=false, $show_question_text=true)
Get the question solution output.
isCorrectMatching($pair, $definition, $term)
getAfterParticipationSuppressionAnswerPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
getPreview($show_question_only=false, $showInlineFeedback=false)
getAfterParticipationSuppressionQuestionPostVars()
Returns a list of postvars which will be suppressed in the form output when used in scoring adjustmen...
writeQuestionSpecificPostData(ilPropertyFormGUI $form)
Extracts the question specific values from $_POST and applies them to the data object.
getSpecificFeedbackOutput($userSolution)
Returns the answer specific feedback for the question.
writeAnswerSpecificPostData(ilPropertyFormGUI $form)
Extracts the answer specific values from $_POST and applies them to the data object.
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
setQuestionTabs()
Sets the ILIAS tabs for this question type.
getAggregatedAnswersView($relevant_answers)
Returns an html string containing a question specific representation of the answers so far given in t...
getTestOutput($active_id, $pass, $is_postponed=false, $user_post_solution=false, $inlineFeedback=false)
getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex)
populateQuestionSpecificFormPart(\ilPropertyFormGUI $form)
__construct($id=-1)
assMatchingQuestionGUI constructor
Class for matching questions.
Basic GUI class for assessment questions.
populateTaxonomyFormSection(ilPropertyFormGUI $form)
setErrorMessage($errormessage)
addTab_QuestionHints(ilTabsGUI $tabs)
adds the hints tab to ilTabsGUI
addQuestionFormCommandButtons($form)
Add the command buttons of a question properties form.
getILIASPage($html="")
Returns the ILIAS Page around a question.
getQuestionTemplate()
get question template
addTab_SuggestedSolution(ilTabsGUI $tabs, $classname)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
output question page
getUseIntermediateSolution()
Get if intermediate solution should be used for solution output.
addBackTab(ilTabsGUI $ilTabs)
hasCorrectSolution($activeId, $passIndex)
addTab_QuestionFeedback(ilTabsGUI $tabs)
adds the feedback tab to ilTabsGUI
addBasicQuestionFormProperties($form)
Add basic question form properties: assessment: title, author, description, question,...
addTab_QuestionPreview(ilTabsGUI $tabsGUI)
getGenericFeedbackOutput($active_id, $pass)
Returns the answer specific feedback for the question.
This class represents a hidden form property in a property form.
This class represents a key value pair wizard property in a property form.
This class represents a single choice wizard property in a property form.
This class represents a number property in a property form.
This class represents a property form user interface.
addItem($a_item)
Add Item (Property, SectionHeader).
getItemByPostVar($a_post_var)
Get Item by POST variable.
This class represents a property in a property form.
This class represents an option in a radio group.
This class represents a selection list property in a property form.
special template class to simplify handling of ITX/PEAR
static getStyleSheetLocation($mode="output", $a_css_name="", $a_css_location="")
get full style sheet file name (path inclusive) of current user
static sendFailure($a_info="", $a_keep=false)
Send Failure Message to Screen.
static getImagePath($img, $module_path="", $mode="output", $offline=false)
get image path (for images located in a template directory)
static prepareFormOutput($a_str, $a_strip=false)
prepares string output for html forms @access public
static signFile($path_to_file)
static initjQuery(ilGlobalTemplateInterface $a_tpl=null)
inits and adds the jQuery JS-File to the global or a passed template
static initjQueryUI($a_tpl=null)
inits and adds the jQuery-UI JS-File to the global template (see included_components....
Interface ilGuiAnswerScoringAdjustable.
Interface ilGuiQuestionScoringAdjustable.
if($format !==null) $name
Definition: metadata.php:230
$index
Definition: metadata.php:128
$i
Definition: metadata.php:24
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$url
$errors
$DIC
Definition: xapitoken.php:46