19declare(strict_types=1);
58 private readonly UIFactory $ui_factory,
59 private readonly UIRenderer $ui_renderer,
64 private readonly ContentStyle $content_style,
65 private readonly HTTPServices
$http,
72 $this->ref_id = $this->
object->getRefId();
73 $this->main_settings = $this->
object->getMainSettings();
74 $this->data_factory =
new DataFactory();
76 $this->test_session = (new \ilTestSessionFactory($this->
object, $this->
database, $this->
user))->getSession();
78 $this->test_passes_selector = new \ilTestPassesSelector($this->
database, $this->
object);
79 $this->test_passes_selector->setActiveId($this->test_session->getActiveId());
80 $this->test_passes_selector->setLastFinishedPass($this->test_session->getLastFinishedPass());
81 $this->password_checker = new \ilTestPasswordChecker($this->rbac_system, $this->
user, $this->
object, $this->
lng);
86 if ($this->
access->checkAccess(
'read',
'', $this->ref_id)) {
87 $this->{$this->
ctrl->getCmd(self::DEFAULT_CMD)}();
93 if (!$this->
object->getMainSettings()->getAdditionalSettings()->getHideInfoTab()) {
94 $this->
ctrl->redirectByClass([\ilRepositoryGUI::class, \ilObjTestGUI::class, \ilInfoScreenGUI::class]);
97 $this->tpl->setOnScreenMessage(
'failure', sprintf(
98 $this->
lng->txt(
'msg_no_perm_read_item'),
99 $this->object->getTitle()
102 $this->
ctrl->redirectByClass(\ilRepositoryGUI::class);
108 $this->tpl->setPermanentLink($this->
object->getType(), $this->ref_id);
119 $this->tpl->setContent(
120 $this->ui_renderer->render(
121 $this->handleRenderLauncher(
129 $this->tpl->setContent(
130 $this->ui_renderer->render(
131 $this->addOfflineMessageBoxIfNecessary(
140 if (!$this->
object->getOfflineStatus()) {
144 $offline_message_box = $this->ui_factory->messageBox()->info($this->
lng->txt(
'test_is_offline'));
146 if (!$this->
access->checkAccess(
'write',
'', $this->object->getRefId())) {
147 $elements[] = $offline_message_box;
151 $elements[] = $offline_message_box->withLinks([
152 $this->ui_factory->link()->standard(
153 $this->lng->txt(
'test_edit_settings'),
154 $this->ctrl->getLinkTargetByClass(SettingsMainGUI::class)
162 $message_box_message =
'';
163 $message_box_message_elements = [];
165 $exam_conditions_enabled = $this->main_settings->getIntroductionSettings()->getExamConditionsCheckboxEnabled();
166 $password_enabled = $this->main_settings->getAccessSettings()->getPasswordEnabled();
167 $test_behaviour_settings = $this->main_settings->getTestBehaviourSettings();
169 if ($exam_conditions_enabled && $password_enabled) {
170 $message_box_message_elements[] = $this->
lng->txt(
'tst_launcher_status_message_conditions_and_password');
171 } elseif ($exam_conditions_enabled) {
172 $message_box_message_elements[] = $this->
lng->txt(
'tst_launcher_status_message_conditions');
173 } elseif ($password_enabled) {
174 $message_box_message_elements[] = $this->
lng->txt(
'tst_launcher_status_message_password');
177 if ($test_behaviour_settings->getProcessingTimeEnabled() && !$this->isUserOutOfProcessingTime()) {
178 $message_box_message_elements[] = sprintf(
179 $this->
lng->txt(
'tst_time_limit_message'),
180 $test_behaviour_settings->getProcessingTimeAsMinutes()
184 $nr_of_tries = $this->
object->getNrOfTries();
186 if ($nr_of_tries !== 0) {
187 $message_box_message_elements[] = sprintf($this->
lng->txt(
'tst_attempt_limit_message'), $nr_of_tries);
190 if ($this->
object->isStartingTimeEnabled() && !$this->object->startingTimeReached()) {
191 $message_box_message_elements[] = sprintf(
192 $this->
lng->txt(
'detail_starting_time_not_reached'),
197 if ($this->
object->isEndingTimeEnabled() && !$this->object->endingTimeReached()) {
198 $message_box_message_elements[] = sprintf(
199 $this->
lng->txt(
'tst_exam_ending_time_message'),
204 foreach ($message_box_message_elements as $message_box_message_element) {
205 $message_box_message .=
' ' . $message_box_message_element;
208 if (!empty($message_box_message)) {
209 $elements[] = $this->ui_factory->messageBox()->info($message_box_message);
217 $introduction = $this->
object->getIntroduction();
220 $this->main_settings->getIntroductionSettings()->getIntroductionEnabled() &&
221 !empty($introduction)
223 $this->content_style->gui()->addCss($this->tpl, $this->ref_id);
224 $elements[] = $this->ui_factory->panel()->standard(
225 $this->
lng->txt(
'tst_introduction'),
226 $this->ui_factory->legacy()->content($introduction),
241 $launcher_factory = $this->ui_factory->launcher();
243 if ($this->
object->isStartingTimeEnabled() && !$this->object->startingTimeReached()) {
244 return $launcher_factory
245 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
247 $this->
lng->txt(
'detail_starting_time_not_reached'),
253 if ($this->
object->isEndingTimeEnabled() && $this->object->endingTimeReached()) {
254 return $launcher_factory
255 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
256 ->withButtonLabel(sprintf(
257 $this->
lng->txt(
'detail_ending_time_reached'),
264 return $launcher_factory
265 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
266 ->withButtonLabel($this->
lng->txt(
'tst_out_of_time_message'),
false)
270 $participant_access = $this->test_access->isParticipantAllowed(
275 if ($participant_access === ParticipantAccess::NOT_INVITED) {
276 return $launcher_factory
277 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
278 ->withButtonLabel($this->
lng->txt(
'tst_exam_not_assigned_participant_disclaimer'),
false)
282 if ($participant_access !== ParticipantAccess::ALLOWED) {
283 return $launcher_factory
284 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
285 ->withButtonLabel($participant_access->getAccessForbiddenMessage($this->lng),
false)
290 return $launcher_factory
291 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
292 ->withButtonLabel($this->
lng->txt(
'tst_launcher_button_label_passes_limit_reached'),
false);
296 return $launcher_factory
297 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
298 ->withButtonLabel($this->
lng->txt(
'tst_already_passed_cannot_retake'),
false)
302 $next_pass_allowed_timestamp = 0;
303 if (!$this->
object->isNextPassAllowed($this->test_passes_selector, $next_pass_allowed_timestamp)) {
304 return $launcher_factory
305 ->inline($this->data_factory->link(
'', $this->data_factory->uri($this->http->request()->getUri()->__toString())))
308 $this->
lng->txt(
'wait_for_next_pass_hint_msg'),
328 return $this->data_factory->link(
329 $this->
lng->txt(
'tst_resume_test'),
338 $this->ui_factory->input()->field()->group($this->getModalLauncherInputs()),
339 function (
Result $result) {
343 )->withModalSubmitLabel($this->
lng->txt(
'continue'));
345 $request = $this->
http->request();
346 $key =
'launcher_id';
347 if (array_key_exists($key, $request->getQueryParams())
348 && $request->getQueryParams()[$key] ===
'exam_modal') {
349 $launcher = $launcher->withRequest($request);
357 return $this->data_factory->link(
358 $this->
lng->txt(
'tst_exam_start'),
359 $this->data_factory->uri(
360 $this->http->request()->getUri()->__toString()
361 )->withParameter(
'launcher_id',
'exam_modal')
367 if ($this->main_settings->getIntroductionSettings()->getExamConditionsCheckboxEnabled()) {
368 $modal_inputs[
'exam_conditions'] = $this->ui_factory->input()->field()->checkbox(
369 $this->
lng->txt(
'tst_exam_conditions'),
370 $this->lng->txt(
'tst_exam_conditions_label')
371 )->withRequired(
true)
372 ->withAdditionalTransformation(
373 $this->
refinery->custom()->constraint(
374 static fn(
bool $value):
bool => $value,
375 $this->
lng->txt(
'tst_exam_conditions_not_checked_message'),
380 if ($this->main_settings->getAccessSettings()->getPasswordEnabled()) {
381 $modal_inputs[
'exam_password'] = $this->ui_factory->input()->field()->password(
382 $this->
lng->txt(
'tst_exam_password'),
383 $this->lng->txt(
'tst_exam_password_label')
384 )->withRevelation(
true)
386 ->withAdditionalTransformation(
387 $this->
refinery->custom()->constraint(
388 fn(
Password $value):
bool => $value->
toString() === $this->main_settings->getAccessSettings()->getPassword(),
389 $this->lng->txt(
'tst_exam_password_invalid_message'),
394 if ($this->
user->isAnonymous()) {
395 $access_code_input = $this->ui_factory->input()->field()->text(
396 $this->
lng->txt(
'tst_exam_access_code'),
397 $this->lng->txt(
'tst_exam_access_code_label')
398 )->withAdditionalTransformation(
399 $this->
refinery->custom()->constraint(
400 fn(
string $value):
bool => $value ===
'' || $this->test_session->isAccessCodeUsed($value),
401 $this->lng->txt(
'tst_exam_access_code_invalid_message'),
405 $access_code_from_session = $this->test_session->getAccessCodeFromSession();
406 if ($access_code_from_session) {
407 $access_code_input = $access_code_input->withValue($access_code_from_session);
410 $modal_inputs[
'exam_access_code'] = $access_code_input;
414 $this->test_passes_selector->getLastFinishedPass() >= 0
415 && $this->main_settings->getParticipantFunctionalitySettings()->getUsePreviousAnswerAllowed()
417 $modal_inputs[
'exam_use_previous_answers'] = $this->ui_factory->input()->field()->checkbox(
418 $this->
lng->txt(
'tst_exam_use_previous_answers'),
419 $this->lng->txt(
'tst_exam_use_previous_answers_label')
420 )->withAdditionalTransformation(
421 $this->
refinery->custom()->transformation(
422 static fn(
bool $value):
string => $value ?
'1' :
'0'
427 return $modal_inputs ?? [];
432 $exam_conditions_enabled = $this->main_settings->getIntroductionSettings()->getExamConditionsCheckboxEnabled();
433 $password_enabled = $this->main_settings->getAccessSettings()->getPasswordEnabled();
435 if ($exam_conditions_enabled && $password_enabled) {
436 $modal_message_box_message = $this->
lng->txt(
'tst_exam_modal_message_conditions_and_password');
437 } elseif ($exam_conditions_enabled) {
438 $modal_message_box_message = $this->
lng->txt(
'tst_exam_modal_message_conditions');
439 } elseif ($password_enabled) {
440 $modal_message_box_message = $this->
lng->txt(
'tst_exam_modal_message_password');
443 return isset($modal_message_box_message) ? $this->ui_factory->messageBox()->info($modal_message_box_message) :
null;
448 return $this->data_factory->link(
449 $this->
lng->txt(
'tst_exam_start'),
457 return $this->data_factory->uri(
458 ILIAS_HTTP_PATH .
'/' . $this->
ctrl->getLinkTargetByClass(
462 $this->
object->isFixedTest()
477 $anonymous = $this->
user->isAnonymous();
478 if (array_key_exists(
'exam_access_code', $result->
value())) {
479 $value = $result->
value()[
'exam_access_code'];
480 if ($anonymous && !empty($value)) {
481 $this->test_session->setAccessCodeToSession($value);
483 $this->test_session->unsetAccessCodeInSession();
489 $this->main_settings->getParticipantFunctionalitySettings()->getUsePreviousAnswerAllowed()
491 $this->
user->setPref(
492 'tst_use_previous_answers',
493 $result->
value()[
'exam_use_previous_answers'] ??
'0'
495 $this->
user->update();
498 if ($this->main_settings->getAccessSettings()->getPasswordEnabled()) {
499 $this->checkPassword($result);
502 $this->
ctrl->redirectByClass(
511 $password = $result->value()[
'exam_password']?->
toString();
512 if ($password === $this->main_settings->getAccessSettings()->getPassword()) {
518 $this->test_session->setPasswordChecked(
false);
523 if ($this->
object->getOfflineStatus()
524 || !$this->object->isComplete($this->object->getQuestionSetConfig())) {
533 $test_behaviour_settings = $this->
object->getMainSettings()->getTestBehaviourSettings();
534 if (!$test_behaviour_settings->getProcessingTimeEnabled()
535 || $test_behaviour_settings->getResetProcessingTime()) {
539 $active_id = $this->test_passes_selector->getActiveId();
540 $last_started_pass = $this->test_session->getLastStartedPass();
541 return $last_started_pass !==
null
542 && $this->
object->isMaxProcessingTimeReached(
543 $this->
object->getStartingTimeOfUser($active_id, $last_started_pass),
550 if ($this->main_settings->getTestBehaviourSettings()->getBlockAfterPassedEnabled()) {
551 return $this->test_passes_selector->getLastFinishedPass() >= 0
552 && $this->test_passes_selector->hasTestPassedOnce($this->test_session->getActiveId());
560 $nr_of_tries = $this->
object->getNrOfTries();
562 return $nr_of_tries === 0 || (count($this->test_passes_selector->getExistingPasses()) <= $nr_of_tries && count($this->test_passes_selector->getClosedPasses()) < $nr_of_tries);
567 return (count($this->test_passes_selector->getExistingPasses()) - count($this->test_passes_selector->getClosedPasses())) === 1;
573 $this->main_settings->getIntroductionSettings()->getExamConditionsCheckboxEnabled()
574 || $this->main_settings->getAccessSettings()->getPasswordEnabled()
575 || $this->main_settings->getParticipantFunctionalitySettings()->getUsePreviousAnswerAllowed()
576 && $this->test_passes_selector->getLastFinishedPass() >= 0
577 || $this->user->isAnonymous()
583 return $this->ui_factory->messageBox()->failure(
584 $this->
lng->txt(
'tst_skl_level_thresholds_missing')
586 $this->ui_factory->link()->standard(
587 $this->lng->txt(
'tst_skl_level_thresholds_link'),
588 $this->ctrl->getLinkTargetByClass(
590 \ilRepositoryGUI::class,
591 \ilObjTestGUI::class,
592 \ilTestSkillAdministrationGUI::class,
593 \ilTestSkillLevelThresholdsGUI::class
603 if (!$this->
object->isSkillServiceEnabled()) {
607 $questionContainerId = $this->
object->getId();
609 $assignmentList = new \ilAssQuestionSkillAssignmentList($this->
database);
610 $assignmentList->setParentObjId($questionContainerId);
611 $assignmentList->loadFromDb();
613 foreach ($assignmentList->getUniqueAssignedSkills() as
$data) {
614 foreach (
$data[
'skill']->getLevelData() as $level) {
615 $threshold = new \ilTestSkillLevelThreshold($this->
database);
616 $threshold->setTestId($this->
object->getTestId());
617 $threshold->setSkillBaseId(
$data[
'skill_base_id']);
618 $threshold->setSkillTrefId(
$data[
'skill_tref_id']);
619 $threshold->setSkillLevelId($level[
'id']);
621 if (!$threshold->dbRecordExists()) {
Builds a Color from either hex- or rgb values.
A Link is the often used combination of a label and an URL.
A password is used as part of credentials for authentication.
The scope of this class is split ilias-conform URI's into components.
areSkillLevelThresholdsMissing()
handleRenderIntroduction(array $elements)
readonly DataFactory $data_factory
getModalLauncherMessageBox()
__construct(private readonly \ilObjTest $object, private readonly \ilObjUser $user, private readonly UIFactory $ui_factory, private readonly UIRenderer $ui_renderer, private readonly \ilLanguage $lng, private readonly Refinery $refinery, private readonly \ilCtrlInterface $ctrl, private readonly \ilGlobalTemplateInterface $tpl, private readonly ContentStyle $content_style, private readonly HTTPServices $http, private readonly TabsManager $tabs_manager, private readonly \ilAccessHandler $access, private readonly \ilTestAccess $test_access, private readonly \ilDBInterface $database, private readonly \ilRbacSystem $rbac_system)
addOfflineMessageBoxIfNecessary(array $elements)
buildLauncherLinkUrl(string $cmd)
handleRenderMessageBox(array $elements)
handleRenderLauncher(array $elements)
blockUserAfterHavingPassed()
readonly ilTestSession $test_session
evaluateLauncherModalForm(Result $result)
ilTestPasswordChecker $password_checker
checkPassword(Result $result)
getSkillLevelThresholdsMissingInfo()
isUserOutOfProcessingTime()
readonly MainSettings $main_settings
readonly ilTestPassesSelector $test_passes_selector
@ilCtrl_Calls ILIAS\Test\Settings\MainSettings\SettingsMainGUI: ilPropertyFormGUI @ilCtrl_Calls ILIAS...
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false, ?ilObjUser $user=null,)
@classDescription Date and time handling
class ilRbacSystem system function like checkAccess, addActiveRole ... Supporting system functions ar...
static set(string $a_var, $a_val)
Set a value.
const CMD_SHOW_SKILL_THRESHOLDS
return['delivery_method'=> 'php',]
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
A result encapsulates a value or an error and simplifies the handling of those.
isError()
Get to know if the result is an error.
value()
Get the encapsulated value.
withButtonLabel(string $label, bool $launchable=true)
Labels the button that launches the process; if the process is not launchable for the user,...
withInputs(Group $fields, \Closure $evaluation, ?MessageBox\MessageBox $instruction=null)
If the Launcher is configured with Inputs, an Roundtrip Modal is shown with these Inputs.
withLinks(array $links)
Get a Message Box like this, but with links.
An entity that renders components to a string output.
Interface ilAccessHandler This interface combines all available interfaces which can be called via gl...
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static http()
Fetches the global http state from ILIAS.
TestParticipantInteractionTypes
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.