ILIAS  trunk Revision v11.0_alpha-2638-g80c1d007f79
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_PRINT_QUESTIONS = 'print_questions';
47  private const ACTION_PRINT_ANSWERS = 'print_answers';
48  private const ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS = 'download_files';
49 
50  private string $table_id;
51 
55  public function __construct(
56  private readonly UIFactory $ui_factory,
57  private readonly UIRenderer $ui_renderer,
58  private \ilGlobalTemplateInterface $tpl,
59  private readonly ServerRequestInterface $request,
60  private readonly QuestionsTableQuery $table_query,
61  private readonly Language $lng,
62  private readonly \ilCtrl $ctrl,
63  private readonly TestQuestionsRepository $questionrepository,
64  private readonly Printer $question_printer,
65  private readonly \ilObjTest $test_obj,
66  private readonly bool $is_adjusting_questions_with_results_allowed,
67  private readonly bool $is_in_test_with_results,
68  private readonly bool $is_in_test_with_random_question_set,
69  private readonly \ilTestQuestionSetConfigFactory $test_question_set_config_factory
70  ) {
71  $this->table_id = (string) $test_obj->getId();
72  }
73 
74  public function setDisabledActions(
75  OrderingRow $row
76  ): OrderingRow {
77  $disable_default_actions = $this->is_in_test_with_random_question_set
78  || $this->is_in_test_with_results;
79 
80  return $row->withDisabledAction(
81  self::ACTION_DELETE,
82  $this->is_in_test_with_random_question_set && !$this->is_in_test_with_results
83  )->withDisabledAction(self::ACTION_COPY, $disable_default_actions)
84  ->withDisabledAction(self::ACTION_ADD_TO_POOL, $this->is_in_test_with_random_question_set)
85  ->withDisabledAction(self::ACTION_EDIT_QUESTION, $disable_default_actions)
86  ->withDisabledAction(self::ACTION_EDIT_PAGE, $disable_default_actions)
87  ->withDisabledAction(
88  self::ACTION_ADJUST,
89  $this->is_adjusting_questions_with_results_allowed && !$this->is_in_test_with_results
90  )->withDisabledAction(self::ACTION_FEEDBACK, $disable_default_actions)
91  ->withDisabledAction(self::ACTION_PRINT_ANSWERS, !$this->is_in_test_with_results)
92  ->withDisabledAction(
93  self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS,
94  $row->getCellContent('type_tag') !== $this->lng->txt(\assFileUpload::class)
95  || !$this->questionrepository->questionHasAnswers((int) $row->getId())
96  );
97  }
98 
99  public function getOrderActionUrl(): URI
100  {
101  return $this->table_query->getActionURL(self::ACTION_SAVE_ORDER);
102  }
103 
104  public function getActions(): array
105  {
106  $ag = fn(string $type, string $label, $action): TableAction => $this->
107  ui_factory->table()->action()->$type(
108  $this->lng->txt($label),
109  ...$this->table_query->getRowBoundURLBuilder($action)
110  );
111 
112  $actions = [
113  self::ACTION_PREVIEW => $ag('single', 'preview', self::ACTION_PREVIEW),
114  self::ACTION_ADJUST => $ag('single', 'tst_corrections_qst_form', self::ACTION_ADJUST),
115  self::ACTION_STATISTICS => $ag('single', 'statistics', self::ACTION_STATISTICS),
116  self::ACTION_EDIT_QUESTION => $ag('single', 'edit_question', self::ACTION_EDIT_QUESTION),
117  self::ACTION_EDIT_PAGE => $ag('single', 'edit_page', self::ACTION_EDIT_PAGE),
118  self::ACTION_FEEDBACK => $ag('single', 'tst_feedback', self::ACTION_FEEDBACK),
119  self::ACTION_PRINT_ANSWERS => $ag('single', 'print_answers', self::ACTION_PRINT_ANSWERS),
120  self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS => $ag('single', 'download_all_files', self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS),
121  ];
122 
123  if (!$this->test_obj->isRandomTest()) {
124  $actions[self::ACTION_DELETE] = $ag('standard', 'delete', self::ACTION_DELETE)
125  ->withAsync();
126  }
127 
128  return $actions + [
129  self::ACTION_COPY => $ag('standard', 'copy', self::ACTION_COPY),
130  self::ACTION_ADD_TO_POOL => $ag('standard', 'copy_and_link_to_questionpool', self::ACTION_ADD_TO_POOL),
131  self::ACTION_PRINT_QUESTIONS => $ag('multi', 'print', self::ACTION_PRINT_QUESTIONS)
132  ];
133  }
134 
136  {
137  return function (int $question_id): string {
138  [$url_builder, $row_id_token] = $this->table_query->getRowBoundURLBuilder(self::ACTION_PREVIEW);
139  return $url_builder->withParameter($row_id_token, (string) $question_id)
140  ->buildURI()
141  ->__toString();
142  };
143  }
144 
148  public function handleCommand(
149  string $cmd,
150  array $row_ids,
151  \Closure $protect_by_write_protection,
152  \Closure $copy_and_link_to_questionpool,
153  \Closure $get_table
154  ): bool {
155  switch ($cmd) {
156  case self::ACTION_SAVE_ORDER:
157  $protect_by_write_protection();
158  $data = $get_table()->getTableComponent()->getData();
159  $this->test_obj->setQuestionOrder(array_flip($data), []);
160  $this->tpl->setOnScreenMessage('success', $this->lng->txt('saved_successfully'), true);
161  return true;
162 
163  case self::ACTION_PREVIEW:
165  current($row_ids),
166  \ilAssQuestionPreviewGUI::class,
168  );
169  return false;
170 
171  case self::ACTION_ADJUST:
173  current($row_ids),
174  \ilTestCorrectionsGUI::class,
175  'showQuestion'
176  );
177  return false;
178 
179  case self::ACTION_STATISTICS:
181  current($row_ids),
182  \ilAssQuestionPreviewGUI::class,
184  );
185  return false;
186 
187  case self::ACTION_EDIT_QUESTION:
188  $question_id = current($row_ids);
189  $qtype = $this->test_obj->getQuestionType($question_id);
190  $target_class = $qtype . 'GUI';
192  $question_id,
193  $target_class,
194  'editQuestion'
195  );
196  return false;
197 
198  case self::ACTION_EDIT_PAGE:
200  current($row_ids),
201  \ilAssQuestionPageGUI::class,
202  'edit'
203  );
204  return false;
205 
206  case self::ACTION_FEEDBACK:
208  current($row_ids),
209  \ilAssQuestionFeedbackEditingGUI::class,
211  );
212  return false;
213 
214  case self::ACTION_DELETE:
215  echo $this->ui_renderer->renderAsync(
216  $this->getDeleteConfirmation(array_filter($row_ids))
217  );
218  exit();
219 
220  case self::ACTION_DELETE_CONFIRMED:
221  $row_ids = $this->request->getParsedBody()['interruptive_items'] ?? [];
222  if (array_filter($row_ids) === []) {
223  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
224  return true;
225  }
226  $protect_by_write_protection();
227  if ($this->is_in_test_with_results) {
228  $this->test_obj->removeQuestionsWithResults($row_ids);
229  } else {
230  $this->test_obj->removeQuestions($row_ids);
231  $this->test_obj->saveCompleteStatus($this->test_question_set_config_factory->getQuestionSetConfig());
232  }
233  $this->tpl->setOnScreenMessage('success', $this->lng->txt('tst_questions_removed'), true);
234  return true;
235 
236  case self::ACTION_COPY:
237  if (array_filter($row_ids) === []) {
238  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
239  return true;
240  }
241  $protect_by_write_protection();
242  $this->test_obj->copyQuestions($row_ids);
243  $this->tpl->setOnScreenMessage('success', $this->lng->txt('copy_questions_success'), true);
244  return true;
245 
246  case self::ACTION_ADD_TO_POOL:
247  if (array_filter($row_ids) === []) {
248  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('tst_no_question_selected_for_moving_to_qpl'));
249  return true;
250  }
251  $protect_by_write_protection();
252  if (!$this->checkQuestionParametersForCopyToPool($row_ids)) {
253  return true;
254  }
255  $copy_and_link_to_questionpool();
256  return false;
257 
258  case self::ACTION_PRINT_QUESTIONS:
259  if (array_filter($row_ids) === []) {
260  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('msg_no_questions_selected'));
261  return true;
262  }
263  $protect_by_write_protection();
264  $this->question_printer->printSelectedQuestions(
265  array_reduce(
266  Types::cases(),
267  function (array $c, Types $v): array {
268  $c[$v->getLabel($this->lng)] = $this->table_query
269  ->getPrintViewTypeURL(self::ACTION_PRINT_QUESTIONS, $v->value)->__toString();
270  return $c;
271  },
272  []
273  ),
274  Types::tryFrom($this->table_query->getPrintViewType()) ?? Types::RESULTS_VIEW_TYPE_SHOW,
275  $row_ids
276  );
277  return false;
278 
279  case self::ACTION_PRINT_ANSWERS:
280  $protect_by_write_protection();
281  $this->question_printer->printAnswers(current($row_ids));
282  return false;
283 
284  case self::ACTION_DOWNLOAD_FILE_QUESTION_ANSWERS:
285  $protect_by_write_protection();
286  $question_object = \assQuestion::instantiateQuestion(current($row_ids));
287  if ($question_object instanceof \ilObjFileHandlingQuestionType) {
288  $question_object->deliverFileUploadZIPFile(
289  $this->test_obj->getRefId(),
290  $this->test_obj->getTestId(),
291  $this->test_obj->getTitle()
292  );
293  }
294 
295  // no break
296  default:
297  throw new \InvalidArgumentException("No such table_cmd: '$cmd'.");
298  }
299  }
300 
301  private function getDeleteConfirmation(array $row_ids): Interruptive
302  {
303  $items = [];
304  foreach ($row_ids as $id) {
305  $qdata = $this->test_obj->getQuestionDataset($id);
306  $type = $this->questionrepository->getQuestionPropertiesForQuestionId($id)
307  ->getGeneralQuestionProperties()->getTypeName($this->lng);
308  $icon = $this->ui_renderer->render(
309  $this->ui_factory->symbol()->icon()->standard('ques', $type, 'small')
310  );
311  $items[] = $this->ui_factory->modal()->interruptiveItem()->keyvalue(
312  (string) $id,
313  $icon . ' ' . $qdata->title,
314  $type
315  );
316  }
317 
318  return $this->ui_factory->modal()->interruptive(
319  $this->lng->txt('remove'),
320  $this->lng->txt(
321  $this->is_in_test_with_results
322  ? 'tst_remove_questions_and_results'
323  : 'tst_remove_questions'
324  ),
325  $this->table_query->getActionURL(self::ACTION_DELETE_CONFIRMED)->__toString()
326  )
327  ->withAffectedItems($items);
328  }
329 
331  int $question_id,
332  string $target_class,
333  string $cmd
334  ): void {
335 
336  $this->ctrl->setParameterByClass(
337  $target_class,
338  'q_id',
339  $question_id
340  );
341 
342  $this->ctrl->setParameterByClass(
343  $target_class,
344  'calling_test',
345  (string) $this->test_obj->getRefId()
346  );
347 
348  $this->ctrl->redirectByClass($target_class, $cmd);
349  }
350 
354  private function checkQuestionParametersForCopyToPool(array $question_ids): bool
355  {
356  $question_properties = $this->questionrepository
357  ->getQuestionPropertiesForQuestionIds($question_ids);
358  foreach ($question_ids as $q_id) {
359  if (!$this->questionrepository->originalQuestionExists($q_id)) {
360  continue;
361  }
362 
363  $type = \ilObject::_lookupType(
365  $question_properties[$q_id]->getGeneralQuestionProperties()->getOriginalId()
366  )
367  );
368 
369  if ($type !== 'tst') {
370  $this->tpl->setOnScreenMessage('failure', $this->lng->txt('tst_link_only_unassigned'), true);
371  return false;
372  }
373  }
374  return true;
375  }
376 }
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:25
__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)
exit
static lookupParentObjId(int $question_id)
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
global $lng
Definition: privfeed.php:31
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)