ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
class.ilTestCorrectionsGUI.php
Go to the documentation of this file.
1 <?php
2 
24 
34 {
36 
38  protected ilCtrl $ctrl;
39  protected ilLanguage $language;
40  protected ilTabsGUI $tabs;
41  protected ilHelpGUI $help;
42  protected UIServices $ui;
45  protected ilObjTest $testOBJ;
47 
53  public function __construct(Container $DIC, ilObjTest $testOBJ)
54  {
55  $this->database = $DIC->database();
56  $this->ctrl = $DIC->ctrl();
57  $this->language = $DIC->language();
58  $this->tabs = $DIC->tabs();
59  $this->help = $DIC->help();
60  $this->ui = $DIC->ui();
61  $this->refinery = $DIC->refinery();
62  $this->request = $DIC->http()->request();
63  $this->testOBJ = $testOBJ;
64  $this->testrequest = $DIC->test()->internal()->request();
65  $this->testAccess = new ilTestAccess($testOBJ->getRefId(), $testOBJ->getTestId());
66  }
67 
68  public function executeCommand()
69  {
70  if (!$this->testAccess->checkCorrectionsAccess()) {
72  }
73  if (
74  $this->testrequest->isset('eqid') && (int) $this->testrequest->raw('eqid')
75  && $this->testrequest->isset('eqpl') && (int) $this->testrequest->raw('eqpl')
76  ) {
77  $this->ctrl->setParameter($this, 'qid', (int) $this->testrequest->raw('eqid'));
78  $this->ctrl->redirect($this, 'showQuestion');
79  }
80  if ($this->testrequest->isset('removeQid') && (int) $this->testrequest->raw('removeQid')) {
81  $this->ctrl->setParameter($this, 'qid', (int) $this->testrequest->raw('removeQid'));
82  $this->ctrl->redirect($this, 'confirmQuestionRemoval');
83  }
84 
85  if ((int) $this->testrequest->raw('qid')
86  && !$this->checkQuestion((int) $this->testrequest->raw('qid'))) {
88  }
89 
90  $this->ctrl->saveParameter($this, 'qid');
91 
92  switch ($this->ctrl->getNextClass($this)) {
93  default:
94 
95  $command = $this->ctrl->getCmd('showQuestionList');
96  $this->{$command}();
97  }
98  }
99 
100  protected function showQuestionList()
101  {
102  $this->tabs->activateTab(ilTestTabsManager::TAB_ID_CORRECTION);
103 
104  $ui = $this->ui;
105 
106  if ($this->testOBJ->isFixedTest()) {
107  $table_gui = new ilTestQuestionsTableGUI(
108  $this,
109  'showQuestionList',
110  $this->testOBJ->getRefId()
111  );
112 
113  $table_gui->setQuestionRemoveRowButtonEnabled(true);
114  $table_gui->init();
115 
116  $table_gui->setData($this->getQuestions());
117 
118  $rendered_gui_component = $table_gui->getHTML();
119  } else {
121  $txt = $lng->txt('tst_corrections_incompatible_question_set_type');
122 
123  $infoBox = $ui->factory()->messageBox()->info($txt);
124 
125  $rendered_gui_component = $ui->renderer()->render($infoBox);
126  }
127 
128  $ui->mainTemplate()->setContent($rendered_gui_component);
129  }
130 
131  protected function showQuestion(ilPropertyFormGUI $form = null)
132  {
133  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
134 
135  $this->setCorrectionTabsContext($questionGUI, 'question');
136 
137  if ($form === null) {
138  $form = $this->buildQuestionCorrectionForm($questionGUI);
139  }
140 
141  $this->populatePageTitleAndDescription($questionGUI);
142  $this->ui->mainTemplate()->setContent($form->getHTML());
143  }
144 
145  protected function saveQuestion()
146  {
147  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
148 
149  $form = $this->buildQuestionCorrectionForm($questionGUI);
150 
151  $form->setValuesByPost();
152 
153  if (!$form->checkInput()) {
154  $questionGUI->prepareReprintableCorrectionsForm($form);
155 
156  $this->showQuestion($form);
157  return;
158  }
159 
160  $questionGUI->saveCorrectionsFormProperties($form);
161  $questionGUI->object->setPoints($questionGUI->object->getMaximumPoints());
162  $questionGUI->object->saveToDb();
163 
164  $scoring = new ilTestScoring($this->testOBJ);
165  $scoring->setPreserveManualScores(false);
166  $scoring->setQuestionId($questionGUI->object->getId());
167  $scoring->recalculateSolutions();
168 
169  $this->ui->mainTemplate()->setOnScreenMessage('success', $this->language->txt('saved_successfully'), true);
170  $this->ctrl->redirect($this, 'showQuestion');
171  }
172 
174  {
175  $form = new ilPropertyFormGUI();
176  $form->setFormAction($this->ctrl->getFormAction($this));
177  $form->setId('tst_question_correction');
178 
179  $form->setTitle($this->language->txt('tst_corrections_qst_form'));
180 
181  $hiddenQid = new ilHiddenInputGUI('qid');
182  $hiddenQid->setValue($questionGUI->object->getId());
183  $form->addItem($hiddenQid);
184 
185  $questionGUI->populateCorrectionsFormProperties($form);
186 
187  $scoring = new ilTestScoring($this->testOBJ);
188  $scoring->setQuestionId($questionGUI->object->getId());
189 
190  if ($scoring->getNumManualScorings()) {
191  $form->addCommandButton('confirmManualScoringReset', $this->language->txt('save'));
192  } else {
193  $form->addCommandButton('saveQuestion', $this->language->txt('save'));
194  }
195 
196  return $form;
197  }
198 
199  protected function addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath = array())
200  {
201  foreach ($array as $name => $value) {
202  if ($name == 'cmd' && !count($curPath)) {
203  continue;
204  }
205 
206  if (count($curPath)) {
207  $name = "[{$name}]";
208  }
209 
210  if (is_array($value)) {
211  $nextPath = array_merge($curPath, array($name));
212  $this->addHiddenItemsFromArray($gui, $value, $nextPath);
213  } else {
214  $postVar = implode('', $curPath) . $name;
215  $gui->addHiddenItem($postVar, $value);
216  }
217  }
218  }
219 
220  protected function confirmManualScoringReset()
221  {
222  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
223 
224  $this->setCorrectionTabsContext($questionGUI, 'question');
225 
226  $scoring = new ilTestScoring($this->testOBJ);
227  $scoring->setQuestionId($questionGUI->object->getId());
228 
229  $confirmation = sprintf(
230  $this->language->txt('tst_corrections_manscore_reset_warning'),
231  $scoring->getNumManualScorings(),
232  $questionGUI->object->getTitleForHTMLOutput(),
233  $questionGUI->object->getId()
234  );
235 
236  $gui = new ilConfirmationGUI();
237  $gui->setHeaderText($confirmation);
238  $gui->setFormAction($this->ctrl->getFormAction($this));
239  $gui->setCancel($this->language->txt('cancel'), 'showQuestion');
240  $gui->setConfirm($this->language->txt('confirm'), 'saveQuestion');
241 
242  $this->addHiddenItemsFromArray($gui, $this->testrequest->getParsedBody());
243 
244  $this->ui->mainTemplate()->setContent($gui->getHTML());
245  }
246 
247  protected function showSolution()
248  {
249  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
250 
251  $this->setCorrectionTabsContext($questionGUI, 'solution');
252 
253  $pageGUI = new ilAssQuestionPageGUI($questionGUI->object->getId());
254  $pageGUI->setRenderPageContainer(false);
255  $pageGUI->setEditPreview(true);
256  $pageGUI->setEnabledTabs(false);
257 
258  $solutionHTML = $questionGUI->getSolutionOutput(
259  0,
260  null,
261  false,
262  false,
263  true,
264  false,
265  true,
266  false,
267  true
268  );
269 
270  $pageGUI->setQuestionHTML(array($questionGUI->object->getId() => $solutionHTML));
271  $pageGUI->setPresentationTitle($questionGUI->object->getTitleForHTMLOutput());
272 
273  $tpl = new ilTemplate('tpl.tst_corrections_solution_presentation.html', true, true, 'Modules/Test');
274  $tpl->setVariable('SOLUTION_PRESENTATION', $pageGUI->preview());
275 
276  $this->populatePageTitleAndDescription($questionGUI);
277 
278  $this->ui->mainTemplate()->setContent($tpl->get());
279 
280  $this->ui->mainTemplate()->setCurrentBlock("ContentStyle");
281  $stylesheet = ilObjStyleSheet::getContentStylePath(0);
282  $this->ui->mainTemplate()->setVariable("LOCATION_CONTENT_STYLESHEET", $stylesheet);
283  $this->ui->mainTemplate()->parseCurrentBlock();
284 
285  $this->ui->mainTemplate()->setCurrentBlock("SyntaxStyle");
286  $stylesheet = ilObjStyleSheet::getSyntaxStylePath();
287  $this->ui->mainTemplate()->setVariable("LOCATION_SYNTAX_STYLESHEET", $stylesheet);
288  $this->ui->mainTemplate()->parseCurrentBlock();
289  }
290 
291  protected function showAnswerStatistic()
292  {
293  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
294  $solutions = $this->getSolutions($questionGUI->object);
295 
296  $this->setCorrectionTabsContext($questionGUI, 'answers');
297 
298  $tablesHtml = '';
299 
300  foreach ($questionGUI->getSubQuestionsIndex() as $subQuestionIndex) {
301  $table = $questionGUI->getAnswerFrequencyTableGUI(
302  $this,
303  'showAnswerStatistic',
304  $solutions,
305  $subQuestionIndex
306  );
307 
308  $tablesHtml .= $table->getHTML() . $table->getAdditionalHtml();
309  }
310 
311  $this->populatePageTitleAndDescription($questionGUI);
312  $this->ui->mainTemplate()->setContent($tablesHtml);
313  }
314 
315  protected function addAnswer()
316  {
317  $form_builder = new ilAddAnswerFormBuilder($this, $this->ui->factory(), $this->refinery, $this->language, $this->ctrl);
318 
319  $form = $form_builder->buildAddAnswerForm()
320  ->withRequest($this->request);
321 
322  $data = $form->getData();
323  $question_id = $data['question_id'];
324 
325  if (!$this->checkQuestion($question_id)) {
326  $this->ui->mainTemplate()->setOnScreenMessage('failure', $this->language->txt('form_input_not_valid'));
327  $this->showAnswerStatistic();
328  return;
329  }
330 
331  $question_gui = $this->getQuestion($question_id);
332 
333  $question_index = $data['question_index'];
334  $answer_value = $data['answer_value'];
335  $points = $data['points'];
336 
337  if (!$points) {
338  $this->ui->mainTemplate()->setOnScreenMessage('failure', $this->language->txt('err_no_numeric_value'));
339  $this->showAnswerStatistic();
340  return;
341  }
342 
343  if ($question_gui->object->isAddableAnswerOptionValue($question_index, $answer_value)) {
344  $question_gui->object->addAnswerOptionValue($question_index, $answer_value, $points);
345  $question_gui->object->saveToDb();
346  }
347 
348  $scoring = new ilTestScoring($this->testOBJ);
349  $scoring->setPreserveManualScores(true);
350  $scoring->recalculateSolutions();
351 
352  $this->ui->mainTemplate()->setOnScreenMessage('success', $this->language->txt('saved_successfully'));
353  $this->showAnswerStatistic();
354  }
355 
356  protected function confirmQuestionRemoval()
357  {
358  $this->tabs->activateTab(ilTestTabsManager::TAB_ID_CORRECTION);
359 
360  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
361 
362  $confirmation = sprintf(
363  $this->language->txt('tst_corrections_qst_remove_confirmation'),
364  $questionGUI->object->getTitleForHTMLOutput(),
365  $questionGUI->object->getId()
366  );
367 
368  $buttons = array(
369  $this->ui->factory()->button()->standard(
370  $this->language->txt('confirm'),
371  $this->ctrl->getLinkTarget($this, 'performQuestionRemoval')
372  ),
373  $this->ui->factory()->button()->standard(
374  $this->language->txt('cancel'),
375  $this->ctrl->getLinkTarget($this, 'showQuestionList')
376  )
377  );
378 
379  $this->ui->mainTemplate()->setContent($this->ui->renderer()->render(
380  $this->ui->factory()->messageBox()->confirmation($confirmation)->withButtons($buttons)
381  ));
382  }
383 
384  protected function performQuestionRemoval(): void
385  {
386  $questionGUI = $this->getQuestion((int) $this->testrequest->raw('qid'));
387  $scoring = new ilTestScoring($this->testOBJ);
388 
389  $participantData = new ilTestParticipantData($this->database, $this->language);
390  $participantData->load($this->testOBJ->getTestId());
391 
392  // remove question solutions
393  $questionGUI->object->removeAllExistingSolutions();
394 
395  // remove test question results
396  $scoring->removeAllQuestionResults($questionGUI->object->getId());
397 
398  // remove question from test and reindex remaining questions
399  $this->testOBJ->removeQuestion($questionGUI->object->getId());
400  $reindexedSequencePositionMap = $this->testOBJ->reindexFixedQuestionOrdering();
401  $this->testOBJ->loadQuestions();
402 
403  // remove questions from all sequences
404  $this->testOBJ->removeQuestionFromSequences(
405  $questionGUI->object->getId(),
406  $participantData->getActiveIds(),
407  $reindexedSequencePositionMap
408  );
409 
410  // update pass and test results
411  $scoring->updatePassAndTestResults($participantData->getActiveIds());
412 
413  // trigger learning progress
414  ilLPStatusWrapper::_refreshStatus($this->testOBJ->getId(), $participantData->getUserIds());
415 
416  // finally delete the question itself
417  $questionGUI->object->delete($questionGUI->object->getId());
418 
419  // check for empty test and set test offline
420  if (!count($this->testOBJ->getTestQuestions())) {
421  $this->testOBJ->setOnline(false);
422  $this->testOBJ->saveToDb(true);
423  }
424 
425  $this->ctrl->setParameter($this, 'qid', '');
426  $this->ctrl->redirect($this, 'showQuestionList');
427  }
428 
429  protected function setCorrectionTabsContext(assQuestionGUI $questionGUI, $activeTabId)
430  {
431  $this->tabs->clearTargets();
432  $this->tabs->clearSubTabs();
433 
434  $this->help->setScreenIdComponent("tst");
435  $this->help->setScreenId("scoringadjust");
436  $this->help->setSubScreenId($activeTabId);
437 
438 
439  $this->tabs->setBackTarget(
440  $this->language->txt('back'),
441  $this->ctrl->getLinkTarget($this, 'showQuestionList')
442  );
443 
444  $this->tabs->addTab(
445  'question',
446  $this->language->txt('tst_corrections_tab_question'),
447  $this->ctrl->getLinkTarget($this, 'showQuestion')
448  );
449 
450  $this->tabs->addTab(
451  'solution',
452  $this->language->txt('tst_corrections_tab_solution'),
453  $this->ctrl->getLinkTarget($this, 'showSolution')
454  );
455 
456  if ($questionGUI->isAnswerFrequencyStatisticSupported()) {
457  $this->tabs->addTab(
458  'answers',
459  $this->language->txt('tst_corrections_tab_statistics'),
460  $this->ctrl->getLinkTarget($this, 'showAnswerStatistic')
461  );
462  }
463 
464  $this->tabs->activateTab($activeTabId);
465  }
466 
470  protected function populatePageTitleAndDescription(assQuestionGUI $questionGUI)
471  {
472  $this->ui->mainTemplate()->setTitle($questionGUI->object->getTitleForHTMLOutput());
473  $this->ui->mainTemplate()->setDescription($questionGUI->outQuestionType());
474  }
475 
480  protected function checkQuestion($qId): bool
481  {
482  if (!$this->testOBJ->isTestQuestion($qId)) {
483  return false;
484  }
485 
486  $questionGUI = $this->getQuestion($qId);
487 
488  if (!$this->supportsAdjustment($questionGUI)) {
489  return false;
490  }
491 
492  if (!$this->allowedInAdjustment($questionGUI)) {
493  return false;
494  }
495 
496  return true;
497  }
498 
503  protected function getQuestion($qId): assQuestionGUI
504  {
505  $question = assQuestion::instantiateQuestionGUI($qId);
506  $question->object->setObjId($this->testOBJ->getId());
507 
508  return $question;
509  }
510 
511  protected function getSolutions(assQuestion $question): array
512  {
513  $solutionRows = array();
514 
515  foreach ($this->testOBJ->getParticipants() as $activeId => $participantData) {
516  $passesSelector = new ilTestPassesSelector($this->database, $this->testOBJ);
517  $passesSelector->setActiveId($activeId);
518  $passesSelector->loadLastFinishedPass();
519 
520  foreach ($passesSelector->getClosedPasses() as $pass) {
521  foreach ($question->getSolutionValues($activeId, $pass) as $row) {
522  $solutionRows[] = $row;
523  }
524  }
525  }
526 
527  return $solutionRows;
528  }
529 
533  protected function getQuestions(): array
534  {
535  $questions = array();
536 
537  foreach ($this->testOBJ->getTestQuestions() as $questionData) {
538  $questionGUI = $this->getQuestion($questionData['question_id']);
539 
540  if (!$this->supportsAdjustment($questionGUI)) {
541  continue;
542  }
543 
544  if (!$this->allowedInAdjustment($questionGUI)) {
545  continue;
546  }
547 
548  $questions[] = $questionData;
549  }
550 
551  return $questions;
552  }
553 
561  protected function supportsAdjustment(\assQuestionGUI $question_object): bool
562  {
563  return ($question_object instanceof ilGuiQuestionScoringAdjustable
564  || $question_object instanceof ilGuiAnswerScoringAdjustable)
565  && ($question_object->object instanceof ilObjQuestionScoringAdjustable
566  || $question_object->object instanceof ilObjAnswerScoringAdjustable);
567  }
568 
575  protected function allowedInAdjustment(\assQuestionGUI $question_object): bool
576  {
577  $setting = new ilSetting('assessment');
578  $types = explode(',', $setting->get('assessment_scoring_adjustment'));
579  require_once './Modules/TestQuestionPool/classes/class.ilObjQuestionPool.php';
580  $type_def = array();
581  foreach ($types as $type) {
583  }
584 
585  $type = $question_object->getQuestionType();
586  if (in_array($type, $type_def)) {
587  return true;
588  }
589  return false;
590  }
591 }
getSolutionValues($active_id, $pass=null, bool $authorized=true)
Loads solutions of a given user from the database an returns it.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
database()
Get interface to the Database.
Definition: Container.php:38
buildQuestionCorrectionForm(assQuestionGUI $questionGUI)
$type
addHiddenItemsFromArray(ilConfirmationGUI $gui, $array, $curPath=array())
$lng
Abstract basic class which is to be extended by the concrete assessment question type classes...
static getQuestionTypeByTypeId($type_id)
Help GUI class.
tabs()
Get interface to the tabs.
Definition: Container.php:110
getTestId()
Gets the database id of the additional test data.
addHiddenItem(string $a_post_var, string $a_value)
setQuestionRemoveRowButtonEnabled(bool $questionRemoveRowButtonEnabled)
static accessViolationRedirect()
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Customizing of pimple-DIC for ILIAS.
Definition: Container.php:31
static instantiateQuestionGUI(int $a_question_id)
global $DIC
Definition: feed.php:28
if($format !==null) $name
Definition: metadata.php:247
Provides fluid interface to RBAC services.
Definition: UIServices.php:23
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
populateCorrectionsFormProperties(ilPropertyFormGUI $form)
setCorrectionTabsContext(assQuestionGUI $questionGUI, $activeTabId)
setRenderPageContainer(bool $a_val)
renderer()
Get a renderer for UI components.
Definition: UIServices.php:43
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
language()
Get interface to the i18n service.
Definition: Container.php:86
getSolutions(assQuestion $question)
static _refreshStatus(int $a_obj_id, ?array $a_users=null)
allowedInAdjustment(\assQuestionGUI $question_object)
Returns if the question type is allowed for adjustments in the global test administration.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$txt
Definition: error.php:13
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static getContentStylePath(int $a_style_id, bool $add_random=true, bool $add_token=true)
get content style path static (to avoid full reading)
supportsAdjustment(\assQuestionGUI $question_object)
Returns if the given question object support scoring adjustment.
__construct(Container $DIC, ilObjTest $testOBJ)
ilTestCorrectionsGUI constructor.
InternalRequestService $testrequest
populatePageTitleAndDescription(assQuestionGUI $questionGUI)
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...
factory()
Get the factory that crafts UI components.
Definition: UIServices.php:35
ctrl()
Get the interface to the control structure.
Definition: Container.php:54
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
if($DIC->http() ->request() ->getMethod()=="GET" &&isset($DIC->http() ->request() ->getQueryParams()['tex'])) $tpl
Definition: latex.php:41
ui()
Get the interface to get services from UI framework.
Definition: Container.php:118
mainTemplate()
Get the ILIAS main template.
Definition: UIServices.php:53
showQuestion(ilPropertyFormGUI $form=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...