ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
Renderer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
25use ILIAS\UI\Renderer as RendererInterface;
33
35{
39 public function render(Component\Component $component, RendererInterface $default_renderer): string
40 {
41 if ($component instanceof Component\Table\Presentation) {
42 return $this->renderPresentationTable($component, $default_renderer);
43 }
44 if ($component instanceof Component\Table\PresentationRow) {
45 return $this->renderPresentationRow($component, $default_renderer);
46 }
47 if ($component instanceof Component\Table\Data) {
48 return $this->renderDataTable($component, $default_renderer);
49 }
50 if ($component instanceof Component\Table\DataRow
51 && !$component instanceof Component\Table\OrderingRow
52 ) {
53 return $this->renderDataRow($component, $default_renderer);
54 }
55 if ($component instanceof Component\Table\Ordering) {
56 return $this->renderOrderingTable($component, $default_renderer);
57 }
58 if ($component instanceof Component\Table\OrderingRow) {
59 return $this->renderOrderingRow($component, $default_renderer);
60 }
61 $this->cannotHandleComponent($component);
62 }
63
64 protected function renderPresentationTable(
65 Component\Table\Presentation $component,
66 RendererInterface $default_renderer
67 ): string {
68 $tpl = $this->getTemplate("tpl.presentationtable.html", true, true);
69 $tpl->setVariable("TITLE", $component->getTitle());
70 $expcollapsebtns = [];
71 if ($sig_ta = $component->getExpandCollapseAllSignal()) {
72 $sig_ta_expand = clone $sig_ta;
73 $sig_ta_expand->addOption('expand', true);
74 $expcollapsebtns[] = $this->getUIFactory()->button()
75 ->standard($this->txt('presentation_table_expand'), '')
76 ->withOnClick($sig_ta_expand);
77 $sig_ta_collapse = clone $sig_ta;
78 $sig_ta_collapse->addOption('expand', false);
79 $expcollapsebtns[] = $this->getUIFactory()->button()
80 ->standard($this->txt('presentation_table_collapse'), '')
81 ->withOnClick($sig_ta_collapse);
82 $component = $component->withAdditionalOnLoadCode(
83 static fn($id) => "
84 il.UI.table.presentation.init('{$id}');
85 $(document).on('$sig_ta', function(event, signal_data) { il.UI.table.presentation.get('$id').expandAll(signal_data); return false; });
86 "
87 );
88 }
89
90 $tpl->setVariable("EXPANDCOLLAPSEALL", $default_renderer->render($expcollapsebtns));
91
92 $vcs = $component->getViewControls();
93 if ($vcs) {
94 $tpl->setVariable("VC", $default_renderer->render($vcs));
95 }
96
97 $id = $this->bindJavaScript($component);
98 $tpl->setVariable("ID", $id);
99
100 $row_mapping = $component->getRowMapping();
101 $data = $component->getData();
102 $component_id = $id;
103
104 if (empty($data)) {
105 $this->renderEmptyPresentationRow($tpl, $default_renderer, $this->txt('ui_table_no_records'));
106 return $tpl->get();
107 }
108
109 foreach ($data as $record) {
110 $row = $row_mapping(
111 new PresentationRow($component->getSignalGenerator(), $component_id),
112 $record,
113 $this->getUIFactory(),
114 $component->getEnvironment()
115 );
116
117 $tpl->setCurrentBlock("row");
118 $tpl->setVariable("ROW", $default_renderer->render($row));
119 $tpl->parseCurrentBlock();
120 }
121
122 return $tpl->get();
123 }
124
125 protected function renderPresentationRow(
127 RendererInterface $default_renderer
128 ): string {
129 $f = $this->getUIFactory();
130 $tpl = $this->getTemplate("tpl.presentationrow.html", true, true);
131
132 $component = $this->registerSignals($component->withResetSignals());
133 $sig_show = $component->getShowSignal();
134 $sig_hide = $component->getCloseSignal();
135 $sig_toggle = $component->getToggleSignal();
136 $id = $this->bindJavaScript($component);
137
138 $expander = $f->button()->shy( '', '')->withSymbol($f->symbol()->glyph()->expand())
139 ->withOnClick($sig_show);
140 $collapser = $f->button()->shy('', '')->withSymbol($f->symbol()->glyph()->collapse())
141 ->withOnClick($sig_hide);
142 $shy_expander = $f->button()->shy($this->txt("presentation_table_more"), "")
143 ->withOnClick($sig_show);
144
145 $tpl->setVariable("ID", $id);
146 $tpl->setVariable("EXPANDER", $default_renderer->render($expander));
147 $tpl->setVariable("COLLAPSER", $default_renderer->render($collapser));
148 $tpl->setVariable("SHY_EXPANDER", $default_renderer->render($shy_expander));
149
150 if ($symbol = $component->getLeadingSymbol()) {
151 $tpl->setVariable("SYMBOL", $default_renderer->render($symbol));
152 }
153 $tpl->setVariable("HEADLINE", $component->getHeadline());
154 $tpl->setVariable("TOGGLE_SIGNAL", $sig_toggle);
155 $subheadline = $component->getSubheadline();
156 if ($subheadline) {
157 $tpl->setVariable("SUBHEADLINE", $subheadline);
158 }
159
160 foreach ($component->getImportantFields() as $label => $value) {
161 $tpl->setCurrentBlock("important_field");
162 if (is_string($label)) {
163 $tpl->setVariable("IMPORTANT_FIELD_LABEL", $label);
164 }
165 $tpl->setVariable("IMPORTANT_FIELD_VALUE", $value);
166 $tpl->parseCurrentBlock();
167 }
168
169 $tpl->setVariable("DESCLIST", $default_renderer->render($component->getContent()));
170
171 $further_fields_headline = $component->getFurtherFieldsHeadline();
172 $further_fields = $component->getFurtherFields();
173
174 if (count($further_fields) > 0) {
175 $tpl->touchBlock("has_further_fields");
176
177 if ($further_fields_headline) {
178 $tpl->setVariable("FURTHER_FIELDS_HEADLINE", $further_fields_headline);
179 }
180
181 foreach ($further_fields as $label => $value) {
182 $tpl->setCurrentBlock("further_field");
183 if (is_string($label)) {
184 $tpl->setVariable("FIELD_LABEL", $label);
185 }
186 $tpl->setVariable("FIELD_VALUE", $value);
187 $tpl->parseCurrentBlock();
188 }
189 }
190
191 $action = $component->getAction();
192 if (!is_null($action)) {
193 $tpl->setCurrentBlock("button");
194 $tpl->setVariable("BUTTON", $default_renderer->render($action));
195 $tpl->parseCurrentBlock();
196 }
197
198 return $tpl->get();
199 }
200
201 public function renderDataTable(Component\Table\Data $component, RendererInterface $default_renderer): string
202 {
203 $tpl = $this->getTemplate("tpl.datatable.html", true, true);
204 $component = $this->registerActions($component);
205
206 [$component, $view_controls] = $component->applyViewControls(
207 $component->getFilter(),
208 $component->getAdditionalParameters()
209 );
210
211 $rows = $component->getDataRetrieval()->getRows(
212 $component->getRowBuilder(),
213 array_keys($component->getVisibleColumns()),
214 $component->getRange(),
215 $component->getOrder(),
216 $component->getAdditionalViewControlData(),
217 $component->getFilter(),
218 $component->getAdditionalParameters()
219 );
220
221 // these column counts and index numbers are meant for aria attributes in the html tpl
222 $compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
223 if ($component->hasMultiActions()) { // adds column with checkbox before first data column which is numbered 1.
224 $compensate_col_index += 1;
225 }
226 $compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
227 if ($component->hasMultiActions()) {
228 $compensate_col_count += 1;
229 }
230 if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
231 $compensate_col_count += 1;
232 }
233
234 $id = $this->bindJavaScript($component);
235 $tpl->setVariable('ID', $id);
236 $tpl->setVariable('TITLE', $component->getTitle());
237 $tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
238 $tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
239
240 $sortation_signal = null;
241 // if the generator is empty, and thus invalid, we render an empty row.
242 if (!$rows->valid()) {
243 $this->renderFullWidthDataCell($component, $tpl, $this->txt('ui_table_no_records'));
244 } else {
245 $this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
246 $this->appendTableRows($tpl, $rows, $default_renderer);
247
248 if ($component->hasMultiActions()) {
249 $multi_actions = $component->getMultiActions();
250 $modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $id);
251 $multi_actions_dropdown = $this->buildMultiActionsDropdown(
252 $multi_actions,
253 $component->getMultiActionSignal(),
254 $modal->getShowSignal()
255 );
256 $multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
257 $tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
258 $tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
259 $tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
260 $tpl->setVariable('MULTI_ACTION_WARNING', $default_renderer->render($this->getUrlTooLongWarning()));
261 $tpl->setVariable('MULTI_ACTION_WARNING_BUTTON_CLOSE_LABEL', $this->txt('datatable_close_warning'));
262 }
263
264 $sortation_view_control = array_filter(
265 $view_controls->getInputs(),
266 static fn($i): bool => $i instanceof Component\Input\ViewControl\Sortation
267 );
268 if ($sortation_view_control) {
269 $sortation_signal = array_shift($sortation_view_control)->getInternalSignal();
270 $sortation_signal->addOption('parent_container', $id);
271 }
272 }
273
274 $this->renderTableHeader($default_renderer, $component, $tpl, $sortation_signal, $compensate_col_index);
275 return $tpl->get();
276 }
277
278 protected function renderTableHeader(
279 RendererInterface $default_renderer,
280 Component\Table\Data $component,
281 Template $tpl,
282 ?Component\Signal $sortation_signal,
283 int $compensate_col_index,
284 ): void {
285 $order = $component->getOrder();
286 $glyph_factory = $this->getUIFactory()->symbol()->glyph();
287 $sort_col = key($order->get());
288 $sort_direction = current($order->get());
289 $columns = $component->getVisibleColumns();
290
291 foreach ($columns as $col_id => $col) {
292 $param_sort_direction = Order::ASC;
293 $col_title = $col->getTitle();
294 if ($col_id === $sort_col) {
295 if ($sort_direction === Order::ASC) {
296 $sortation = "ascending"; // aria-sort should not be translated and always be in English
297 $param_sort_direction = Order::DESC;
298 }
299 if ($sort_direction === Order::DESC) {
300 $sortation = "descending"; // aria-sort should not be translated and always be in English
301 $param_sort_direction = Order::ASC;
302 }
303 }
304
305 $tpl->setCurrentBlock('header_cell');
306
307 $tpl->setVariable('COL_INDEX', (string) $col->getIndex() + $compensate_col_index);
308
309 if ($col->isSortable() && !is_null($sortation_signal)) {
310 $sort_signal = clone $sortation_signal;
311 $sort_signal->addOption('value', "$col_id:$param_sort_direction");
312 $col_title = $default_renderer->render(
313 $this->getUIFactory()->button()->shy($col_title, $sort_signal)
314 );
315
316 if ($col_id === $sort_col) {
317 $sortation_glyph = $this->getUIFactory()->button()->shy('', '')
318 ->withSymbol(
319 $sort_direction === Order::ASC ?
320 $glyph_factory->sortAscending() :
321 $glyph_factory->sortDescending()
322 )
323 ->withOnClick($sort_signal);
324 $tpl->setVariable('COL_SORTATION', $sortation);
325 $tpl->setVariable('COL_SORTATION_GLYPH', $default_renderer->render($sortation_glyph));
326 }
327 }
328
329 $tpl->setVariable('COL_TITLE', $col_title);
330 $tpl->setVariable('COL_TYPE', strtolower($col->getType()));
331 $tpl->parseCurrentBlock();
332 }
333 }
334
335 protected function renderActionsHeader(
336 RendererInterface $default_renderer,
337 Component\Table\Table $component,
338 Template $tpl,
339 int $compensate_col_count,
340 ): void {
341 if ($component->hasSingleActions()) {
342 $tpl->setVariable('COL_INDEX_ACTION', (string) $component->getColumnCount() + $compensate_col_count);
343 $tpl->setVariable('COL_TITLE_ACTION', $this->txt('actions'));
344 }
345
346 if ($component->hasMultiActions()) {
347 $f = $this->getUIFactory();
348 $glyph_factory = $f->symbol()->glyph();
349 $signal = $component->getSelectionSignal();
350 $sig_all = clone $signal;
351 $sig_all->addOption('select', true);
352 $select_all = $f->button()->shy('', '')
353 ->withSymbol($glyph_factory->add())
354 ->withOnClick($sig_all);
355 $signal->addOption('select', false);
356 $select_none = $f->button()->shy('', '')
357 ->withSymbol($glyph_factory->close())
358 ->withOnClick($signal);
359 $tpl->setVariable('SELECTION_CONTROL_SELECT', $default_renderer->render($select_all));
360 $tpl->setVariable('SELECTION_CONTROL_DESELECT', $default_renderer->render($select_none));
361 }
362
363 if ($component instanceof Component\Table\Ordering) {
364 if (!$component->isOrderingDisabled() && !$component->hasMultiActions()) {
365 $tpl->touchBlock('header_rowselection_cell');
366 }
367 }
368 }
369
374 protected function renderFullWidthDataCell(Component\Table\Data $component, Template $tpl, string $content): void
375 {
376 $cell_tpl = $this->getTemplate('tpl.datacell.html', true, true);
377 $cell_tpl->setCurrentBlock('cell');
378 $cell_tpl->setVariable('CELL_CONTENT', $content);
379 $cell_tpl->setVariable('COL_SPAN', count($component->getVisibleColumns()));
380 $cell_tpl->setVariable('COL_TYPE', 'full-width');
381 $cell_tpl->setVariable('COL_INDEX', '1');
382 $cell_tpl->parseCurrentBlock();
383
384 $tpl->setCurrentBlock('row');
385 $tpl->setVariable('ALTERNATION', 'even');
386 $tpl->setVariable('CELLS', $cell_tpl->get());
387 $tpl->parseCurrentBlock();
388 }
389
390 protected function registerActions(Component\Table\Table $component): Component\Table\Table
391 {
392 $opt_action_id = Action::OPT_ACTIONID;
393 $opt_row_id = Action::OPT_ROWID;
394
395 if ($component->hasMultiActions()) {
396 $component = $component->withAdditionalOnLoadCode(
397 static fn($id): string => "il.UI.table.data.get('{$id}').selectAll(false);"
398 );
399 }
400
401 $actions = [];
402 foreach ($component->getAllActions() as $action_id => $action) {
403 $component = $component->withAdditionalOnLoadCode($this->getActionRegistration((string) $action_id, $action));
404 if ($action->isAsync()) {
405 $signal = clone $component->getAsyncActionSignal();
406 $signal->addOption(Action::OPT_ACTIONID, $action_id);
407 $action = $action->withSignalTarget($signal);
408 }
409 $actions[$action_id] = $action;
410 }
411 $component = $component->withActions($actions);
412
413 $component = $component
414 ->withAdditionalOnLoadCode(
415 static fn($id): string =>
416 "il.UI.table.data.init('{$id}','{$opt_action_id}','{$opt_row_id}');"
417 )
418 ->withAdditionalOnLoadCode($this->getAsyncActionHandler($component->getAsyncActionSignal()))
419 ->withAdditionalOnLoadCode($this->getMultiActionHandler($component->getMultiActionSignal()))
420 ->withAdditionalOnLoadCode($this->getSelectionHandler($component->getSelectionSignal()));
421
422 return $component;
423 }
424
425 protected function appendTableRows(
426 Template $tpl,
427 \Generator|array $rows,
428 RendererInterface $default_renderer
429 ): void {
430 $alternate = 'even';
431 foreach ($rows as $row) {
432 $row_contents = $default_renderer->render($row);
433 $alternate = ($alternate === 'odd') ? 'even' : 'odd';
434 $tpl->setCurrentBlock('row');
435 $tpl->setVariable('ALTERNATION', $alternate);
436 $tpl->setVariable('CELLS', $row_contents);
437 $tpl->parseCurrentBlock();
438 }
439 }
440
441 protected function renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content): void
442 {
443 $row_tpl = $this->getTemplate('tpl.presentationrow_empty.html', true, true);
444 $row_tpl->setVariable('CONTENT', $content);
445 $tpl->setVariable('ROW', $row_tpl->get());
446 }
447
452 array $actions,
453 string $table_id
454 ): \ILIAS\UI\Component\Modal\RoundTrip {
455 $f = $this->getUIFactory();
456
457 $msg = $f->messageBox()->confirmation($this->txt('datatable_multiactionmodal_msg'));
458
459 $select = $f->input()->field()->select(
460 $this->txt('datatable_multiactionmodal_actionlabel'),
461 array_map(
462 static fn($action): string => $action->getLabel(),
463 $actions
464 ),
465 ""
466 )->withRequired(true);
467 $submit = $f->button()->primary($this->txt('datatable_multiactionmodal_apply'), '')
468 ->withOnLoadCode(
469 static fn($id): string => "$('#{$id}').click(function() { il.UI.table.data.get('{$table_id}').doActionForAll(this); return false; });"
470 );
471 $modal = $f->modal()
472 ->roundtrip($this->txt('datatable_multiactionmodal_title'), [$msg, $select])
473 ->withActionButtons([$submit]);
474 return $modal;
475 }
476
477 protected function getUrlTooLongWarning(): \ILIAS\UI\Component\MessageBox\MessageBox
478 {
479 $f = $this->getUIFactory();
480 return $f->messageBox()->failure($this->txt('warning_url_too_long_msg'));
481 }
482
486 protected function buildMultiActionsDropdown(
487 array $actions,
488 Component\Signal $action_signal,
489 Component\Signal $modal_signal,
491 if ($actions === []) {
492 return null;
493 }
494 $f = $this->getUIFactory();
495 $glyph = $f->symbol()->glyph()->bulletlist();
496 $buttons = [];
497 $all_obj_buttons = [];
498 foreach ($actions as $action_id => $act) {
499 $signal = clone $action_signal;
500 $signal->addOption(Action::OPT_ACTIONID, $action_id);
501 $buttons[] = $f->button()->shy($act->getLabel(), $signal);
502 }
503
504 $buttons[] = $f->divider()->horizontal();
505 $buttons[] = $f->button()->shy($this->txt('datatable_multiactionmodal_listentry'), '#')->withOnClick($modal_signal);
506
507 return $f->dropdown()->standard($buttons)->withLabel($this->txt('datatable_multiaction_label'));
508 }
509
510 protected function getAsyncActionHandler(Component\Signal $action_signal): \Closure
511 {
512 return static function ($id) use ($action_signal): string {
513 return "
514 $(document).on('{$action_signal}', function(event, signal_data) {
515 il.UI.table.data.get('{$id}').doSingleAction(signal_data);
516 return false;
517 });";
518 };
519 }
520 protected function getMultiActionHandler(Component\Signal $action_signal): \Closure
521 {
522 return static function ($id) use ($action_signal): string {
523 return "
524 $(document).on('{$action_signal}', function(event, signal_data) {
525 il.UI.table.data.get('{$id}').doMultiAction(signal_data);
526 return false;
527 });";
528 };
529 }
530
531 protected function getSelectionHandler(Component\Signal $selection_signal): \Closure
532 {
533 return static function ($id) use ($selection_signal): string {
534 return "
535 $(document).on('{$selection_signal}', function(event, signal_data) {
536 il.UI.table.data.get('{$id}').selectAll(signal_data.options.select);
537 return false;
538 });
539 ";
540 };
541 }
542
543 protected function getActionRegistration(
544 string $action_id,
545 Action $action
546 ): \Closure {
547 $async = $action->isAsync() ? 'true' : 'false';
548 $url_builder_js = $action->getURLBuilderJS();
549 $tokens_js = $action->getURLBuilderTokensJS();
550
551 return static function ($id) use ($action_id, $async, $url_builder_js, $tokens_js): string {
552 return "
553 il.UI.table.data.get('{$id}').registerAction('{$action_id}', {$async}, {$url_builder_js}, {$tokens_js});
554 ";
555 };
556 }
557
558 public function renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer): string
559 {
560 $cell_tpl = $this->getTemplate("tpl.datacell.html", true, true);
561 $this->fillCells($component, $cell_tpl, $default_renderer);
562
563
564 return $cell_tpl->get();
565 }
566
567 public function renderOrderingRow(Component\Table\OrderingRow $component, RendererInterface $default_renderer): string
568 {
569 $cell_tpl = $this->getTemplate("tpl.orderingcell.html", true, true);
570 $this->fillCells($component, $cell_tpl, $default_renderer);
571
572
573 if ($component->isOrderingDisabled()) {
574 return $cell_tpl->get();
575 }
576
577 $namesource = new class () implements NameSource {
578 public function getNewName(): string
579 {
580 return '';
581 }
582 public function getNewDedicatedName(string $dedicated_name): string
583 {
584 return $dedicated_name;
585 }
586 };
587
588 $numeric_label = $this->txt("ui_table_order");
589 $input = $this->getUIFactory()->input()->field()->numeric($numeric_label)
590 ->withDedicatedName($component->getId())
591 ->withNameFrom($namesource)
592 ->withValue($component->getPosition() * 10);
593 $cell_tpl->setVariable('ORDER_INPUT', $default_renderer->render($input));
594
595 return $cell_tpl->get();
596 }
597
598
599 protected function fillCells(
601 Template $cell_tpl,
602 RendererInterface $default_renderer
603 ) {
604 $cols = $row->getColumns();
605 foreach ($cols as $col_id => $column) {
606 if ($column->isHighlighted()) {
607 $cell_tpl->touchBlock('highlighted');
608 }
609 $cell_tpl->setCurrentBlock('cell');
610 $cell_tpl->setVariable('COL_TYPE', strtolower($column->getType()));
611 $cell_content = $row->getCellContent($col_id);
612 if ($cell_content instanceof Component\Component) {
613 $cell_content = $default_renderer->render($cell_content);
614 }
615 $cell_tpl->setVariable('CELL_CONTENT', $cell_content);
616 $cell_tpl->setVariable('CELL_COL_TITLE', $row->getColumns()[$col_id]->getTitle());
617 $cell_tpl->parseCurrentBlock();
618 }
619
620 if ($row->tableHasMultiActions()) {
621 $cell_tpl->setVariable('ROW_ID', $row->getId());
622 }
623 if ($row->tableHasSingleActions()) {
624 $row_actions_dropdown = $this->getSingleActionsForRow(
625 $row->getId(),
626 $row->getActions()
627 );
628 $cell_tpl->setVariable('ACTION_CONTENT', $default_renderer->render($row_actions_dropdown));
629 }
630
631 if ($row instanceof Component\Table\OrderingRow) {
632 if (!$row->isOrderingDisabled()) {
633 $drag_handle = $this->getUIFactory()->symbol()->glyph()->dragHandle();
634 $drag_handle = $default_renderer->render($drag_handle);
635 $cell_tpl->setVariable('DRAG_HANDLE', $drag_handle);
636 }
637 }
638 }
639
643 protected function getSingleActionsForRow(string $row_id, array $actions): \ILIAS\UI\Component\Dropdown\Standard
644 {
645 $f = $this->getUIFactory();
646 $buttons = [];
647 foreach ($actions as $act) {
648 $act = $act->withRowId($row_id);
649 $target = $act->getTarget();
650 if ($target instanceof URI) {
651 $target = (string) $target;
652 }
653 $buttons[] = $f->button()->shy($act->getLabel(), $target);
654 }
655 return $f->dropdown()->standard($buttons);
656 }
657
658
659 public function renderOrderingTable(Component\Table\Ordering $component, RendererInterface $default_renderer): string
660 {
661 $tpl = $this->getTemplate("tpl.orderingtable.html", true, true);
662 $component = $this->registerActions($component);
663
664 [$component, $view_controls] = $component->applyViewControls();
665
666 $rows = $component->getDataBinding()->getRows(
667 $component->getRowBuilder(),
668 array_keys($component->getVisibleColumns()),
669 );
670
671
672 if (!$component->isOrderingDisabled()) {
673 $component = $component->withAdditionalOnLoadCode(
674 static fn($id): string => "il.UI.table.ordering.init('{$id}');"
675 );
676 }
677
678 // these column counts and index numbers are meant for aria attributes in the html tpl
679 $compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
680 // for column with checkbox and/or drag handle before first data column which is numbered 1.
681 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
682 $compensate_col_index += 1; // checkbox & drag handle
683 }
684 if (!$component->isOrderingDisabled()) {
685 $compensate_col_index += 1; // Position input
686 }
687
688 $compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
689 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
690 $compensate_col_count += 1; // checkbox & drag handle
691 }
692 if (!$component->isOrderingDisabled()) {
693 $compensate_col_count += 1; // Position input
694 }
695 if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
696 $compensate_col_count += 1;
697 }
698
699 $tableid = $this->bindJavaScript($component) ?? $this->createId();
700
701 if (!$component->isOrderingDisabled()) {
702 $submit = $this->getUIFactory()->button()->standard($this->txt('sorting_save'), "")
703 ->withOnLoadCode(static fn($id) => "document.getElementById('$id').addEventListener('click',
704 function() {document.querySelector('#$tableid form.c-table-ordering__form').submit();return false;});");
705
706 $tpl->setVariable('FORM_BUTTONS', $default_renderer->render($submit));
707 $tpl->setVariable('POS_INPUT_TITLE', $this->txt('table_posinput_col_title'));
708
709 }
710
711 $tpl->setVariable('ID', $tableid);
712 $tpl->setVariable('TARGET_URL', $component->getTargetURL() ? $component->getTargetURL()->__toString() : '#');
713 $tpl->setVariable('TITLE', $component->getTitle());
714 $tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
715 $tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
716
717 $columns = $component->getVisibleColumns();
718 foreach ($columns as $col_id => $col) {
719 $col_title = $col->getTitle();
720 $tpl->setCurrentBlock('header_cell');
721 $tpl->setVariable('COL_INDEX', (string) $col->getIndex() + $compensate_col_index);
722 $tpl->setVariable('COL_TITLE', $col_title);
723 $tpl->setVariable('COL_TYPE', strtolower($col->getType()));
724 $tpl->parseCurrentBlock();
725 }
726
727 $rows = iterator_to_array($rows);
728 $r = [];
729 foreach ($rows as $idx => $row) {
730 $r[] = $row
731 ->withPosition($idx + 1)
732 ->withOrderingDisabled($component->isOrderingDisabled());
733 }
734
735 $this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
736 $this->appendTableRows($tpl, $r, $default_renderer);
737
738 if ($component->hasMultiActions()) {
739 $multi_actions = $component->getMultiActions();
740 $modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $tableid);
741 $multi_actions_dropdown = $this->buildMultiActionsDropdown(
742 $multi_actions,
743 $component->getMultiActionSignal(),
744 $modal->getShowSignal()
745 );
746
747 $tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
748 $tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
749 $tpl->setVariable('MULTI_ACTION_WARNING', $default_renderer->render($this->getUrlTooLongWarning()));
750 $tpl->setVariable('MULTI_ACTION_WARNING_BUTTON_CLOSE_LABEL', $this->txt('datatable_close_warning'));
751 }
752 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
753 $multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
754 $tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
755 }
756
757 return $tpl->get();
758 }
759
763 public function registerResources(ResourceRegistry $registry): void
764 {
765 parent::registerResources($registry);
766 $registry->register('assets/js/table.min.js');
767 $registry->register('assets/js/modal.min.js');
768 }
769
771 {
772 $show = $component->getShowSignal();
773 $close = $component->getCloseSignal();
774 $toggle = $component->getToggleSignal();
775 $table_id = $component->getTableId();
776 return $component->withAdditionalOnLoadCode(
777 static fn($id): string =>
778 "$(document).on('$show', function() { il.UI.table.presentation.get('$table_id').expandRow('$id'); return false; });" .
779 "$(document).on('$close', function() { il.UI.table.presentation.get('$table_id').collapseRow('$id'); return false; });" .
780 "$(document).on('$toggle', function() { il.UI.table.presentation.get('$table_id').toggleRow('$id'); return false; });"
781 );
782 }
783}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
Both the subject and the direction need to be specified when expressing an order.
Definition: Order.php:29
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
This implements commonalities between inputs.
Definition: Input.php:43
registerSignals(Component\Table\PresentationRow $component)
Definition: Renderer.php:770
fillCells(Component\Table\DataRow $row, Template $cell_tpl, RendererInterface $default_renderer)
Definition: Renderer.php:599
registerActions(Component\Table\Table $component)
Definition: Renderer.php:390
renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:558
getAsyncActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:510
getMultiActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:520
renderOrderingTable(Component\Table\Ordering $component, RendererInterface $default_renderer)
Definition: Renderer.php:659
getActionRegistration(string $action_id, Action $action)
Definition: Renderer.php:543
buildMultiActionsAllObjectsModal(array $actions, string $table_id)
Definition: Renderer.php:451
renderPresentationRow(Component\Table\PresentationRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:125
buildMultiActionsDropdown(array $actions, Component\Signal $action_signal, Component\Signal $modal_signal,)
Definition: Renderer.php:486
renderPresentationTable(Component\Table\Presentation $component, RendererInterface $default_renderer)
Definition: Renderer.php:64
renderFullWidthDataCell(Component\Table\Data $component, Template $tpl, string $content)
Renders a full-width cell with a single message within, indication there is no data to display.
Definition: Renderer.php:374
getSingleActionsForRow(string $row_id, array $actions)
Definition: Renderer.php:643
renderActionsHeader(RendererInterface $default_renderer, Component\Table\Table $component, Template $tpl, int $compensate_col_count,)
Definition: Renderer.php:335
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:39
renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content)
Definition: Renderer.php:441
renderTableHeader(RendererInterface $default_renderer, Component\Table\Data $component, Template $tpl, ?Component\Signal $sortation_signal, int $compensate_col_index,)
Definition: Renderer.php:278
getSelectionHandler(Component\Signal $selection_signal)
Definition: Renderer.php:531
renderOrderingRow(Component\Table\OrderingRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:567
appendTableRows(Template $tpl, \Generator|array $rows, RendererInterface $default_renderer)
Definition: Renderer.php:425
renderDataTable(Component\Table\Data $component, RendererInterface $default_renderer)
Definition: Renderer.php:201
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:763
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
Definition: UI.php:24
return true
This describes commonalities between all types of Dropdowns.
Definition: Dropdown.php:35
This describes a Sortation View Control.
Definition: Sortation.php:29
Interface to be extended by components that have the possibility to bind to Javascript.
withAdditionalOnLoadCode(Closure $binder)
Add some onload-code to the component instead of replacing the existing one.
Describes a source for input names.
Definition: NameSource.php:27
Registry for resources required by rendered output like Javascript or CSS.
register(string $name)
Add a dependency.
Interface to templating as it is used in the UI framework.
Definition: Template.php:29
setVariable(string $name, $value)
Set a variable in the current block.
get(?string $block=null)
Get the rendered template or a specific block.
setCurrentBlock(string $name)
Set the block to work on.
touchBlock(string $name)
Touch a block without working further on it.
parseCurrentBlock()
Parse the block that is currently worked on.
An entity that renders components to a string output.
Definition: Renderer.php:31
button(string $caption, string $cmd)
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
if(!file_exists('../ilias.ini.php'))