ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
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->initViewSettings();
183 $this->view_settings->parse();
184 $this->requested_item_ref_id = (int) ($this->http->request()->getQueryParams()['item_ref_id'] ?? 0);
185 $this->initData();
186
187 $this->ctrl->setParameter($this, 'view', $this->view_settings->getView());
188 if ($this->view_settings->isTilePresentation()) {
189 $this->setPresentation(self::PRES_MAIN_LEG);
190 } else {
191 $this->setPresentation(self::PRES_SEC_LIST);
192 }
193 }
194
195 protected function initAndShow(): string
196 {
197 $this->init();
198 if ($this->parent === ilDashboardGUI::class) {
199 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
200 }
201
202 return $this->getHTML();
203 }
204
205 public function getHTML(): string
206 {
207 $this->setTitle(
208 $this->lng->txt('dash_' . $this->view_settings->getViewName($this->view_settings->getView()))
209 );
210
211 if (!$this->data) {
212 return $this->emptyHandling();
213 }
214
215 $this->addCommandActions();
216 $this->setData($this->getItemGroups());
217
218 $modal = $this->manual_sort_modal ? $this->ui->renderer()->render($this->manual_sort_modal) : '';
219 return parent::getHTML() . $modal;
220 }
221
225 public function setData(array $a_data): void
226 {
227 $this->data = array_filter(
228 array_map(
229 static fn($group) => array_filter($group, static fn($item) => $item instanceof BlockDTO),
230 $a_data
231 )
232 );
233 }
234
238 public function getData(): array
239 {
240 return parent::getData();
241 }
242
246 public function groupItemsByStartDate(): array
247 {
248 $data = $this->getData();
250 $items = array_merge(...array_values($data));
251
252 $groups = [
253 'upcoming' => [],
254 'ongoing' => [],
255 'ended' => [],
256 'not_dated' => []
257 ];
258 foreach ($items as $item) {
259 if ($item->isDated()) {
260 if ($item->hasNotStarted()) {
261 $groups['upcoming'][] = $item;
262 } elseif ($item->isRunning()) {
263 $groups['ongoing'][] = $item;
264 } else {
265 $groups['ended'][] = $item;
266 }
267 } else {
268 $groups['not_dated'][] = $item;
269 }
270 }
271
272 foreach ($groups as $key => $group) {
273 $group = $this->sortByTitle($group);
274 if ($key !== 'not_dated') {
275 $group = $this->sortByDate($group, $key === 'upcoming');
276 }
277 $groups[$this->lng->txt('pd_' . $key)] = $group;
278 unset($groups[$key]);
279 }
280 return $groups;
281 }
282
286 protected function groupItemsByType(): array
287 {
288 $object_types_by_container = $this->obj_definition->getGroupedRepositoryObjectTypes(
289 ['cat', 'crs', 'grp', 'fold']
290 );
291 $grouped_items = [];
292 $data = $this->getData();
294 $data = array_merge(...array_values($data));
295
296 foreach ($object_types_by_container as $type_title => $type) {
297 if (!$this->obj_definition->isPlugin($type_title)) {
298 $title = $this->lng->txt('objs_' . $type_title);
299 } else {
300 $pl = ilObjectPlugin::getPluginObjectByType($type_title);
301 $title = $pl->txt('objs_' . $type_title);
302 }
303
304 foreach ($data as $item) {
305 if (in_array($item->getType(), $type['objs'])) {
306 $grouped_items[$title][] = $item;
307 }
308 }
309 }
310
311 foreach ($grouped_items as $key => $group) {
312 $grouped_items[$key] = $this->sortByTitle($group);
313 }
314
315 return $grouped_items;
316 }
317
321 protected function groupItemsByLocation(): array
322 {
323 $grouped_items = [];
324 $data = $this->getData();
326 $data = array_merge(...array_values($data));
327
328 $parent_ref_ids = array_values(array_unique(
329 array_map(fn(BlockDTO $item): ?int => $this->tree->getParentId($item->getRefId()), $data)
330 ));
331 $this->object_cache->preloadReferenceCache($parent_ref_ids);
332
333 foreach ($data as $item) {
334 $parent_ref = $this->tree->getParentId($item->getRefId());
335 if ($this->isRootNode($parent_ref)) {
336 $title = $this->getRepositoryTitle();
337 } else {
338 $title = $this->object_cache->lookupTitle($this->object_cache->lookupObjId($parent_ref));
339 }
340 $grouped_items[$title][] = $item;
341 }
342 $grouped_items = array_map($this->sortByTitle(...), $grouped_items);
343 return $grouped_items;
344 }
345
346 final protected function isRootNode(int $refId): bool
347 {
348 return $this->tree->getRootId() === $refId;
349 }
350
351 protected function getRepositoryTitle(): string
352 {
353 $nd = $this->tree->getNodeData($this->tree->getRootId());
354 $title = $nd['title'];
355
356 if ($title === 'ILIAS') {
357 $title = $this->lng->txt('repository');
358 }
359
360 return $title;
361 }
362
363 public function addCommandActions(): void
364 {
365 $sortings = $this->view_settings->getSelectableSortingModes();
366 if (count($sortings) > 1) {
367 foreach ($sortings as $sorting) {
369 global $DIC;
370 $signal = $this->signal_generator->create();
371 // $signal = $DIC['ui.signal_generator']->create();
372 $this->manual_sort_modal = $this->ui->factory()->modal()->roundtrip(
373 $this->lng->txt('dash_manual_sorting_title'),
374 [$this->manually()]
375 )->withAdditionalOnLoadCode(fn($id) => "document.getElementById('$id').addEventListener('close', () => {window.location = window.location;});");
376
377 $this->manual_sort_modal = $this->manual_sort_modal->withAdditionalOnLoadCode(fn($id) => (
378 "il.Dashboard.moveModalButtons($id)"
379 ));
380 }
381 $this->addSortOption(
382 $sorting,
383 '<span data-action="' . $sorting . '">' . $this->lng->txt(ilObjDashboardSettingsGUI::DASH_SORT_PREFIX . $sorting) . '</span>',
384 $sorting === $this->view_settings->getEffectiveSortingMode()
385 );
386 }
387 $this->setSortTarget($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
388 }
389
390 $presentations = $this->view_settings->getSelectablePresentationModes();
391 foreach ($presentations as $presentation) {
392 $this->ctrl->setParameter($this, 'presentation', $presentation);
393 $this->addPresentation(
394 $this->ui->renderer()->render($this->ui->factory()->symbol()->glyph()->{$presentation . 'View'}()),
395 $this->ctrl->getLinkTarget($this, 'changePDItemPresentation'),
396 $presentation === $this->view_settings->getEffectivePresentationMode()
397 );
398 $this->ctrl->setParameter($this, 'presentation', null);
399 }
400
401 if ($this->removeMultipleEnabled()) {
402 $this->addBlockCommand(
403 $this->ctrl->getLinkTarget($this, 'manage'),
404 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
405 '',
406 $this->getRemoveModal()
407 );
408 }
409 }
410
411 public function getRemoveModal(): RoundTrip
412 {
413 $items = $this->getManageFields();
414 if ($items !== []) {
415 $modal = $this->ui->factory()->modal()->roundtrip(
416 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
417 [
418 $this->ui->factory()->messageBox()->confirmation($this->lng->txt('dash_' . $this->getBlockType() . '_remove_info')),
419 $this->ui->factory()->messageBox()->info($this->lng->txt('select_one')),
420 ],
421 $items,
422 $this->ctrl->getLinkTargetByClass([ilDashboardGUI::class, $this::class], 'confirmedRemove')
423 )->withSubmitLabel($this->lng->txt('dash_' . $this->getBlockType() . '_remove'));
424
425 $modal = $modal->withOnLoadCode(static fn($id) => "il.Dashboard.confirmModal($id)");
426 } else {
427 $modal = $this->ui->factory()->modal()->roundtrip(
428 $this->lng->txt('dash_' . $this->getBlockType() . '_remove_multiple'),
429 $this->ui->factory()->messageBox()->info($this->lng->txt('dash_no_items_to_manage'))
430 );
431 }
432
433 return $modal;
434 }
435
436 protected function getManageFields(): array
437 {
438 $inputs = [];
439 foreach ($this->getItemGroups() as $key => $item_group) {
440 $options = [];
441 foreach ($item_group as $item) {
442 $icon = $this->ui->renderer()->render($this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($item->getObjId()), ''));
443 if ($this instanceof ilMembershipBlockGUI) {
444 if ($this->rbacsystem->checkAccess('leave', $item->getRefId())) {
445 if ($item->getType() === 'crs' || $item->getType() === 'grp') {
446 $members_obj = ilParticipants::getInstance($item->getRefId());
447 if (!$members_obj->checkLastAdmin([$this->user->getId()])) {
448 continue;
449 }
450 }
451 $options[$item->getRefId()] = $icon . $item->getTitle();
452 }
453 } else {
454 $options[$item->getRefId()] = $icon . $item->getTitle();
455 }
456 }
457 if ($options !== []) {
458 $inputs[] = $this->ui->factory()->input()->field()->multiSelect((string) $key, $options)
459 ->withAdditionalTransformation($this->refinery->to()->listOf($this->refinery->kindlyTo()->int()));
460 }
461 }
462
463 return $inputs;
464 }
465
466 public function executeCommand(): string
467 {
468 $next_class = $this->ctrl->getNextClass();
469 $cmd = $this->ctrl->getCmd('getHTML');
470
471 switch ($next_class) {
472 case strtolower(ilCommonActionDispatcherGUI::class):
474 if ($gui instanceof ilCommonActionDispatcherGUI) {
475 $this->ctrl->forwardCommand($gui);
476 }
477 break;
478
479 default:
480 switch ($cmd) {
481 case 'confirmedRemove':
482 $form = $this->ui->factory()->input()->container()->form()->standard('', $this->getManageFields())->withRequest($this->http->request());
483 $this->confirmedRemove(array_merge(...array_filter($form->getData())));
484 // no break
485 default:
486 if (method_exists($this, $cmd . 'Object')) {
487 return $this->{$cmd . 'Object'}();
488 }
489 }
490 }
491 return '';
492 }
493
494 public function manually(): Ordering
495 {
496 $request = $this->http->request();
497 $columns = [
498 'title' => $this->factory->table()->column()->text('Title')
499 ];
500
501 $records = null;
502 $proc = function ($b) use (&$records) {
503 return yield from array_map(fn($x) => $b->buildOrderingRow((string) $x['id'], $x), $records);
504 };
505
506 $uri = new URI((string) $request->getUri());
507 parse_str($uri->getQuery(), $query);
508 $uri = $uri->withQuery(http_build_query(array_merge(
509 $query,
510 ['view' => $this->view_settings->getView()]
511 )));
512 $table = $this->factory->table()
513 ->ordering(new \ILIAS\Dashboard\TableData($proc), $uri, '', $columns)
514 ->withRequest($request);
515
516 $int = $this->refinery->byTrying([$this->refinery->kindlyTo()->int(), $this->refinery->always(null)]);
517
518 if ($request->getMethod() === 'POST' && $this->view_settings->getView() === $this->http->wrapper()->query()->retrieve('view', $int)) {
519 $data = $table->getData();
520 if ($data) {
521 $this->view_settings->storeActorSortingMode('manually');
522 $this->view_settings->storeActorSortingData(array_flip($data));
523 $this->ctrl->redirectByClass(ilDashboardGUI::class);
524 }
525 }
526
527 $icon_for = fn(int $obj_id) => $this->renderer->render(
528 $this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($obj_id), '')
529 );
530
531 $records = array_map(fn($x) => [
532 'id' => $x->getRefId(),
533 'title' => $icon_for($x->getObjId()) . $x->getTitle(),
534 ], array_values($this->sortManually($this->getItemGroups())));
535
536 return $table;
537 }
538
539 public function getViewControlsForPanel(): array
540 {
541 global $DIC;
542 if (!$this->manual_sort_modal) {
543 return parent::getViewControlsForPanel();
544 }
545 $show = $this->manual_sort_modal->getShowSignal();
546 $modal_signals = json_encode(['manually' => (string) $show]);
547 $url = json_encode($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
548 $signal = $this->signal_generator->create();
549 $code = fn($id) => "il.Dashboard.showModalOnSort($id, $url, '$signal', $modal_signals)";
550 $compontents = array_map(
551 fn($x) => $x instanceof Sortation ? $x->withOnSort($signal)->withAdditionalOnLoadCode($code) : $x,
552 parent::getViewControlsForPanel()
553 );
554
555 return $compontents;
556 }
557
558 public function viewDashboardObject(): void
559 {
560 $this->initAndShow();
561 }
562
563 public function changePDItemSortingObject(): string
564 {
565 $this->view_settings->storeActorSortingMode(
566 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['sorting'] ?? ''))
567 );
568
569 return $this->initAndShow();
570 }
571
572 public function changePDItemPresentationObject(): string
573 {
574 $this->view_settings->storeActorPresentationMode(
575 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['presentation'] ?? ''))
576 );
577 return $this->initAndShow();
578 }
579
583 public function getItemGroups(): array
584 {
585 switch ($this->view_settings->getEffectiveSortingMode()) {
587 $data = $this->getData();
588 $data = array_merge(...array_values($data));
589 $data = $this->sortByTitle($data);
590 return ['' => $data];
592 return $this->groupItemsByStartDate();
594 return ['' => $this->sortManually($this->getData())];
596 $groups = $this->groupItemsByType();
597 ksort($groups, SORT_NATURAL);
598 return $groups;
600 default:
601 $groups = $this->groupItemsByLocation();
602 ksort($groups, SORT_NATURAL);
603 return $groups;
604 }
605 }
606
607 public function addToDeskObject(): void
608 {
609 $this->favourites_manager->add($this->user->getId(), $this->requested_item_ref_id);
610 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('rep_added_to_favourites'), true);
611 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
612 }
613
614 public function removeFromDeskObject(): void
615 {
616 $this->favourites_manager->remove($this->user->getId(), $this->requested_item_ref_id);
617 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('rep_removed_from_favourites'), true);
618 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
619 }
620
621 public function removeMultipleEnabled(): bool
622 {
623 return false;
624 }
625
629 public function confirmedRemove(array $ids): void
630 {
631 }
632
633 public function byType(string $a_type): ilObjectListGUI
634 {
635 $class = $this->obj_definition->getClassName($a_type);
636 if (!$class) {
637 throw new ilException(sprintf('Could not find a class for object type: %s', $a_type));
638 }
639
640 $location = $this->obj_definition->getLocation($a_type);
641 if (!$location) {
642 throw new ilException(sprintf('Could not find a class location for object type: %s', $a_type));
643 }
644
645 $full_class = 'ilObj' . $class . 'ListGUI';
646 $item_list_gui = new $full_class();
647
648 $item_list_gui->setContainerObject($this);
649 $item_list_gui->enableNotes(false);
650 $item_list_gui->enableComments(false);
651 $item_list_gui->enableTags(false);
652
653 $item_list_gui->enableIcon(true);
654 $item_list_gui->enableDelete(false);
655 $item_list_gui->enableCut(false);
656 $item_list_gui->enableCopy(false);
657 $item_list_gui->enableLink(false);
658 $item_list_gui->enableInfoScreen(true);
659
660 $item_list_gui->enableCommands(true, true);
661
662 return $item_list_gui;
663 }
664
668 private function sortByDate(array $data, bool $asc = true): array
669 {
670 usort(
671 $data,
672 static fn(BlockDTO $left, BlockDTO $right): int =>
673 ($asc ? -1 : 1) *
674 (($right->getStartDate()?->get(IL_CAL_UNIX) ?? 0) - ($left->getStartDate()?->get(IL_CAL_UNIX) ?? 0))
675 );
676 return $data;
677 }
678
682 private function sortByTitle(array $data): array
683 {
684 usort(
685 $data,
686 static fn(BlockDTO $left, BlockDTO $right): int => strcasecmp($left->getTitle(), $right->getTitle())
687 );
688 return $data;
689 }
690
691 private function sortManually(array $data): array
692 {
693 $data = array_merge(...array_values($data));
694 $new_at_botton = 'bot' === ($this->view_settings->getEffectiveSortingOptions()['new_items'] ?? 'bot');
695 $default = $new_at_botton ? INF : -INF;
696 $manual_sorting = $this->view_settings->getEffectiveSortingData();
697 usort($data, fn(BlockDTO $l, BlockDTO $r) => (
698 ($manual_sorting[$l->getRefId()] ?? $default) <=> ($manual_sorting[$r->getRefId()] ?? $default)
699 ));
700
701 return $data;
702 }
703}
$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)
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