ILIAS  release_10 Revision v10.1-43-ga1241a92c2f
QuestionsTableActions.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
23 use ILIAS\Test\Questions\Properties\Repository as TestQuestionsRepository;
29 use ILIAS\Data\URI;
32 
34 {
35  private const ACTION_SAVE_ORDER = 'save_order';
36  private const ACTION_DELETE = 'delete';
37  private const ACTION_DELETE_CONFIRMED = 'deletion_confirmed';
38  private const ACTION_COPY = 'copy';
39  private const ACTION_ADD_TO_POOL = 'add_qpl';
40  private const ACTION_PREVIEW = 'preview';
41  private const ACTION_ADJUST = 'correction';
42  private const ACTION_STATISTICS = 'statistics';
43  private const ACTION_EDIT_QUESTION = 'edit_question';
44  private const ACTION_EDIT_PAGE = 'edit_page';
45  private const ACTION_FEEDBACK = 'feedback';
46  private const ACTION_HINTS = 'hints';
47  private const ACTION_PRINT_QUESTIONS = 'print_questions';
48  private const ACTION_PRINT_ANSWERS = 'print_answers';
49  private const ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS = 'download_files';
50 
51  private string $table_id;
52 
56  public function __construct(
57  private readonly UIFactory $ui_factory,
58  private readonly UIRenderer $ui_renderer,
59  private \ilGlobalTemplateInterface $tpl,
60  private readonly ServerRequestInterface $request,
61  private readonly QuestionsTableQuery $table_query,
62  private readonly Language $lng,
63  private readonly \ilCtrl $ctrl,
64  private readonly TestQuestionsRepository $questionrepository,
65  private readonly Printer $question_printer,
66  private readonly \ilObjTest $test_obj,
67  private readonly bool $is_adjusting_questions_with_results_allowed,
68  private readonly bool $is_in_test_with_results,
69  private readonly bool $is_in_test_with_random_question_set,
70  private readonly \ilTestQuestionSetConfigFactory $test_question_set_config_factory
71  ) {
72  $this->table_id = (string) $test_obj->getId();
73  }
74 
75  public function setDisabledActions(
76  OrderingRow $row
77  ): OrderingRow {
78  $disable_default_actions = $this->is_in_test_with_random_question_set
79  || $this->is_in_test_with_results;
80 
81  return $row->withDisabledAction(
82  self::ACTION_DELETE,
83  $this->is_in_test_with_random_question_set && !$this->is_in_test_with_results
84  )->withDisabledAction(self::ACTION_COPY, $disable_default_actions)
85  ->withDisabledAction(self::ACTION_ADD_TO_POOL, $this->is_in_test_with_random_question_set)
86  ->withDisabledAction(self::ACTION_EDIT_QUESTION, $disable_default_actions)
87  ->withDisabledAction(self::ACTION_EDIT_PAGE, $disable_default_actions)
88  ->withDisabledAction(
89  self::ACTION_ADJUST,
90  $this->is_adjusting_questions_with_results_allowed && !$this->is_in_test_with_results
91  )->withDisabledAction(self::ACTION_FEEDBACK, $disable_default_actions)
92  ->withDisabledAction(self::ACTION_HINTS, $disable_default_actions)
93  ->withDisabledAction(self::ACTION_PRINT_ANSWERS, !$this->is_in_test_with_results)
94  ->withDisabledAction(
95  self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS,
96  $row->getCellContent('type_tag') !== $this->lng->txt(\assFileUpload::class)
97  || !$this->questionrepository->questionHasAnswers((int) $row->getId())
98  );
99  }
100 
101  public function getOrderActionUrl(): URI
102  {
103  return $this->table_query->getActionURL(self::ACTION_SAVE_ORDER);
104  }
105 
106  public function getActions(): array
107  {
108  $ag = fn(string $type, string $label, $action): TableAction => $this->
109  ui_factory->table()->action()->$type(
110  $this->lng->txt($label),
111  ...$this->table_query->getRowBoundURLBuilder($action)
112  );
113 
114  $actions = [
115  self::ACTION_PREVIEW => $ag('single', 'preview', self::ACTION_PREVIEW),
116  self::ACTION_ADJUST => $ag('single', 'tst_corrections_qst_form', self::ACTION_ADJUST),
117  self::ACTION_STATISTICS => $ag('single', 'statistics', self::ACTION_STATISTICS),
118  self::ACTION_EDIT_QUESTION => $ag('single', 'edit_question', self::ACTION_EDIT_QUESTION),
119  self::ACTION_EDIT_PAGE => $ag('single', 'edit_page', self::ACTION_EDIT_PAGE),
120  self::ACTION_FEEDBACK => $ag('single', 'tst_feedback', self::ACTION_FEEDBACK),
121  self::ACTION_HINTS => $ag('single', 'tst_question_hints_tab', self::ACTION_HINTS),
122  self::ACTION_PRINT_ANSWERS => $ag('single', 'print_answers', self::ACTION_PRINT_ANSWERS),
123  self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS => $ag('single', 'download_all_files', self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS),
124  ];
125 
126  if (!$this->test_obj->isRandomTest()) {
127  $actions[self::ACTION_DELETE] = $ag('standard', 'delete', self::ACTION_DELETE)
128  ->withAsync();
129  }
130 
131  return $actions + [
132  self::ACTION_COPY => $ag('standard', 'copy', self::ACTION_COPY),
133  self::ACTION_ADD_TO_POOL => $ag('standard', 'copy_and_link_to_questionpool', self::ACTION_ADD_TO_POOL),
134  self::ACTION_PRINT_QUESTIONS => $ag('multi', 'print', self::ACTION_PRINT_QUESTIONS)
135  ];
136  }
137 
139  {
140  return function (int $question_id): string {
141  [$url_builder, $row_id_token] = $this->table_query->getRowBoundURLBuilder(self::ACTION_PREVIEW);
142  return $url_builder->withParameter($row_id_token, (string) $question_id)
143  ->buildURI()
144  ->__toString();
145  };
146  }
147 
151  public function handleCommand(
152  string $cmd,
153  array $row_ids,
154  \Closure $protect_by_write_protection,
155  \Closure $copy_and_link_to_questionpool,
156  \Closure $get_table
157  ): bool {
158  switch ($cmd) {
159  case self::ACTION_SAVE_ORDER:
160  $protect_by_write_protection();
161  $data = $get_table()->getTableComponent()->getData();
162  $this->test_obj->setQuestionOrder(array_flip($data), []);
163  $this->tpl->setOnScreenMessage('success', $this->lng->txt('saved_successfully'), true);
164  return true;
165 
166  case self::ACTION_PREVIEW:
168  current($row_ids),
169  \ilAssQuestionPreviewGUI::class,
171  );
172  return false;
173 
174  case self::ACTION_ADJUST:
176  current($row_ids),
177  \ilTestCorrectionsGUI::class,
178  'showQuestion'
179  );
180  return false;
181 
182  case self::ACTION_STATISTICS:
184  current($row_ids),
185  \ilAssQuestionPreviewGUI::class,
187  );
188  return false;
189 
190  case self::ACTION_EDIT_QUESTION:
191  $question_id = current($row_ids);
192  $qtype = $this->test_obj->getQuestionType($question_id);
193  $target_class = $qtype . 'GUI';
195  $question_id,
196  $target_class,
197  'editQuestion'
198  );
199  return false;
200 
201  case self::ACTION_EDIT_PAGE:
203  current($row_ids),
204  \ilAssQuestionPageGUI::class,
205  'edit'
206  );
207  return false;
208 
209  case self::ACTION_FEEDBACK:
211  current($row_ids),
212  \ilAssQuestionFeedbackEditingGUI::class,
214  );
215  return false;
216 
217  case self::ACTION_HINTS:
219  current($row_ids),
220  \ilAssQuestionHintsGUI::class,
222  );
223  return false;
224 
225  case self::ACTION_DELETE:
226  echo $this->ui_renderer->renderAsync(
227  $this->getDeleteConfirmation(array_filter($row_ids))
228  );
229  exit();
230 
231  case self::ACTION_DELETE_CONFIRMED:
232  $row_ids = $this->request->getParsedBody()['interruptive_items'] ?? [];
233  if (array_filter($row_ids) === []) {
234  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
235  return true;
236  }
237  $protect_by_write_protection();
238  if ($this->is_in_test_with_results) {
239  $this->test_obj->removeQuestionsWithResults($row_ids);
240  } else {
241  $this->test_obj->removeQuestions($row_ids);
242  $this->test_obj->saveCompleteStatus($this->test_question_set_config_factory->getQuestionSetConfig());
243  }
244  $this->tpl->setOnScreenMessage('success', $this->lng->txt('tst_questions_removed'), true);
245  return true;
246 
247  case self::ACTION_COPY:
248  if (array_filter($row_ids) === []) {
249  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
250  return true;
251  }
252  $protect_by_write_protection();
253  $this->test_obj->copyQuestions($row_ids);
254  $this->tpl->setOnScreenMessage('success', $this->lng->txt('copy_questions_success'), true);
255  return true;
256 
257  case self::ACTION_ADD_TO_POOL:
258  if (array_filter($row_ids) === []) {
259  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('tst_no_question_selected_for_moving_to_qpl'));
260  return true;
261  }
262  $protect_by_write_protection();
263  if (!$this->checkQuestionParametersForCopyToPool($row_ids)) {
264  return true;
265  }
266  $copy_and_link_to_questionpool();
267  return false;
268 
269  case self::ACTION_PRINT_QUESTIONS:
270  if (array_filter($row_ids) === []) {
271  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
272  return true;
273  }
274  $protect_by_write_protection();
275  $this->question_printer->printSelectedQuestions(
276  array_reduce(
277  Types::cases(),
278  function (array $c, Types $v): array {
279  $c[$v->getLabel($this->lng)] = $this->table_query
280  ->getPrintViewTypeURL(self::ACTION_PRINT_QUESTIONS, $v->value)->__toString();
281  return $c;
282  },
283  []
284  ),
285  Types::tryFrom($this->table_query->getPrintViewType()) ?? Types::RESULTS_VIEW_TYPE_SHOW,
286  $row_ids
287  );
288  return false;
289 
290  case self::ACTION_PRINT_ANSWERS:
291  $protect_by_write_protection();
292  $this->question_printer->printAnswers(current($row_ids));
293  return false;
294 
295  case self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS:
296  $protect_by_write_protection();
297  $question_object = \assQuestion::instantiateQuestion(current($row_ids));
298  if ($question_object instanceof \ilObjFileHandlingQuestionType) {
299  $question_object->deliverFileUploadZIPFile(
300  $this->test_obj->getRefId(),
301  $this->test_obj->getTestId(),
302  $this->test_obj->getTitle()
303  );
304  }
305 
306  // no break
307  default:
308  throw new \InvalidArgumentException("No such table_cmd: '$cmd'.");
309  }
310  }
311 
312  private function getDeleteConfirmation(array $row_ids): Interruptive
313  {
314  $items = [];
315  foreach ($row_ids as $id) {
316  $qdata = $this->test_obj->getQuestionDataset($id);
317  $type = $this->questionrepository->getQuestionPropertiesForQuestionId($id)
318  ->getGeneralQuestionProperties()->getTypeName($this->lng);
319  $icon = $this->ui_renderer->render(
320  $this->ui_factory->symbol()->icon()->standard('ques', $type, 'small')
321  );
322  $items[] = $this->ui_factory->modal()->interruptiveItem()->keyvalue(
323  (string) $id,
324  $icon . ' ' . $qdata->title,
325  $type
326  );
327  }
328 
329  return $this->ui_factory->modal()->interruptive(
330  $this->lng->txt('remove'),
331  $this->lng->txt(
332  $this->is_in_test_with_results
333  ? 'tst_remove_questions_and_results'
334  : 'tst_remove_questions'
335  ),
336  $this->table_query->getActionURL(self::ACTION_DELETE_CONFIRMED)->__toString()
337  )
338  ->withAffectedItems($items);
339  }
340 
342  int $question_id,
343  string $target_class,
344  string $cmd
345  ): void {
346 
347  $this->ctrl->setParameterByClass(
348  $target_class,
349  'q_id',
350  $question_id
351  );
352 
353  $this->ctrl->setParameterByClass(
354  $target_class,
355  'calling_test',
356  (string) $this->test_obj->getRefId()
357  );
358 
359  $this->ctrl->redirectByClass($target_class, $cmd);
360  }
361 
365  private function checkQuestionParametersForCopyToPool(array $question_ids): bool
366  {
367  $question_properties = $this->questionrepository
368  ->getQuestionPropertiesForQuestionIds($question_ids);
369  foreach ($question_ids as $q_id) {
370  if (!$this->questionrepository->originalQuestionExists($q_id)) {
371  continue;
372  }
373 
374  $type = \ilObject::_lookupType(
376  $question_properties[$q_id]->getGeneralQuestionProperties()->getOriginalId()
377  )
378  );
379 
380  if ($type !== 'tst') {
381  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('tst_link_only_unassigned'), true);
382  return false;
383  }
384  }
385  return true;
386  }
387 }
withDisabledAction(string $action_id, bool $disable=true)
Refer to an Action by its id and disable it for this row/record only.
redirectWithQuestionParameters(int $question_id, string $target_class, string $cmd)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$c
Definition: deliver.php:9
const CMD_SHOW_LIST
command constants
__construct(private readonly UIFactory $ui_factory, private readonly UIRenderer $ui_renderer, private \ilGlobalTemplateInterface $tpl, private readonly ServerRequestInterface $request, private readonly QuestionsTableQuery $table_query, private readonly Language $lng, private readonly \ilCtrl $ctrl, private readonly TestQuestionsRepository $questionrepository, private readonly Printer $question_printer, private readonly \ilObjTest $test_obj, private readonly bool $is_adjusting_questions_with_results_allowed, private readonly bool $is_in_test_with_results, private readonly bool $is_in_test_with_random_question_set, private readonly \ilTestQuestionSetConfigFactory $test_question_set_config_factory)
static instantiateQuestion(int $question_id)
The scope of this class is split ilias-conform URI&#39;s into components.
Definition: URI.php:34
static lookupParentObjId(int $question_id)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:24
global $lng
Definition: privfeed.php:32
handleCommand(string $cmd, array $row_ids, \Closure $protect_by_write_protection, \Closure $copy_and_link_to_questionpool, \Closure $get_table)
static _lookupType(int $id, bool $reference=false)