ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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 private string $content;
38 private string $parent;
41 private mixed $object_cache;
42 private ilTree $tree;
43 private mixed $objDefinition;
45 protected ilLogger $logging;
46 protected Services $http;
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->objDefinition = $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 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 {
175 return $this->viewSettings;
176 }
177
178 public function init(): void
179 {
180 $this->lng->loadLanguageModule('dash');
181 $this->lng->loadLanguageModule('rep');
182 $this->lng->loadLanguageModule('pd');
183 $this->initViewSettings();
184 $this->viewSettings->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->viewSettings->getCurrentView());
189 if ($this->viewSettings->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->viewSettings->getViewName($this->viewSettings->getCurrentView()))
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->objDefinition->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->objDefinition->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 ksort($grouped_items);
344 $grouped_items = array_map($this->sortByTitle(...), $grouped_items);
345 return $grouped_items;
346 }
347
348 protected function isRootNode(int $refId): bool
349 {
350 return $this->tree->getRootId() === $refId;
351 }
352
353 protected function getRepositoryTitle(): string
354 {
355 $nd = $this->tree->getNodeData($this->tree->getRootId());
356 $title = $nd['title'];
357
358 if ($title === 'ILIAS') {
359 $title = $this->lng->txt('repository');
360 }
361
362 return $title;
363 }
364
365 public function addCommandActions(): void
366 {
367 $sortings = $this->viewSettings->getSelectableSortingModes();
368 if (count($sortings) > 1) {
369 foreach ($sortings as $sorting) {
371 global $DIC;
372 $signal = $this->signal_generator->create();
373 // $signal = $DIC['ui.signal_generator']->create();
374 $this->manual_sort_modal = $this->ui->factory()->modal()->roundtrip(
375 $this->lng->txt('dash_manual_sorting_title'),
376 [$this->manually()]
377 )->withAdditionalOnLoadCode(fn($id) => "document.getElementById('$id').addEventListener('close', () => {window.location = window.location;});");
378
379 $this->manual_sort_modal = $this->manual_sort_modal->withAdditionalOnLoadCode(fn($id) => (
380 "il.Dashboard.moveModalButtons($id)"
381 ));
382 }
383 $this->addSortOption(
384 $sorting,
385 '<span data-action="' . $sorting . '">' . $this->lng->txt(ilObjDashboardSettingsGUI::DASH_SORT_PREFIX . $sorting) . '</span>',
386 $sorting === $this->viewSettings->getEffectiveSortingMode()
387 );
388 }
389 $this->setSortTarget($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
390 }
391
392 $presentations = $this->viewSettings->getSelectablePresentationModes();
393 foreach ($presentations as $presentation) {
394 $this->ctrl->setParameter($this, 'presentation', $presentation);
395 $this->addPresentation(
396 $this->ui->renderer()->render($this->ui->factory()->symbol()->glyph()->{$presentation . 'View'}()),
397 $this->ctrl->getLinkTarget($this, 'changePDItemPresentation'),
398 $presentation === $this->viewSettings->getEffectivePresentationMode()
399 );
400 $this->ctrl->setParameter($this, 'presentation', null);
401 }
402
403 if ($this->removeMultipleEnabled()) {
404 $this->addBlockCommand(
405 $this->ctrl->getLinkTarget($this, 'manage'),
406 $this->getRemoveMultipleActionText(),
407 '',
408 $this->getRemoveModal()
409 );
410 }
411 }
412
413 public function getRemoveModal(): RoundTrip
414 {
415 $items = $this->getManageFields();
416 if ($items !== []) {
417 if ($this->viewSettings->isSelectedItemsViewActive()) {
418 $question = $this->lng->txt('dash_info_sure_remove_from_favs');
419 } else {
420 $this->lng->loadLanguageModule('mmbr');
421 $question = $this->lng->txt('mmbr_info_delete_sure_unsubscribe');
422 }
423 $modal = $this->ui->factory()->modal()->roundtrip(
424 $this->getRemoveMultipleActionText(),
425 [
426 $this->ui->factory()->messageBox()->confirmation($question),
427 $this->ui->factory()->messageBox()->info($this->lng->txt('select_one')),
428 ],
429 $items,
430 $this->ctrl->getLinkTargetByClass([ilDashboardGUI::class, $this::class], 'confirmedRemove')
431 )->withSubmitLabel($this->getRemoveMultipleActionText());
432
433 $modal = $modal->withOnLoadCode(static fn($id) => "il.Dashboard.confirmModal($id)");
434 } else {
435 $modal = $this->ui->factory()->modal()->roundtrip(
436 $this->getRemoveMultipleActionText(),
437 $this->ui->factory()->messageBox()->info($this->lng->txt('pd_no_items_to_manage'))
438 );
439 }
440
441 return $modal;
442 }
443
444 protected function getManageFields(): array
445 {
446 $inputs = [];
447 foreach ($this->getItemGroups() as $key => $item_group) {
448 $options = [];
449 foreach ($item_group as $item) {
450 $icon = $this->ui->renderer()->render($this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($item->getObjId()), ''));
451 if ($this instanceof ilMembershipBlockGUI) {
452 if ($this->rbacsystem->checkAccess('leave', $item->getRefId())) {
453 if ($item->getType() === 'crs' || $item->getType() === 'grp') {
454 $members_obj = ilParticipants::getInstance($item->getRefId());
455 if (!$members_obj->checkLastAdmin([$this->user->getId()])) {
456 continue;
457 }
458 }
459 $options[$item->getRefId()] = $icon . $item->getTitle();
460 }
461 } else {
462 $options[$item->getRefId()] = $icon . $item->getTitle();
463 }
464 }
465 if ($options !== []) {
466 $inputs[] = $this->ui->factory()->input()->field()->multiSelect((string) $key, $options)
467 ->withAdditionalTransformation($this->refinery->to()->listOf($this->refinery->kindlyTo()->int()));
468 }
469 }
470
471 return $inputs;
472 }
473
474 public function executeCommand(): string
475 {
476 $next_class = $this->ctrl->getNextClass();
477 $cmd = $this->ctrl->getCmd('getHTML');
478
479 switch ($next_class) {
480 case strtolower(ilCommonActionDispatcherGUI::class):
482 if ($gui instanceof ilCommonActionDispatcherGUI) {
483 $this->ctrl->forwardCommand($gui);
484 }
485 break;
486
487 default:
488 switch ($cmd) {
489 case 'confirmedRemove':
490 $form = $this->ui->factory()->input()->container()->form()->standard('', $this->getManageFields())->withRequest($this->http->request());
491 $this->confirmedRemove(array_merge(...array_filter($form->getData())));
492 // no break
493 default:
494 if (method_exists($this, $cmd . 'Object')) {
495 return $this->{$cmd . 'Object'}();
496 }
497 }
498 }
499 return '';
500 }
501
502 public function manually(): Ordering
503 {
504 $request = $this->http->request();
505 $columns = [
506 'title' => $this->factory->table()->column()->text('Title')
507 ];
508
509 $records = null;
510 $proc = function ($b) use (&$records) {
511 return yield from array_map(fn($x) => $b->buildOrderingRow((string) $x['id'], $x), $records);
512 };
513
514 $uri = new URI((string) $request->getUri());
515 parse_str($uri->getQuery(), $query);
516 $uri = $uri->withQuery(http_build_query(array_merge(
517 $query,
518 ['view' => $this->viewSettings->getCurrentView()]
519 )));
520 $table = $this->factory->table()
521 ->ordering(new \ILIAS\Dashboard\TableData($proc), $uri, '', $columns)
522 ->withRequest($request);
523
524 $int = $this->refinery->byTrying([$this->refinery->kindlyTo()->int(), $this->refinery->always(null)]);
525
526 if ($request->getMethod() === 'POST' && $this->viewSettings->getCurrentView() === $this->http->wrapper()->query()->retrieve('view', $int)) {
527 $data = $table->getData();
528 if ($data) {
529 $this->viewSettings->storeActorSortingMode('manually');
530 $this->viewSettings->storeActorSortingData(array_flip($data));
531 $this->ctrl->redirectByClass(ilDashboardGUI::class);
532 }
533 }
534
535 $icon_for = fn(int $obj_id) => $this->renderer->render(
536 $this->ui->factory()->symbol()->icon()->custom(ilObject::_getIcon($obj_id), '')
537 );
538
539 $records = array_map(fn($x) => [
540 'id' => $x->getRefId(),
541 'title' => $icon_for($x->getObjId()) . $x->getTitle(),
542 ], array_values($this->sortManually($this->getItemGroups())));
543
544 return $table;
545 }
546
547 public function getViewControlsForPanel(): array
548 {
549 global $DIC;
550 if (!$this->manual_sort_modal) {
551 return parent::getViewControlsForPanel();
552 }
553 $show = $this->manual_sort_modal->getShowSignal();
554 $modal_signals = json_encode(['manually' => (string) $show]);
555 $url = json_encode($this->ctrl->getLinkTarget($this, 'changePDItemSorting'));
556 $signal = $this->signal_generator->create();
557 $code = fn($id) => "il.Dashboard.showModalOnSort($id, $url, '$signal', $modal_signals)";
558 $compontents = array_map(
559 fn($x) => $x instanceof Sortation ? $x->withOnSort($signal)->withAdditionalOnLoadCode($code) : $x,
560 parent::getViewControlsForPanel()
561 );
562
563 return $compontents;
564 }
565
566 public function viewDashboardObject(): void
567 {
568 $this->initAndShow();
569 }
570
571 public function changePDItemSortingObject(): string
572 {
573 $this->viewSettings->storeActorSortingMode(
574 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['sorting'] ?? ''))
575 );
576
577 return $this->initAndShow();
578 }
579
580 public function changePDItemPresentationObject(): string
581 {
582 $this->viewSettings->storeActorPresentationMode(
583 ilUtil::stripSlashes((string) ($this->http->request()->getQueryParams()['presentation'] ?? ''))
584 );
585 return $this->initAndShow();
586 }
587
591 public function getItemGroups(): array
592 {
593 switch ($this->viewSettings->getEffectiveSortingMode()) {
595 $data = $this->getData();
596 $data = array_merge(...array_values($data));
597 $data = $this->sortByTitle($data);
598 return ['' => $data];
600 return $this->groupItemsByStartDate();
602 return $this->groupItemsByType();
604 return ['' => $this->sortManually($this->getData())];
606 default:
607 return $this->groupItemsByLocation();
608 }
609 }
610
611 public function addToDeskObject(): void
612 {
613 $this->favourites_manager->add($this->user->getId(), $this->requested_item_ref_id);
614 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('rep_added_to_favourites'), true);
615 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
616 }
617
618 public function removeFromDeskObject(): void
619 {
620 $this->favourites_manager->remove($this->user->getId(), $this->requested_item_ref_id);
621 $this->main_tpl->setOnScreenMessage('success', $this->lng->txt('rep_removed_from_favourites'), true);
622 $this->ctrl->redirectByClass(ilDashboardGUI::class, 'show');
623 }
624
625 abstract public function removeMultipleEnabled(): bool;
626
627 abstract public function getRemoveMultipleActionText(): string;
628
632 public function confirmedRemove(array $ids): void
633 {
634 }
635
636 public function byType(string $a_type): ilObjectListGUI
637 {
638 $class = $this->objDefinition->getClassName($a_type);
639 if (!$class) {
640 throw new ilException(sprintf('Could not find a class for object type: %s', $a_type));
641 }
642
643 $location = $this->objDefinition->getLocation($a_type);
644 if (!$location) {
645 throw new ilException(sprintf('Could not find a class location for object type: %s', $a_type));
646 }
647
648 $full_class = 'ilObj' . $class . 'ListGUI';
649 $item_list_gui = new $full_class();
650
651 $item_list_gui->setContainerObject($this);
652 $item_list_gui->enableNotes(false);
653 $item_list_gui->enableComments(false);
654 $item_list_gui->enableTags(false);
655
656 $item_list_gui->enableIcon(true);
657 $item_list_gui->enableDelete(false);
658 $item_list_gui->enableCut(false);
659 $item_list_gui->enableCopy(false);
660 $item_list_gui->enableLink(false);
661 $item_list_gui->enableInfoScreen(true);
662
663 $item_list_gui->enableCommands(true, true);
664
665 return $item_list_gui;
666 }
667
671 private function sortByDate(array $data, bool $asc = true): array
672 {
673 usort(
674 $data,
675 static fn(BlockDTO $left, BlockDTO $right): int =>
676 ($asc ? -1 : 1) *
677 (($right->getStartDate()?->get(IL_CAL_UNIX) ?? 0) - ($left->getStartDate()?->get(IL_CAL_UNIX) ?? 0))
678 );
679 return $data;
680 }
681
685 private function sortByTitle(array $data): array
686 {
687 usort(
688 $data,
689 static fn(BlockDTO $left, BlockDTO $right): int => strcmp($left->getTitle(), $right->getTitle())
690 );
691 return $data;
692 }
693
694 private function sortManually(array $data): array
695 {
696 $data = array_merge(...array_values($data));
697 $new_at_botton = 'bot' === ($this->viewSettings->getEffectiveSortingOptions()['new_items'] ?? 'bot');
698 $default = $new_at_botton ? INF : -INF;
699 $manual_sorting = $this->viewSettings->getEffectiveSortingData();
700 usort($data, fn(BlockDTO $l, BlockDTO $r) => (
701 ($manual_sorting[$l->getRefId()] ?? $default) <=> ($manual_sorting[$r->getRefId()] ?? $default)
702 ));
703
704 return $data;
705 }
706}
$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.
addCustomCommandsToActionMenu(ilObjectListGUI $itemListGui, int $ref_id)
preloadData(array $data)
Can be overwritten in subclasses.
ilPDSelectedItemsBlockViewSettings $viewSettings
sortByDate(array $data, bool $asc=true)
readonly SignalGenerator $signal_generator
ilFavouritesManager $favourites_manager
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="")
$nd
Definition: error.php:30
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:68
$refId
Definition: xapitoken.php:58