ILIAS  release_8 Revision v8.24
class.assQuestionGUI.php
Go to the documentation of this file.
1<?php
2
29abstract class assQuestionGUI
30{
31 public const FORM_MODE_EDIT = 'edit';
32 public const FORM_MODE_ADJUST = 'adjust';
33
34 public const FORM_ENCODING_URLENCODE = 'application/x-www-form-urlencoded';
35 public const FORM_ENCODING_MULTIPART = 'multipart/form-data';
36
37 protected const SUGGESTED_SOLUTION_COMMANDS = [
38 'cancelSuggestedSolution',
39 'saveSuggestedSolution',
40 'suggestedsolution'
41 ];
42
43 public const CORRECTNESS_NOT_OK = 0;
44 public const CORRECTNESS_MOSTLY_OK = 1;
45 public const CORRECTNESS_OK = 2;
46
47 protected const HAS_SPECIAL_QUESTION_COMMANDS = false;
48
54 public const ALLOWED_PLAIN_TEXT_TAGS = "<em>, <strong>";
55
56 public const SESSION_PREVIEW_DATA_BASE_INDEX = 'ilAssQuestionPreviewAnswers';
57 private $ui;
63
64 private $tree;
67
68 protected \ILIAS\Notes\GUIService $notes_gui;
69
70 protected ilCtrl $ctrl;
71 private array $new_id_listeners = array();
72 private int $new_id_listener_cnt = 0;
73
76
80
81 public $error;
82 public string $errormessage;
83
85 public int $sequence_no;
86
88 public int $question_count;
89
90 private $taxonomyIds = array();
91
92 private $targetGuiClass = null;
93
94 private string $questionActionCmd = 'handleQuestionAction';
95
97
99
100 public const PRESENTATION_CONTEXT_TEST = 'pContextTest';
101 public const PRESENTATION_CONTEXT_RESULTS = 'pContextResults';
102
103 private ?string $presentationContext = null;
104
105 public const RENDER_PURPOSE_PLAYBACK = 'renderPurposePlayback';
106 public const RENDER_PURPOSE_DEMOPLAY = 'renderPurposeDemoplay';
107 public const RENDER_PURPOSE_PREVIEW = 'renderPurposePreview';
108 public const RENDER_PURPOSE_PRINT_PDF = 'renderPurposePrintPdf';
109 public const RENDER_PURPOSE_INPUT_VALUE = 'renderPurposeInputValue';
110
112
113 public const EDIT_CONTEXT_AUTHORING = 'authoring';
114 public const EDIT_CONTEXT_ADJUSTMENT = 'adjustment';
115
117
118 private bool $previousSolutionPrefilled = false;
119
121 protected \ILIAS\TestQuestionPool\InternalRequestService $request;
122
123 public function __construct()
124 {
126 global $DIC;
127 $this->lng = $DIC['lng'];
128 $this->tpl = $DIC['tpl'];
129 $this->ctrl = $DIC['ilCtrl'];
130 $this->ui = $DIC->ui();
131 $this->ilObjDataCache = $DIC['ilObjDataCache'];
132 $this->access = $DIC->access();
133 $this->ilHelp = $DIC['ilHelp'];
134 $this->ilUser = $DIC['ilUser'];
135 $this->ilTabs = $DIC['ilTabs'];
136 $this->rbacsystem = $DIC['rbacsystem'];
137 $this->request = $DIC->testQuestionPool()->internal()->request();
138 $this->tree = $DIC['tree'];
139 $this->ilDB = $DIC->database();
140 $this->component_repository = $DIC['component.repository'];
141 $this->ctrl->saveParameter($this, "q_id");
142 $this->ctrl->saveParameter($this, "prev_qid");
143 $this->ctrl->saveParameter($this, "calling_test");
144 $this->ctrl->saveParameter($this, "calling_consumer");
145 $this->ctrl->saveParameter($this, "consumer_context");
146 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'test_express_mode');
147 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'calling_consumer');
148 $this->ctrl->saveParameterByClass('ilAssQuestionPageGUI', 'consumer_context');
149 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'test_express_mode');
150 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'calling_consumer');
151 $this->ctrl->saveParameterByClass('ilobjquestionpoolgui', 'consumer_context');
152
153 $this->errormessage = $this->lng->txt("fill_out_all_required_fields");
154 $this->notes_gui = $DIC->notes()->gui();
155 }
156
157 public function hasInlineFeedback(): bool
158 {
159 return false;
160 }
161
162 public function addHeaderAction(): void
163 {
164 }
165
166 public function redrawHeaderAction(): void
167 {
168 echo $this->getHeaderAction() . $this->ui->mainTemplate()->getOnLoadCodeForAsynch();
169 exit;
170 }
171
172 public function getHeaderAction(): string
173 {
174 $parentObjType = $this->ilObjDataCache->lookupType($this->object->getObjId());
175
176 $dispatcher = new ilCommonActionDispatcherGUI(
178 $this->access,
179 $parentObjType,
180 $this->request->getRefId(),
181 $this->object->getObjId()
182 );
183
184 $dispatcher->setSubObject("quest", $this->object->getId());
185
186 $ha = $dispatcher->initHeaderAction();
187 $ha->enableComments(true, false);
188
189 return $ha->getHeaderAction($this->ui->mainTemplate());
190 }
191
192 public function getNotesHTML(): string
193 {
194 $notesGUI = new ilNoteGUI($this->object->getObjId(), $this->object->getId(), 'quest');
195 $notesGUI->enablePublicNotes(true);
196 $notesGUI->enablePublicNotesDeletion(true);
197
198 return $notesGUI->getCommentsHTML();
199 }
200
201 public function executeCommand()
202 {
203 $this->ilHelp->setScreenIdComponent('qpl');
204
205 $next_class = $this->ctrl->getNextClass($this);
206
207 switch ($next_class) {
208 case 'ilformpropertydispatchgui':
209 $form = $this->buildEditForm();
210 $form_prop_dispatch = new ilFormPropertyDispatchGUI();
211 $form_prop_dispatch->setItem($form->getItemByPostVar(ilUtil::stripSlashes($this->request->raw('postvar'))));
212 $this->ctrl->forwardCommand($form_prop_dispatch);
213 break;
214
215 default:
216 $cmd = $this->ctrl->getCmd('editQuestion');
217
218 if (in_array($cmd, self::SUGGESTED_SOLUTION_COMMANDS)) {
219 $this->suggestedsolution();
220 return;
221 }
222 if (in_array($cmd, ['save', 'saveReturn', 'editQuestion'])) {
224 $this->$cmd();
225 return;
226 }
227 if (method_exists($this, $cmd)) {
228 $this->$cmd();
229 return;
230 }
231
232 if ($this->hasSpecialQuestionCommands() === true) {
233 $this->callSpecialQuestionCommands($cmd);
234 }
235 }
236 }
237
238 protected function hasSpecialQuestionCommands(): bool
239 {
240 return static::HAS_SPECIAL_QUESTION_COMMANDS;
241 }
242
244 public function getType(): string
245 {
246 return $this->getQuestionType();
247 }
248
249 public function getPresentationContext(): ?string
250 {
252 }
253
254 public function setPresentationContext(string $presentationContext): void
255 {
256 $this->presentationContext = $presentationContext;
257 }
258
259 public function isTestPresentationContext(): bool
260 {
262 }
263
264 // hey: previousPassSolutions - setter/getter for Previous Solution Prefilled flag
265 public function isPreviousSolutionPrefilled(): bool
266 {
268 }
269
271 {
272 $this->previousSolutionPrefilled = $previousSolutionPrefilled;
273 }
274 // hey.
275
276 public function getRenderPurpose(): string
277 {
279 }
280
281 public function setRenderPurpose(string $renderPurpose): void
282 {
283 $this->renderPurpose = $renderPurpose;
284 }
285
286 public function isRenderPurposePrintPdf(): bool
287 {
289 }
290
291 public function isRenderPurposePreview(): bool
292 {
294 }
295
296 public function isRenderPurposeInputValue(): bool
297 {
299 }
300
301 public function isRenderPurposePlayback(): bool
302 {
304 }
305
306 public function isRenderPurposeDemoplay(): bool
307 {
309 }
310
311 public function renderPurposeSupportsFormHtml(): bool
312 {
313 if ($this->isRenderPurposePrintPdf()) {
314 return false;
315 }
316
317 if ($this->isRenderPurposeInputValue()) {
318 return false;
319 }
320
321 return true;
322 }
323
324 public function getEditContext(): string
325 {
326 return $this->editContext;
327 }
328
329 public function setEditContext(string $editContext): void
330 {
331 $this->editContext = $editContext;
332 }
333
334 public function isAuthoringEditContext(): bool
335 {
337 }
338
339 public function isAdjustmentEditContext(): bool
340 {
342 }
343
344 public function setAdjustmentEditContext(): void
345 {
346 $this->setEditContext(self::EDIT_CONTEXT_ADJUSTMENT);
347 }
348
350 {
352 }
353
355 {
356 $this->navigationGUI = $navigationGUI;
357 }
358
359 public function setTaxonomyIds(array $taxonomyIds): void
360 {
361 $this->taxonomyIds = $taxonomyIds;
362 }
363
364 public function getTaxonomyIds(): array
365 {
366 return $this->taxonomyIds;
367 }
368
369 public function setTargetGui($linkTargetGui): void
370 {
371 $this->setTargetGuiClass(get_class($linkTargetGui));
372 }
373
374 public function setTargetGuiClass($targetGuiClass): void
375 {
376 $this->targetGuiClass = $targetGuiClass;
377 }
378
379 public function getTargetGuiClass(): ?string
380 {
382 }
383
385 {
386 $this->questionHeaderBlockBuilder = $questionHeaderBlockBuilder;
387 }
388
389 // fau: testNav - get the question header block bulder (for tweaking)
391 {
393 }
394 // fau.
395
396 public function setQuestionActionCmd(string $questionActionCmd): void
397 {
398 $this->questionActionCmd = $questionActionCmd;
399
400 if (is_object($this->object)) {
401 $this->object->questionActionCmd = $questionActionCmd;
402 }
403 }
404
405 public function getQuestionActionCmd(): string
406 {
408 }
409
414 protected function writePostData(bool $always = false): int
415 {
416 return 0;
417 }
418
419 public function assessment(): void
420 {
421 $stats_table = new ilQuestionCumulatedStatisticsTableGUI($this, 'assessment', '', $this->object);
422 $usage_table = new ilQuestionUsagesTableGUI($this, 'assessment', '', $this->object);
423
424 $this->tpl->setContent(implode('<br />', array(
425 $stats_table->getHTML(),
426 $usage_table->getHTML()
427 )));
428 }
429
433 public static function _getQuestionGUI(string $question_type = '', int $question_id = -1): ?assQuestionGUI
434 {
435 global $DIC;
436 $ilCtrl = $DIC['ilCtrl'];
437 $ilDB = $DIC['ilDB'];
438 $lng = $DIC['lng'];
439
440 if (($question_type === '') && ($question_id > 0)) {
441 $question_type = assQuestion::getQuestionTypeFromDb($question_id);
442 }
443
444 if ($question_type === '') {
445 return null;
446 }
447
448 assQuestion::_includeClass($question_type, 1);
449
450 $question_type_gui = $question_type . 'GUI';
451 $question = new $question_type_gui();
452
453 $feedbackObjectClassname = assQuestion::getFeedbackClassNameByQuestionType($question_type);
454 $question->object->feedbackOBJ = new $feedbackObjectClassname($question->object, $ilCtrl, $ilDB, $lng);
455
456 if ($question_id > 0) {
457 $question->object->loadFromDb($question_id);
458 }
459
460 return $question;
461 }
462
466 public static function _getGUIClassNameForId($a_q_id): string
467 {
468 $q_type = assQuestion::getQuestionTypeFromDb($a_q_id);
469 $class_name = assQuestionGUI::_getClassNameForQType($q_type);
470 return $class_name;
471 }
472
476 public static function _getClassNameForQType($q_type): string
477 {
478 return $q_type . "GUI";
479 }
480
482 {
483 foreach ($this->getPresentationJavascripts() as $jsFile) {
484 $tpl->addJavaScript($jsFile);
485 }
486 }
487
488 public function getPresentationJavascripts(): array
489 {
490 return array();
491 }
492
493 public function getQuestionTemplate(): void
494 {
495 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x (ilObjTestGUI::executeCommand, switch -> default case -> $this->prepareOutput(); already added a template to the CONTENT variable wrapped in a block named content)
496 if (!$this->tpl->blockExists('content')) {
497 $this->tpl->addBlockFile("CONTENT", "content", "tpl.il_as_qpl_content.html", "Modules/TestQuestionPool");
498 }
499 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x (ilObjTestGUI::executeCommand, switch -> default case -> $this->prepareOutput(); already added a template to the STATUSLINE variable wrapped in a block named statusline)
500 if (!$this->tpl->blockExists('statusline')) {
501 $this->tpl->addBlockFile("STATUSLINE", "statusline", "tpl.statusline.html");
502 }
503 // @todo Björn: Maybe this has to be changed for PHP 7/ILIAS 5.2.x because ass[XYZ]QuestionGUI::editQuestion is called multiple times
504 if (!$this->tpl->blockExists('adm_content')) {
505 $this->tpl->addBlockFile("ADM_CONTENT", "adm_content", "tpl.il_as_question.html", "Modules/TestQuestionPool");
506 }
507 }
508
509 protected function renderEditForm(ilPropertyFormGUI $form): void
510 {
511 $this->getQuestionTemplate();
512 $this->tpl->setVariable("QUESTION_DATA", $form->getHTML());
513 }
514
518 public function getILIASPage(string $html = ""): string
519 {
520 $page_gui = new ilAssQuestionPageGUI($this->object->getId());
521 $page_gui->setQuestionHTML(
522 [$this->object->getId() => $html]
523 );
524 $presentation = $page_gui->presentation();
525 $presentation = preg_replace("/src=\"\\.\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $presentation);
526 return $presentation;
527 }
528
529 public function outQuestionPage($a_temp_var, $a_postponed = false, $active_id = "", $html = "", $inlineFeedbackEnabled = false): string
530 {
531 if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
532 // hey
533 $this->tpl->setOnScreenMessage('info', $this->getPreviousSolutionProvidedMessage());
535 } elseif ($this->object->getTestPresentationConfig()->isUnchangedAnswerPossible()) {
536 $html .= $this->getUseUnchangedAnswerCheckboxHtml();
537 }
538
539 $this->lng->loadLanguageModule("content");
540
541 $page_gui = new ilAssQuestionPageGUI($this->object->getId());
542 $page_gui->setOutputMode("presentation");
543 $page_gui->setTemplateTargetVar($a_temp_var);
544
545 if ($this->getNavigationGUI()) {
546 $html .= $this->getNavigationGUI()->getHTML();
547 $page_gui->setQuestionActionsHTML($this->getNavigationGUI()->getActionsHTML());
548 }
549
550 if (strlen($html)) {
551 $page_gui->setQuestionHTML(array($this->object->getId() => $html));
552 }
553
554 $page_gui->setPresentationTitle($this->questionHeaderBlockBuilder->getPresentationTitle());
555 $page_gui->setQuestionInfoHTML($this->questionHeaderBlockBuilder->getQuestionInfoHTML());
556
557 return $page_gui->presentation();
558 }
559
560 protected function getUseUnchangedAnswerCheckboxHtml(): string
561 {
562 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'Modules/TestQuestionPool');
563 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel());
564 return $tpl->get();
565 }
566
567 protected function getPreviousSolutionProvidedMessage(): string
568 {
569 return $this->lng->txt('use_previous_solution_advice');
570 }
571
573 {
574 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'Modules/TestQuestionPool');
575 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->lng->txt('use_previous_solution'));
576 return $tpl->get();
577 }
578
579 public function cancel(): void
580 {
581 if ($this->request->raw("calling_test")) {
582 $_GET["ref_id"] = $this->request->raw("calling_test");
583 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $this->request->raw("calling_test"));
584 } elseif ($this->request->raw("test_ref_id")) {
585 $_GET["ref_id"] = $this->request->raw("test_ref_id");
586 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $this->request->raw("test_ref_id"));
587 } else {
588 if ($this->request->raw("q_id") > 0) {
589 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $this->request->getQuestionId());
590 $this->ctrl->redirectByClass("ilAssQuestionPageGUI", "edit");
591 } else {
592 $this->ctrl->redirectByClass("ilobjquestionpoolgui", "questions");
593 }
594 }
595 }
596
597 public function originalSyncForm(string $return_to = "", string $return_to_feedback = ''): void
598 {
599 if (strlen($return_to)) {
600 $this->ctrl->setParameter($this, "return_to", $return_to);
601 } elseif ($this->request->raw('return_to')) {
602 $this->ctrl->setParameter($this, "return_to", $this->request->raw('return_to'));
603 }
604 if (strlen($return_to_feedback)) {
605 $this->ctrl->setParameter($this, 'return_to_fb', 'true');
606 }
607
608 $this->ctrl->saveParameter($this, 'test_express_mode');
609
610 $template = new ilTemplate("tpl.il_as_qpl_sync_original.html", true, true, "Modules/TestQuestionPool");
611 $template->setVariable("BUTTON_YES", $this->lng->txt("yes"));
612 $template->setVariable("BUTTON_NO", $this->lng->txt("no"));
613 $template->setVariable("FORM_ACTION", $this->ctrl->getFormAction($this));
614 $template->setVariable("TEXT_SYNC", $this->lng->txt("confirm_sync_questions"));
615 $this->tpl->setVariable("ADM_CONTENT", $template->get());
616 }
617
618 public function sync(): void
619 {
620 $original_id = $this->object->getOriginalId();
621 if ($original_id) {
622 $this->object->syncWithOriginal();
623 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
624 }
625 if (strlen($this->request->raw("return_to"))) {
626 $this->ctrl->redirect($this, $this->request->raw("return_to"));
627 }
628 if (strlen($this->request->raw("return_to_fb"))) {
629 $this->ctrl->redirectByClass('ilAssQuestionFeedbackEditingGUI', 'showFeedbackForm');
630 } else {
631 if ($this->request->isset('calling_consumer') && (int) $this->request->raw('calling_consumer')) {
632 $ref_id = (int) $this->request->raw('calling_consumer');
634 if ($consumer instanceof ilQuestionEditingFormConsumer) {
635 $this->ctrl->redirectToURL($consumer->getQuestionEditingFormBackTarget($this->request->raw('consumer_context')));
636 }
637 $this->ctrl->redirectToURL(ilLink::_getLink($ref_id));
638 }
639
640 if ($this->request->raw('test_express_mode')) {
641 $this->ctrl->redirectToURL(ilTestExpressPage::getReturnToPageLink($this->object->getId()));
642 } else {
643 $this->ctrl->redirectByClass(ilAssQuestionPreviewGUI::class, ilAssQuestionPreviewGUI::CMD_SHOW);
644 }
645 }
646 }
647
648 public function cancelSync(): void
649 {
650 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
651
652 if (strlen($this->request->raw("return_to"))) {
653 $this->ctrl->redirect($this, $this->request->raw("return_to"));
654 }
655 if (strlen($this->request->raw('return_to_fb'))) {
656 $this->ctrl->redirectByClass('ilAssQuestionFeedbackEditingGUI', 'showFeedbackForm');
657 } else {
658 if ($this->request->isset('calling_consumer') && (int) $this->request->raw('calling_consumer')) {
659 $ref_id = (int) $this->request->raw('calling_consumer');
661 if ($consumer instanceof ilQuestionEditingFormConsumer) {
662 $this->ctrl->redirectToURL($consumer->getQuestionEditingFormBackTarget($this->request->raw('consumer_context')));
663 }
664 $this->ctrl->redirectToURL(ilLink::_getLink($ref_id));
665 }
666
667 if ($this->request->raw('test_express_mode')) {
668 $this->ctrl->redirectToURL(ilTestExpressPage::getReturnToPageLink($this->object->getId()));
669 } else {
670 $this->ctrl->redirectByClass(ilAssQuestionPreviewGUI::class, ilAssQuestionPreviewGUI::CMD_SHOW);
671 }
672 }
673 }
674
675 public function saveEdit(): void
676 {
678 $result = $this->writePostData();
679 if ($result == 0) {
680 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType());
681 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType());
682 $this->object->saveToDb();
683 $originalexists = $this->object->_questionExists($this->object->getOriginalId());
684
685 if ($this->request->raw("calling_test") && $originalexists && assQuestion::_isWriteable($this->object->getOriginalId(), $ilUser->getId())) {
686 $this->ctrl->redirect($this, "originalSyncForm");
687 } elseif ($this->request->raw("calling_test")) {
688 $_GET["ref_id"] = $this->request->raw("calling_test");
689 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $this->request->raw("calling_test"));
690 return;
691 } elseif ($this->request->raw("test_ref_id")) {
692 global $DIC;
693 $tree = $DIC['tree'];
694 $ilDB = $DIC['ilDB'];
695 $component_repository = $DIC['component.repository'];
696 // TODO: Courier Antipattern!
697 $_GET["ref_id"] = $this->request->raw("test_ref_id");
698 $test = new ilObjTest($this->request->raw("test_ref_id"), true);
699
700 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $component_repository, $test);
701
702 $test->insertQuestion($testQuestionSetConfigFactory->getQuestionSetConfig(), $this->object->getId());
703
704 ilUtil::redirect("ilias.php?baseClass=ilObjTestGUI&cmd=questions&ref_id=" . $this->request->raw("test_ref_id"));
705 } else {
706 $this->ctrl->setParameter($this, "q_id", $this->object->getId());
707 $this->editQuestion();
708 if (ilSession::get("info") != null) {
709 $this->tpl->setOnScreenMessage('success', ilSession::get("info") . "<br />" . $this->lng->txt("msg_obj_modified"), false);
710 } else {
711 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), false);
712 }
713 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $this->object->getId());
714 $this->ctrl->redirectByClass("ilAssQuestionPageGUI", "edit");
715 }
716 }
717 }
718
719 public function save(): void
720 {
722 $old_id = $this->request->raw("q_id");
723 $result = $this->writePostData();
724
725 if ($result == 0) {
726 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType());
727 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType());
728 $this->object->saveToDb();
729 if ($this->object->getOriginalId() == null) {
730 $originalexists = false;
731 } else {
732 $originalexists = $this->object->_questionExistsInPool($this->object->getOriginalId());
733 }
734
735 if (($this->request->raw("calling_test") ||
736 ($this->request->isset('calling_consumer')
737 && (int) $this->request->raw('calling_consumer')))
738 && $originalexists && assQuestion::_isWriteable($this->object->getOriginalId(), $ilUser->getId())) {
739 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
740 $this->ctrl->setParameter($this, 'return_to', 'editQuestion');
741 $this->ctrl->redirect($this, "originalSyncForm");
742 return;
743 }
744
745 if ($this->request->raw("calling_test")) {
746 $test = new ilObjTest($this->request->raw("calling_test"));
747 if (!assQuestion::_questionExistsInTest($this->object->getId(), $test->getTestId())) {
750 global $DIC;
751 $component_repository = $DIC['component.repository'];
752
753 $test = new ilObjTest($this->request->raw("calling_test"), true);
754 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $component_repository, $test);
755
756 $new_q_id = $this->object->getId();
757 if ($test->getRefId() !== $this->request->int('ref_id')) {
758 $new_q_id = $this->object->duplicate(true, $this->object->getTitle(), $this->object->getAuthor(), $this->object->getOwner(), $test->getId());
759 }
760
761 $test->insertQuestion(
762 $testQuestionSetConfigFactory->getQuestionSetConfig(),
763 $new_q_id,
764 true
765 );
766
767 if ($this->request->isset('prev_qid')) {
768 $test->moveQuestionAfter($new_q_id, $this->request->raw('prev_qid'));
769 }
770
771 $this->ctrl->setParameter($this, 'q_id', $new_q_id);
772 $this->ctrl->setParameter($this, 'ref_id', $this->request->raw('calling_test'));
773 $this->ctrl->setParameter($this, 'calling_test', $this->request->raw("calling_test"));
774 }
775 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
776 $this->ctrl->redirect($this, 'editQuestion');
777 } else {
778 $this->callNewIdListeners($this->object->getId());
779
780 if ($this->object->getId() != $old_id) {
781 // first save
782 $this->ctrl->setParameterByClass($this->request->raw("cmdClass"), "q_id", $this->object->getId());
783 $this->ctrl->setParameterByClass($this->request->raw("cmdClass"), "sel_question_types", $this->request->raw("sel_question_types"));
784 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
785 if ($this->request->raw('prev_qid')) {
786 // @todo: bheyser/mbecker wtf? ..... thx@jposselt ....
787 $test = new ilObjTest($this->request->getRefId(), true);
788 $test->moveQuestionAfter($this->request->raw('prev_qid'), $this->object->getId());
789 }
790 if ($this->request->raw('express_mode')) {
794
795 // TODO: Courier Antipattern!
796 $test = new ilObjTest($this->request->getRefId(), true);
797 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $component_repository, $test);
798 $test->insertQuestion(
799 $testQuestionSetConfigFactory->getQuestionSetConfig(),
800 $this->object->getId()
801 );
802 $_REQUEST['q_id'] = $this->object->getId();
804 }
805
806 $this->ctrl->redirectByClass($this->request->raw("cmdClass"), "editQuestion");
807 }
808 if (ilSession::get("info") != null) {
809 $this->tpl->setOnScreenMessage('success', ilSession::get("info") . "<br />" . $this->lng->txt("msg_obj_modified"), true);
810 } else {
811 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
812 }
813 $this->ctrl->redirect($this, 'editQuestion');
814 }
815 }
816 $tabs = $this->ilTabs;
817 $tabs->setTabActive('edit_question');
818 }
819
820 public function saveReturn(): void
821 {
823 $old_id = $this->request->getQuestionId();
824 $result = $this->writePostData();
825 if ($result == 0) {
826 $ilUser->setPref("tst_lastquestiontype", $this->object->getQuestionType());
827 $ilUser->writePref("tst_lastquestiontype", $this->object->getQuestionType());
828 $this->object->saveToDb($old_id);
829 if ($this->object->getOriginalId() == null) {
830 $originalexists = false;
831 } else {
832 $originalexists = $this->object->_questionExistsInPool($this->object->getOriginalId());
833 }
834 if (($this->request->raw("calling_test") || ($this->request->isset('calling_consumer')
835 && (int) $this->request->raw('calling_consumer')))
836 && $originalexists && assQuestion::_isWriteable($this->object->getOriginalId(), $ilUser->getId())) {
837 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
838 $this->ctrl->setParameter($this, 'test_express_mode', $this->request->raw('test_express_mode'));
839 $this->ctrl->redirect($this, "originalSyncForm");
840 return;
841 } elseif ($this->request->raw("calling_test")) {
842 $test = new ilObjTest($this->request->raw("calling_test"));
843 if (!assQuestion::_questionExistsInTest($this->object->getId(), $test->getTestId())) {
847 $test = new ilObjTest($this->request->raw("calling_test"), true);
848
849 $testQuestionSetConfigFactory = new ilTestQuestionSetConfigFactory($tree, $ilDB, $component_repository, $test);
850
851 $new_q_id = $this->object->getId();
852 if ($test->getRefId() !== $this->request->int('ref_id')) {
853 $new_q_id = $this->object->duplicate(true, $this->object->getTitle(), $this->object->getAuthor(), $this->object->getOwner(), $test->getId());
854 }
855
856 $test->insertQuestion(
857 $testQuestionSetConfigFactory->getQuestionSetConfig(),
858 $new_q_id,
859 true
860 );
861
862 if ($this->request->isset('prev_qid')) {
863 $test->moveQuestionAfter($new_q_id, $this->request->raw('prev_qid'));
864 }
865
866 $this->ctrl->setParameter($this, 'q_id', $new_q_id);
867 $this->ctrl->setParameter($this, 'ref_id', $this->request->raw('calling_test'));
868 $this->ctrl->setParameter($this, 'calling_test', $this->request->raw("calling_test"));
869 }
870 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
871 $this->ctrl->redirectByClass('ilAssQuestionPreviewGUI', ilAssQuestionPreviewGUI::CMD_SHOW);
872 } else {
873 if ($this->object->getId() != $old_id) {
874 $this->callNewIdListeners($this->object->getId());
875 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
876 $this->ctrl->redirectByClass("ilobjquestionpoolgui", "questions");
877 }
878 if (ilSession::get("info") != null) {
879 $this->tpl->setOnScreenMessage('success', ilSession::get("info") . "<br />" . $this->lng->txt("msg_obj_modified"), true);
880 } else {
881 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
882 }
883 $this->ctrl->redirectByClass('ilAssQuestionPreviewGUI', ilAssQuestionPreviewGUI::CMD_SHOW);
884 }
885 }
886 $tabs = $this->ilTabs;
887 $tabs->setTabActive('edit_question');
888 }
889
890 public function apply(): void
891 {
892 $this->writePostData();
893 $this->object->saveToDb();
894 $this->ctrl->setParameter($this, "q_id", $this->object->getId());
895 $this->editQuestion();
896 }
897
901 public function getContextPath($cont_obj, int $a_endnode_id, int $a_startnode_id = 1): string
902 {
903 $path = "";
904
905 $tmpPath = $cont_obj->getLMTree()->getPathFull($a_endnode_id, $a_startnode_id);
906
907 // count -1, to exclude the learning module itself
908 for ($i = 1; $i < (count($tmpPath) - 1); $i++) {
909 if ($path != "") {
910 $path .= " > ";
911 }
912
913 $path .= $tmpPath[$i]["title"];
914 }
915
916 return $path;
917 }
918
919 public function setSequenceNumber(int $nr): void
920 {
921 $this->sequence_no = $nr;
922 }
923
924 public function getSequenceNumber(): int
925 {
926 return $this->sequence_no;
927 }
928
929 public function setQuestionCount(int $a_question_count): void
930 {
931 $this->question_count = $a_question_count;
932 }
933
934 public function getQuestionCount(): int
935 {
937 }
938
939 public function getErrorMessage(): string
940 {
941 return $this->errormessage;
942 }
943
944 public function setErrorMessage(string $errormessage): void
945 {
946 $this->errormessage = $errormessage;
947 }
948
949 public function addErrorMessage(string $errormessage): void
950 {
951 $this->errormessage .= ((strlen($this->errormessage)) ? "<br />" : "") . $errormessage;
952 }
953
955 public function outAdditionalOutput(): void
956 {
957 }
958
959 public function getQuestionType(): string
960 {
961 return $this->object->getQuestionType();
962 }
963
964 public function getAsValueAttribute(string $a_value): string
965 {
966 $result = "";
967 if (strlen($a_value)) {
968 $result = " value=\"$a_value\" ";
969 }
970 return $result;
971 }
972
973 // scorm2004-start
978 public function addNewIdListener($a_object, string $a_method, string $a_parameters = ""): void
979 {
981 $this->new_id_listeners[$cnt]["object"] = &$a_object;
982 $this->new_id_listeners[$cnt]["method"] = $a_method;
983 $this->new_id_listeners[$cnt]["parameters"] = $a_parameters;
984 $this->new_id_listener_cnt++;
985 }
986
987 public function callNewIdListeners(int $a_new_id): void
988 {
989 for ($i = 0; $i < $this->new_id_listener_cnt; $i++) {
990 $this->new_id_listeners[$i]["parameters"]["new_id"] = $a_new_id;
991 $object = &$this->new_id_listeners[$i]["object"];
992 $method = $this->new_id_listeners[$i]["method"];
993 $parameters = $this->new_id_listeners[$i]["parameters"];
994 $object->$method($parameters);
995 }
996 }
997
999 {
1000 if (!$this->object->getSelfAssessmentEditingMode()) {
1001 $form->addCommandButton("saveReturn", $this->lng->txt("save_return"));
1002 }
1003 $form->addCommandButton("save", $this->lng->txt("save"));
1004 }
1005
1007 {
1008 // title
1009 $title = new ilTextInputGUI($this->lng->txt("title"), "title");
1010 $title->setMaxLength(100);
1011 $title->setValue($this->object->getTitle());
1012 $title->setRequired(true);
1013 $form->addItem($title);
1014
1015 if (!$this->object->getSelfAssessmentEditingMode()) {
1016 // author
1017 $author = new ilTextInputGUI($this->lng->txt("author"), "author");
1018 $author->setValue($this->object->getAuthor());
1019 $author->setMaxLength(512);
1020 $author->setRequired(true);
1021 $form->addItem($author);
1022
1023 // description
1024 $description = new ilTextInputGUI($this->lng->txt("description"), "comment");
1025 $description->setValue($this->object->getComment());
1026 $description->setRequired(false);
1027 $form->addItem($description);
1028 } else {
1029 // author as hidden field
1030 $hi = new ilHiddenInputGUI("author");
1031 $author = ilLegacyFormElementsUtil::prepareFormOutput($this->object->getAuthor());
1032 if (trim($author) == "") {
1033 $author = "-";
1034 }
1035 $hi->setValue($author);
1036 $form->addItem($hi);
1037 }
1038
1039 // lifecycle
1040 $lifecycle = new ilSelectInputGUI($this->lng->txt('qst_lifecycle'), 'lifecycle');
1041 $lifecycle->setOptions($this->object->getLifecycle()->getSelectOptions($this->lng));
1042 $lifecycle->setValue($this->object->getLifecycle()->getIdentifier());
1043 $form->addItem($lifecycle);
1044
1045 // questiontext
1046 $question = new ilTextAreaInputGUI($this->lng->txt("question"), "question");
1047 $question->setValue($this->object->getQuestion());
1048 $question->setRequired(true);
1049 $question->setRows(10);
1050 $question->setCols(80);
1051
1052 if (!$this->object->getSelfAssessmentEditingMode()) {
1053 if ($this->object->getAdditionalContentEditingMode() != assQuestion::ADDITIONAL_CONTENT_EDITING_MODE_IPE) {
1054 $question->setUseRte(true);
1055 $question->setRteTags(ilObjAdvancedEditing::_getUsedHTMLTags("assessment"));
1056 $question->addPlugin("latex");
1057 $question->addButton("latex");
1058 $question->addButton("pastelatex");
1059 $question->setRTESupport($this->object->getId(), "qpl", "assessment");
1060 }
1061 } else {
1063 $question->setUseTagsForRteOnly(false);
1064 }
1065 $form->addItem($question);
1067 }
1068
1070 {
1071 if (!$this->object->getSelfAssessmentEditingMode()) {
1072 return;
1073 }
1074
1075 $nr_tries = $this->object->getNrOfTries() ?? $this->object->getDefaultNrOfTries();
1076
1077 if ($nr_tries < 1) {
1078 $nr_tries = "";
1079 }
1080
1081 $ni = new ilNumberInputGUI($this->lng->txt("qst_nr_of_tries"), "nr_of_tries");
1082 $ni->setValue($nr_tries);
1083 $ni->setMinValue(0);
1084 $ni->setSize(5);
1085 $ni->setMaxLength(5);
1086 $form->addItem($ni);
1087 }
1088
1089 protected function saveTaxonomyAssignments(): void
1090 {
1091 if (count($this->getTaxonomyIds())) {
1092 foreach ($this->getTaxonomyIds() as $taxonomyId) {
1093 $postvar = "tax_node_assign_$taxonomyId";
1094
1095 $tax_node_assign = new ilTaxAssignInputGUI($taxonomyId, true, '', $postvar);
1096 // TODO: determine tst/qpl when tax assigns become maintainable within tests
1097 $tax_node_assign->saveInput("qpl", $this->object->getObjId(), "quest", $this->object->getId());
1098 }
1099 }
1100 }
1101
1102 protected function populateTaxonomyFormSection(ilPropertyFormGUI $form): void
1103 {
1104 if ($this->getTaxonomyIds() !== []) {
1105 // this is needed by ilTaxSelectInputGUI in some cases
1106 ilOverlayGUI::initJavaScript();
1107
1108 $sectHeader = new ilFormSectionHeaderGUI();
1109 $sectHeader->setTitle($this->lng->txt('qpl_qst_edit_form_taxonomy_section'));
1110 $form->addItem($sectHeader);
1111
1112 foreach ($this->getTaxonomyIds() as $taxonomyId) {
1113 $taxonomy = new ilObjTaxonomy($taxonomyId);
1114 $label = sprintf($this->lng->txt('qpl_qst_edit_form_taxonomy'), $taxonomy->getTitle());
1115 $postvar = "tax_node_assign_$taxonomyId";
1116
1117 $taxSelect = new ilTaxSelectInputGUI($taxonomy->getId(), $postvar, true);
1118 $taxSelect->setTitle($label);
1119
1120
1121 $taxNodeAssignments = new ilTaxNodeAssignment(ilObject::_lookupType($this->object->getObjId()), $this->object->getObjId(), 'quest', $taxonomyId);
1122 $assignedNodes = $taxNodeAssignments->getAssignmentsOfItem($this->object->getId());
1123
1124 $taxSelect->setValue(array_map(function ($assignedNode) {
1125 return $assignedNode['node_id'];
1126 }, $assignedNodes));
1127 $form->addItem($taxSelect);
1128 }
1129 }
1130 }
1131
1135 public function getGenericFeedbackOutput(int $active_id, ?int $pass): string
1136 {
1137 $output = '';
1138 $manual_feedback = ilObjTest::getManualFeedback($active_id, $this->object->getId(), $pass);
1139 if ($manual_feedback !== '') {
1140 return $manual_feedback;
1141 }
1142
1143 $correct_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true);
1144 $incorrect_feedback = $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false);
1145 if ($correct_feedback . $incorrect_feedback !== '') {
1146 $output = $this->genericFeedbackOutputBuilder($correct_feedback, $incorrect_feedback, $active_id, $pass);
1147 }
1148
1149 if ($this->object->isAdditionalContentEditingModePageObject()) {
1150 return $output;
1151 }
1152 return $this->object->prepareTextareaOutput($output, true);
1153 }
1154
1156 string $feedback_correct,
1157 string $feedback_incorrect,
1158 int $active_id,
1159 ?int $pass
1160 ): string {
1161 if ($pass === null) {
1162 return '';
1163 }
1164 $reached_points = $this->object->calculateReachedPoints($active_id, $pass);
1165 $max_points = $this->object->getMaximumPoints();
1166 if ($reached_points == $max_points) {
1167 return $feedback_correct;
1168 }
1169
1170 return $feedback_incorrect;
1171 }
1172
1174 {
1175 return $this->object->prepareTextareaOutput(
1176 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true),
1177 true
1178 );
1179 }
1180
1182 {
1183 return $this->object->prepareTextareaOutput(
1184 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false),
1185 true
1186 );
1187 }
1188
1193 abstract public function getSpecificFeedbackOutput(array $userSolution): string;
1194
1195 public function outQuestionType(): string
1196 {
1197 $count = $this->object->usageNumber();
1198
1199 if ($this->object->_questionExistsInPool($this->object->getId()) && $count) {
1200 global $DIC;
1201 $rbacsystem = $DIC['rbacsystem'];
1202 if ($rbacsystem->checkAccess("write", $this->request->getRefId())) {
1203 $this->tpl->setOnScreenMessage('info', sprintf($this->lng->txt("qpl_question_is_in_use"), $count));
1204 }
1205 }
1206
1207 return assQuestion::_getQuestionTypeName($this->object->getQuestionType());
1208 }
1209
1210 public function suggestedsolution(): void
1211 {
1213 $ilAccess = $this->access;
1214
1215 $cmd = $this->request->raw('cmd');
1216 $save = is_array($cmd) && array_key_exists('saveSuggestedSolution', $cmd);
1217 if ($save && $this->request->int('deleteSuggestedSolution') === 1) {
1218 $this->object->deleteSuggestedSolutions();
1219 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1220 $this->ctrl->redirect($this, "suggestedsolution");
1221 }
1222
1223 $output = "";
1224 $solution_array = $this->object->getSuggestedSolution(0);
1225 $options = array(
1226 "lm" => $this->lng->txt("obj_lm"),
1227 "st" => $this->lng->txt("obj_st"),
1228 "pg" => $this->lng->txt("obj_pg"),
1229 "git" => $this->lng->txt("glossary_term"),
1230 "file" => $this->lng->txt("fileDownload"),
1231 "text" => $this->lng->txt("solutionText")
1232 );
1233
1234 if (!array_key_exists('type', $solution_array)) {
1235 $solution_array['type'] = '';
1236 }
1237
1238 $solution_type = $this->ctrl->getCmd() === 'cancelSuggestedSolution'
1239 ? $solution_array["type"]
1240 : $this->request->raw('solutiontype');
1241 if (is_string($solution_type) &&
1242 strcmp($solution_type, "file") == 0 &&
1243 strcmp($solution_array["type"], "file") != 0) {
1244 $solution_array = array(
1245 "type" => "file"
1246 );
1247 } elseif (is_string($solution_type) &&
1248 strcmp($solution_type, "text") == 0 &&
1249 strcmp($solution_array["type"], "text") != 0) {
1250 $oldsaveSuggestedSolutionOutputMode = $this->getRenderPurpose();
1251 $this->setRenderPurpose(self::RENDER_PURPOSE_INPUT_VALUE);
1252
1253 $solution_array = array(
1254 "type" => "text",
1255 "value" => $this->getSolutionOutput(0, null, false, false, true, false, true)
1256 );
1257 $this->setRenderPurpose($oldsaveSuggestedSolutionOutputMode);
1258 }
1259
1260 $solution_filename = $this->request->raw('filename');
1261 if ($save &&
1262 is_string($solution_filename) &&
1263 strlen($solution_filename)) {
1264 $solution_array["value"]["filename"] = $solution_filename;
1265 }
1266
1267 $solution_text = $this->request->raw('solutiontext');
1268 if ($save &&
1269 is_string($solution_text) &&
1270 strlen($solution_text)) {
1271 $solution_array["value"] = $solution_text;
1272 }
1273 if (isset($solution_array['type']) && $solution_array['type'] !== "") {
1274 $form = new ilPropertyFormGUI();
1275 $form->setFormAction($this->ctrl->getFormAction($this));
1276 $form->setTitle($this->lng->txt("solution_hint"));
1277 $form->setMultipart(true);
1278 $form->setTableWidth("100%");
1279 $form->setId("suggestedsolutiondisplay");
1280
1281 // suggested solution output
1282 $title = new ilSolutionTitleInputGUI($this->lng->txt("showSuggestedSolution"), "solutiontype");
1283 $template = new ilTemplate("tpl.il_as_qpl_suggested_solution_input_presentation.html", true, true, "Modules/TestQuestionPool");
1284 if (array_key_exists("internal_link", $solution_array) &&
1285 strlen($solution_array["internal_link"])) {
1286 $href = assQuestion::_getInternalLinkHref($solution_array["internal_link"]);
1287 $template->setCurrentBlock("preview");
1288 $template->setVariable("TEXT_SOLUTION", $this->lng->txt("suggested_solution"));
1289 $template->setVariable("VALUE_SOLUTION", " <a href=\"$href\" target=\"content\">" . $this->lng->txt("view") . "</a> ");
1290 $template->parseCurrentBlock();
1291 } elseif ((strcmp($solution_array["type"], "file") == 0) &&
1292 array_key_exists('value', $solution_array) && is_array($solution_array["value"])) {
1293 $href = $this->object->getSuggestedSolutionPathWeb() . ($solution_array["value"]["name"] ?? "");
1294 $template->setCurrentBlock("preview");
1295 $template->setVariable("TEXT_SOLUTION", $this->lng->txt("suggested_solution"));
1296 $template->setVariable("VALUE_SOLUTION", " <a href=\"$href\" target=\"content\">" . ilLegacyFormElementsUtil::prepareFormOutput(
1297 (strlen(
1298 $solution_array["value"]["filename"]
1299 )) ? $solution_array["value"]["filename"] : $solution_array["value"]["name"]
1300 ) . "</a> ");
1301 $template->parseCurrentBlock();
1302 }
1303 $template->setVariable("TEXT_TYPE", $this->lng->txt("type"));
1304 $template->setVariable("VALUE_TYPE", $options[$solution_array["type"]]);
1305 $title->setHtml($template->get());
1306 $deletesolution = new ilCheckboxInputGUI("", "deleteSuggestedSolution");
1307 $deletesolution->setOptionTitle($this->lng->txt("deleteSuggestedSolution"));
1308 $title->addSubItem($deletesolution);
1309 $form->addItem($title);
1310
1311 if (strcmp($solution_array["type"], "file") == 0) {
1312 // file
1313 $file = new ilFileInputGUI($this->lng->txt("fileDownload"), "file");
1314 $file->setRequired(true);
1315 $file->enableFileNameSelection("filename");
1316
1317 if ($save && array_key_exists("file", $_FILES) &&
1318 array_key_exists("tmp_name", $_FILES["file"]) &&
1319 $_FILES["file"]["tmp_name"] && $file->checkInput()) {
1320 if (!file_exists($this->object->getSuggestedSolutionPath())) {
1321 ilFileUtils::makeDirParents($this->object->getSuggestedSolutionPath());
1322 }
1323
1325 $_FILES["file"]["tmp_name"],
1326 $_FILES["file"]["name"],
1327 $this->object->getSuggestedSolutionPath() . $_FILES["file"]["name"]
1328 );
1329 if ($res) {
1330 ilFileUtils::renameExecutables($this->object->getSuggestedSolutionPath());
1331
1332 // remove an old file download
1333 if (array_key_exists('value', $solution_array) && is_array($solution_array["value"])) {
1334 @unlink($this->object->getSuggestedSolutionPath() . $solution_array["value"]["name"]);
1335 }
1336 $file->setValue($_FILES["file"]["name"]);
1337 $this->object->saveSuggestedSolution("file", "", 0, array("name" => $_FILES["file"]["name"], "type" => $_FILES["file"]["type"], "size" => $_FILES["file"]["size"], "filename" => $_POST["filename"]));
1338
1339 if (($this->request->raw("calling_test") ||
1340 ($this->request->isset('calling_consumer') && (int) $this->request->raw('calling_consumer'))) &&
1341 $this->object->_questionExistsInPool($this->object->getOriginalId()) &&
1342 assQuestion::_isWriteable($this->object->getOriginalId(), $ilUser->getId())) {
1343 $this->originalSyncForm("suggestedsolution");
1344 return;
1345 } else {
1346 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1347 $this->ctrl->redirect($this, "suggestedsolution");
1348 }
1349 } else {
1350 // BH: $res as info string? wtf? it holds a bool or something else!!?
1351 $this->tpl->setOnScreenMessage('info', $res);
1352 }
1353 } else {
1354 if (array_key_exists('value', $solution_array) && is_array($solution_array["value"])) {
1355 $file->setValue($solution_array["value"]["name"]);
1356 $file->setFilename((strlen($solution_array["value"]["filename"])) ? $solution_array["value"]["filename"] : $solution_array["value"]["name"]);
1357 }
1358 }
1359 $form->addItem($file);
1360 $hidden = new ilHiddenInputGUI("solutiontype");
1361 $hidden->setValue("file");
1362 $form->addItem($hidden);
1363 } elseif (strcmp($solution_array["type"], "text") == 0) {
1364 $solutionContent = $solution_array['value'];
1365 $solutionContent = $this->object->fixSvgToPng($solutionContent);
1366 $solutionContent = $this->object->fixUnavailableSkinImageSources($solutionContent);
1367 $question = new ilTextAreaInputGUI($this->lng->txt("solutionText"), "solutiontext");
1368 $question->setValue($this->object->prepareTextareaOutput($solutionContent));
1369 $question->setRequired(true);
1370 $question->setRows(10);
1371 $question->setCols(80);
1372 $question->setUseRte(true);
1373 $question->addPlugin("latex");
1374 $question->addButton("latex");
1375 $question->setRTESupport($this->object->getId(), "qpl", "assessment");
1376 $hidden = new ilHiddenInputGUI("solutiontype");
1377 $hidden->setValue("text");
1378 $form->addItem($hidden);
1379 $form->addItem($question);
1380 }
1381 if ($ilAccess->checkAccess("write", "", $this->request->getRefId())) {
1382 $form->addCommandButton('cancelSuggestedSolution', $this->lng->txt('cancel'));
1383 $form->addCommandButton('saveSuggestedSolution', $this->lng->txt('save'));
1384 }
1385
1386 if ($save) {
1387 if ($form->checkInput()) {
1388 switch ($solution_array["type"]) {
1389 case "file":
1390 $this->object->saveSuggestedSolution("file", "", 0, array(
1391 "name" => $solution_array["value"]["name"],
1392 "type" => $solution_array["value"]["type"],
1393 "size" => $solution_array["value"]["size"],
1394 "filename" => $_POST["filename"]
1395 ));
1396 break;
1397 case "text":
1398 $this->object->saveSuggestedSolution("text", "", 0, $solution_array["value"]);
1399 break;
1400 }
1401 $originalexists = !is_null($this->object->getOriginalId()) &&
1402 $this->object->_questionExistsInPool($this->object->getOriginalId());
1403 if (($this->request->raw("calling_test") || ($this->request->isset('calling_consumer')
1404 && (int) $this->request->raw('calling_consumer'))) && $originalexists
1405 && assQuestion::_isWriteable($this->object->getOriginalId(), $ilUser->getId())) {
1406 $this->originalSyncForm("suggestedsolution");
1407 return;
1408 } else {
1409 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1410 $this->ctrl->redirect($this, "suggestedsolution");
1411 }
1412 }
1413 }
1414
1415 $output = $form->getHTML();
1416 }
1417
1418 $savechange = (strcmp($this->ctrl->getCmd(), "saveSuggestedSolutionType") == 0) ? true : false;
1419
1420 $changeoutput = "";
1421 if ($ilAccess->checkAccess("write", "", $this->request->getRefId())) {
1422 $formchange = new ilPropertyFormGUI();
1423 $formchange->setFormAction($this->ctrl->getFormAction($this));
1424 $formchange->setTitle((count($solution_array)) ? $this->lng->txt("changeSuggestedSolution") : $this->lng->txt("addSuggestedSolution"));
1425 $formchange->setMultipart(false);
1426 $formchange->setTableWidth("100%");
1427 $formchange->setId("suggestedsolution");
1428
1429 $solutiontype = new ilRadioGroupInputGUI($this->lng->txt("suggestedSolutionType"), "solutiontype");
1430 foreach ($options as $opt_value => $opt_caption) {
1431 $solutiontype->addOption(new ilRadioOption($opt_caption, $opt_value));
1432 }
1433 if (count($solution_array)) {
1434 $solutiontype->setValue($solution_array["type"]);
1435 }
1436 $solutiontype->setRequired(true);
1437 $formchange->addItem($solutiontype);
1438
1439 $formchange->addCommandButton("saveSuggestedSolutionType", $this->lng->txt("select"));
1440
1441 if ($savechange) {
1442 $formchange->checkInput();
1443 }
1444 $changeoutput = $formchange->getHTML();
1445 }
1446
1447 $this->tpl->setVariable("ADM_CONTENT", $changeoutput . $output);
1448 }
1449
1450 public function outSolutionExplorer(): void
1451 {
1452 global $DIC;
1453 $tree = $DIC['tree'];
1454
1455 $type = $this->request->raw("link_new_type");
1456 $search = $this->request->raw("search_link_type");
1457 $this->ctrl->setParameter($this, "link_new_type", $type);
1458 $this->ctrl->setParameter($this, "search_link_type", $search);
1459 $this->ctrl->saveParameter($this, array("subquestion_index", "link_new_type", "search_link_type"));
1460
1461 $this->tpl->setOnScreenMessage('info', $this->lng->txt("select_object_to_link"));
1462
1463 $parent_ref_id = $tree->getParentId($this->request->getRefId());
1464 $exp = new ilSolutionExplorer($this->ctrl->getLinkTarget($this, 'suggestedsolution'), get_class($this));
1465 $exp->setExpand($this->request->raw('expand_sol') ? $this->request->raw('expand_sol') : $parent_ref_id);
1466 $exp->setExpandTarget($this->ctrl->getLinkTarget($this, 'outSolutionExplorer'));
1467 $exp->setTargetGet("ref_id");
1468 $exp->setRefId($this->request->getRefId());
1469 $exp->addFilter($type);
1470 $exp->setSelectableType($type);
1471 if ($this->request->isset('expandCurrentPath') && $this->request->raw('expandCurrentPath')) {
1472 $exp->expandPathByRefId($parent_ref_id);
1473 }
1474
1475 // build html-output
1476 $exp->setOutput(0);
1477
1478 $template = new ilTemplate("tpl.il_as_qpl_explorer.html", true, true, "Modules/TestQuestionPool");
1479 $template->setVariable("EXPLORER_TREE", $exp->getOutput());
1480 $template->setVariable("BUTTON_CANCEL", $this->lng->txt("cancel"));
1481 $template->setVariable("FORMACTION", $this->ctrl->getFormAction($this, "suggestedsolution"));
1482 $this->tpl->setVariable("ADM_CONTENT", $template->get());
1483 }
1484
1485 public function saveSuggestedSolutionType(): void
1486 {
1487 global $DIC;
1488 $tree = $DIC['tree'];
1489
1490 switch ($_POST["solutiontype"]) {
1491 case "lm":
1492 $type = "lm";
1493 $search = "lm";
1494 break;
1495 case "git":
1496 $type = "glo";
1497 $search = "glo";
1498 break;
1499 case "st":
1500 $type = "lm";
1501 $search = "st";
1502 break;
1503 case "pg":
1504 $type = "lm";
1505 $search = "pg";
1506 break;
1507 case "file":
1508 case "text":
1509 default:
1510 $this->suggestedsolution();
1511 return;
1512 }
1513 if (isset($_POST['solutiontype'])) {
1514 $this->ctrl->setParameter($this, 'expandCurrentPath', 1);
1515 }
1516 $this->ctrl->setParameter($this, "link_new_type", $type);
1517 $this->ctrl->setParameter($this, "search_link_type", $search);
1518 $this->ctrl->redirect($this, "outSolutionExplorer");
1519 }
1520
1521 public function cancelExplorer(): void
1522 {
1523 $this->ctrl->redirect($this, "suggestedsolution");
1524 }
1525
1526 public function outPageSelector(): void
1527 {
1528 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1529
1530 $cont_obj_gui = new ilObjContentObjectGUI('', $this->request->raw('source_id'), true);
1531 $cont_obj = $cont_obj_gui->getObject();
1532 $pages = ilLMPageObject::getPageList($cont_obj->getId());
1533 $shownpages = array();
1534 $tree = $cont_obj->getLMTree();
1535 $chapters = $tree->getSubtree($tree->getNodeData($tree->getRootId()));
1536
1537 $rows = array();
1538
1539 foreach ($chapters as $chapter) {
1540 $chapterpages = $tree->getChildsByType($chapter['obj_id'], 'pg');
1541 foreach ($chapterpages as $page) {
1542 if ($page['type'] == $this->request->raw('search_link_type')) {
1543 array_push($shownpages, $page['obj_id']);
1544
1545 if ($tree->isInTree($page['obj_id'])) {
1546 $path_str = $this->getContextPath($cont_obj, $page['obj_id']);
1547 } else {
1548 $path_str = '---';
1549 }
1550
1551 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']);
1552 $rows[] = array(
1553 'title' => $page['title'],
1554 'description' => ilLegacyFormElementsUtil::prepareFormOutput($path_str),
1555 'text_add' => $this->lng->txt('add'),
1556 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type']))
1557 );
1558 }
1559 }
1560 }
1561 foreach ($pages as $page) {
1562 if (!in_array($page['obj_id'], $shownpages)) {
1563 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']);
1564 $rows[] = array(
1565 'title' => $page['title'],
1566 'description' => '---',
1567 'text_add' => $this->lng->txt('add'),
1568 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type']))
1569 );
1570 }
1571 }
1572
1573 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1574 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($this->request->raw('search_link_type'))));
1575 $table->setData($rows);
1576
1577 $this->tpl->setContent($table->getHTML());
1578 }
1579
1580 public function outChapterSelector(): void
1581 {
1582 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1583
1584 $cont_obj_gui = new ilObjContentObjectGUI('', $this->request->raw('source_id'), true);
1585 $cont_obj = $cont_obj_gui->getObject();
1586 $ctree = $cont_obj->getLMTree();
1587 $nodes = $ctree->getSubtree($ctree->getNodeData($ctree->getRootId()));
1588
1589 $rows = array();
1590
1591 foreach ($nodes as $node) {
1592 if ($node['type'] == $this->request->raw('search_link_type')) {
1593 $this->ctrl->setParameter($this, $node['type'], $node['obj_id']);
1594 $rows[] = array(
1595 'title' => $node['title'],
1596 'description' => '',
1597 'text_add' => $this->lng->txt('add'),
1598 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($node['type']))
1599 );
1600 }
1601 }
1602
1603 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1604 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($this->request->raw('search_link_type'))));
1605 $table->setData($rows);
1606
1607 $this->tpl->setContent($table->getHTML());
1608 }
1609
1610 public function outGlossarySelector(): void
1611 {
1612 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1613
1614 $glossary = new ilObjGlossary($this->request->raw('source_id'), true);
1615 $terms = $glossary->getTermList();
1616
1617 $rows = array();
1618
1619 foreach ($terms as $term) {
1620 $this->ctrl->setParameter($this, 'git', $term['id']);
1621 $rows[] = array(
1622 'title' => $term['term'],
1623 'description' => '',
1624 'text_add' => $this->lng->txt('add'),
1625 'href_add' => $this->ctrl->getLinkTarget($this, 'addGIT')
1626 );
1627 }
1628
1629 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1630 $table->setTitle($this->lng->txt('glossary_term'));
1631 $table->setData($rows);
1632
1633 $this->tpl->setContent($table->getHTML());
1634 }
1635
1636 public function linkChilds(): void
1637 {
1638 $this->ctrl->saveParameter($this, array("subquestion_index", "link_new_type", "search_link_type"));
1639 switch ($this->request->raw("search_link_type")) {
1640 case "pg":
1641 $this->outPageSelector();
1642 break;
1643 case "st":
1644 $this->outChapterSelector();
1645 break;
1646 case "glo":
1647 $this->outGlossarySelector();
1648 break;
1649 case "lm":
1650 $subquestion_index = ($this->request->raw("subquestion_index") > 0) ? $this->request->raw("subquestion_index") : 0;
1651 $this->object->saveSuggestedSolution("lm", "il__lm_" . $this->request->raw("source_id"), $subquestion_index);
1652 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1653 $this->ctrl->redirect($this, "suggestedsolution");
1654 break;
1655 }
1656 }
1657
1658 public function addPG(): void
1659 {
1660 $subquestion_index = 0;
1661 if (strlen($this->request->raw("subquestion_index")) && $this->request->raw("subquestion_index") >= 0) {
1662 $subquestion_index = $this->request->raw("subquestion_index");
1663 }
1664 $this->object->saveSuggestedSolution("pg", "il__pg_" . $this->request->raw("pg"), $subquestion_index);
1665 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1666 $this->ctrl->redirect($this, "suggestedsolution");
1667 }
1668
1669 public function addST(): void
1670 {
1671 $subquestion_index = 0;
1672 if (strlen($this->request->raw("subquestion_index")) && $this->request->raw("subquestion_index") >= 0) {
1673 $subquestion_index = $this->request->raw("subquestion_index");
1674 }
1675 $this->object->saveSuggestedSolution("st", "il__st_" . $this->request->raw("st"), $subquestion_index);
1676 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1677 $this->ctrl->redirect($this, "suggestedsolution");
1678 }
1679
1680 public function addGIT(): void
1681 {
1682 $subquestion_index = 0;
1683 if (strlen($this->request->raw("subquestion_index")) && $this->request->raw("subquestion_index") >= 0) {
1684 $subquestion_index = $this->request->raw("subquestion_index");
1685 }
1686 $this->object->saveSuggestedSolution("git", "il__git_" . $this->request->raw("git"), $subquestion_index);
1687 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1688 $this->ctrl->redirect($this, "suggestedsolution");
1689 }
1690
1691 public function isSaveCommand(): bool
1692 {
1693 return in_array($this->ctrl->getCmd(), array('save', 'saveEdit', 'saveReturn'));
1694 }
1695
1696 public static function getCommandsFromClassConstants(
1697 string $guiClassName,
1698 string $cmdConstantNameBegin = 'CMD_'
1699 ): array {
1700 $reflectionClass = new ReflectionClass($guiClassName);
1701
1702 $commands = null;
1703
1704 if ($reflectionClass instanceof ReflectionClass) {
1705 $commands = array();
1706
1707 foreach ($reflectionClass->getConstants() as $constName => $constValue) {
1708 if (substr($constName, 0, strlen($cmdConstantNameBegin)) == $cmdConstantNameBegin) {
1709 $commands[] = $constValue;
1710 }
1711 }
1712 }
1713
1714 return $commands;
1715 }
1716
1717 public function setQuestionTabs(): void
1718 {
1719 $this->ilTabs->clearTargets();
1720
1721 $this->setDefaultTabs($this->ilTabs);
1722 $this->setQuestionSpecificTabs($this->ilTabs);
1723 $this->addBackTab($this->ilTabs);
1724 }
1725
1726 protected function setDefaultTabs(ilTabsGUI $ilTabs): void
1727 {
1728 $this->ctrl->setParameterByClass("ilAssQuestionPageGUI", "q_id", $this->request->getQuestionId());
1729 $q_type = $this->object->getQuestionType();
1730
1731 if (strlen($q_type)) {
1732 $classname = $q_type . "GUI";
1733 $this->ctrl->setParameterByClass(strtolower($classname), "sel_question_types", $q_type);
1734 $this->ctrl->setParameterByClass(strtolower($classname), "q_id", $this->request->getQuestionId());
1735 }
1736
1737 if ($this->request->isset("q_id")) {
1738 $this->addTab_Question($ilTabs);
1739 }
1740
1741 // add tab for question feedback within common class assQuestionGUI
1742 $this->addTab_QuestionFeedback($ilTabs);
1743
1744 // add tab for question hint within common class assQuestionGUI
1745 $this->addTab_QuestionHints($ilTabs);
1746
1747 // add tab for question's suggested solution within common class assQuestionGUI
1748 $this->addTab_SuggestedSolution($ilTabs, $classname);
1749
1750 $this->addBackTab($ilTabs);
1751 }
1752
1753 protected function setQuestionSpecificTabs(ilTabsGUI $ilTabs): void
1754 {
1755 }
1756
1757 public function addTab_SuggestedSolution(ilTabsGUI $tabs, string $classname): void
1758 {
1759 if ($this->request->getQuestionId()) {
1760 $tabs->addTarget(
1761 "suggested_solution",
1762 $this->ctrl->getLinkTargetByClass($classname, "suggestedsolution"),
1763 array("suggestedsolution", "saveSuggestedSolution", "outSolutionExplorer", "cancel",
1764 "addSuggestedSolution","cancelExplorer", "linkChilds", "removeSuggestedSolution"
1765 ),
1766 $classname,
1767 ""
1768 );
1769 }
1770 }
1771
1772 final public function getEditQuestionTabCommands(): array
1773 {
1774 return array_merge($this->getBasicEditQuestionTabCommands(), $this->getAdditionalEditQuestionCommands());
1775 }
1776
1777 protected function getBasicEditQuestionTabCommands(): array
1778 {
1779 return array('editQuestion', 'save', 'saveEdit', 'originalSyncForm');
1780 }
1781
1782 protected function getAdditionalEditQuestionCommands(): array
1783 {
1784 return array();
1785 }
1786
1787 protected function addTab_QuestionFeedback(ilTabsGUI $tabs): void
1788 {
1789 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionFeedbackEditingGUI');
1790
1791 $tabLink = $this->ctrl->getLinkTargetByClass('ilAssQuestionFeedbackEditingGUI', ilAssQuestionFeedbackEditingGUI::CMD_SHOW);
1792
1793 $tabs->addTarget('tst_feedback', $tabLink, $tabCommands, $this->ctrl->getCmdClass(), '');
1794 }
1795
1796 protected function addTab_QuestionHints(ilTabsGUI $tabs): void
1797 {
1798 switch ($this->ctrl->getCmdClass()) {
1799 case 'ilassquestionhintsgui':
1800 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionHintsGUI');
1801 break;
1802
1803 case 'ilassquestionhintgui':
1804 $tabCommands = self::getCommandsFromClassConstants('ilAssQuestionHintGUI');
1805 break;
1806
1807 default:
1808
1809 $tabCommands = array();
1810 }
1811
1812 $tabLink = $this->ctrl->getLinkTargetByClass('ilAssQuestionHintsGUI', ilAssQuestionHintsGUI::CMD_SHOW_LIST);
1813
1814 $tabs->addTarget('tst_question_hints_tab', $tabLink, $tabCommands, $this->ctrl->getCmdClass(), '');
1815 }
1816
1817 protected function addTab_Question(ilTabsGUI $tabsGUI): void
1818 {
1819 $tabsGUI->addTarget(
1820 'edit_question',
1821 $this->ctrl->getLinkTargetByClass(
1822 array('ilrepositorygui','ilobjquestionpoolgui', get_class($this)),
1823 'editQuestion'
1824 ),
1825 'editQuestion',
1826 '',
1827 '',
1828 false
1829 );
1830 }
1831
1832 // TODO: OWN "PASS" IN THE REFACTORING getSolutionOutput
1833 abstract public function getSolutionOutput(
1834 $active_id,
1835 $pass = null,
1836 $graphicalOutput = false,
1837 $result_output = false,
1838 $show_question_only = true,
1839 $show_feedback = false,
1840 $show_correct_solution = false,
1841 $show_manual_scoring = false,
1842 $show_question_text = true
1843 ): string;
1844
1845 protected function hasCorrectSolution($activeId, $passIndex): bool
1846 {
1847 $reachedPoints = $this->object->getAdjustedReachedPoints((int) $activeId, (int) $passIndex, true);
1848 $maximumPoints = $this->object->getMaximumPoints();
1849
1850 return $reachedPoints == $maximumPoints;
1851 }
1852
1853 public function isAutosaveable(): bool
1854 {
1855 return $this->object->isAutosaveable();
1856 }
1857
1858 protected function writeQuestionGenericPostData(): void
1859 {
1860 $this->object->setTitle($_POST["title"]);
1861 $this->object->setAuthor($_POST["author"]);
1862 $this->object->setComment($_POST["comment"] ?? '');
1863 if ($this->object->getSelfAssessmentEditingMode()) {
1864 $this->object->setNrOfTries((int) ($_POST['nr_of_tries'] ?? 0));
1865 }
1866
1867 try {
1868 $lifecycle = ilAssQuestionLifecycle::getInstance($_POST['lifecycle']);
1869 $this->object->setLifecycle($lifecycle);
1871 }
1872
1873 $this->object->setQuestion(ilUtil::stripOnlySlashes($_POST['question']));
1874 }
1875
1876 // TODO: OWN "PASS" IN THE REFACTORING getPreview
1877 abstract public function getPreview($show_question_only = false, $showInlineFeedback = false);
1878
1879 final public function outQuestionForTest(
1880 string $formaction,
1881 int $active_id,
1882 ?int $pass,
1883 bool $is_question_postponed = false,
1884 $user_post_solutions = false,
1885 bool $show_specific_inline_feedback = false
1886 ): void {
1887 $formaction = $this->completeTestOutputFormAction($formaction, $active_id, $pass);
1888
1889 $test_output = $this->getTestOutput(
1890 $active_id,
1891 $pass,
1892 $is_question_postponed,
1893 $user_post_solutions,
1894 $show_specific_inline_feedback
1895 );
1896
1897 $this->magicAfterTestOutput();
1898
1899 $this->tpl->setVariable("QUESTION_OUTPUT", $test_output);
1900 $this->tpl->setVariable("FORMACTION", $formaction);
1901 $this->tpl->setVariable("ENCTYPE", 'enctype="' . $this->getFormEncodingType() . '"');
1902 $this->tpl->setVariable("FORM_TIMESTAMP", (string) time());
1903 }
1904
1905 // hey: prevPassSolutions - $pass will be passed always from now on
1906 protected function completeTestOutputFormAction($formAction, $active_id, $pass)
1907 // hey.
1908 {
1909 return $formAction;
1910 }
1911
1912 public function magicAfterTestOutput(): void
1913 {
1914 return;
1915 }
1916
1917 // TODO: OWN "PASS" IN THE REFACTORING getPreview
1918 abstract public function getTestOutput(
1919 $active_id,
1920 $pass,
1921 $is_question_postponed,
1922 $user_post_solutions,
1923 $show_specific_inline_feedback
1924 );
1925
1926 public function getFormEncodingType(): string
1927 {
1928 return self::FORM_ENCODING_URLENCODE;
1929 }
1930
1931 protected function addBackTab(ilTabsGUI $ilTabs): void
1932 {
1933 $this->ctrl->saveParameterByClass(ilAssQuestionPreviewGUI::class, 'prev_qid');
1934 $ilTabs->setBackTarget(
1935 $this->lng->txt('backtocallingpage'),
1936 $this->ctrl->getLinkTargetByClass(ilAssQuestionPreviewGUI::class, ilAssQuestionPreviewGUI::CMD_SHOW)
1937 );
1938 }
1939
1940 public function setPreviewSession(ilAssQuestionPreviewSession $previewSession): void
1941 {
1942 $this->previewSession = $previewSession;
1943 }
1944
1949 {
1950 return $this->previewSession;
1951 }
1952
1954 {
1955 $form = new ilPropertyFormGUI();
1956 $form->setFormAction($this->ctrl->getFormAction($this));
1957 $form->setId($this->getType());
1958 $form->setTitle($this->outQuestionType());
1959 $form->setTableWidth('100%');
1960 $form->setMultipart(true);
1961 return $form;
1962 }
1963
1964 public function showHints(): void
1965 {
1966 $this->ctrl->redirectByClass('ilAssQuestionHintsGUI', ilAssQuestionHintsGUI::CMD_SHOW_LIST);
1967 }
1968
1969 protected function escapeTemplatePlaceholders(string $text): string
1970 {
1971 return str_replace(['{','}'], ['&#123;','&#125;'], $text);
1972 }
1973
1974 protected function buildEditForm(): ilPropertyFormGUI
1975 {
1976 $this->editQuestion(true); // TODO bheyser: editQuestion should be added to the abstract base class with a unified signature
1977 return $this->editForm;
1978 }
1979
1980 public function buildFocusAnchorHtml(): string
1981 {
1982 return '<div id="focus"></div>';
1983 }
1984
1986 {
1987 return true;
1988 }
1989
1990 public function getSubQuestionsIndex(): array
1991 {
1992 return array(0);
1993 }
1994
1995 public function getAnswersFrequency($relevantAnswers, $questionIndex): array
1996 {
1997 return array();
1998 }
1999
2000 public function getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex): ilAnswerFrequencyStatisticTableGUI
2001 {
2002 $table = new ilAnswerFrequencyStatisticTableGUI($parentGui, $parentCmd, $this->object);
2003 $table->setQuestionIndex($questionIndex);
2004 $table->setData($this->getAnswersFrequency($relevantAnswers, $questionIndex));
2005 $table->initColumns();
2006 return $table;
2007 }
2008
2010 {
2011 }
2012
2014 {
2015 }
2016
2018 {
2019 }
2020
2021
2022 protected function generateCorrectnessIconsForCorrectness(int $correctness): string
2023 {
2024 switch ($correctness) {
2025 case self::CORRECTNESS_NOT_OK:
2026 $icon_name = 'icon_not_ok.svg';
2027 $label = $this->lng->txt("answer_is_wrong");
2028 break;
2029 case self::CORRECTNESS_MOSTLY_OK:
2030 $icon_name = 'icon_ok.svg';
2031 $label = $this->lng->txt("answer_is_not_correct_but_positive");
2032 break;
2033 case self::CORRECTNESS_OK:
2034 $icon_name = 'icon_ok.svg';
2035 $label = $this->lng->txt("answer_is_right");
2036 break;
2037 default:
2038 return '';
2039 }
2040 $path = ilUtil::getImagePath($icon_name);
2041 $icon = $this->ui->factory()->symbol()->icon()->custom(
2042 $path,
2043 $label
2044 );
2045 return $this->ui->renderer()->render($icon);
2046 }
2047
2056 public static function prepareTextareaOutput($txt_output, $prepare_for_latex_output = false, $omitNl2BrWhenTextArea = false)
2057 {
2058 $result = $txt_output;
2059 $is_html = false;
2060 if (strlen(strip_tags($result)) < strlen($result)) {
2061 $is_html = true;
2062 }
2063
2064 // removed: did not work with magic_quotes_gpc = On
2065 if (!$is_html) {
2066 if (!$omitNl2BrWhenTextArea) {
2067 // if the string does not contain HTML code, replace the newlines with HTML line breaks
2068 $result = preg_replace("/[\n]/", "<br />", $result);
2069 }
2070 } else {
2071 // patch for problems with the <pre> tags in tinyMCE
2072 if (preg_match_all("/(<pre>.*?<\/pre>)/ims", $result, $matches)) {
2073 foreach ($matches[0] as $found) {
2074 $replacement = "";
2075 if (strpos("\n", $found) === false) {
2076 $replacement = "\n";
2077 }
2078 $removed = preg_replace("/<br\s*?\/>/ims", $replacement, $found);
2079 $result = str_replace($found, $removed, $result);
2080 }
2081 }
2082 }
2083
2084 // since server side mathjax rendering does include svg-xml structures that indeed have linebreaks,
2085 // do latex conversion AFTER replacing linebreaks with <br>. <svg> tag MUST NOT contain any <br> tags.
2086 if ($prepare_for_latex_output) {
2087 $result = ilMathJax::getInstance()->insertLatexImages($result, "<span class\=\"latex\">", "<\/span>");
2088 $result = ilMathJax::getInstance()->insertLatexImages($result, "\[tex\]", "\[\/tex\]");
2089 }
2090
2091 if ($prepare_for_latex_output) {
2092 // replace special characters to prevent problems with the ILIAS template system
2093 // eg. if someone uses {1} as an answer, nothing will be shown without the replacement
2094 $result = str_replace("{", "&#123;", $result);
2095 $result = str_replace("}", "&#125;", $result);
2096 $result = str_replace("\\", "&#92;", $result);
2097 }
2098
2099 return $result;
2100 }
2101
2106 protected function cleanupAnswerText(array $answer_text, bool $is_rte): array
2107 {
2108 if (!is_array($answer_text)) {
2109 return [];
2110 }
2111
2112 if ($is_rte) {
2114 $answer_text,
2115 false,
2117 );
2118 }
2119
2121 $answer_text,
2122 true,
2123 self::ALLOWED_PLAIN_TEXT_TAGS
2124 );
2125 }
2126
2127 protected function addSaveOnEnterOnLoadCode(): void
2128 {
2129 $this->tpl->addOnloadCode("
2130 let form = document.querySelector('#ilContentContainer form');
2131 let button = form.querySelector('input[name=\"cmd[save]\"]');
2132 if (button === null) {
2133 button = form.querySelector('input[name=\"cmd[saveFQ]\"]');
2134 };
2135 if (form && button) {
2136 form.addEventListener('keydown', function (e) {
2137 if (e.key === 'Enter'
2138 && e.target.type !== 'textarea'
2139 && e.target.type !== 'submit'
2140 && e.target.type !== 'file'
2141 ) {
2142 e.preventDefault();
2143 form.requestSubmit(button);
2144 }
2145 })
2146 }
2147 ");
2148 }
2149}
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
populateTaxonomyFormSection(ilPropertyFormGUI $form)
getAsValueAttribute(string $a_value)
outAdditionalOutput()
Why are you here? Some magic for plugins?
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
const SESSION_PREVIEW_DATA_BASE_INDEX
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)
static _getClassNameForQType($q_type)
getType()
needed for page editor compliance
const ALLOWED_PLAIN_TEXT_TAGS
sk - 12.05.2023: This const is also used in ilKprimChoiceWizardInputGUI.
addTab_QuestionHints(ilTabsGUI $tabs)
addTab_Question(ilTabsGUI $tabsGUI)
ilObjectDataCache $ilObjDataCache
setTargetGuiClass($targetGuiClass)
setQuestionSpecificTabs(ilTabsGUI $ilTabs)
getContextPath($cont_obj, int $a_endnode_id, int $a_startnode_id=1)
get context path in content object tree
callNewIdListeners(int $a_new_id)
static getCommandsFromClassConstants(string $guiClassName, string $cmdConstantNameBegin='CMD_')
getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex)
static _getQuestionGUI(string $question_type='', int $question_id=-1)
Creates a question gui representation and returns the alias to the question gui.
int $sequence_no
sequence number in test
escapeTemplatePlaceholders(string $text)
setPreviousSolutionPrefilled(bool $previousSolutionPrefilled)
setPresentationContext(string $presentationContext)
setTargetGui($linkTargetGui)
originalSyncForm(string $return_to="", string $return_to_feedback='')
setRenderPurpose(string $renderPurpose)
getILIASPage(string $html="")
Returns the ILIAS Page around a question.
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
ilComponentRepository $component_repository
ilGlobalPageTemplate $tpl
prepareReprintableCorrectionsForm(ilPropertyFormGUI $form)
addBasicQuestionFormProperties(ilPropertyFormGUI $form)
setQuestionHeaderBlockBuilder(\ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder)
renderEditForm(ilPropertyFormGUI $form)
static prepareTextareaOutput($txt_output, $prepare_for_latex_output=false, $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output where latex code may be in it If the text is HTML-free,...
int $question_count
question count in test
cleanupAnswerText(array $answer_text, bool $is_rte)
sk - 12.05.2023: This is one more of those that we need, but don't want.
addTab_SuggestedSolution(ilTabsGUI $tabs, string $classname)
getTestOutput( $active_id, $pass, $is_question_postponed, $user_post_solutions, $show_specific_inline_feedback)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
genericFeedbackOutputBuilder(string $feedback_correct, string $feedback_incorrect, int $active_id, ?int $pass)
ILIAS Notes GUIService $notes_gui
getGenericFeedbackOutput(int $active_id, ?int $pass)
static _getGUIClassNameForId($a_q_id)
getSpecificFeedbackOutput(array $userSolution)
Returns the answer specific feedback for the question.
ILIAS TestQuestionPool InternalRequestService $request
getAnswersFrequency($relevantAnswers, $questionIndex)
addErrorMessage(string $errormessage)
populateJavascriptFilesRequiredForWorkForm(ilGlobalTemplateInterface $tpl)
setEditContext(string $editContext)
setPreviewSession(ilAssQuestionPreviewSession $previewSession)
setQuestionCount(int $a_question_count)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder
outQuestionForTest(string $formaction, int $active_id, ?int $pass, bool $is_question_postponed=false, $user_post_solutions=false, bool $show_specific_inline_feedback=false)
setDefaultTabs(ilTabsGUI $ilTabs)
setErrorMessage(string $errormessage)
completeTestOutputFormAction($formAction, $active_id, $pass)
ilTestQuestionNavigationGUI $navigationGUI
addBackTab(ilTabsGUI $ilTabs)
ilPropertyFormGUI $editForm
writePostData(bool $always=false)
Evaluates a posted edit form and writes the form data in the question object.
setTaxonomyIds(array $taxonomyIds)
hasCorrectSolution($activeId, $passIndex)
addNewIdListener($a_object, string $a_method, string $a_parameters="")
Add a listener that is notified with the new question ID, when a new question is saved.
addTab_QuestionFeedback(ilTabsGUI $tabs)
setQuestionActionCmd(string $questionActionCmd)
generateCorrectnessIconsForCorrectness(int $correctness)
getPreview($show_question_only=false, $showInlineFeedback=false)
ilAccessHandler $access
setNavigationGUI(?ilTestQuestionNavigationGUI $navigationGUI)
addNumberOfTriesToFormIfNecessary(ilPropertyFormGUI $form)
Abstract basic class which is to be extended by the concrete assessment question type classes.
const ADDITIONAL_CONTENT_EDITING_MODE_IPE
static _getQuestionTypeName($type_tag)
static _questionExistsInTest(int $question_id, int $test_id)
static _includeClass(string $question_type, int $gui=0)
static getQuestionTypeFromDb(int $question_id)
static getFeedbackClassNameByQuestionType(string $questionType)
static _getInternalLinkHref(string $target="")
static _isWriteable(int $question_id, int $user_id)
static stripSlashesRecursive($a_data, bool $a_strip_html=true, string $a_allow="")
const CMD_SHOW_LIST
command constants
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getSelfAssessmentTags()
Get tags allowed in question tags in self assessment mode.
This class represents a checkbox property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilCtrl provides processing control methods.
This class represents a file property in a property form.
static makeDirParents(string $a_dir)
Create a new directory and all parent directories.
static renameExecutables(string $a_dir)
static moveUploadedFile(string $a_file, string $a_name, string $a_target, bool $a_raise_errors=true, string $a_mode="move_uploaded")
move uploaded file
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
addJavaScript(string $a_js_file, bool $a_add_version_parameter=true, int $a_batch=2)
Add a javascript file that should be included in the header.
setVariable(string $variable, $value='')
Sets the given variable to the given value.
Help GUI class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getPageList(int $lm_id)
language handling
static prepareFormOutput($a_str, bool $a_strip=false)
static getInstance()
Singleton: get instance for use in ILIAS requests with a config loaded from the settings.
Notes GUI class.
This class represents a number property in a property form.
static _getUsedHTMLTags(string $a_module="")
Returns an array of all allowed HTML tags for text editing.
static _getUsedHTMLTagsAsString(string $a_module="")
Returns a string of all allowed HTML tags for text editing.
Class ilObjContentObjectGUI.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getManualFeedback($active_id, $question_id, $pass)
Retrieves the feedback comment for a question in a test if it is finalized.
User class.
setPref(string $a_keyword, ?string $a_value)
writePref(string $a_keyword, string $a_value)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getInstanceByRefId(int $ref_id, bool $stop_on_error=true)
get an instance of an Ilias object by reference id
static _lookupType(int $id, bool $reference=false)
This class represents a property form user interface.
addCommandButton(string $a_cmd, string $a_text, string $a_id="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a property in a property form.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This class represents a selection list property in a property form.
static get(string $a_var)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
setBackTarget(string $a_title, string $a_target, string $a_frame="")
setTabActive(string $a_id)
addTarget(string $a_text, string $a_link, $a_cmd="", $a_cmdClass="", string $a_frame="", bool $a_activate=false, bool $a_dir_text=false)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
special template class to simplify handling of ITX/PEAR
static getReturnToPageLink($q_id=null)
This class represents a text area property in a property form.
This class represents a text property in a property form.
static stripOnlySlashes(string $a_str)
static getImagePath(string $img, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
static redirect(string $a_script)
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
global $DIC
Definition: feed.php:28
$ilUser
Definition: imgupload.php:34
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Readable part of repository interface to ilComponentDataDB.
Interface ilDBInterface.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
exit
Definition: login.php:28
$ref_id
Definition: ltiauth.php:67
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
$i
Definition: metadata.php:41
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
$type
$_GET['client_id']
Definition: saml1-acs.php:21
$rows
Definition: xhr_table.php:10