ILIAS  trunk Revision v12.0_alpha-1540-g00f839d5fa1
class.ilDashboardBlockGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
33
34abstract class ilDashboardBlockGUI extends ilBlockGUI implements ilDesktopItemHandling
35{
36 protected string $content;
37 protected readonly ilRbacSystem $rbacsystem;
38 protected string $parent;
41 protected mixed $object_cache;
42 protected readonly ilTree $tree;
43 protected readonly mixed $obj_definition;
44 protected readonly ilSetting $settings;
45 protected readonly ilLogger $logging;
46 protected readonly Services $http;
47 protected readonly Factory $refinery;
50 protected array $data;
53
54 public function __construct()
55 {
57 global $DIC;
58 $this->http = $DIC->http();
59 $this->refinery = $DIC->refinery();
60 $this->logging = $DIC->logger()->root();
61 $this->settings = $DIC->settings();
62 $this->object_cache = $DIC['ilObjDataCache'];
63 $this->tree = $DIC->repositoryTree();
64 $this->obj_definition = $DIC['objDefinition'];
65 $this->rbacsystem = $DIC->rbac()->system();
66 $this->favourites_manager = new ilFavouritesManager();
67 $this->parent = $this->ctrl->getCurrentClassPath()[0] ?? '';
68 $this->signal_generator = new SignalGenerator();
69 $this->init();
70 }
71
72 abstract public function initViewSettings(): void;
73
74 abstract public function initData(): void;
75
76 abstract public function emptyHandling(): string;
77
78 public function addCustomCommandsToActionMenu(ilObjectListGUI $itemListGui, int $ref_id): void
79 {
80 }
81
83 {
84 $itemListGui = $this->byType($data->getType());
85 $this->addCustomCommandsToActionMenu($itemListGui, $data->getRefId());
86 $card = $itemListGui->getAsCard(
87 $data->getRefId(),
88 $data->getObjId(),
89 $data->getType(),
90 $data->getTitle(),
91 $data->getDescription()
92 );
93
94 return $card;
95 }
96
97 protected function getListItemGroups(): array
98 {
99 $groupedCards = [];
100 foreach ($this->loadData() as $title => $group) {
101 $items = [];
102 foreach ($group as $datum) {
103 $item = $this->getListItemForDataDTO($datum);
104 if ($item !== null) {
105 $items[] = $item;
106 }
107 }
108 $groupedCards[] = $this->factory->item()->group((string) $title, $items);
109 }
110
111 return $groupedCards;
112 }
113
115 {
116 $itemListGui = $this->byType($data->getType());
117 $this->addCustomCommandsToActionMenu($itemListGui, $data->getRefId());
118 $list_item = $itemListGui->getAsListItem(
119 $data->getRefId(),
120 $data->getObjId(),
121 $data->getType(),
122 $data->getTitle(),
123 $data->getDescription()
124 );
125
126 $list_item = $list_item->withProperties($list_item->getProperties() + $data->getAdditionalData());
127
128 return $list_item;
129 }
130
131 final protected function isRepositoryObject(): bool
132 {
133 return false;
134 }
135
136 protected function getLegacyContent(): string
137 {
138 $groupedCards = [];
139 foreach ($this->loadData() as $title => $group) {
140 $cards = array_filter(array_map($this->getCardForData(...), $group));
141 if ($cards) {
142 $groupedCards[] = $this->ui->factory()->panel()->sub(
143 (string) $title,
144 $this->factory->deck($cards)->withNormalCardsSize()
145 );
146 }
147 }
148
149 if ($groupedCards) {
150 return $this->renderer->render($groupedCards);
151 }
152
153 return $this->getNoItemFoundContent();
154 }
155
156 protected function preloadData(array $data): void
157 {
158 $obj_ids = [];
159 foreach ($data as $group) {
160 foreach ($group as $datum) {
161 $obj_ids[] = $datum->getObjId();
162 }
163 }
165 parent::preloadData($data);
166 }
167
168 public function getNoItemFoundContent(): string
169 {
170 return $this->emptyHandling();
171 }
172
174 {
176 }
177
178 public function init(): void
179 {
180 $this->lng->loadLanguageModule('dash');
181 $this->lng->loadLanguageModule('pd');
182 $this->setLimit(PHP_INT_MAX);
183 $this->initViewSettings();
184 $this->view_settings->parse();
185 $this->requested_item_ref_id = (int) ($this->http->request()->getQueryParams()['item_ref_id'] ?? 0);
186 $this->initData();
187
188 $this->ctrl->setParameter($this, 'view', $this->view_settings->getView());
189 if ($this->view_settings->isTilePresentation()) {
190 $this->setPresentation(self::PRES_MAIN_LEG);
191 } else {
192 $this->setPresentation(self::PRES_SEC_LIST);
193 }
194 }
195
196 protected function initAndShow(): string
197 {
198 $this->init();
199 if ($this->parent === ilDashboardGUI::class) {
200 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
201 }
202
203 return $this->getHTML();
204 }
205
206 public function getHTML(): string
207 {
208 $this->setTitle(
209 $this->lng->txt('dash_' . $this->view_settings->getViewName($this->view_settings->getView()))
210 );
211
212 if (!$this->data) {
213 return $this->emptyHandling();
214 }
215
216 $this->addCommandActions();
217 $this->setData($this->getItemGroups());
218
219 $modal = $this->manual_sort_modal ? $this->ui->renderer()->render($this->manual_sort_modal) : '';
220 return parent::getHTML() . $modal;
221 }
222
226 public function setData(array $a_data): void
227 {
228 $this->data = array_filter(
229 array_map(
230 static fn($group) => array_filter($group, static fn($item) => $item instanceof BlockDTO),
231 $a_data
232 )
233 );
234 }
235
239 public function getData(): array
240 {
241 return parent::getData();
242 }
243
247 public function groupItemsByStartDate(): array
248 {
249 $data = $this->getData();
251 $items = array_merge(...array_values($data));
252
253 $groups = [
254 'upcoming' => [],
255 'ongoing' => [],
256 'ended' => [],
257 'not_dated' => []
258 ];
259 foreach ($items as $item) {
260 if ($item->isDated()) {
261 if ($item->hasNotStarted()) {
262 $groups['upcoming'][] = $item;
263 } elseif ($item->isRunning()) {
264 $groups['ongoing'][] = $item;
265 } else {
266 $groups['ended'][] = $item;
267 }
268 } else {
269 $groups['not_dated'][] = $item;
270 }
271 }
272
273 foreach ($groups as $key => $group) {
274 $group = $this->sortByTitle($group);
275 if ($key !== 'not_dated') {
276 $group = $this->sortByDate($group, $key === 'upcoming');
277 }
278 $groups[$this->lng->txt('pd_' . $key)] = $group;
279 unset($groups[$key]);
280 }
281 return $groups;
282 }
283
287 protected function groupItemsByType(): array
288 {
289 $object_types_by_container = $this->obj_definition->getGroupedRepositoryObjectTypes(
290 ['cat', 'crs', 'grp', 'fold']
291 );
292 $grouped_items = [];
293 $data = $this->getData();
295 $data = array_merge(...array_values($data));
296
297 foreach ($object_types_by_container as $type_title => $type) {
298 if (!$this->obj_definition->isPlugin($type_title)) {
299 $title = $this->lng->txt('objs_' . $type_title);
300 } else {
301 $pl = ilObjectPlugin::getPluginObjectByType($type_title);
302 $title = $pl->txt('objs_' . $type_title);
303 }
304
305 foreach ($data as $item) {
306 if (in_array($item->getType(), $type['objs'])) {
307 $grouped_items[$title][] = $item;
308 }
309 }
310 }
311
312 foreach ($grouped_items as $key => $group) {
313 $grouped_items[$key] = $this->sortByTitle($group);
314 }
315
316 return $grouped_items;
317 }
318
322 protected function groupItemsByLocation(): array
323 {
324 $grouped_items = [];
325 $data = $this->getData();
327 $data = array_merge(...array_values($data));
328
329 $parent_ref_ids = array_values(array_unique(
330 array_map(fn(BlockDTO $item): ?int => $this->tree->getParentId($item->getRefId()), $data)
331 ));
332 $this->object_cache->preloadReferenceCache($parent_ref_ids);
333
334 foreach ($data as $item) {
335 $parent_ref = $this->tree->getParentId($item->getRefId());
336 if ($this->isRootNode($parent_ref)) {
337 $title = $this->getRepositoryTitle();
338 } else {
339 $title = $this->object_cache->lookupTitle($this->object_cache->lookupObjId($parent_ref));
340 }
341 $grouped_items[$title][] = $item;
342 }
343 $grouped_items = array_map($this->sortByTitle(...), $grouped_items);
344 return $grouped_items;
345 }
346
347 final protected function isRootNode(int $refId): bool
348 {
349 return $this->tree->getRootId() === $refId;
350 }
351
352 protected function getRepositoryTitle(): string
353 {
354 $nd = $this->tree->getNodeData($this->tree->getRootId());
355 $title = $nd['title'];
356
357 if ($title === 'ILIAS') {
358 $title = $this->lng->txt('repository');
359 }
360
361 return $title;
362 }
363
364 public function addCommandActions(): void
365 {
366 $sortings = $this->view_settings->getSelectableSortingModes();
367 if (count($sortings) > 1) {
368 foreach ($sortings as $sorting) {
370 global $DIC;
371 $signal = $this->signal_generator->create();
372 // $signal = $DIC['ui.signal_generator']->create();
373 $this->manual_sort_modal = $this->ui->factory()->modal()->roundtrip(
374 $this->lng->txt('dash_manual_sorting_title'),
375 [$this->manually()]
376 )->withAdditionalOnLoadCode(fn($id) => "document.getElementById('$id').addEventListener('close', () => {window.location = window.location;});");
377
378 $this->manual_sort_modal = $this->manual_sort_modal->withAdditionalOnLoadCode(fn($id) => (
379 "il.Dashboard.moveModalButtons($id)"
380 ));
381 }
382 $this->addSortOption(
383 $sorting,
384 '<span data-action="' . $sorting . '">' . $this->lng->txt(ilObjDashboardSettingsGUI::DASH_SORT_PREFIX . $sorting) . '</span>',
385 $sorting === $this->view_settings->getEffectiveSortingMode()
386 );
387 }
388 $this->setSortTarget($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
389 }
390
391 $presentations = $this->view_settings->getSelectablePresentationModes();
392 foreach ($presentations as $presentation) {
393 $this->ctrl->setParameter($this, 'presentation', $presentation);
394 $this->addPresentation(
395 $this->ui->renderer()->render($this->ui->factory()->symbol()->glyph()->{$presentation . 'View'}()),
396 $this->ctrl->getLinkTarget($this, 'changePDItemPresentation'),
397 $presentation === $this->view_settings->getEffectivePresentationMode()
398 );
399 $this->ctrl->setParameter($this, 'presentation', null);
400 }
401
402 if ($this->removeMultipleEnabled()) {
403 $this->addBlockCommand(
404 $this->ctrl->getLinkTarget($this, 'manage'),
405 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
406 '',
407 $this->getRemoveModal()
408 );
409 }
410 }
411
412 public function getRemoveModal(): RoundTrip
413 {
414 $items = $this->getManageFields();
415 if ($items !== []) {
416 $modal = $this->ui->factory()->modal()->roundtrip(
417 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
418 [
419 $this->ui->factory()->messageBox()->confirmation($this->lng->txt('dash_' . $this->getBlockType() . '_remove_info')),
420 $this->ui->factory()->messageBox()->info($this->lng->txt('select_one')),
421 ],
422 $items,
423 $this->ctrl->getLinkTargetByClass([ilDashboardGUI::class, $this::class], 'confirmedRemove')
424 )->withSubmitLabel($this->lng->txt('dash_' . $this->getBlockType() . '_remove'));
425
426 $modal = $modal->withOnLoadCode(static fn($id) => "il.Dashboard.confirmModal($id)");
427 } else {
428 $modal = $this->ui->factory()->modal()->roundtrip(
429 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
430 $this->ui->factory()->messageBox()->info($this->lng->txt('dash_no_items_to_manage'))
431 );
432 }
433
434 return $modal;
435 }
436
437 protected function getManageFields(): array
438 {
439 $inputs = [];
440 foreach ($this->getItemGroups() as $key => $item_group) {
441 $options = [];
442 foreach ($item_group as $item) {
443 $icon = $this->ui->renderer()->render($this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($item->getObjId()), ''));
444 if ($this instanceof ilMembershipBlockGUI) {
445 if ($this->rbacsystem->checkAccess('leave', $item->getRefId())) {
446 if ($item->getType() === 'crs' || $item->getType() === 'grp') {
447 $members_obj = ilParticipants::getInstance($item->getRefId());
448 if (!$members_obj->checkLastAdmin([$this->user->getId()])) {
449 continue;
450 }
451 }
452 $options[$item->getRefId()] = $icon . $item->getTitle();
453 }
454 } else {
455 $options[$item->getRefId()] = $icon . $item->getTitle();
456 }
457 }
458 if ($options !== []) {
459 $inputs[] = $this->ui->factory()->input()->field()->multiSelect((string) $key, $options)
460 ->withAdditionalTransformation($this->refinery->to()->listOf($this->refinery->kindlyTo()->int()));
461 }
462 }
463
464 return $inputs;
465 }
466
467 public function executeCommand(): string
468 {
469 $next_class = $this->ctrl->getNextClass();
470 $cmd = $this->ctrl->getCmd('getHTML');
471
472 switch ($next_class) {
473 case strtolower(ilCommonActionDispatcherGUI::class):
475 if ($gui instanceof ilCommonActionDispatcherGUI) {
476 $this->ctrl->forwardCommand($gui);
477 }
478 break;
479
480 default:
481 switch ($cmd) {
482 case 'confirmedRemove':
483 $form = $this->ui->factory()->input()->container()->form()->standard('', $this->getManageFields())->withRequest($this->http->request());
484 $this->confirmedRemove(array_merge(...array_filter($form->getData())));
485 // no break
486 default:
487 if (method_exists($this, $cmd . 'Object')) {
488 return $this->{$cmd . 'Object'}();
489 }
490 }
491 }
492 return '';
493 }
494
495 public function manually(): Ordering
496 {
497 $request = $this->http->request();
498 $columns = [
499 'title' => $this->factory->table()->column()->text('Title')
500 ];
501
502 $records = null;
503 $proc = function ($b) use (&$records) {
504 return yield from array_map(fn($x) => $b->buildOrderingRow((string) $x['id'], $x), $records);
505 };
506
507 $uri = new URI((string) $request->getUri());
508 parse_str($uri->getQuery(), $query);
509 $uri = $uri->withQuery(http_build_query(array_merge(
510 $query,
511 ['view' => $this->view_settings->getView()]
512 )));
513 $table = $this->factory->table()
514 ->ordering(new \ILIAS\Dashboard\TableData($proc), $uri, '', $columns)
515 ->withRequest($request);
516
517 $int = $this->refinery->byTrying([$this->refinery->kindlyTo()->int(), $this->refinery->always(null)]);
518
519 if ($request->getMethod() === 'POST' && $this->view_settings->getView() === $this->http->wrapper()->query()->retrieve('view', $int)) {
520 $data = $table->getData();
521 if ($data) {
522 $this->view_settings->storeActorSortingMode('manually');
523 $this->view_settings->storeActorSortingData(array_flip($data));
524 $this->ctrl->redirectByClass(ilDashboardGUI::class);
525 }
526 }
527
528 $icon_for = fn(int $obj_id) => $this->renderer->render(
529 $this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($obj_id), '')
530 );
531
532 $records = array_map(fn($x) => [
533 'id' => $x->getRefId(),
534 'title' => $icon_for($x->getObjId()) . $x->getTitle(),
535 ], array_values($this->sortManually($this->getItemGroups())));
536
537 return $table;
538 }
539
540 public function getViewControlsForPanel(): array
541 {
542 global $DIC;
543 if (!$this->manual_sort_modal) {
544 return parent::getViewControlsForPanel();
545 }
546 $show = $this->manual_sort_modal->getShowSignal();
547 $modal_signals = json_encode(['manually' => (string) $show]);
548 $url = json_encode($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
549 $signal = $this->signal_generator->create();
550 $code = fn($id) => "il.Dashboard.showModalOnSort($id, $url, '$signal', $modal_signals)";
551 $compontents = array_map(
552 fn($x) => $x instanceof Sortation ? $x->withOnSort($signal)->withAdditionalOnLoadCode($code) : $x,
553 parent::getViewControlsForPanel()
554 );
555
556 return $compontents;
557 }
558
559 public function viewDashboardObject(): void
560 {
561 $this->initAndShow();
562 }
563
564 public function changePDItemSortingObject(): string
565 {
566 $this->view_settings->storeActorSortingMode(
567 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['sorting'] ?? ''))
568 );
569
570 return $this->initAndShow();
571 }
572
573 public function changePDItemPresentationObject(): string
574 {
575 $this->view_settings->storeActorPresentationMode(
576 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['presentation'] ?? ''))
577 );
578 return $this->initAndShow();
579 }
580
584 public function getItemGroups(): array
585 {
586 switch ($this->view_settings->getEffectiveSortingMode()) {
588 $data = $this->getData();
589 $data = array_merge(...array_values($data));
590 $data = $this->sortByTitle($data);
591 return ['' => $data];
593 return $this->groupItemsByStartDate();
595 return ['' => $this->sortManually($this->getData())];
597 $groups = $this->groupItemsByType();
598 ksort($groups, SORT_NATURAL);
599 return $groups;
601 default:
602 $groups = $this->groupItemsByLocation();
603 ksort($groups, SORT_NATURAL);
604 return $groups;
605 }
606 }
607
608
609 public function addToDeskObject(): void
610 {
611 $this->favourites_manager->add($this->user->getId(), $this->requested_item_ref_id);
612 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('added_to_favourites'), true);
613 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
614 }
615
616 public function removeFromDeskObject(): void
617 {
618 $this->favourites_manager->remove($this->user->getId(), $this->requested_item_ref_id);
619 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('removed_from_favourites'), true);
620 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
621 }
622
623 public function removeMultipleEnabled(): bool
624 {
625 return false;
626 }
627
631 public function confirmedRemove(array $ids): void
632 {
633 }
634
635 public function byType(string $a_type): ilObjectListGUI
636 {
637 $class = $this->obj_definition->getClassName($a_type);
638 if (!$class) {
639 throw new ilException(sprintf('Could not find a class for object type: %s', $a_type));
640 }
641
642 $location = $this->obj_definition->getLocation($a_type);
643 if (!$location) {
644 throw new ilException(sprintf('Could not find a class location for object type: %s', $a_type));
645 }
646
647 $full_class = 'ilObj' . $class . 'ListGUI';
648 $item_list_gui = new $full_class();
649
650 $item_list_gui->setContainerObject($this);
651 $item_list_gui->enableNotes(false);
652 $item_list_gui->enableComments(false);
653 $item_list_gui->enableTags(false);
654
655 $item_list_gui->enableIcon(true);
656 $item_list_gui->enableDelete(false);
657 $item_list_gui->enableCut(false);
658 $item_list_gui->enableCopy(false);
659 $item_list_gui->enableLink(false);
660 $item_list_gui->enableInfoScreen(true);
661
662 $item_list_gui->enableCommands(true, true);
663
664 return $item_list_gui;
665 }
666
670 private function sortByDate(array $data, bool $asc = true): array
671 {
672 usort(
673 $data,
674 static fn(BlockDTO $left, BlockDTO $right): int =>
675 ($asc ? -1 : 1) *
676 (($right->getStartDate()?->get(IL_CAL_UNIX) ?? 0) - ($left->getStartDate()?->get(IL_CAL_UNIX) ?? 0))
677 );
678 return $data;
679 }
680
684 private function sortByTitle(array $data): array
685 {
686 usort(
687 $data,
688 static fn(BlockDTO $left, BlockDTO $right): int => strcasecmp($left->getTitle(), $right->getTitle())
689 );
690 return $data;
691 }
692
693 private function sortManually(array $data): array
694 {
695 $data = array_merge(...array_values($data));
696 $new_at_botton = 'bot' === ($this->view_settings->getEffectiveSortingOptions()['new_items'] ?? 'bot');
697 $default = $new_at_botton ? INF : -INF;
698 $manual_sorting = $this->view_settings->getEffectiveSortingData();
699 usort($data, fn(BlockDTO $l, BlockDTO $r) => (
700 ($manual_sorting[$l->getRefId()] ?? $default) <=> ($manual_sorting[$r->getRefId()] ?? $default)
701 ));
702
703 return $data;
704 }
705}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
renderer()
factory()
$location
Definition: buildRTE.php:22
Builds data types.
Definition: Factory.php:36
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
Stream factory which enables the user to create streams without the knowledge of the concrete class.
Definition: Streams.php:32
Class Services.
Definition: Services.php:38
const IL_CAL_UNIX
This class represents a block method of a block.
array $presentations
addPresentation(string $label, string $target, bool $active)
setTitle(string $a_title)
setPresentation(int $type)
loadData()
Load data for current page.
addBlockCommand(string $a_href, string $a_text, string $a_onclick="", ?RoundTrip $modal=null)
setLimit(int $a_limit)
addSortOption(string $option, string $label, bool $active)
setSortTarget(string $target)
Class ilCommonActionDispatcherGUI.
static getInstanceFromAjaxCall()
(Re-)Build instance from ajax call
getLegacyContent()
Get legacy content.
isRepositoryObject()
Returns whether block has a corresponding repository object.
readonly ilRbacSystem $rbacsystem
readonly ilFavouritesManager $favourites_manager
addCustomCommandsToActionMenu(ilObjectListGUI $itemListGui, int $ref_id)
preloadData(array $data)
Can be overwritten in subclasses.
sortByDate(array $data, bool $asc=true)
ilPDSelectedItemsBlockViewSettings $view_settings
readonly SignalGenerator $signal_generator
Base class for ILIAS Exception handling.
Manages favourites, currently the interface for other components, needs discussion.
static preloadListGUIData(array $a_obj_ids)
Component logger with individual log levels by component id.
static getPluginObjectByType(string $type)
Return either a repoObject plugin or a orgunit extension plugin or null if the type is not a plugin.
static _getIcon(int $obj_id=0, string $size="big", string $type="", bool $offline=false)
Get icon for repository item.
static getInstance(int $a_ref_id)
class ilRbacSystem system function like checkAccess, addActiveRole ... Supporting system functions ar...
ILIAS Setting Class.
Tree class data representation in hierachical trees using the Nested Set Model with Gaps by Joe Celco...
static stripSlashes(string $a_str, bool $a_strip_html=true, string $a_allow="")
Interface ResponseHeader.
Common interface to all items.
Definition: Item.php:32
This describes a Table to specify the order of its data (rows).
Definition: Ordering.php:29
This describes a Sortation Control.
Definition: Sortation.php:32
withOnSort(Signal $signal)
Get a component like this, triggering a signal of another component.
$ref_id
Definition: ltiauth.php:66
static http()
Fetches the global http state from ILIAS.
__construct(Container $dic, ilPlugin $plugin)
@inheritDoc
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
global $DIC
Definition: shib_login.php:26
$url
Definition: shib_logout.php:70
$refId
Definition: xapitoken.php:56