ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
QuestionsTableActions.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use ILIAS\Test\Questions\Properties\Repository as TestQuestionsRepository;
24use ILIAS\UI\Factory as UIFactory;
25use ILIAS\UI\Renderer as UIRenderer;
31use Psr\Http\Message\ServerRequestInterface;
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
135 public function getQuestionTargetLinkBuilder(): \Closure
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:
164 $this->redirectWithQuestionParameters(
165 current($row_ids),
166 \ilAssQuestionPreviewGUI::class,
168 );
169 return false;
170
171 case self::ACTION_ADJUST:
172 $this->redirectWithQuestionParameters(
173 current($row_ids),
174 \ilTestCorrectionsGUI::class,
175 'showQuestion'
176 );
177 return false;
178
179 case self::ACTION_STATISTICS:
180 $this->redirectWithQuestionParameters(
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';
191 $this->redirectWithQuestionParameters(
192 $question_id,
193 $target_class,
194 'editQuestion'
195 );
196 return false;
197
198 case self::ACTION_EDIT_PAGE:
199 $this->redirectWithQuestionParameters(
200 current($row_ids),
201 \ilAssQuestionPageGUI::class,
202 'edit'
203 );
204 return false;
205
206 case self::ACTION_FEEDBACK:
207 $this->redirectWithQuestionParameters(
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
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}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
redirectWithQuestionParameters(int $question_id, string $target_class, string $cmd)
handleCommand(string $cmd, array $row_ids, \Closure $protect_by_write_protection, \Closure $copy_and_link_to_questionpool, \Closure $get_table)
__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 lookupParentObjId(int $question_id)
static instantiateQuestion(int $question_id)
Class ilCtrl provides processing control methods.
static _lookupType(int $id, bool $reference=false)
$c
Definition: deliver.php:25
exit
withAffectedItems(array $items)
Get a modal like this listing the given items in the content section below the message.
withDisabledAction(string $action_id, bool $disable=true)
Refer to an Action by its id and disable it for this row/record only.
An entity that renders components to a string output.
Definition: Renderer.php:31
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
global $lng
Definition: privfeed.php:31