ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
class.assQuestionGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
29use ILIAS\Refinery\Factory as Refinery;
31
36abstract class assQuestionGUI
37{
43 public const ALLOWED_PLAIN_TEXT_TAGS = "<em>, <strong>";
44
45 public const SESSION_PREVIEW_DATA_BASE_INDEX = 'ilAssQuestionPreviewAnswers';
46
47 public const FORM_MODE_EDIT = 'edit';
48 public const FORM_MODE_ADJUST = 'adjust';
49
50 public const FORM_ENCODING_URLENCODE = 'application/x-www-form-urlencoded';
51 public const FORM_ENCODING_MULTIPART = 'multipart/form-data';
52
53 public const CORRECTNESS_NOT_OK = 0;
54 public const CORRECTNESS_MOSTLY_OK = 1;
55 public const CORRECTNESS_OK = 2;
56
57 public const RENDER_PURPOSE_PLAYBACK = 'renderPurposePlayback';
58 public const RENDER_PURPOSE_DEMOPLAY = 'renderPurposeDemoplay';
59 public const RENDER_PURPOSE_PREVIEW = 'renderPurposePreview';
60 public const RENDER_PURPOSE_PRINT_PDF = 'renderPurposePrintPdf';
61 public const RENDER_PURPOSE_INPUT_VALUE = 'renderPurposeInputValue';
62
63 public const EDIT_CONTEXT_AUTHORING = 'authoring';
64 public const EDIT_CONTEXT_ADJUSTMENT = 'adjustment';
65
66 public const PRESENTATION_CONTEXT_TEST = 'pContextTest';
67 public const PRESENTATION_CONTEXT_RESULTS = 'pContextResults';
68
69 protected const HAS_SPECIAL_QUESTION_COMMANDS = false;
70
71 protected const SUGGESTED_SOLUTION_COMMANDS_CANCEL = 'cancelSuggestedSolution';
72 protected const SUGGESTED_SOLUTION_COMMANDS_SAVE = 'saveSuggestedSolution';
73 protected const SUGGESTED_SOLUTION_COMMANDS_DEFAULT = 'suggestedsolution';
74
75 private const CMD_SAVE = 'save';
76 private const CMD_SAVE_AND_RETURN = 'saveReturn';
77
78 private const CMD_SYNC_QUESTION = 'syncQuestion';
79 public const CMD_SYNC_QUESTION_AND_RETURN = 'syncQuestionReturn';
80
81 protected const QUESTION_SAVE_CMDS = [
84 ];
85
94 'uploadchoice',
95 'uploadImage',
96 'changeToPictures',
97 'uploadElementImage',
98 'uploadterms',
99 'uploaddefintions'
100 ];
101
102 protected UIServices $ui;
108 private ilTree $tree;
110 protected ilLogger $logger;
114 protected ilCtrl $ctrl;
119 protected ilLanguage $lng;
120 protected Refinery $refinery;
121
122 protected $error;
123 protected string $errormessage;
124
126 protected int $sequence_no;
127
129 protected int $question_count;
130
131 private $taxonomyIds = [];
132
133 private $targetGuiClass = null;
134
135 private string $questionActionCmd = 'handleQuestionAction';
136
138
140
141
142 private ?string $presentationContext = null;
143
145
147
148 private bool $previousSolutionPrefilled = false;
149
150 protected ?ilPropertyFormGUI $editForm = null;
153 protected bool $parent_type_is_lm = false;
154
156 private ?string $copy_to_new_pool_on_save = null;
157
158 private ?int $move_after_question_with_id = null;
159 private bool $context_allows_sync_to_pool = false;
160 private string $question_sync_modal = '';
161
162 public function __construct()
163 {
165 global $DIC;
166 $this->lng = $DIC['lng'];
167 $this->tpl = $DIC['tpl'];
168 $this->ctrl = $DIC['ilCtrl'];
169 $this->current_user = $DIC['ilUser'];
170 $this->ui = $DIC->ui();
171 $this->ilObjDataCache = $DIC['ilObjDataCache'];
172 $this->access = $DIC->access();
173 $this->ilHelp = $DIC['ilHelp'];
174 $this->tabs_gui = $DIC['ilTabs'];
175 $this->rbacsystem = $DIC['rbacsystem'];
176 $this->tree = $DIC['tree'];
177 $this->db = $DIC->database();
178 $this->logger = $DIC['ilLog'];
179 $this->component_repository = $DIC['component.repository'];
180 $this->refinery = $DIC['refinery'];
181
182 $local_dic = QuestionPoolDIC::dic();
183 $this->forms_helper = new ilTestLegacyFormsHelper();
184 $this->request_data_collector = $local_dic['request_data_collector'];
185 $this->questionrepository = $local_dic['question.general_properties.repository'];
186
187 $this->errormessage = $this->lng->txt("fill_out_all_required_fields");
188 $this->notes_gui = $DIC->notes()->gui();
189 }
190
191 abstract public function editQuestion(
192 bool $checkonly = false,
193 ?bool $is_save_cmd = null
194 ): bool;
195
200 abstract public function getSpecificFeedbackOutput(array $userSolution): string;
201
202 abstract public function getSolutionOutput(
203 int $active_id,
204 ?int $pass = null,
205 bool $graphical_output = false,
206 bool $result_output = false,
207 bool $show_question_only = true,
208 bool $show_feedback = false,
209 bool $show_correct_solution = false,
210 bool $show_manual_scoring = false,
211 bool $show_question_text = true,
212 bool $show_inline_feedback = true
213 ): string;
214
215 abstract public function getPreview(
216 bool $show_question_only = false,
217 bool $show_inline_feedback = false
218 ): string;
219
220 abstract public function getTestOutput(
221 int $active_id,
222 int $pass,
223 bool $is_question_postponed = false,
224 array|bool $user_post_solutions = false,
225 bool $show_specific_inline_feedback = false
226 ): string;
227
228 public function renderSolutionOutput(
229 mixed $user_solutions,
230 int $active_id,
231 ?int $pass,
232 bool $graphical_output = false,
233 bool $result_output = false,
234 bool $show_question_only = true,
235 bool $show_feedback = false,
236 bool $show_correct_solution = false,
237 bool $show_manual_scoring = false,
238 bool $show_question_text = true,
239 bool $show_autosave_title = false,
240 bool $show_inline_feedback = false,
241 ): ?string {
242 return null;
243 }
244
250 public function getObject(): assQuestion
251 {
252 return $this->object;
253 }
254
259 public function setObject(assQuestion $question): void
260 {
261 $this->object = $question;
262 }
263
264 public function setCopyToExistingPoolOnSave(?int $pool_ref_id): void
265 {
266 $this->copy_to_existing_pool_on_save = $pool_ref_id;
267 }
268
270 {
271 return $this->copy_to_existing_pool_on_save;
272 }
273
274 public function setCopyToNewPoolOnSave(?string $pool_title): void
275 {
276 $this->copy_to_new_pool_on_save = $pool_title;
277 }
278
279 public function getCopyToNewPoolOnSave(): ?string
280 {
281 return $this->copy_to_new_pool_on_save;
282 }
283
284 public function setMoveAfterQuestionId(?int $question_id): void
285 {
286 $this->move_after_question_with_id = $question_id;
287 }
288
289 public function getMoveAfterQuestionId(): ?int
290 {
291 return $this->move_after_question_with_id;
292 }
293
294 public function hasInlineFeedback(): bool
295 {
296 return false;
297 }
298
299 public function addHeaderAction(): void
300 {
301 }
302
303 public function redrawHeaderAction(): void
304 {
305 echo $this->getHeaderAction() . $this->ui->mainTemplate()->getOnLoadCodeForAsynch();
306 exit;
307 }
308
309 public function getHeaderAction(): string
310 {
311 $parentObjType = $this->ilObjDataCache->lookupType($this->object->getObjId());
312
313 $dispatcher = new ilCommonActionDispatcherGUI(
315 $this->access,
316 $parentObjType,
317 $this->request_data_collector->getRefId(),
318 $this->object->getObjId()
319 );
320
321 $dispatcher->setSubObject("quest", $this->object->getId());
322
323 $ha = $dispatcher->initHeaderAction();
324 $ha->enableComments(true, false);
325
326 return $ha->getHeaderAction($this->ui->mainTemplate());
327 }
328
329 public function getCommentsPanelHTML(): string
330 {
331 $comment_gui = new ilCommentGUI($this->object->getObjId(), $this->object->getId(), 'quest');
332 return $comment_gui->getListHTML();
333 }
334
335 public function executeCommand()
336 {
337 $this->ilHelp->setScreenIdComponent('qpl');
338
339 $next_class = $this->ctrl->getNextClass($this);
340
341 switch ($next_class) {
342 case 'ilformpropertydispatchgui':
343 $form = $this->buildEditForm();
344 $form_prop_dispatch = new ilFormPropertyDispatchGUI();
345 $form_prop_dispatch->setItem($form->getItemByPostVar(ilUtil::stripSlashes($this->request_data_collector->string('postvar'))));
346 $this->ctrl->forwardCommand($form_prop_dispatch);
347 break;
348 default:
349 $cmd = $this->ctrl->getCmd('editQuestion');
350 switch ($cmd) {
351 case self::SUGGESTED_SOLUTION_COMMANDS_CANCEL:
352 case self::SUGGESTED_SOLUTION_COMMANDS_SAVE:
353 case self::SUGGESTED_SOLUTION_COMMANDS_DEFAULT:
354 case 'saveSuggestedSolutionType':
355 case 'saveContentsSuggestedSolution':
356 case 'deleteSuggestedSolution':
357 case 'linkChilds':
358 case 'cancelExplorer':
359 case 'outSolutionExplorer':
360 case 'addST':
361 case 'addPG':
362 case 'addGIT':
363 case 'save':
364 case 'saveReturn':
365 case self::CMD_SYNC_QUESTION:
366 case self::CMD_SYNC_QUESTION_AND_RETURN:
367 case 'editQuestion':
368 $this->$cmd();
369 break;
370 default:
371 if (method_exists($this, $cmd)) {
372 $this->$cmd();
373 }
374 }
375 }
376 }
377
378 protected function hasSpecialQuestionCommands(): bool
379 {
380 return static::HAS_SPECIAL_QUESTION_COMMANDS;
381 }
382
384 public function getType(): string
385 {
386 return $this->getQuestionType();
387 }
388
389 public function getPresentationContext(): ?string
390 {
391 return $this->presentationContext;
392 }
393
394 public function setPresentationContext(string $presentationContext): void
395 {
396 $this->presentationContext = $presentationContext;
397 }
398
399 public function isTestPresentationContext(): bool
400 {
401 return $this->getPresentationContext() == self::PRESENTATION_CONTEXT_TEST;
402 }
403
404 // hey: previousPassSolutions - setter/getter for Previous Solution Prefilled flag
405 public function isPreviousSolutionPrefilled(): bool
406 {
407 return $this->previousSolutionPrefilled;
408 }
409
410 public function setPreviousSolutionPrefilled(bool $previousSolutionPrefilled): void
411 {
412 $this->previousSolutionPrefilled = $previousSolutionPrefilled;
413 }
414 // hey.
415
416 public function getRenderPurpose(): string
417 {
418 return $this->renderPurpose;
419 }
420
421 public function setRenderPurpose(string $renderPurpose): void
422 {
423 $this->renderPurpose = $renderPurpose;
424 }
425
426 public function isRenderPurposePrintPdf(): bool
427 {
428 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PRINT_PDF;
429 }
430
431 public function isRenderPurposePreview(): bool
432 {
433 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PREVIEW;
434 }
435
436 public function isRenderPurposeInputValue(): bool
437 {
438 return $this->getRenderPurpose() == self::RENDER_PURPOSE_INPUT_VALUE;
439 }
440
441 public function isRenderPurposePlayback(): bool
442 {
443 return $this->getRenderPurpose() == self::RENDER_PURPOSE_PLAYBACK;
444 }
445
446 public function isRenderPurposeDemoplay(): bool
447 {
448 return $this->getRenderPurpose() == self::RENDER_PURPOSE_DEMOPLAY;
449 }
450
451 public function renderPurposeSupportsFormHtml(): bool
452 {
453 if ($this->isRenderPurposePrintPdf()) {
454 return false;
455 }
456
457 if ($this->isRenderPurposeInputValue()) {
458 return false;
459 }
460
461 return true;
462 }
463
464 public function getEditContext(): string
465 {
466 return $this->editContext;
467 }
468
469 public function setEditContext(string $editContext): void
470 {
471 $this->editContext = $editContext;
472 }
473
474 public function isAuthoringEditContext(): bool
475 {
476 return $this->getEditContext() == self::EDIT_CONTEXT_AUTHORING;
477 }
478
479 public function isAdjustmentEditContext(): bool
480 {
481 return $this->getEditContext() == self::EDIT_CONTEXT_ADJUSTMENT;
482 }
483
484 public function setAdjustmentEditContext(): void
485 {
486 $this->setEditContext(self::EDIT_CONTEXT_ADJUSTMENT);
487 }
488
490 {
491 return $this->navigationGUI;
492 }
493
494 public function setNavigationGUI(?ilTestQuestionNavigationGUI $navigationGUI): void
495 {
496 $this->navigationGUI = $navigationGUI;
497 }
498
499 public function setTaxonomyIds(array $taxonomyIds): void
500 {
501 $this->taxonomyIds = $taxonomyIds;
502 }
503
504 public function getTaxonomyIds(): array
505 {
506 return $this->taxonomyIds;
507 }
508
509 public function setTargetGui($linkTargetGui): void
510 {
511 $this->setTargetGuiClass(get_class($linkTargetGui));
512 }
513
514 public function setTargetGuiClass(string $targetGuiClass): void
515 {
516 $this->targetGuiClass = $targetGuiClass;
517 }
518
519 public function getTargetGuiClass(): ?string
520 {
521 return $this->targetGuiClass;
522 }
523
524 public function setQuestionHeaderBlockBuilder(\ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder): void
525 {
526 $this->questionHeaderBlockBuilder = $questionHeaderBlockBuilder;
527 }
528
529 // fau: testNav - get the question header block bulder (for tweaking)
531 {
532 return $this->questionHeaderBlockBuilder;
533 }
534 // fau.
535
536 public function setQuestionActionCmd(string $questionActionCmd): void
537 {
538 $this->questionActionCmd = $questionActionCmd;
539
540 if (is_object($this->object)) {
541 $this->object->questionActionCmd = $questionActionCmd;
542 }
543 }
544
545 public function getQuestionActionCmd(): string
546 {
547 return $this->questionActionCmd;
548 }
549
554 protected function writePostData(bool $always = false): int
555 {
556 return 0;
557 }
558
559 public function assessment(): void
560 {
561 $stats_table = new ilQuestionCumulatedStatisticsTableGUI($this, 'assessment', '', $this->object, $this->questionrepository);
562 $usage_table = new ilQuestionUsagesTableGUI($this, 'assessment', '', $this->object);
563
564 $this->tpl->setContent(implode('<br />', [
565 $stats_table->getHTML(),
566 $usage_table->getHTML()
567 ]));
568 }
569
573 public static function _getQuestionGUI(string $question_type = '', int $question_id = -1): ?assQuestionGUI
574 {
575 global $DIC;
576 $ilCtrl = $DIC['ilCtrl'];
577 $ilDB = $DIC['ilDB'];
578 $lng = $DIC['lng'];
579
580 if (($question_type === '') && ($question_id > 0)) {
581 $question_type = QuestionPoolDIC::dic()['question.general_properties.repository']
582 ->getForQuestionId($question_id)?->getClassName();
583 }
584
585 if ($question_type === null || $question_type === '') {
586 return null;
587 }
588
589 $question_type_gui = $question_type . 'GUI';
590 $question = new $question_type_gui();
591
592 $feedbackObjectClassname = assQuestion::getFeedbackClassNameByQuestionType($question_type);
593 $question->object->feedbackOBJ = new $feedbackObjectClassname($question->object, $ilCtrl, $ilDB, $lng);
594
595 if ($question_id > 0) {
596 $question->object->loadFromDb($question_id);
597 }
598
599 return $question;
600 }
601
603 {
604 foreach ($this->getPresentationJavascripts() as $jsFile) {
605 $tpl->addJavaScript($jsFile);
606 }
607 }
608
609 public function getPresentationJavascripts(): array
610 {
611 return [];
612 }
613
614 public function getQuestionTemplate(): void
615 {
616 // @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
617 if (!$this->tpl->blockExists('adm_content')) {
618 $this->tpl->addBlockFile('ADM_CONTENT', 'adm_content', 'tpl.il_as_question.html', 'components/ILIAS/TestQuestionPool');
619 }
620 }
621
622 protected function renderEditForm(ilPropertyFormGUI $form): void
623 {
624 $this->addSaveOnEnterOnLoadCode();
625 $this->getQuestionTemplate();
626 $this->tpl->setVariable('QUESTION_DATA', $form->getHTML() . $this->question_sync_modal);
627 }
628
632 public function getILIASPage(string $html = ""): string
633 {
634 $page_gui = new ilAssQuestionPageGUI($this->object->getId());
635 $page_gui->setFileDownloadLink($this->buildFileDownloadLink());
636 $page_gui->setQuestionHTML(
637 [$this->object->getId() => $html]
638 );
639 $presentation = $page_gui->presentation();
640 $presentation = preg_replace("/src=\"\\.\\//ims", "src=\"" . ILIAS_HTTP_PATH . "/", $presentation);
641 return $presentation;
642 }
643
644 public function outQuestionPage($a_temp_var, $a_postponed = false, $active_id = "", $html = "", $inlineFeedbackEnabled = false): string
645 {
646 if ($this->object->getTestPresentationConfig()->isSolutionInitiallyPrefilled()) {
647 // hey
648 $this->tpl->setOnScreenMessage('info', $this->getPreviousSolutionProvidedMessage());
649 $html .= $this->getPreviousSolutionConfirmationCheckboxHtml();
650 } elseif ($this->object->getTestPresentationConfig()->isUnchangedAnswerPossible()) {
651 $html .= $this->getUseUnchangedAnswerCheckboxHtml();
652 }
653
654 $this->lng->loadLanguageModule("content");
655
656 $page_gui = new ilAssQuestionPageGUI($this->object->getId());
657 $page_gui->setFileDownloadLink($this->buildFileDownloadLink());
658 $page_gui->setOutputMode('presentation');
659 $page_gui->setTemplateTargetVar($a_temp_var);
660
661 if ($this->getNavigationGUI()) {
662 $html .= $this->getNavigationGUI()->getHTML();
663 $page_gui->setQuestionActionsHTML($this->getNavigationGUI()->getActionsHTML());
664 }
665
666 if (strlen($html)) {
667 $page_gui->setQuestionHTML([$this->object->getId() => $html]);
668 }
669
670 $page_gui->setPresentationTitle($this->questionHeaderBlockBuilder->getPresentationTitle());
671 $page_gui->setQuestionInfoHTML($this->questionHeaderBlockBuilder->getQuestionInfoHTML());
672
673 return $page_gui->presentation();
674 }
675
676 protected function getUseUnchangedAnswerCheckboxHtml(): string
677 {
678 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'components/ILIAS/TestQuestionPool');
679 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->object->getTestPresentationConfig()->getUseUnchangedAnswerLabel());
680 return $tpl->get();
681 }
682
683 protected function getPreviousSolutionProvidedMessage(): string
684 {
685 return $this->lng->txt('use_previous_solution_advice');
686 }
687
689 {
690 $tpl = new ilTemplate('tpl.tst_question_additional_behaviour_checkbox.html', true, true, 'components/ILIAS/TestQuestionPool');
691 $tpl->setVariable('TXT_FORCE_FORM_DIFF_LABEL', $this->lng->txt('use_previous_solution'));
692 return $tpl->get();
693 }
694
695 public function syncQuestion(): void
696 {
697 $original_id = $this->object->getOriginalId();
698 if ($original_id !== null) {
699 $this->object->syncWithOriginal();
700 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
701 }
702 }
703
704 public function saveReturn(): void
705 {
706 $old_id = $this->request_data_collector->getQuestionId();
707 $this->setAdditionalContentEditingModeFromPost();
708 $result = $this->writePostData();
709 if ($result !== 0) {
710 $this->tabs_gui->setTabActive('edit_question');
711 return;
712 }
713
714 $this->object->getCurrentUser()->setPref('tst_lastquestiontype', $this->object->getQuestionType());
715 $this->object->getCurrentUser()->writePref('tst_lastquestiontype', $this->object->getQuestionType());
716 $this->object->saveToDb($old_id);
717
718 $this->questionrepository->questionExistsInPool($this->object->getOriginalId());
719 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
720 $this->ctrl->redirectByClass(ilAssQuestionPreviewGUI::class, ilAssQuestionPreviewGUI::CMD_SHOW);
721 }
722
723 public function saveQuestion(): bool
724 {
725 $this->setAdditionalContentEditingModeFromPost();
726 $result = $this->writePostData();
727 $this->tabs_gui->activateTab('edit_question');
728
729 if ($result !== 0) {
730 return false;
731 }
732
733 $this->object->getCurrentUser()->setPref('tst_lastquestiontype', $this->object->getQuestionType());
734 $this->object->getCurrentUser()->writePref('tst_lastquestiontype', $this->object->getQuestionType());
735
736 if ($this->request_data_collector->getQuestionId() === 0) {
737 if ($this->object->getId() < 1) {
738 $this->object->createNewQuestion();
739 }
740 $this->setQuestionTabs();
741 }
742
743 if ($this->needsSyncQuery()) {
744 $cmd = strpos($this->ctrl->getCmd(), 'Return') === false
745 ? self::CMD_SYNC_QUESTION
746 : self::CMD_SYNC_QUESTION_AND_RETURN;
747 $this->question_sync_modal = $this->getQuestionSyncModal($cmd);
748 }
749
750 $this->object->saveToDb();
751 return true;
752 }
753
755 {
756 $additional_content_editing_mode = $this->request_data_collector->string('additional_content_editing_mode');
757 if ($additional_content_editing_mode !== ''
758 && in_array($additional_content_editing_mode, $this->object->getValidAdditionalContentEditingModes())) {
759 $this->object->setAdditionalContentEditingMode($additional_content_editing_mode);
760 }
761 }
762
763 protected function setTestSpecificProperties(): void
764 {
765 if ($this->request_data_collector->isset('pool_ref')) {
766 $this->copy_to_existing_pool_on_save = $this->request_data_collector->int('pool_ref');
767 }
768
769 if ($this->request_data_collector->isset('pool_title')) {
770 $this->copy_to_new_pool_on_save = $this->request_data_collector->string('pool_title');
771 }
772
773 if ($this->request_data_collector->isset('move_after_question_with_id')) {
774 $this->move_after_question_with_id = $this->request_data_collector->int('move_after_question_with_id');
775 }
776 }
777
781 public function getContextPath($cont_obj, int $a_endnode_id, int $a_startnode_id = 1): string
782 {
783 $path = "";
784
785 $tmpPath = $cont_obj->getLMTree()->getPathFull($a_endnode_id, $a_startnode_id);
786
787 // count -1, to exclude the learning module itself
788 for ($i = 1; $i < (count($tmpPath) - 1); $i++) {
789 if ($path != "") {
790 $path .= " > ";
791 }
792
793 $path .= $tmpPath[$i]["title"];
794 }
795
796 return $path;
797 }
798
799 public function setSequenceNumber(int $nr): void
800 {
801 $this->sequence_no = $nr;
802 }
803
804 public function getSequenceNumber(): int
805 {
806 return $this->sequence_no;
807 }
808
809 public function setQuestionCount(int $a_question_count): void
810 {
811 $this->question_count = $a_question_count;
812 }
813
814 public function getQuestionCount(): int
815 {
816 return $this->question_count;
817 }
818
819 public function getErrorMessage(): string
820 {
821 return $this->errormessage;
822 }
823
824 public function setErrorMessage(string $errormessage): void
825 {
826 $this->errormessage = $errormessage;
827 }
828
829 public function addErrorMessage(string $errormessage): void
830 {
831 $this->errormessage .= ((strlen($this->errormessage)) ? "<br />" : "") . $errormessage;
832 }
833
834 public function getQuestionType(): string
835 {
836 return $this->object->getQuestionType();
837 }
838
839 public function getAsValueAttribute(string $a_value): string
840 {
841 $result = "";
842 if (strlen($a_value)) {
843 $result = " value=\"$a_value\" ";
844 }
845 return $result;
846 }
847
849 {
850 if (!$this->object->getSelfAssessmentEditingMode()) {
851 $form->addCommandButton("saveReturn", $this->lng->txt("save_return"));
852 }
853 $form->addCommandButton("save", $this->lng->txt("save"));
854 }
855
857 {
858 $title = new ilTextInputGUI($this->lng->txt('title'), 'title');
859 $title->setMaxLength(100);
860 $title->setValue($this->object->getTitle());
861 $title->setRequired(true);
862 $form->addItem($title);
863
864 if (!$this->object->getSelfAssessmentEditingMode()) {
865 // author
866 $author = new ilTextInputGUI($this->lng->txt('author'), 'author');
867 $author->setValue($this->object->getAuthor());
868 $author->setMaxLength(512);
869 $author->setRequired(true);
870 $form->addItem($author);
871
872 // description
873 $description = new ilTextInputGUI($this->lng->txt('description'), 'comment');
874 $description->setValue($this->object->getComment());
875 $description->setRequired(false);
876 $description->setMaxLength(1000);
877 $form->addItem($description);
878 } else {
879 // author as hidden field
880 $hi = new ilHiddenInputGUI('author');
881 $author = ilLegacyFormElementsUtil::prepareFormOutput($this->object->getAuthor());
882 if (trim($author) == "") {
883 $author = "-";
884 }
885 $hi->setValue($author);
886 $form->addItem($hi);
887 }
888
889 // lifecycle
890 $lifecycle = new ilSelectInputGUI($this->lng->txt('qst_lifecycle'), 'lifecycle');
891 $lifecycle->setOptions($this->object->getLifecycle()->getSelectOptions($this->lng));
892 $lifecycle->setValue($this->object->getLifecycle()->getIdentifier());
893 $form->addItem($lifecycle);
894
895 // questiontext
896 $question = new ilTextAreaInputGUI($this->lng->txt('question'), 'question');
897 $question->setValue($this->object->getQuestion());
898 $question->setRequired(true);
899 $question->setRows(10);
900 $question->setCols(80);
901
902 if (!$this->object->getSelfAssessmentEditingMode()) {
903 if ($this->object->getAdditionalContentEditingMode() !== assQuestion::ADDITIONAL_CONTENT_EDITING_MODE_IPE) {
904 $question->setUseRte(true);
905 $question->setRteTags(ilRTESettings::_getUsedHTMLTags('assessment'));
906 $question->setRTESupport($this->object->getId(), 'qpl', 'assessment');
907 }
908 } else {
910 $question->setUseTagsForRteOnly(false);
911 }
912 $question->setInfo($this->lng->txt('latex_edit_info'));
913 $form->addItem($question);
914
915 $question_type = new ilHiddenInputGUI('question_type');
916 $question_type->setValue((string) $this->getQuestionType());
917 $form->addItem($question_type);
918
919 if ($this->copy_to_existing_pool_on_save !== null) {
920 $pool_ref = new ilHiddenInputGUI('pool_ref');
921 $pool_ref->setValue((string) $this->copy_to_existing_pool_on_save);
922 $form->addItem($pool_ref);
923 }
924
925 if ($this->copy_to_new_pool_on_save !== null) {
926 $pool_title = new ilHiddenInputGUI('pool_title');
927 $pool_title->setValue($this->copy_to_new_pool_on_save);
928 $form->addItem($pool_title);
929 }
930
931 if ($this->move_after_question_with_id !== null) {
932 $move_after_question_id = new ilHiddenInputGUI('move_after_question_with_id');
933 $move_after_question_id->setValue((string) $this->move_after_question_with_id);
934 $form->addItem($move_after_question_id);
935 }
936
937 $additional_content_editing_mode = new ilHiddenInputGUI('additional_content_editing_mode');
938 $additional_content_editing_mode->setValue($this->object->getAdditionalContentEditingMode());
939 $form->addItem($additional_content_editing_mode);
940
941 $this->addNumberOfTriesToFormIfNecessary($form);
942 }
943
945 {
946 if (!$this->object->getSelfAssessmentEditingMode()) {
947 return;
948 }
949
950 $nr_tries = $this->object->getNrOfTries() ?? $this->object->getDefaultNrOfTries();
951
952 if ($nr_tries < 1) {
953 $nr_tries = "";
954 }
955
956 $ni = new ilNumberInputGUI($this->lng->txt("qst_nr_of_tries"), "nr_of_tries");
957 $ni->setValue((string) $nr_tries);
958 $ni->setMinValue(0);
959 $ni->setSize(5);
960 $ni->setMaxLength(5);
961 $form->addItem($ni);
962 }
963
964 protected function saveTaxonomyAssignments(): void
965 {
966 if (count($this->getTaxonomyIds())) {
967 foreach ($this->getTaxonomyIds() as $taxonomyId) {
968 $postvar = "tax_node_assign_$taxonomyId";
969
970 $tax_node_assign = new ilTaxAssignInputGUI($taxonomyId, true, '', $postvar);
971 // TODO: determine tst/qpl when tax assigns become maintainable within tests
972 $tax_node_assign->saveInput("qpl", $this->object->getObjId(), "quest", $this->object->getId());
973 }
974 }
975 }
976
977 protected function populateTaxonomyFormSection(ilPropertyFormGUI $form): void
978 {
979 if ($this->getTaxonomyIds() !== []) {
980 $sectHeader = new ilFormSectionHeaderGUI();
981 $sectHeader->setTitle($this->lng->txt('qpl_qst_edit_form_taxonomy_section'));
982 $form->addItem($sectHeader);
983
984 foreach ($this->getTaxonomyIds() as $taxonomyId) {
985 $taxonomy = new ilObjTaxonomy($taxonomyId);
986 $label = sprintf($this->lng->txt('qpl_qst_edit_form_taxonomy'), $taxonomy->getTitle());
987 $postvar = "tax_node_assign_$taxonomyId";
988
989 $taxSelect = new ilTaxSelectInputGUI($taxonomy->getId(), $postvar, true);
990 $taxSelect->setTitle($label);
991
992
993 $taxNodeAssignments = new ilTaxNodeAssignment(ilObject::_lookupType($this->object->getObjId()), $this->object->getObjId(), 'quest', $taxonomyId);
994 $assignedNodes = $taxNodeAssignments->getAssignmentsOfItem($this->object->getId());
995
996 $taxSelect->setValue(array_map(function ($assignedNode) {
997 return $assignedNode['node_id'];
998 }, $assignedNodes));
999 $form->addItem($taxSelect);
1000 }
1001 }
1002 }
1003
1007 public function getGenericFeedbackOutput(int $active_id, ?int $pass): string
1008 {
1009 $manual_feedback = ilObjTest::getManualFeedback($active_id, $this->object->getId(), $pass);
1010 if ($manual_feedback !== '') {
1011 return $manual_feedback;
1012 }
1013
1014 $output = $this->genericFeedbackOutputBuilder($active_id, $pass);
1015
1016 if ($this->object->isAdditionalContentEditingModePageObject()) {
1017 return $output;
1018 }
1019 return $this->renderLatex(ilLegacyFormElementsUtil::prepareTextareaOutput($output, true));
1020 }
1021
1023 int $active_id,
1024 ?int $pass
1025 ): string {
1026 if ($pass === null) {
1027 return '';
1028 }
1029 $reached_points = $this->object->calculateReachedPoints($active_id, $pass);
1030 $max_points = $this->object->getMaximumPoints();
1031
1032 $is_correct = false;
1033 if ($reached_points == $max_points) {
1034 $is_correct = true;
1035 }
1036
1037 return $this->object->feedbackOBJ->getGenericFeedbackTestPresentation(
1038 $this->object->getId(),
1039 $is_correct
1040 );
1041 }
1042
1044 {
1045 return $this->renderLatex(ilLegacyFormElementsUtil::prepareTextareaOutput(
1046 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), true),
1047 true
1048 ));
1049 }
1050
1052 {
1053 return $this->renderLatex(
1055 $this->object->feedbackOBJ->getGenericFeedbackTestPresentation($this->object->getId(), false),
1056 true
1057 )
1058 );
1059 }
1060
1061 public function outQuestionType(): string
1062 {
1063 $count = $this->questionrepository->usageCount($this->object->getId());
1064
1065 if ($this->questionrepository->questionExistsInPool($this->object->getId()) && $count) {
1066 if ($this->rbacsystem->checkAccess("write", $this->request_data_collector->getRefId())) {
1067 $this->tpl->setOnScreenMessage('info', sprintf($this->lng->txt("qpl_question_is_in_use"), $count));
1068 }
1069 }
1070
1071 return $this->lng->txt($this->object->getQuestionType());
1072 }
1073
1074 protected function getTypeOptions(): array
1075 {
1076 foreach (SuggestedSolution::TYPES as $k => $v) {
1077 $options[$k] = $this->lng->txt($v);
1078 }
1079 return $options;
1080 }
1081
1082 public function saveSuggestedSolution(): void
1083 {
1084 $this->suggestedsolution(true);
1085 }
1086
1087 public function cancelSuggestedSolution(): void
1088 {
1089 $this->suggestedsolution();
1090 }
1091
1092 public function suggestedsolution(bool $save = false): void
1093 {
1094 if ($save && $this->request_data_collector->int('deleteSuggestedSolution') === 1) {
1095 $this->object->deleteSuggestedSolutions();
1096 $this->tpl->setOnScreenMessage('success', $this->lng->txt("msg_obj_modified"), true);
1097 $this->ctrl->redirect($this, 'suggestedsolution');
1098 }
1099
1100 $output = '';
1101
1102 $solution = $this->object->getSuggestedSolution();
1103 $options = $this->getTypeOptions();
1104
1105 $solution_type = $this->ctrl->getCmd() === 'cancelSuggestedSolution'
1106 ? $solution?->getType()
1107 : $this->request_data_collector->string('solutiontype');
1108 if ($solution_type === SuggestedSolution::TYPE_FILE
1109 && ($solution === null || $solution->getType() !== SuggestedSolution::TYPE_FILE)
1110 ) {
1111 $solution = $this->getSuggestedSolutionsRepo()->create(
1112 $this->object->getId(),
1113 SuggestedSolution::TYPE_FILE
1114 );
1115 }
1116
1117 $solution_filename = $this->request_data_collector->string('filename');
1118 if ($save && !empty($solution_filename)) {
1119 $solution = $solution->withTitle($solution_filename);
1120 }
1121
1122 if ($solution !== null) {
1123 $form = new ilPropertyFormGUI();
1124 $form->setFormAction($this->ctrl->getFormAction($this));
1125 $form->setTitle($this->lng->txt('solution_hint'));
1126 $form->setMultipart(true);
1127 $form->setTableWidth('100%');
1128 $form->setId('suggestedsolutiondisplay');
1129
1130 $title = new ilSolutionTitleInputGUI($this->lng->txt('showSuggestedSolution'), 'solutiontype');
1131 $template = new ilTemplate(
1132 'tpl.il_as_qpl_suggested_solution_input_presentation.html',
1133 true,
1134 true,
1135 'components/ILIAS/TestQuestionPool'
1136 );
1137
1138 if ($solution->isOfTypeLink()) {
1139 $href = $this->object->getInternalLinkHref($solution->getInternalLink());
1140 $template->setCurrentBlock('preview');
1141 $template->setVariable('TEXT_SOLUTION', $this->lng->txt('suggested_solution'));
1142 $template->setVariable(
1143 'VALUE_SOLUTION',
1144 " <a href='{$href}' target='content'>{$this->lng->txt('view')}</a> "
1145 );
1146 $template->parseCurrentBlock();
1147 } elseif (
1148 $solution->isOfTypeFile()
1149 && $solution->getFilename()
1150 ) {
1151 $href = $this->object->getSuggestedSolutionPathWeb() . $solution->getFilename();
1152 $link = " <a href='{$href}' target='content'>"
1153 . ilLegacyFormElementsUtil::prepareFormOutput($solution->getTitle())
1154 . '</a> ';
1155 $template->setCurrentBlock('preview');
1156 $template->setVariable('TEXT_SOLUTION', $this->lng->txt('suggested_solution'));
1157 $template->setVariable('VALUE_SOLUTION', $link);
1158 $template->parseCurrentBlock();
1159 }
1160
1161 $template->setVariable('TEXT_TYPE', $this->lng->txt('type'));
1162 $template->setVariable('VALUE_TYPE', $options[$solution->getType()]);
1163
1164 $title->setHtml($template->get());
1165 $deletesolution = new ilCheckboxInputGUI('', 'deleteSuggestedSolution');
1166 $deletesolution->setOptionTitle($this->lng->txt('deleteSuggestedSolution'));
1167 $title->addSubItem($deletesolution);
1168 $form->addItem($title);
1169
1170 if ($solution->isOfTypeFile()) {
1171 $file = new ilFileInputGUI($this->lng->txt('fileDownload'), 'file');
1172 $file->setRequired(true);
1173 $file->enableFileNameSelection('filename');
1174
1175 if ($save && $_FILES && $_FILES['file']['tmp_name'] && $file->checkInput()) {
1176 if (!file_exists($this->object->getSuggestedSolutionPath())) {
1177 ilFileUtils::makeDirParents($this->object->getSuggestedSolutionPath());
1178 }
1179
1181 $_FILES['file']['tmp_name'],
1182 $_FILES['file']['name'],
1183 $this->object->getSuggestedSolutionPath() . $_FILES['file']['name']
1184 );
1185 if ($res) {
1186 ilFileUtils::renameExecutables($this->object->getSuggestedSolutionPath());
1187
1188 // remove an old file download
1189 if ($solution->getFilename()) {
1190 @unlink($this->object->getSuggestedSolutionPath() . $solution->getFilename());
1191 }
1192
1193 $file->setValue($_FILES['file']['name']);
1194 $solution = $solution
1195 ->withFilename($_FILES['file']['name'])
1196 ->withMime($_FILES['file']['type'])
1197 ->withSize($_FILES['file']['size'])
1198 ->withTitle($this->request_data_collector->string('filename'));
1199
1200 $this->getSuggestedSolutionsRepo()->update([$solution]);
1201
1202 $this->tpl->setOnScreenMessage('success', $this->lng->txt('suggested_solution_added_successfully'), true);
1203 $this->ctrl->redirect($this, 'suggestedsolution');
1204 } else {
1205 // BH: $res as info string? wtf? it holds a bool or something else!!?
1206 $this->tpl->setOnScreenMessage('info', $res);
1207 }
1208 } else {
1209 if ($solution->getFilename()) {
1210 $file->setValue($solution->getFilename());
1211 $file->setFilename($solution->getTitle());
1212 }
1213 }
1214 $form->addItem($file);
1215 $hidden = new ilHiddenInputGUI('solutiontype');
1216 $hidden->setValue('file');
1217 $form->addItem($hidden);
1218 }
1219 if ($this->access->checkAccess('write', '', $this->request_data_collector->getRefId())) {
1220 $form->addCommandButton('cancelSuggestedSolution', $this->lng->txt('cancel'));
1221 $form->addCommandButton('saveSuggestedSolution', $this->lng->txt('save'));
1222 }
1223
1224 if ($save) {
1225 if ($form->checkInput()) {
1226 if ($solution->isOfTypeFile()) {
1227 $solution = $solution->withTitle($this->request_data_collector->string('filename'));
1228 }
1229
1230 if (!$solution->isOfTypeLink()) {
1231 $this->getSuggestedSolutionsRepo()->update([$solution]);
1232 }
1233
1234 $this->tpl->setOnScreenMessage('success', $this->lng->txt('msg_obj_modified'), true);
1235 $this->ctrl->redirect($this, 'suggestedsolution');
1236 }
1237 }
1238
1239 $output = $form->getHTML();
1240 }
1241
1242 $savechange = $this->ctrl->getCmd() === 'saveSuggestedSolutionType';
1243
1244 $changeoutput = '';
1245 if ($this->access->checkAccess('write', '', $this->request_data_collector->getRefId())) {
1246 $formchange = new ilPropertyFormGUI();
1247 $formchange->setFormAction($this->ctrl->getFormAction($this));
1248
1249 $title = $solution ? $this->lng->txt('changeSuggestedSolution') : $this->lng->txt('addSuggestedSolution');
1250 $formchange->setTitle($title);
1251 $formchange->setMultipart(false);
1252 $formchange->setTableWidth('100%');
1253 $formchange->setId('suggestedsolution');
1254
1255 $solutiontype = new ilRadioGroupInputGUI($this->lng->txt('suggestedSolutionType'), 'solutiontype');
1256 foreach ($options as $opt_value => $opt_caption) {
1257 $solutiontype->addOption(new ilRadioOption($opt_caption, $opt_value));
1258 }
1259 if ($solution) {
1260 $solutiontype->setValue($solution->getType());
1261 }
1262 $solutiontype->setRequired(true);
1263 $formchange->addItem($solutiontype);
1264
1265 $formchange->addCommandButton('saveSuggestedSolutionType', $this->lng->txt('select'));
1266
1267 if ($savechange) {
1268 $formchange->checkInput();
1269 }
1270 $changeoutput = $formchange->getHTML();
1271 }
1272 $this->tabs_gui->activateTab('suggested_solution');
1273 $this->tpl->setVariable('ADM_CONTENT', $changeoutput . $output);
1274 }
1275
1276 public function outSolutionExplorer(): void
1277 {
1278 $type = $this->request_data_collector->string("link_new_type");
1279 $search = $this->request_data_collector->string("search_link_type");
1280 $this->ctrl->setParameter($this, "link_new_type", $type);
1281 $this->ctrl->setParameter($this, "search_link_type", $search);
1282 $this->ctrl->saveParameter($this, ["subquestion_index", "link_new_type", "search_link_type"]);
1283
1284 $this->tpl->setOnScreenMessage('info', $this->lng->txt("select_object_to_link"));
1285
1286 $parent_ref_id = $this->tree->getParentId($this->request_data_collector->getRefId());
1287 $exp = new ilSolutionExplorer($this->ctrl->getLinkTarget($this, 'suggestedsolution'), get_class($this));
1288 $exp->setExpand($this->request_data_collector->raw('expand_sol') ? $this->request_data_collector->raw('expand_sol') : $parent_ref_id);
1289 $exp->setExpandTarget($this->ctrl->getLinkTarget($this, 'outSolutionExplorer'));
1290 $exp->setTargetGet("ref_id");
1291 $exp->setRefId($this->request_data_collector->getRefId());
1292 $exp->addFilter($type);
1293 $exp->setSelectableType($type);
1294 if ($this->request_data_collector->isset('expandCurrentPath') && $this->request_data_collector->raw('expandCurrentPath')) {
1295 $exp->expandPathByRefId($parent_ref_id);
1296 }
1297
1298 // build html-output
1299 $exp->setOutput(0);
1300
1301 $template = new ilTemplate("tpl.il_as_qpl_explorer.html", true, true, "components/ILIAS/TestQuestionPool");
1302 $template->setVariable("EXPLORER_TREE", $exp->getOutput());
1303 $template->setVariable("BUTTON_CANCEL", $this->lng->txt("cancel"));
1304 $template->setVariable("FORMACTION", $this->ctrl->getFormAction($this, "suggestedsolution"));
1305 $this->tpl->setVariable("ADM_CONTENT", $template->get());
1306 }
1307
1308 public function saveSuggestedSolutionType(): void
1309 {
1310 $solution_type = $this->request_data_collector->string('solutiontype');
1311
1312 switch ($solution_type) {
1313 case 'lm':
1314 $type = 'lm';
1315 $search = 'lm';
1316 break;
1317 case 'git':
1318 $type = 'glo';
1319 $search = 'glo';
1320 break;
1321 case 'st':
1322 $type = 'lm';
1323 $search = 'st';
1324 break;
1325 case 'pg':
1326 $type = 'lm';
1327 $search = 'pg';
1328 break;
1329 case 'file':
1330 case 'text':
1331 default:
1332 $this->suggestedsolution();
1333 return;
1334 }
1335
1336 if (isset($solution_type)) {
1337 $this->ctrl->setParameter($this, 'expandCurrentPath', 1);
1338 }
1339 $this->ctrl->setParameter($this, 'link_new_type', $type);
1340 $this->ctrl->setParameter($this, 'search_link_type', $search);
1341 $this->ctrl->redirect($this, 'outSolutionExplorer');
1342 }
1343
1344 public function cancelExplorer(): void
1345 {
1346 $this->ctrl->redirect($this, 'suggestedsolution');
1347 }
1348
1349 public function outPageSelector(): void
1350 {
1351 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1352
1353 $cont_obj_gui = new ilObjContentObjectGUI('', $this->request_data_collector->int('source_id'), true);
1354 $cont_obj = $cont_obj_gui->getObject();
1355 $pages = ilLMPageObject::getPageList($cont_obj->getId());
1356 $shownpages = [];
1357 $tree = $cont_obj->getLMTree();
1358 $chapters = $tree->getSubtree($tree->getNodeData($tree->getRootId()));
1359
1360 $rows = [];
1361
1362 foreach ($chapters as $chapter) {
1363 $chapterpages = $tree->getChildsByType($chapter['obj_id'], 'pg');
1364 foreach ($chapterpages as $page) {
1365 if ($page['type'] == $this->request_data_collector->raw('search_link_type')) {
1366 array_push($shownpages, $page['obj_id']);
1367
1368 if ($tree->isInTree($page['obj_id'])) {
1369 $path_str = $this->getContextPath($cont_obj, $page['obj_id']);
1370 } else {
1371 $path_str = '---';
1372 }
1373
1374 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']);
1375 $rows[] = [
1376 'title' => $page['title'],
1377 'description' => ilLegacyFormElementsUtil::prepareFormOutput($path_str),
1378 'text_add' => $this->lng->txt('add'),
1379 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type']))
1380 ];
1381 }
1382 }
1383 }
1384 foreach ($pages as $page) {
1385 if (!in_array($page['obj_id'], $shownpages)) {
1386 $this->ctrl->setParameter($this, $page['type'], $page['obj_id']);
1387 $rows[] = [
1388 'title' => $page['title'],
1389 'description' => '---',
1390 'text_add' => $this->lng->txt('add'),
1391 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($page['type']))
1392 ];
1393 }
1394 }
1395
1396 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1397 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($this->request_data_collector->string('search_link_type'))));
1398 $table->setData($rows);
1399
1400 $this->tpl->setContent($table->getHTML());
1401 }
1402
1403 public function outChapterSelector(): void
1404 {
1405 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1406
1407 $cont_obj_gui = new ilObjContentObjectGUI('', $this->request_data_collector->int('source_id'), true);
1408 $cont_obj = $cont_obj_gui->getObject();
1409 $ctree = $cont_obj->getLMTree();
1410 $nodes = $ctree->getSubtree($ctree->getNodeData($ctree->getRootId()));
1411
1412 $rows = [];
1413
1414 foreach ($nodes as $node) {
1415 if ($node['type'] == $this->request_data_collector->raw('search_link_type')) {
1416 $this->ctrl->setParameter($this, $node['type'], $node['obj_id']);
1417 $rows[] = [
1418 'title' => $node['title'],
1419 'description' => '',
1420 'text_add' => $this->lng->txt('add'),
1421 'href_add' => $this->ctrl->getLinkTarget($this, 'add' . strtoupper($node['type']))
1422 ];
1423 }
1424 }
1425
1426 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1427 $table->setTitle($this->lng->txt('obj_' . ilUtil::stripSlashes($this->request_data_collector->string('search_link_type'))));
1428 $table->setData($rows);
1429
1430 $this->tpl->setContent($table->getHTML());
1431 }
1432
1433 public function outGlossarySelector(): void
1434 {
1435 $this->ctrl->setParameter($this, 'q_id', $this->object->getId());
1436
1437 $glossary = new ilObjGlossary($this->request_data_collector->int('source_id'), true);
1438 $terms = $glossary->getTermList();
1439
1440 $rows = [];
1441
1442 foreach ($terms as $term) {
1443 $this->ctrl->setParameter($this, 'git', $term['id']);
1444 $rows[] = [
1445 'title' => $term['term'],
1446 'description' => '',
1447 'text_add' => $this->lng->txt('add'),
1448 'href_add' => $this->ctrl->getLinkTarget($this, 'addGIT')
1449 ];
1450 }
1451
1452 $table = new ilQuestionInternalLinkSelectionTableGUI($this, 'cancelExplorer', __METHOD__);
1453 $table->setTitle($this->lng->txt('glossary_term'));
1454 $table->setData($rows);
1455
1456 $this->tpl->setContent($table->getHTML());
1457 }
1458
1459 protected function createSuggestedSolutionLinkingTo(string $type, string $target)
1460 {
1461 $repo = $this->getSuggestedSolutionsRepo();
1462 $question_id = $this->object->getId();
1463 $subquestion_index = $this->request_data_collector->int('subquestion_index');
1464 $subquestion_index = ($subquestion_index > 0) ? $subquestion_index : 0;
1465
1466 $solution = $repo->create($question_id, $type)
1467 ->withSubquestionIndex($subquestion_index)
1468 ->withInternalLink($target);
1469
1470 $repo->update([$solution]);
1471 }
1472
1473 public function linkChilds(): void
1474 {
1475 $this->ctrl->saveParameter($this, ["subquestion_index", "link_new_type", "search_link_type"]);
1476 switch ($this->request_data_collector->string("search_link_type")) {
1477 case "pg":
1478 $this->outPageSelector();
1479 break;
1480 case "st":
1481 $this->outChapterSelector();
1482 break;
1483 case "glo":
1484 $this->outGlossarySelector();
1485 break;
1486 case "lm":
1487 $target = "il__lm_" . $this->request_data_collector->raw("source_id");
1488 $this->createSuggestedSolutionLinkingTo('lm', $target);
1489 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1490 $this->ctrl->redirect($this, 'suggestedsolution');
1491 break;
1492 }
1493 }
1494
1495 public function addPG(): void
1496 {
1497 $target = "il__pg_" . $this->request_data_collector->raw("pg");
1498 $this->createSuggestedSolutionLinkingTo('pg', $target);
1499 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1500 $this->ctrl->redirect($this, 'suggestedsolution');
1501 }
1502
1503 public function addST(): void
1504 {
1505 $target = "il__st_" . $this->request_data_collector->raw("st");
1506 $this->createSuggestedSolutionLinkingTo('st', $target);
1507 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1508 $this->ctrl->redirect($this, 'suggestedsolution');
1509 }
1510
1511 public function addGIT(): void
1512 {
1513 $target = "il__git_" . $this->request_data_collector->raw("git");
1514 $this->createSuggestedSolutionLinkingTo('git', $target);
1515 $this->tpl->setOnScreenMessage('success', $this->lng->txt("suggested_solution_added_successfully"), true);
1516 $this->ctrl->redirect($this, 'suggestedsolution');
1517 }
1518
1519 public function isSaveCommand(): bool
1520 {
1521 return in_array($this->ctrl->getCmd(), self::QUESTION_SAVE_CMDS);
1522 }
1523
1524 public static function getCommandsFromClassConstants(
1525 string $guiClassName,
1526 string $cmdConstantNameBegin = 'CMD_'
1527 ): array {
1528 $reflectionClass = new ReflectionClass($guiClassName);
1529
1530 $commands = null;
1531
1532 if ($reflectionClass instanceof ReflectionClass) {
1533 $commands = [];
1534
1535 foreach ($reflectionClass->getConstants() as $constName => $constValue) {
1536 if (substr($constName, 0, strlen($cmdConstantNameBegin)) == $cmdConstantNameBegin) {
1537 $commands[] = $constValue;
1538 }
1539 }
1540 }
1541
1542 return $commands;
1543 }
1544
1545 public function setQuestionTabs(): void
1546 {
1547 $this->tabs_gui->clearTargets();
1548
1549 if ($this->object->getId() > 0) {
1550 $this->setDefaultTabs($this->tabs_gui);
1551 $this->setQuestionSpecificTabs($this->tabs_gui);
1552 $this->tabs_gui->activateTab('edit_question');
1553 }
1554 $this->addBackTab($this->tabs_gui);
1555 }
1556
1557 protected function setDefaultTabs(ilTabsGUI $tabs_gui): void
1558 {
1559 $this->ctrl->setParameterByClass(ilAssQuestionPageGUI::class, 'q_id', $this->object->getId());
1560 $this->ctrl->setParameterByClass(static::class, 'q_id', $this->object->getId());
1561
1562 $this->addTab_Question($tabs_gui);
1563 $this->addTab_QuestionFeedback($tabs_gui);
1564 $this->addTab_SuggestedSolution($tabs_gui, static::class);
1565 }
1566
1567 protected function setQuestionSpecificTabs(ilTabsGUI $tabs_gui): void
1568 {
1569 }
1570
1571 public function addTab_SuggestedSolution(ilTabsGUI $tabs, string $classname): void
1572 {
1573 $this->ctrl->setParameterByClass($classname, 'q_id', $this->object->getId());
1574 $tabs->addTarget(
1575 'suggested_solution',
1576 $this->ctrl->getLinkTargetByClass($classname, 'suggestedsolution'),
1577 [
1578 'suggestedsolution',
1579 'saveSuggestedSolution',
1580 'outSolutionExplorer',
1581 'cancel',
1582 'addSuggestedSolution',
1583 'cancelExplorer' ,
1584 'linkChilds',
1585 'removeSuggestedSolution'
1586 ]
1587 );
1588 }
1589
1590 final public function getEditQuestionTabCommands(): array
1591 {
1592 return array_merge($this->getBasicEditQuestionTabCommands(), $this->getAdditionalEditQuestionCommands());
1593 }
1594
1595 protected function getBasicEditQuestionTabCommands(): array
1596 {
1597 return ['editQuestion', 'save', 'originalSyncForm'];
1598 }
1599
1600 protected function getAdditionalEditQuestionCommands(): array
1601 {
1602 return [];
1603 }
1604
1605 protected function addTab_QuestionFeedback(ilTabsGUI $tabs): void
1606 {
1607 $this->ctrl->setParameterByClass(ilAssQuestionFeedbackEditingGUI::class, 'q_id', $this->object->getId());
1608
1609 $tabs->addTab(
1610 'feedback',
1611 $this->lng->txt('tst_feedback'),
1612 $this->ctrl->getLinkTargetByClass(
1613 ilAssQuestionFeedbackEditingGUI::class,
1615 )
1616 );
1617 }
1618
1619 protected function addTab_Question(ilTabsGUI $tabs_gui): void
1620 {
1621 $tabs_gui->addTarget(
1622 'edit_question',
1623 $this->ctrl->getLinkTargetByClass(
1624 static::class,
1625 'editQuestion'
1626 ),
1627 'editQuestion',
1628 '',
1629 '',
1630 false
1631 );
1632 }
1633
1634 protected function hasCorrectSolution($activeId, $passIndex): bool
1635 {
1636 $reachedPoints = $this->object->getAdjustedReachedPoints((int) $activeId, (int) $passIndex, true);
1637 $maximumPoints = $this->object->getMaximumPoints();
1638
1639 return $reachedPoints == $maximumPoints;
1640 }
1641
1642 public function isAutosaveable(): bool
1643 {
1644 return $this->object instanceof QuestionAutosaveable;
1645 }
1646
1647 protected function writeQuestionGenericPostData(): void
1648 {
1649 $this->object->setTitle($this->request_data_collector->string('title'));
1650 $this->object->setAuthor($this->request_data_collector->string('author'));
1651 $this->object->setComment($this->request_data_collector->string('comment'));
1652
1653 $nr_of_tries = $this->request_data_collector->int('nr_of_tries');
1654 if ($this->object->getSelfAssessmentEditingMode()) {
1655 $this->object->setNrOfTries($nr_of_tries);
1656 }
1657
1658 try {
1660 $this->request_data_collector->string('lifecycle')
1661 );
1662 $this->object->setLifecycle($lifecycle);
1664 }
1665
1666 $this->object->setQuestion(
1668 $this->request_data_collector->string('question')
1669 )
1670 );
1671
1672 $this->setTestSpecificProperties();
1673 }
1674
1675 final public function outQuestionForTest(
1676 string $formaction,
1677 int $active_id,
1678 ?int $pass,
1679 bool $is_question_postponed = false,
1680 array|bool $user_post_solutions = false,
1681 bool $show_specific_inline_feedback = false
1682 ): void {
1683 $formaction = $this->completeTestOutputFormAction($formaction, $active_id, $pass);
1684
1685 $test_output = $this->getTestOutput(
1686 $active_id,
1687 $pass,
1688 $is_question_postponed,
1689 $user_post_solutions,
1690 $show_specific_inline_feedback
1691 );
1692
1693 $this->magicAfterTestOutput();
1694
1695 $this->tpl->setVariable("QUESTION_OUTPUT", $test_output);
1696 $this->tpl->setVariable("FORMACTION", $formaction);
1697 $this->tpl->setVariable("ENCTYPE", 'enctype="' . $this->getFormEncodingType() . '"');
1698 $this->tpl->setVariable("FORM_TIMESTAMP", (string) time());
1699 }
1700
1702 string $form_action,
1703 int $active_id,
1704 int $pass
1705 ): string {
1706 return $form_action;
1707 }
1708
1709 public function magicAfterTestOutput(): void
1710 {
1711 return;
1712 }
1713
1714 public function getFormEncodingType(): string
1715 {
1716 return self::FORM_ENCODING_URLENCODE;
1717 }
1718
1719 protected function addBackTab(ilTabsGUI $tabs_gui): void
1720 {
1721 if ($this->object->getId() <= 0) {
1722 $tabs_gui->setBackTarget(
1723 $this->lng->txt('cancel'),
1724 $this->ctrl->getParentReturnByClass(
1726 ilObject::_lookupType($this->object->getObjId())
1727 ) . 'GUI'
1728 )
1729 );
1730 return;
1731 }
1732 $this->ctrl->setParameterByClass(
1733 ilAssQuestionPreviewGUI::class,
1734 'q_id',
1735 $this->object->getId()
1736 );
1737 $this->ctrl->saveParameterByClass(ilAssQuestionPreviewGUI::class, 'prev_qid');
1738 $tabs_gui->setBackTarget(
1739 $this->lng->txt('backtocallingpage'),
1740 $this->ctrl->getLinkTargetByClass(
1741 ilAssQuestionPreviewGUI::class,
1743 )
1744 );
1745 $this->ctrl->clearParameterByClass(ilAssQuestionPreviewGUI::class, 'prev_qid');
1746 }
1747
1748 public function setPreviewSession(ilAssQuestionPreviewSession $preview_session): void
1749 {
1750 $this->preview_session = $preview_session;
1751 }
1752
1757 {
1758 return $this->preview_session;
1759 }
1760
1762 {
1763 $form = new ilPropertyFormGUI();
1764 $form->setFormAction($this->ctrl->getFormAction($this));
1765 $form->setId($this->getType());
1766 $form->setTitle($this->outQuestionType());
1767 $form->setTableWidth('100%');
1768 $form->setMultipart(true);
1769 return $form;
1770 }
1771
1772 protected function escapeTemplatePlaceholders(string $text): string
1773 {
1774 return str_replace(['{','}'], ['&#123;','&#125;'], $text);
1775 }
1776
1777 protected function buildEditForm(): ilPropertyFormGUI
1778 {
1779 $this->editQuestion(true); // TODO bheyser: editQuestion should be added to the abstract base class with a unified signature
1780 return $this->editForm;
1781 }
1782
1783 public function buildFocusAnchorHtml(): string
1784 {
1785 return '<div id="focus"></div>';
1786 }
1787
1789 {
1790 return true;
1791 }
1792
1793 public function getSubQuestionsIndex(): array
1794 {
1795 return [0];
1796 }
1797
1798 public function getAnswersFrequency($relevantAnswers, $questionIndex): array
1799 {
1800 return [];
1801 }
1802
1803 public function getAnswerFrequencyTableGUI($parentGui, $parentCmd, $relevantAnswers, $questionIndex): ilAnswerFrequencyStatisticTableGUI
1804 {
1805 $table = new ilAnswerFrequencyStatisticTableGUI($parentGui, $parentCmd, $this->object);
1806 $table->setQuestionIndex($questionIndex);
1807 $table->setData($this->getAnswersFrequency($relevantAnswers, $questionIndex));
1808 $table->initColumns();
1809 return $table;
1810 }
1811
1813 {
1814 }
1815
1817 {
1818 }
1819
1821 {
1822 }
1823
1824
1825 protected function generateCorrectnessIconsForCorrectness(int $correctness): string
1826 {
1827 switch ($correctness) {
1828 case self::CORRECTNESS_NOT_OK:
1829 $icon_name = 'standard/icon_not_ok.svg';
1830 $label = $this->lng->txt("answer_is_wrong");
1831 break;
1832 case self::CORRECTNESS_MOSTLY_OK:
1833 $icon_name = 'standard/icon_mostly_ok.svg';
1834 $label = $this->lng->txt("answer_is_not_correct_but_positive");
1835 break;
1836 case self::CORRECTNESS_OK:
1837 $icon_name = 'standard/icon_ok.svg';
1838 $label = $this->lng->txt("answer_is_right");
1839 break;
1840 default:
1841 return '';
1842 }
1843 $path = ilUtil::getImagePath($icon_name);
1844 $icon = $this->ui->factory()->symbol()->icon()->custom(
1845 $path,
1846 $label
1847 );
1848 return $this->ui->renderer()->render($icon);
1849 }
1850
1859 public static function prepareTextareaOutput(
1860 ?string $txt_output,
1861 bool $prepare_for_latex_output = false,
1862 bool $omitNl2BrWhenTextArea = false
1863 ): string {
1864 if ($txt_output === null || $txt_output === '') {
1865 return '';
1866 }
1867
1868 $result = $txt_output;
1869 $is_html = false;
1870
1871 if (strlen(strip_tags($result)) < strlen($result)) {
1872 $is_html = true;
1873 }
1874
1875 // removed: did not work with magic_quotes_gpc = On
1876 if (!$is_html) {
1877 if (!$omitNl2BrWhenTextArea) {
1878 // if the string does not contain HTML code, replace the newlines with HTML line breaks
1879 $result = preg_replace("/[\n]/", "<br />", $result);
1880 }
1881 } else {
1882 // patch for problems with the <pre> tags in tinyMCE
1883 if (preg_match_all("/(<pre>.*?<\/pre>)/ims", $result, $matches)) {
1884 foreach ($matches[0] as $found) {
1885 $replacement = "";
1886 if (strpos("\n", $found) === false) {
1887 $replacement = "\n";
1888 }
1889 $removed = preg_replace("/<br\s*?\/>/ims", $replacement, $found);
1890 $result = str_replace($found, $removed, $result);
1891 }
1892 }
1893 }
1894
1895 if ($prepare_for_latex_output) {
1896 $result = ilRTE::replaceLatexSpan($result);
1897
1898 // replace special characters to prevent problems with the ILIAS template system
1899 // eg. if someone uses {1} as an answer, nothing will be shown without the replacement
1900 $result = str_replace("{", "&#123;", $result);
1901 $result = str_replace("}", "&#125;", $result);
1902 $result = str_replace("\\", "&#92;", $result);
1903 }
1904
1905 return $result;
1906 }
1907
1911 protected function renderLatex($content)
1912 {
1913 return $this->ui->renderer()->render($this->ui->factory()->legacy()->latexContent($content));
1914 }
1915
1916 protected ?SuggestedSolutionsDatabaseRepository $suggestedsolution_repo = null;
1918 {
1919 if (is_null($this->suggestedsolution_repo)) {
1920 $dic = QuestionPoolDIC::dic();
1921 $this->suggestedsolution_repo = $dic['question.repo.suggestedsolutions'];
1922 }
1923 return $this->suggestedsolution_repo;
1924 }
1925
1930 protected function cleanupAnswerText(array $answer_text, bool $is_rte): array
1931 {
1932 if (!is_array($answer_text)) {
1933 return [];
1934 }
1935
1936 if ($is_rte) {
1938 $answer_text,
1939 false,
1941 );
1942 }
1943
1945 $answer_text,
1946 true,
1947 self::ALLOWED_PLAIN_TEXT_TAGS
1948 );
1949 }
1950
1951 public function isInLearningModuleContext(): bool
1952 {
1953 return $this->parent_type_is_lm;
1954 }
1955 public function setInLearningModuleContext(bool $flag): void
1956 {
1957 $this->parent_type_is_lm = $flag;
1958 }
1959
1960 protected function addSaveOnEnterOnLoadCode(): void
1961 {
1962 $this->tpl->addOnloadCode("
1963 let form = document.querySelector('#ilContentContainer form');
1964 let button = form.querySelector('input[name=\"cmd[save]\"]');
1965 if (form && button) {
1966 form.addEventListener('keydown', function (e) {
1967 if (e.key === 'Enter'
1968 && e.target.type !== 'textarea'
1969 && e.target.type !== 'submit'
1970 && e.target.type !== 'file'
1971 ) {
1972 e.preventDefault();
1973 form.requestSubmit(button);
1974 }
1975 })
1976 }
1977 ");
1978 }
1979
1980 public function cmdNeedsExistingQuestion(string $cmd): bool
1981 {
1982 return in_array($cmd, static::ADDITIONAL_CMDS_NEEDING_EXISTING_QST);
1983 }
1984
1985 public function setContextAllowsSyncToPool(bool $sync_allowed): void
1986 {
1987 $this->context_allows_sync_to_pool = $sync_allowed;
1988 }
1989
1990 public function needsSyncQuery(): bool
1991 {
1992 return $this->context_allows_sync_to_pool
1993 && $this->object->hasWritableOriginalInQuestionPool();
1994 }
1995
1996 public function getQuestionSyncModal(string $cmd, string $cmd_class = ''): string
1997 {
1998 if ($cmd_class === '') {
1999 $cmd_class = static::class;
2000 }
2001 $modal = $this->ui->factory()->modal()->interruptive(
2002 $this->lng->txt('confirm'),
2003 $this->lng->txt('confirm_sync_questions'),
2004 $this->ctrl->getFormActionByClass($cmd_class, $cmd)
2005 )->withAffectedItems([
2006 $this->ui->factory()->modal()->interruptiveItem()->standard(
2007 (string) $this->object->getOriginalId(),
2008 $this->object->getTitleForHTMLOutput()
2009 )
2010 ])->withActionButtonLabel($this->lng->txt('sync_question_to_pool'));
2011 return $this->ui->renderer()->render(
2012 $modal->withOnLoad($modal->getShowSignal())
2013 );
2014 }
2015
2017 int $active_id,
2018 int $pass,
2019 bool $graphical_output = false,
2020 bool $result_output = false,
2021 bool $show_question_only = true,
2022 bool $show_feedback = false,
2023 bool $show_correct_solution = false,
2024 bool $show_manual_scoring = false,
2025 bool $show_question_text = true,
2026 bool $show_autosave_title = false,
2027 bool $show_inline_feedback = false
2028 ): ?string {
2029 $autosave_solutions = $this->object->getSolutionValues($active_id, $pass, false);
2030 if ($autosave_solutions === []) {
2031 return null;
2032 }
2033 return $this->renderSolutionOutput(
2034 $autosave_solutions,
2035 $active_id,
2036 $pass,
2037 $graphical_output,
2038 $result_output,
2039 $show_question_only,
2040 $show_feedback,
2041 $show_correct_solution,
2042 $show_manual_scoring,
2043 $show_question_text,
2044 $show_autosave_title,
2045 $show_inline_feedback
2046 );
2047 }
2048
2049 public function buildFileDownloadLink(): string
2050 {
2051 if (strtolower($this->request_data_collector->string('cmdClass')) === 'ilassquestionpreviewgui') {
2052 return $this->ctrl->getLinkTargetByClass(ilObjQuestionPoolGUI::class, 'downloadFile');
2053 }
2054 return $this->ctrl->getLinkTargetByClass(ilObjTestGUI::class, 'downloadFile');
2055 }
2056
2057 protected function resetSavedPreviewSession(): void
2058 {
2059 $this->preview_session = new ilAssQuestionPreviewSession(
2060 $this->current_user->getId(),
2061 $this->object->getId()
2062 );
2063 $this->preview_session->setRandomizerSeed(null);
2064 $this->preview_session->setParticipantsSolution(null);
2065 $this->preview_session->setInstantResponseActive(false);
2066 }
2067}
$lifecycle
Provides fluid interface to RBAC services.
Definition: UIServices.php:25
Builds data types.
Definition: Factory.php:36
cmdNeedsExistingQuestion(string $cmd)
populateTaxonomyFormSection(ilPropertyFormGUI $form)
setInLearningModuleContext(bool $flag)
getAsValueAttribute(string $a_value)
getQuestionSyncModal(string $cmd, string $cmd_class='')
saveCorrectionsFormProperties(ilPropertyFormGUI $form)
const SESSION_PREVIEW_DATA_BASE_INDEX
setMoveAfterQuestionId(?int $question_id)
ilAssQuestionPreviewSession $preview_session
getAutoSavedSolutionOutput(int $active_id, int $pass, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_autosave_title=false, bool $show_inline_feedback=false)
const SUGGESTED_SOLUTION_COMMANDS_SAVE
addTab_Question(ilTabsGUI $tabs_gui)
getType()
needed for page editor compliance
const ALLOWED_PLAIN_TEXT_TAGS
sk - 12.05.2023: This const is also used in ilKprimChoiceWizardInputGUI.
outQuestionForTest(string $formaction, int $active_id, ?int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
completeTestOutputFormAction(string $form_action, int $active_id, int $pass)
setCopyToNewPoolOnSave(?string $pool_title)
renderLatex($content)
Wrap content with latex in a LatexContent UI component and render it to be processed by MathJax in th...
ilObjectDataCache $ilObjDataCache
getContextPath($cont_obj, int $a_endnode_id, int $a_startnode_id=1)
get context path in content object tree
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)
addBackTab(ilTabsGUI $tabs_gui)
editQuestion(bool $checkonly=false, ?bool $is_save_cmd=null)
setPreviousSolutionPrefilled(bool $previousSolutionPrefilled)
setQuestionSpecificTabs(ilTabsGUI $tabs_gui)
setPresentationContext(string $presentationContext)
readonly RequestDataCollector $request_data_collector
readonly ilTestLegacyFormsHelper $forms_helper
setTargetGui($linkTargetGui)
const ADDITIONAL_CMDS_NEEDING_EXISTING_QST
There are functions that need an existing question.
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)
getTestOutput(int $active_id, int $pass, bool $is_question_postponed=false, array|bool $user_post_solutions=false, bool $show_specific_inline_feedback=false)
setQuestionHeaderBlockBuilder(\ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder)
renderEditForm(ilPropertyFormGUI $form)
int $question_count
question count in test
setObject(assQuestion $question)
getPreview(bool $show_question_only=false, bool $show_inline_feedback=false)
setTargetGuiClass(string $targetGuiClass)
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)
setPreviewSession(ilAssQuestionPreviewSession $preview_session)
outQuestionPage($a_temp_var, $a_postponed=false, $active_id="", $html="", $inlineFeedbackEnabled=false)
const SUGGESTED_SOLUTION_COMMANDS_DEFAULT
createSuggestedSolutionLinkingTo(string $type, string $target)
ilRbacSystem $rbacsystem
getGenericFeedbackOutput(int $active_id, ?int $pass)
getSpecificFeedbackOutput(array $userSolution)
Returns the answer specific feedback for the question.
setContextAllowsSyncToPool(bool $sync_allowed)
getAnswersFrequency($relevantAnswers, $questionIndex)
addErrorMessage(string $errormessage)
populateJavascriptFilesRequiredForWorkForm(ilGlobalTemplateInterface $tpl)
genericFeedbackOutputBuilder(int $active_id, ?int $pass)
setDefaultTabs(ilTabsGUI $tabs_gui)
setEditContext(string $editContext)
setCopyToExistingPoolOnSave(?int $pool_ref_id)
setQuestionCount(int $a_question_count)
addQuestionFormCommandButtons(ilPropertyFormGUI $form)
static prepareTextareaOutput(?string $txt_output, bool $prepare_for_latex_output=false, bool $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output where latex code may be in it If the text is HTML-free,...
ilQuestionHeaderBlockBuilder $questionHeaderBlockBuilder
setErrorMessage(string $errormessage)
ilTestQuestionNavigationGUI $navigationGUI
getSolutionOutput(int $active_id, ?int $pass=null, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_inline_feedback=true)
ilPropertyFormGUI $editForm
writePostData(bool $always=false)
Evaluates a posted edit form and writes the form data in the question object.
setTaxonomyIds(array $taxonomyIds)
GeneralQuestionPropertiesRepository $questionrepository
hasCorrectSolution($activeId, $passIndex)
suggestedsolution(bool $save=false)
addTab_QuestionFeedback(ilTabsGUI $tabs)
setQuestionActionCmd(string $questionActionCmd)
const SUGGESTED_SOLUTION_COMMANDS_CANCEL
generateCorrectnessIconsForCorrectness(int $correctness)
renderSolutionOutput(mixed $user_solutions, int $active_id, ?int $pass, bool $graphical_output=false, bool $result_output=false, bool $show_question_only=true, bool $show_feedback=false, bool $show_correct_solution=false, bool $show_manual_scoring=false, bool $show_question_text=true, bool $show_autosave_title=false, bool $show_inline_feedback=false,)
ilAccessHandler $access
setNavigationGUI(?ilTestQuestionNavigationGUI $navigationGUI)
addNumberOfTriesToFormIfNecessary(ilPropertyFormGUI $form)
const ADDITIONAL_CONTENT_EDITING_MODE_IPE
static getFeedbackClassNameByQuestionType(string $questionType)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static stripSlashesRecursive($a_data, bool $a_strip_html=true, string $a_allow="")
Question page GUI class.
static getSelfAssessmentTags()
Get tags allowed in question tags in self assessment mode.
This class represents a checkbox property in a property form.
Class ilCommonActionDispatcherGUI.
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 class represents a section header in a property form.
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
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 class represents a hidden form property in a property form.
static getPageList(int $lm_id)
language handling
static prepareTextareaOutput(string $txt_output, bool $prepare_for_latex_output=false, bool $omitNl2BrWhenTextArea=false)
Prepares a string for a text area output where latex code may be in it If the text is HTML-free,...
static prepareFormOutput($a_str, bool $a_strip=false)
Component logger with individual log levels by component id.
This class represents a number property in a property form.
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(int $active_id, int $question_id, ?int $pass)
Retrieves the feedback comment for a question in a test if it is finalized.
User class.
class ilObjectDataCache
static getClassByType(string $obj_type)
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...
static _getUsedHTMLTags(string $module='')
static _getUsedHTMLTagsAsString(string $module='')
static replaceLatexSpan(string $text)
Replace the latex delimiters used by the rich text editor Unfortunately these can't be processed by M...
This class represents a property in a property form.
This class represents an option in a radio group.
class ilRbacSystem system function like checkAccess, addActiveRole ... Supporting system functions ar...
This class represents a selection list property in a property form.
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...
addTab(string $a_id, string $a_text, string $a_link, string $a_frame="")
Add a Tab.
setBackTarget(string $a_title, string $a_target, string $a_frame="")
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
This class represents a text area property in a property form.
This class represents a text property in a property form.
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
isInTree(?int $a_node_id)
get all information of a node.
getNodeData(int $a_node_id, ?int $a_tree_pk=null)
get all information of a node.
getChildsByType(int $a_node_id, string $a_type)
get child nodes of given node by object type
static getImagePath(string $image_name, string $module_path="", string $mode="output", bool $offline=false)
get image path (for images located in a template directory)
static stripOnlySlashes(string $a_str)
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
exit
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.
Interface ilAccessHandler This interface combines all available interfaces which can be called via gl...
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...
$dic
Definition: ltiresult.php:33
$path
Definition: ltiservices.php:30
$res
Definition: ltiservices.php:69
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
global $lng
Definition: privfeed.php:31
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26
$text
Definition: xapiexit.php:21