19declare(strict_types=1);
83 private readonly UIFactory $ui_factory,
84 private readonly UIRenderer $ui_renderer,
91 private readonly \
ilObjUser $current_user,
92 private readonly ?
int $ref_id =
null
94 $this->
lng->loadLanguageModule(
'dateplaner');
99 return $this->ui_factory->table()->data(
101 $this->
lng->txt(
'history'),
114 if ($this->
filter ===
null) {
118 if ($this->filter_data ===
null) {
119 $this->filter_data = $this->ui_service->filter()->getData($this->
filter) ?? [];
125 $field_factory = $this->ui_factory->input()->field();
127 self::FILTER_FIELD_PERIOD => $field_factory->duration($this->
lng->txt(
'cal_period'))
129 ->withFormat($this->log_viewer->buildUserDateTimeFormat())
131 if ($this->ref_id ===
null) {
136 self::FILTER_FIELD_ADMIN => $field_factory->text($this->
lng->txt(
'author')),
137 self::FILTER_FIELD_PARTICIPANT => $field_factory->text($this->
lng->txt(
'tst_participant')),
138 self::FILTER_FIELD_IP => $field_factory->text($this->
lng->txt(
'client_ip')),
139 self::FILTER_FIELD_QUESTION_TITLE => $field_factory->text($this->
lng->txt(
'question_title')),
140 self::FILTER_FIELD_LOG_ENTRY_TYPE => $field_factory->multiSelect(
141 $this->
lng->txt(
'log_entry_type'),
142 $this->buildLogEntryTypesOptionsForFilter()
144 self::FILTER_FIELD_INTERACTION_TYPE => $field_factory->multiSelect(
145 $this->
lng->txt(
'interaction_type'),
146 $this->buildInteractionTypesOptionsForFilter()
150 $active = array_fill(0, count($filter_inputs),
true);
152 $log_table_filter_id =
'log_table_filter_id';
153 if ($this->ref_id !==
null) {
154 $log_table_filter_id .=
"_{$this->ref_id}";
157 $this->
filter = $this->ui_service->filter()->standard(
158 $log_table_filter_id,
170 $f = $this->ui_factory->table()->column();
173 self::COLUMN_DATE_TIME =>
$f->date($this->
lng->txt(
'date_time'), $this->log_viewer->buildUserDateTimeFormat()),
174 self::COLUMN_CORRESPONDING_TEST =>
$f->link($this->
lng->txt(
'test'))->withIsOptional(
true,
true),
175 self::COLUMN_ADMIN =>
$f->text($this->
lng->txt(
'author'))->withIsOptional(
true,
true),
176 self::COLUMN_PARTICIPANT =>
$f->text($this->
lng->txt(
'tst_participant'))->withIsOptional(
true,
true)
179 if ($this->
logger->isIPLoggingEnabled()) {
184 self::COLUMN_QUESTION =>
$f->link($this->
lng->txt(
'question'))->withIsOptional(
true,
true),
185 self::COLUMN_LOG_ENTRY_TYPE =>
$f->text($this->
lng->txt(
'log_entry_type'))->withIsOptional(
true,
true),
186 self::COLUMN_INTERACTION_TYPE =>
$f->text($this->
lng->txt(
'interaction_type'))->withIsOptional(
true,
true)
192 array $visible_column_ids,
195 mixed $additional_viewcontrol_data,
197 mixed $additional_parameters
207 $log_entry_type_filter,
208 $interaction_type_filter
212 'timezone' => new \DateTimeZone($this->current_user->getTimeZone()),
213 'date_format' => $this->log_viewer->buildUserDateTimeFormat()->toString()
216 foreach ($this->logging_repository->getLogs(
217 $this->logger->getInteractionTypes(),
227 $log_entry_type_filter,
228 $interaction_type_filter
230 yield $interaction->getLogEntryAsDataTableRow(
232 $this->title_builder,
240 mixed $additional_viewcontrol_data,
242 mixed $additional_parameters
252 $log_entry_type_filter,
253 $interaction_type_filter
254 ] = $this->prepareFilterData($this->filter_data);
256 return $this->logging_repository->getLogsCount(
257 $this->
logger->getInteractionTypes(),
265 $log_entry_type_filter,
266 $interaction_type_filter
272 array $affected_items
275 self::ACTION_ADDITIONAL_INFORMATION => $this->showAdditionalDetails($affected_items[0]),
276 self::ACTION_EXPORT_AS_CSV => $this->exportTestUserInteractions($affected_items),
277 self::ACTION_CONFIRM_DELETE => $this->showConfirmTestUserInteractionsDeletion($affected_items),
278 self::ACTION_DELETE => $this->deleteTestUserInteractions($affected_items)
284 $log = $this->logging_repository->getLog($affected_item);
286 $this->showErrorModal($this->
lng->txt(
'no_checkbox'));
290 'timezone' => new \DateTimeZone($this->current_user->getTimeZone()),
291 'date_format' => $this->log_viewer->buildUserDateTimeFormat()->toString()
294 echo $this->ui_renderer->renderAsync(
295 $this->ui_factory->modal()->roundtrip(
296 $this->lng->txt(
'additional_info'),
297 $log->getParsedAdditionalInformation(
298 $this->logger->getAdditionalInformationGenerator(),
309 if ($affected_items === []) {
310 $this->showErrorModal($this->
lng->txt(
'no_checkbox'));
313 echo $this->ui_renderer->renderAsync(
314 $this->ui_factory->modal()->interruptive(
315 $this->lng->txt(
'confirmation'),
316 $this->lng->txt(
'confirm_log_deletion'),
317 $this->unmaskCmdNodesFromBuilder($this->url_builder
318 ->withParameter($this->action_parameter_token, self::ACTION_DELETE)
319 ->withParameter($this->row_id_token, $affected_items)
320 ->buildURI()->__toString())
328 if ($this->ref_id !==
null) {
329 $this->tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'log_deletion_not_allowed'));
333 $this->logging_repository->deleteLogs($affected_items);
334 $this->tpl->setOnScreenMessage(
'success', $this->
lng->txt(
'logs_deleted'));
339 if ($affected_items === []) {
340 $this->tpl->setOnScreenMessage(
'info', $this->
lng->txt(
'no_checkbox'));
344 $this->log_viewer->buildExcelWorkbookForLogs(
345 $this->buildLogsFromAffectedItems($affected_items)
346 )->sendToClient(date(
'Y-m-d') . self::EXPORT_FILE_NAME);
351 if ($affected_items[0] !==
'ALL_OBJECTS') {
352 return $this->logging_repository->getLogsByUniqueIdentifiers($affected_items);
355 $this->initializeFilterAndData();
364 $log_entry_type_filter,
365 $interaction_type_filter
366 ] = $this->prepareFilterData($this->filter_data);
367 return $this->logging_repository->getLogs(
368 $this->
logger->getInteractionTypes(),
369 $this->ref_id !==
null ? [$this->ref_id] :
null,
378 $log_entry_type_filter,
379 $interaction_type_filter
385 $af = $this->ui_factory->table()->action();
387 self::ACTION_ID_SHOW_ADDITIONAL_INFO => $af->single(
388 $this->
lng->txt(
'additional_info'),
389 $this->url_builder->withParameter(
390 $this->action_parameter_token,
391 self::ACTION_ADDITIONAL_INFORMATION
395 self::ACTION_ID_EXPORT => $af->multi(
396 $this->
lng->txt(
'export'),
397 $this->url_builder->withParameter(
398 $this->action_parameter_token,
399 self::ACTION_EXPORT_AS_CSV
404 if ($this->ref_id !==
null) {
408 self::ACTION_ID_DELETE => $af->standard(
409 $this->
lng->txt(
'delete'),
410 $this->url_builder->withParameter(
411 $this->action_parameter_token,
412 self::ACTION_CONFIRM_DELETE
424 $lang_prefix = TestUserInteraction::LANG_VAR_PREFIX;
425 $log_entry_types = $this->
logger->getLogEntryTypes();
426 $log_entry_options = [];
427 foreach ($log_entry_types as $log_entry_type) {
428 $log_entry_options [$log_entry_type] = $this->
lng->txt($lang_prefix . $log_entry_type);
430 asort($log_entry_options);
431 return $log_entry_options;
439 $lang_prefix = TestUserInteraction::LANG_VAR_PREFIX;
440 $interaction_types = array_reduce(
441 $this->
logger->getInteractionTypes(),
442 fn(array $et, array $it): array => [...$et, ...$it],
446 $interaction_options = [];
447 foreach ($interaction_types as $interaction_type) {
448 $interaction_options[$interaction_type] = $this->
lng->txt($lang_prefix . $interaction_type);
450 asort($interaction_options);
451 return $interaction_options;
458 $test_filter = $this->ref_id !==
null ? [
$this->ref_id] :
null;
460 $admin_filter =
null;
461 $question_filter =
null;
463 if (!empty($filter_array[self::FILTER_FIELD_PERIOD][0])) {
464 $from_filter = (new \DateTimeImmutable(
465 $filter_array[self::FILTER_FIELD_PERIOD][0],
466 new \DateTimeZone($this->current_user->getTimeZone())
470 if (!empty($filter_array[self::FILTER_FIELD_PERIOD][1])) {
471 $to_filter = (new \DateTimeImmutable(
472 $filter_array[self::FILTER_FIELD_PERIOD][1],
473 new \DateTimeZone($this->current_user->getTimeZone())
477 if (!empty($filter_array[self::FILTER_FIELD_TEST_TITLE])) {
478 $test_filter = array_reduce(
480 static fn(array $ref_ids,
int $obj_id) => array_merge(
488 if (!empty($filter_array[self::FILTER_FIELD_ADMIN])) {
489 $admin_query = new \ilUserQuery();
490 $admin_query->setTextFilter($filter_array[self::FILTER_FIELD_ADMIN]);
491 $admin_filter = $this->extractIdsFromUserQuery(
492 $admin_query->query()
496 if (!empty($filter_array[self::FILTER_FIELD_PARTICIPANT])) {
497 $pax_query = new \ilUserQuery();
498 $pax_query->setTextFilter($filter_array[self::FILTER_FIELD_PARTICIPANT]);
499 $pax_filter = $this->extractIdsFromUserQuery(
504 if (!empty($filter_array[self::FILTER_FIELD_QUESTION_TITLE])) {
505 $question_filter = $this->question_repo->searchQuestionIdsByTitle(
506 $filter_array[self::FILTER_FIELD_QUESTION_TITLE]
517 !empty($filter_array[self::FILTER_FIELD_IP]) ? $filter_array[self::FILTER_FIELD_IP] :
null,
518 $filter_array[self::FILTER_FIELD_LOG_ENTRY_TYPE] ??
null,
519 $filter_array[self::FILTER_FIELD_INTERACTION_TYPE] ?? null
525 echo $this->ui_renderer->renderAsync(
526 $this->ui_factory->modal()->roundtrip(
527 $this->lng->txt(
'error'),
528 $this->ui_factory->messageBox()->failure($message)
541 static fn(array $v):
int => $v[
'usr_id'],
552 preg_match(
'/cmdNode=([A-Za-z0-9]+%3)+[A-Za-z0-9]+&/i',
$url, $matches);
553 if (empty($matches[0])) {
556 $replacement = str_replace(
'%3',
':', $matches[0]);
557 return str_replace($matches[0], $replacement,
$url);
Builds a Color from either hex- or rgb values.
Both the subject and the direction need to be specified when expressing an order.
A simple class to express a naive range of whole positive numbers.
extractIdsFromUserQuery(array $response)
const FILTER_FIELD_PERIOD
deleteTestUserInteractions(array $affected_items)
initializeFilterAndData()
const ACTION_EXPORT_AS_CSV
const ACTION_CONFIRM_DELETE
exportTestUserInteractions(array $affected_items)
const FILTER_FIELD_PARTICIPANT
const COLUMN_LOG_ENTRY_TYPE
__construct(private readonly TestLoggingRepository $logging_repository, private readonly TestLogger $logger, private readonly TestLogViewer $log_viewer, private readonly TitleColumnsBuilder $title_builder, private readonly GeneralQuestionPropertiesRepository $question_repo, private readonly \ilUIService $ui_service, private readonly UIFactory $ui_factory, private readonly UIRenderer $ui_renderer, private readonly \ilLanguage $lng, private \ilGlobalTemplateInterface $tpl, private readonly URLBuilder $url_builder, private readonly URLBuilderToken $action_parameter_token, private readonly URLBuilderToken $row_id_token, private readonly StreamDelivery $stream_delivery, private readonly \ilObjUser $current_user, private readonly ?int $ref_id=null)
const FILTER_FIELD_TEST_TITLE
prepareFilterData(array $filter_array)
getRows(Table\DataRowBuilder $row_builder, array $visible_column_ids, Range $range, Order $order, mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
const FILTER_FIELD_INTERACTION_TYPE
unmaskCmdNodesFromBuilder(string $url)
2024-05-07 skergomard: This is a workaround as I didn't find another way
const ACTION_ID_SHOW_ADDITIONAL_INFO
const QUERY_PARAMETER_NAME_SPACE
executeAction(string $action, array $affected_items)
const ACTION_ADDITIONAL_INFORMATION
const COLUMN_INTERACTION_TYPE
showConfirmTestUserInteractionsDeletion(array $affected_items)
buildLogEntryTypesOptionsForFilter()
getTotalRowCount(mixed $additional_viewcontrol_data, mixed $filter_data, mixed $additional_parameters)
const FILTER_FIELD_QUESTION_TITLE
const COLUMN_CORRESPONDING_TEST
buildInteractionTypesOptionsForFilter()
const ACTION_TOKEN_STRING
showErrorModal(string $message)
const FILTER_FIELD_LOG_ENTRY_TYPE
showAdditionalDetails(string $affected_item)
buildLogsFromAffectedItems(array $affected_items)
static _getAllReferences(int $id)
get all reference ids for object ID
static _getIdsForTitle(string $title, string $type='', bool $partial_match=false)
This describes a Data Table.
withActions(array $actions)
An entity that renders components to a string output.
filter(string $filter_id, array $class_path, string $cmd, bool $activated=true, bool $expanded=true)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...