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