ILIAS  trunk Revision v12.0_alpha-16-g3e876e53c80
class.ConsecutiveScoringGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
23use ILIAS\UI\Factory as UIFactory;
24use ILIAS\UI\Renderer as UIRenderer;
26use Psr\Http\Message\ServerRequestInterface;
41
43{
44 public const CMD_VIEW = 'view';
45 public const DEFAULT_COMMAND = 'view';
46
47 private const ACTION_FORM_STATE = 'fs';
48 private const ACTION_STORE_STATE = 'ss';
49 private const ACTION_STORE = 'store';
50 private const ACTION_SCORING_COMPLETE = 'ucomplete';
51 private const ACTION_SCORING_INCOMPLETE = 'uincomplete';
52
53 private const FILTER_USERS = 'fusers';
54 private const FILTER_QUESTIONS = 'fquestions';
55 private const FILTER_ANSWERED = 'fanswerd';
56 private const FILTER_FINAL = 'ffinal';
57 private const FILTER_USER_FINAL = 'fusrfinal';
58 private const FILTER_ONLY = 'only';
59 private const FILTER_HIDE = 'hide';
60 private const FILTER_SCOREDBY = 'fscrby';
61
62 private ?array $filter_values = null;
63 private Prompt $prompt;
64
65 public function __construct(
66 protected readonly \ilCtrlInterface $ctrl,
67 protected readonly \ilGlobalTemplateInterface $tpl,
68 protected readonly \ilTabsGUI $tabs,
69 protected readonly \ilLanguage $lng,
70 protected readonly \ilObjTest $object,
71 protected readonly \ilTestAccess $test_access,
72 protected readonly UIFactory $ui_factory,
73 protected UIRenderer $ui_renderer,
74 protected readonly Refinery $refinery,
75 protected readonly ServerRequestInterface $request,
76 protected readonly ResponseHandler $test_response,
77 protected readonly ConsecutiveScoring $scoring,
78 protected readonly ConsecutiveScoringURLs $scoring_url_builder,
79 protected readonly \ilUIFilterService $filter_service,
80 ) {
81 $this->prompt = $this->ui_factory->prompt()->standard($this->scoring_url_builder->buildURI());
82 }
83 public function executeCommand(): void
84 {
85 if (!$this->test_access->checkScoreParticipantsAccess()
86 && !$this->test_access->checkScoreParticipantsAccessAnon()
87 ) {
88 \ilObjTestGUI::accessViolationRedirect();
89 }
90
91 if (!$this->object->getGlobalSettings()->isManualScoringEnabled()) {
92 // allow only if at least one question type is marked for manual scoring
93 $this->tpl->setOnScreenMessage('failure', $this->lng->txt('manscoring_not_allowed'), true);
94 $this->ctrl->redirectByClass([ilRepositoryGUI::class, ilObjTestGUI::class, ilInfoScreenGUI::class]);
95 }
96
97 $this->tabs->activateTab(TabsManager::TAB_ID_MANUAL_SCORING);
98
99 $act = $this->scoring_url_builder->getAction() ?? self::DEFAULT_COMMAND;
100 [$question_id, $user_id, $attempt_id] = $this->scoring_url_builder->getIdParameters();
101
102 switch ($act) {
104 $answer_section =
105 $this->ui_renderer->render([
106 $this->getUserRepresentation($user_id, $attempt_id),
107 $this->getUserAnswer($question_id, $user_id, $attempt_id, false, false),
108 ]);
109 $response = $this->ui_factory->prompt()->state()->show(
110 $this->getScoringForm(self::ACTION_STORE_STATE, $question_id, $user_id, $attempt_id)
111 )->withTitle( //TODO: should not be on state, but on form
112 $answer_section
113 );
114 $this->test_response->sendAsync(
115 $this->ui_renderer->renderAsync($response)
116 );
117 break;
118
120 $form = $this->getScoringForm(self::ACTION_STORE_STATE, $question_id, $user_id, $attempt_id)
121 ->withRequest($this->request);
122 $formdata = $form->getData();
123 if ($formdata !== null) {
124 $this->store($formdata);
125 $msg = sprintf(
126 $this->lng->txt('tst_saved_manscoring_successfully'),
127 $attempt_id + 1,
128 $this->scoring->getUserFullName($user_id, (string) $attempt_id)
129 );
130 $this->tpl->setOnScreenMessage('success', $msg, true);
131
132 $anchor = sprintf('anchor_%s_%s', $question_id, $user_id);
133 $url = $this->scoring_url_builder
134 ->withAction(self::CMD_VIEW)
135 ->withFragment($anchor)
136 ->withForceRedirect()
137 ->buildURI();
138 $response = $this->ui_factory->prompt()->state()->redirect($url);
139 } else {
140 $response = $this->ui_factory->prompt()->state()->show($form);
141 }
142
143 $this->test_response->sendAsync(
144 $this->ui_renderer->renderAsync($response)
145 );
146 break;
147
149 $form = $this->getScoringForm(self::ACTION_STORE, $question_id, $user_id, $attempt_id)
150 ->withRequest($this->request);
151 $formdata = $form->getData();
152 if ($formdata === null) {
153 $this->tpl->setContent($this->view());
154 } else {
155 $this->store($formdata);
156 $msg = sprintf(
157 $this->lng->txt('tst_saved_manscoring_successfully'),
158 $attempt_id + 1,
159 $this->scoring->getUserFullName($user_id, (string) $attempt_id)
160 );
161 $this->tpl->setOnScreenMessage('success', $msg, true);
162 $this->scoring_url_builder->withAction(self::CMD_VIEW)->redirect();
163 }
164 break;
165
167 $this->scoring->completeScoring($user_id, true);
168 $msg = $this->lng->txt('manscoring_finalized');
169 $this->tpl->setOnScreenMessage('success', $msg, true);
170 $this->scoring_url_builder->withAction(self::CMD_VIEW)->redirect();
171 break;
172
174 $this->scoring->completeScoring($user_id, false);
175 $msg = $this->lng->txt('manscoring_finalized_removed');
176 $this->tpl->setOnScreenMessage('success', $msg, true);
177 $this->scoring_url_builder->withAction(self::CMD_VIEW)->redirect();
178 break;
179
181 $this->tpl->setContent($this->view());
182 break;
183
184 default:
185 throw new \Exception('no such command/action: ' . $act);
186 }
187 }
188
189 protected function view(): string
190 {
191 $filter = $this->getFilterInputs();
192 $this->filter_values = $this->filter_service->getData($filter);
193
194 $sequence = $this->ui_factory->navigation()->sequence(
195 $this,
197 )
198 ->withId('cs_' . (string) $this->object->getRefId())
199 ->withViewControls($this->getViewControls())
200 ->withRequest($this->request);
201
202 return $this->ui_renderer->render([
203 $filter,
204 $sequence
205 ]);
206 }
207
211 public function getAllPositions(
212 ServerRequestInterface $request,
213 mixed $viewcontrol_values,
214 mixed $filter_values
215 ): array {
216 $filter_values = $this->filter_values;
217 return $this->scoring->getPositions()
218 ->applyFilters(...$this->getFilters($filter_values))
219 ->get($viewcontrol_values);
220 }
221
222 public function getSegment(
223 ServerRequestInterface $request,
224 mixed $position_data,
225 mixed $viewcontrol_values,
226 mixed $filter_values
227 ): Segment {
228 if ($position_data === null) {
229 return $this->ui_factory->legacy()->segment(
230 '',
231 $this->lng->txt('ui_table_no_records')
232 );
233 }
234
235 [$usr_active_ids, $question_ids] = $position_data;
236 $usr_active_id = current($usr_active_ids);
237 $question_id = current($question_ids);
238
239 $attempt_id = $this->scoring->getAttemptUsedForEvaluation($usr_active_id);
240 $question_representation = $this->getQuestionRepresentation($question_id, true);
241 $user_representation = $this->getUserRepresentation($usr_active_id, $attempt_id);
242
243 return $this->ui_factory->legacy()->segment(
244 $this->ui_renderer->render(
245 $viewcontrol_values->isUserCentric() ? $user_representation : $question_representation
246 ),
247 $this->ui_renderer->render(
248 $viewcontrol_values->isSingle()
249 ? [
250 $viewcontrol_values->isUserCentric() ? $question_representation : $user_representation,
251 $this->singleSegmentContent($question_id, $usr_active_id, $attempt_id, $request)
252 ]
253 : [
254 $viewcontrol_values->isUserCentric()
255 ? $this->collectForUser($usr_active_id, $question_ids)
256 : $this->collectForQuestion($question_id, $usr_active_ids),
257 $this->prompt
258 ]
259 )
260 )
261 ->withSegmentActions(
262 ...
263 $viewcontrol_values->isUserCentric()
264 ? $this->getSegmentActionsForUser($usr_active_id)
265 : []
266 );
267 }
268
269 protected function getViewControls(): ViewControlContainer
270 {
271 $vcs = [
272 $this->ui_factory->input()->viewControl()->mode(
273 [
274 ConsecutiveScoringMode::ORIENTATION_USER => $this->lng->txt('mode_user'),
275 ConsecutiveScoringMode::ORIENTATION_QUESTION => $this->lng->txt('mode_question'),
276 ]
277 ),
278 $this->ui_factory->input()->viewControl()->mode(
279 [
280 ConsecutiveScoringMode::MODE_ALL_AT_ONCE => $this->lng->txt('mode_allatonce'),
281 ConsecutiveScoringMode::MODE_ONE_BY_ONE => $this->lng->txt('mode_onebyone'),
282 ]
283 )
284 ];
285
286 return $this->ui_factory->input()->container()->viewControl()->standard($vcs)
287 ->withAdditionalTransformation(
288 $this->refinery->custom()->transformation(
289 fn($v) => new ConsecutiveScoringMode(...$v)
290 )
291 );
292 }
293
294 protected function getFilterInputs(): FilterContainer
295 {
296 $filter = [
297 self::FILTER_USERS => $this->ui_factory->input()->field()->multiselect(
298 $this->lng->txt('tst_man_scoring_userselection'),
299 $this->scoring->getParticipantNames()
300 ),
301 self::FILTER_QUESTIONS => $this->ui_factory->input()->field()->multiselect(
302 $this->lng->txt('tst_man_scoring_questionselection'),
303 $this->scoring->getQuestionTitles($this->lng)
304 ),
305 self::FILTER_ANSWERED => $this->ui_factory->input()->field()->select(
306 $this->lng->txt('tst_man_scoring_only_answered'),
307 [
308 self::FILTER_ONLY => $this->lng->txt('tst_man_scoring_answered_only'),
309 self::FILTER_HIDE => $this->lng->txt('tst_man_scoring_answered_hide'),
310 ]
311 )->withValue(null),
312 self::FILTER_FINAL => $this->ui_factory->input()->field()->select(
313 $this->lng->txt('tst_man_scoring_finalized'),
314 [
315 self::FILTER_ONLY => $this->lng->txt('tst_man_scoring_finalized_only'),
316 self::FILTER_HIDE => $this->lng->txt('tst_man_scoring_finalized_hide'),
317 ]
318 )->withValue(null),
319 self::FILTER_USER_FINAL => $this->ui_factory->input()->field()->select(
320 $this->lng->txt('finalized_evaluation'),
321 [
322 self::FILTER_ONLY => $this->lng->txt('evaluated_users'),
323 self::FILTER_HIDE => $this->lng->txt('not_evaluated_users')
324 ]
325 )->withValue(null),
326 self::FILTER_SCOREDBY => $this->ui_factory->input()->field()->multiselect(
327 $this->lng->txt('scored_by'),
328 $this->scoring->getAllFinalizingUserNames()
329 )->withValue(null),
330
331 ];
332 return $this->filter_service->standard(
333 'csfilter_' . (string) $this->object->getRefId(),
334 $this->ctrl->getLinkTarget($this, self::CMD_VIEW),
335 $filter,
336 array_map(fn() => true, $filter),
337 true,
338 true
339 );
340 }
344 protected function getFilters(array $filter_values): array
345 {
346 $filter_values = array_map(fn($v) => $v === '' ? null : $v, $filter_values);
347 $ret = [];
348
349 if ($filter_values[self::FILTER_USERS] !== null) {
350 $ret[] = static fn(array $uids, array $qids): array => [
351 array_intersect($uids, $filter_values[self::FILTER_USERS]),
352 $qids
353 ];
354 }
355
356 if ($filter_values[self::FILTER_QUESTIONS] !== null) {
357 $ret[] = static fn(array $uids, array $qids): array => [
358 $uids,
359 array_intersect($qids, $filter_values[self::FILTER_QUESTIONS])
360 ];
361 }
362
363 if ($filter_values[self::FILTER_USER_FINAL] !== null) {
364 $ret[] = fn(array $uids, array $qids): array => [
365 array_filter(
366 $uids,
367 function ($uid) use ($filter_values) {
368 $complete = $this->scoring->isScoringComplete($uid);
369 $filter = $filter_values[self::FILTER_USER_FINAL];
370 return ($complete && $filter === self::FILTER_ONLY)
371 || (!$complete && $filter === self::FILTER_HIDE);
372 }
373 ),
374 $qids
375 ];
376 }
377
378 if ($filter_values[self::FILTER_ANSWERED] !== null) {
379 $answered = $this->scoring->getAnsweredQuestionIds();
380 $ret[] = function (array $uids, array $qids) use ($filter_values, $answered): array {
381 $qids = ($filter_values[self::FILTER_ANSWERED] === self::FILTER_ONLY)
382 ? array_intersect($qids, $answered[current($uids)])
383 : array_diff($qids, $answered[current($uids)]);
384 return [$uids, $qids];
385 };
386 }
387
388 if ($filter_values[self::FILTER_FINAL] !== null) {
389 $finalized = $this->scoring->getFinalizedFeedbackIds();
390 $ret[] = function (array $uids, array $qids) use ($filter_values, $finalized): array {
391 $qids = ($filter_values[self::FILTER_FINAL] === self::FILTER_ONLY)
392 ? array_intersect($qids, $finalized[current($uids)])
393 : array_diff($qids, $finalized[current($uids)]);
394 return [$uids, $qids];
395 };
396 }
397
398 if ($filter_values[self::FILTER_SCOREDBY] !== null) {
399 $scored_by = $this->scoring->getQidsFinalizedBy($filter_values[self::FILTER_SCOREDBY]);
400 $ret[] = static fn(array $uids, array $qids): array => [
401 $uids,
402 array_intersect($qids, $scored_by),
403 ];
404 }
405
406 return $ret;
407 }
408
409 protected function getUserRepresentation(int $usr_active_id, int $attempt_id): Entity
410 {
411 $usr_fullname = $this->scoring->getUserFullName($usr_active_id, (string) $attempt_id);
412 $usr_avatar = (new \ilUserAvatarResolver((int) $this->scoring->getUserId($usr_active_id, (string) $attempt_id)))->getAvatar();
413 $scored_participant_entity =
414 $this->ui_factory->entity()->standard(
415 $usr_fullname,
416 $usr_avatar
417 )->withDetails(
418 $this->ui_factory->listing()->property()->withProperty(
419 $this->lng->txt('scored_pass'),
420 (string) ($attempt_id + 1)
421 )->withProperty(
422 $this->lng->txt('usr_manscoring_complete'),
423 $this->scoring->isScoringComplete($usr_active_id) ?
424 $this->lng->txt('yes') : $this->lng->txt('no')
425 )->withProperty(
426 $this->lng->txt('exam_id'),
427 \ilObjTest::buildExamId($usr_active_id, $attempt_id, $this->object->getId())
428 )
429 );
430 return $scored_participant_entity;
431 }
432
433 protected function getQuestionRepresentation(int $question_id, bool $show_title = false): LegacyContent
434 {
435 $tpl = new \ilTemplate('tpl.il_as_tst_manual_scoring_consecutive_question.html', true, true, 'components/ILIAS/Test');
436 $question = $this->scoring->getQuestionObject($question_id);
437 $question_text = $question->getQuestion();
438
439 if ($show_title) {
440 $tpl->setCurrentBlock('expandable_title');
441 $question_title = $question->getTitle();
442 $tpl->setVariable('TITLE', $question_title);
443 } else {
444 $tpl->setCurrentBlock('question_only');
445 $tpl->setVariable('EXPAND_COLLAPSE', $this->lng->txt('expand') . '/' . $this->lng->txt('collapse'));
446 }
447 $tpl->setVariable('QUESTION', $question_text);
448 $tpl->parseCurrentBlock();
449
450 return $this->ui_factory->legacy()->content($tpl->get());
451 }
452
453 protected function getUserAnswer(
454 int $question_id,
455 int $usr_active_id,
456 int $attempt_id,
457 bool $show_feedback_html = false,
458 bool $show_grade_btn = false,
459 bool $show_properties = false
460 ): LegacyContent {
461 $question_gui = $this->scoring->getUserQuestionGUI($question_id, $usr_active_id, $attempt_id);
462 $question_solution = $question_gui->getSolutionOutput(
463 $usr_active_id,
464 $attempt_id,
465 $graphical_output = true,
466 $result_output = true,
467 $show_question_only = true,
468 $show_feedback = false,
469 $show_correct_solution = false,
470 $show_manual_scoring = true,
471 $show_question_text = false,
472 $show_inline_feedback = false
473 );
474 $tpl = new \ilTemplate('tpl.il_as_tst_manual_scoring_consecutive_answer.html', true, true, 'components/ILIAS/Test');
475
476 $usr_question = $question_gui->getObject();
477 $feedback = $this->scoring->getSingleManualFeedback($question_id, $usr_active_id, $attempt_id);
478
479 if ($show_properties) {
480 $info =
481 $this->ui_factory->listing()->property()
482 ->withProperty(
483 $this->lng->txt('tst_highscore_score'),
484 implode(' ', [
485 (string) $usr_question->getReachedPoints($usr_active_id, $attempt_id),
486 $this->lng->txt('tst_manscoring_input_of_max'),
487 (string) $usr_question->getMaximumPoints()
488 ])
489 )
490 ->withProperty(
491 $this->lng->txt('finalized_evaluation'),
492 (bool) ($feedback['finalized_evaluation'] ?? false) ?
493 $this->lng->txt('yes') : $this->lng->txt('no')
494 );
495 $info = $this->getWithFinalizedProperties($feedback, $info);
496 $tpl->setVariable('PROPERTIES', $this->ui_renderer->render($info));
497 }
498
499 $tpl->setVariable('ANSWER', $question_solution);
500 if (array_key_exists('feedback', $feedback) && $show_feedback_html) {
501 $tpl->setVariable(
502 'FEEDBACK',
503 $this->refinery->string()->markdown()->toHTML()->transform($feedback['feedback'])
504 );
505 } elseif ($show_feedback_html) {
506 $tpl->setVariable('FEEDBACK', $this->lng->txt('tst_manscoring_no_feedback'));
507 }
508 if ($show_grade_btn) {
509 $tpl->setVariable(
510 'GRADEBTN',
511 $this->ui_renderer->render(
512 $this->getSingleFormButton($question_id, $usr_active_id, $attempt_id)
513 )
514 );
515 }
516
517 return $this->ui_factory->legacy()->content($tpl->get());
518 }
519
520 protected function getScoringForm(string $action, int $question_id, int $usr_active_id, int $attempt_id): Form
521 {
522 $action = $this->scoring_url_builder
523 ->withAction($action)
524 ->withIdParameters($question_id, $usr_active_id, $attempt_id)
525 ->buildURI()->__toString();
526
527 $question = $this->scoring->getQuestionObject($question_id);
528 $max_points = $question->getMaximumPoints();
529 $score = $question->getReachedPoints($usr_active_id, $attempt_id);
530 $feedback = $this->scoring->getSingleManualFeedback($question_id, $usr_active_id, $attempt_id);
531 $feedback_final = (bool) ($feedback['finalized_evaluation'] ?? false);
532 $feedback_txt = $feedback['feedback'] ?? '';
533
534 $inputs = [];
535 $inputs[] = $this->ui_factory->input()->field()->numeric(
536 $this->lng->txt('tst_change_points_for_question')
537 )
538 ->withByline($this->lng->txt('tst_manscoring_input_of_max') . ' ' . $max_points)
540 $this->refinery->custom()->constraint(
541 fn($v) => (float) $v <= $max_points,
542 fn() => sprintf(
543 $this->lng->txt('tst_manscoring_maxpoints_exceeded_input_alert'),
544 $max_points
545 )
546 )
547 )
548 ->withAdditionalTransformation($this->refinery->kindlyTo()->float())
549 ->withValue($score);
550
551 $inputs[] = $this->ui_factory->input()->field()->markdown(
553 $this->lng->txt('set_manual_feedback')
554 )
555 ->withAdditionalTransformation($this->refinery->to()->string())
556 ->withValue($feedback_txt);
557
558 $inputs[] = $this->ui_factory->input()->field()->checkbox(
559 $this->lng->txt('finalized_evaluation')
560 )
561 ->withAdditionalTransformation($this->refinery->kindlyTo()->bool())
562 ->withValue($feedback_final);
563
564 $to_int = $this->refinery->kindlyTo()->int();
565 $inputs[] = $this->ui_factory->input()->field()->group(
566 [
567 $this->ui_factory->input()->field()->hidden()
568 ->withAdditionalTransformation($to_int)
569 ->withValue($question_id),
570 $this->ui_factory->input()->field()->hidden()
571 ->withAdditionalTransformation($to_int)
572 ->withValue($usr_active_id),
573 $this->ui_factory->input()->field()->hidden()
574 ->withAdditionalTransformation($to_int)
575 ->withValue($attempt_id)
576 ]
577 )
579 $this->refinery->custom()->transformation(
580 fn($values) => [
581 'qid' => $values[0],
582 'usr_active_id' => $values[1],
583 'attempt_id' => $values[2],
584 ]
585 )
586 );
587
588 return $this->ui_factory->input()->container()->form()->standard($action, $inputs)
589 ->withAdditionalTransformation(
590 $this->refinery->custom()->transformation(
591 fn($values) => [
592 'qid' => $values[3]['qid'],
593 'usr_active_id' => $values[3]['usr_active_id'],
594 'attempt' => $values[3]['attempt_id'],
595 'score' => $values[0],
596 'final' => $values[2] ?? false,
597 'feedback' => $values[1],
598 'max_points' => $max_points,
599 ]
600 )
601 );
602 }
603
604 protected function collectForUser(int $usr_active_id, array $question_ids): array
605 {
606 $attempt_id = $this->scoring->getAttemptUsedForEvaluation($usr_active_id);
607 $entries = [];
608 foreach ($question_ids as $question_id) {
609 $content = [
610 $this->getQuestionRepresentation($question_id, true),
611 $this->getUserAnswer($question_id, $usr_active_id, $attempt_id, true, true, true)
612 ];
613 $panel = $this->ui_factory->panel()->standard('', $content);
614 $entries[] = $this->ui_factory->legacy()->content(sprintf('<a id="anchor_%s_%s"></a>', $question_id, $usr_active_id));
615 $entries[] = $panel;
616 }
617 return $entries;
618 }
619
620 protected function collectForQuestion(int $question_id, array $usr_active_ids): array
621 {
622 $entries = [];
623 foreach ($usr_active_ids as $usr_active_id) {
624 $attempt_id = $this->scoring->getAttemptUsedForEvaluation($usr_active_id);
625 $content = [
626 $this->getUserRepresentation($usr_active_id, $attempt_id),
627 $this->getUserAnswer($question_id, $usr_active_id, $attempt_id, true, true, true)
628 ];
629 $panel = $this->ui_factory->panel()->standard('', $content);
630 $entries[] = $this->ui_factory->legacy()->content(sprintf('<a id="anchor_%s_%s"></a>', $question_id, $usr_active_id));
631 $entries[] = $panel;
632 }
633 return $entries;
634 }
635
636 protected function singleSegmentContent(
637 int $question_id,
638 int $usr_active_id,
639 int $attempt_id,
640 ServerRequestInterface $request
641 ): Alignment {
642 $form = $this->getScoringForm(self::ACTION_STORE, $question_id, $usr_active_id, $attempt_id);
643 if ($request->getMethod() === 'POST') {
644 $form = $form->withRequest($request);
645 }
646
647 $feedback_properties = $this->getWithFinalizedProperties(
648 $this->scoring->getSingleManualFeedback($question_id, $usr_active_id, $attempt_id),
649 $this->ui_factory->listing()->property()
650 );
651
652 return $this->ui_factory->layout()->alignment()->horizontal()->evenlyDistributed(
653 $this->getUserAnswer($question_id, $usr_active_id, $attempt_id),
654 $this->ui_factory->panel()->standard('', [$form, $feedback_properties])
655 );
656 }
657
658 protected function getSingleFormButton(int $question_id, int $usr_active_id, int $attempt_id): StdButton
659 {
660 $url = $this->scoring_url_builder
661 ->withAction(self::ACTION_FORM_STATE)
662 ->withIdParameters($question_id, $usr_active_id, $attempt_id)
663 ->buildURI();
664
665 return $this->ui_factory->button()->standard(
666 $this->lng->txt('edit_score'),
667 $this->prompt->getShowSignal($url)
668 );
669 }
670
671 protected function getSegmentActionsForUser(int $usr_active_id): array
672 {
673 $done_label = 'set_manscoring_done';
674 $done_action = self::ACTION_SCORING_COMPLETE;
675 if ($this->scoring->isScoringComplete($usr_active_id)) {
676 $done_label = 'set_manscoring_open';
677 $done_action = self::ACTION_SCORING_INCOMPLETE;
678 }
679 $btn_done = $this->ui_factory->button()->standard(
680 $this->lng->txt($done_label),
681 $this->scoring_url_builder
682 ->withAction($done_action)
683 ->withUserId($usr_active_id)
684 ->buildURI()->__toString()
685 );
686 return [$btn_done];
687 }
688
689 protected function store(array $data)
690 {
691 $this->scoring->store(
692 $data['qid'],
693 $data['usr_active_id'],
694 $data['attempt'],
695 $data['score'],
696 $data['final'],
697 $data['feedback'],
698 $data['max_points']
699 );
700 }
701
705 public function getWithFinalizedProperties(array $feedback, PropertyListing $info): PropertyListing
706 {
707 if (array_key_exists('finalized_by_usr_id', $feedback) && $feedback['finalized_by_usr_id'] !== 0) {
708 $feedback_usr_data = $this->object->getUserData([$feedback['finalized_by_usr_id']])[$feedback['finalized_by_usr_id']];
709 $feedback_usr_name = $feedback_usr_data['firstname'] . ' ' . $feedback_usr_data['lastname'];
710 $info = $info->withProperty(
711 $this->lng->txt('finalized_by'),
712 $feedback_usr_name
713 )->withProperty(
714 $this->lng->txt('finalized_on'),
715 (string) $feedback['finalized_time'],
716 false
717 );
718 }
719 return $info;
720 }
721}
Builds a Color from either hex- or rgb values.
Definition: Factory.php:31
Builds data types.
Definition: Factory.php:36
getSingleFormButton(int $question_id, int $usr_active_id, int $attempt_id)
singleSegmentContent(int $question_id, int $usr_active_id, int $attempt_id, ServerRequestInterface $request)
__construct(protected readonly \ilCtrlInterface $ctrl, protected readonly \ilGlobalTemplateInterface $tpl, protected readonly \ilTabsGUI $tabs, protected readonly \ilLanguage $lng, protected readonly \ilObjTest $object, protected readonly \ilTestAccess $test_access, protected readonly UIFactory $ui_factory, protected UIRenderer $ui_renderer, protected readonly Refinery $refinery, protected readonly ServerRequestInterface $request, protected readonly ResponseHandler $test_response, protected readonly ConsecutiveScoring $scoring, protected readonly ConsecutiveScoringURLs $scoring_url_builder, protected readonly \ilUIFilterService $filter_service,)
getUserRepresentation(int $usr_active_id, int $attempt_id)
getAllPositions(ServerRequestInterface $request, mixed $viewcontrol_values, mixed $filter_values)
collectForUser(int $usr_active_id, array $question_ids)
getQuestionRepresentation(int $question_id, bool $show_title=false)
collectForQuestion(int $question_id, array $usr_active_ids)
getSegment(ServerRequestInterface $request, mixed $position_data, mixed $viewcontrol_values, mixed $filter_values)
Receives position data (provided by getAllPositions) and builds a segment.
getWithFinalizedProperties(array $feedback, PropertyListing $info)
getUserAnswer(int $question_id, int $usr_active_id, int $attempt_id, bool $show_feedback_html=false, bool $show_grade_btn=false, bool $show_properties=false)
getScoringForm(string $action, int $question_id, int $usr_active_id, int $attempt_id)
withDetails(PropertyListing|Content ... $details)
Definition: Entity.php:223
language handling
static buildExamId($active_id, $pass, $test_obj_id=null)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
$info
Definition: entry_point.php:21
The SegmentRetrieval defines available stops for the sequence and builds it's segments.
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...
withAdditionalTransformation(Transformation $trafo)
@inheritDoc
withValue($value)
Get an input like this with another value displayed on the client side.
Definition: Group.php:61
global $lng
Definition: privfeed.php:31
if(!file_exists('../ilias.ini.php'))
$url
Definition: shib_logout.php:68
$response
Definition: xapitoken.php:90