ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
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->symbol()->glyph()->expand("#")
139 ->withOnClick($sig_show);
140 $collapser = $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->getFilter(),
217 $component->getAdditionalParameters()
218 );
219
220 // these column counts and index numbers are meant for aria attributes in the html tpl
221 $compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
222 if ($component->hasMultiActions()) { // adds column with checkbox before first data column which is numbered 1.
223 $compensate_col_index += 1;
224 }
225 $compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
226 if ($component->hasMultiActions()) {
227 $compensate_col_count += 1;
228 }
229 if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
230 $compensate_col_count += 1;
231 }
232
233 $id = $this->bindJavaScript($component);
234 $tpl->setVariable('ID', $id);
235 $tpl->setVariable('TITLE', $component->getTitle());
236 $tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
237 $tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
238
239 $sortation_signal = null;
240 // if the generator is empty, and thus invalid, we render an empty row.
241 if (!$rows->valid()) {
242 $this->renderFullWidthDataCell($component, $tpl, $this->txt('ui_table_no_records'));
243 } else {
244 $this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
245 $this->appendTableRows($tpl, $rows, $default_renderer);
246
247 if ($component->hasMultiActions()) {
248 $multi_actions = $component->getMultiActions();
249 $modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $id);
250 $multi_actions_dropdown = $this->buildMultiActionsDropdown(
251 $multi_actions,
252 $component->getMultiActionSignal(),
253 $modal->getShowSignal()
254 );
255 $multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
256 $tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
257 $tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
258 $tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
259 $tpl->setVariable('MULTI_ACTION_WARNING', $default_renderer->render($this->getUrlTooLongWarning()));
260 $tpl->setVariable('MULTI_ACTION_WARNING_BUTTON_CLOSE_LABEL', $this->txt('datatable_close_warning'));
261 }
262
263 $sortation_view_control = array_filter(
264 $view_controls->getInputs(),
265 static fn($i): bool => $i instanceof Component\Input\ViewControl\Sortation
266 );
267 if ($sortation_view_control) {
268 $sortation_signal = array_shift($sortation_view_control)->getInternalSignal();
269 $sortation_signal->addOption('parent_container', $id);
270 }
271 }
272
273 $this->renderTableHeader($default_renderer, $component, $tpl, $sortation_signal, $compensate_col_index);
274 return $tpl->get();
275 }
276
277 protected function renderTableHeader(
278 RendererInterface $default_renderer,
279 Component\Table\Data $component,
280 Template $tpl,
281 ?Component\Signal $sortation_signal,
282 int $compensate_col_index,
283 ): void {
284 $order = $component->getOrder();
285 $glyph_factory = $this->getUIFactory()->symbol()->glyph();
286 $sort_col = key($order->get());
287 $sort_direction = current($order->get());
288 $columns = $component->getVisibleColumns();
289
290 foreach ($columns as $col_id => $col) {
291 $param_sort_direction = Order::ASC;
292 $col_title = $col->getTitle();
293 if ($col_id === $sort_col) {
294 if ($sort_direction === Order::ASC) {
295 $sortation = "ascending"; // aria-sort should not be translated and always be in English
296 $sortation_glyph = $glyph_factory->sortAscending("#");
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 $sortation_glyph = $glyph_factory->sortDescending("#");
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 = $default_renderer->render($sortation_glyph->withOnClick($sort_signal));
318 $tpl->setVariable('COL_SORTATION', $sortation);
319 $tpl->setVariable('COL_SORTATION_GLYPH', $sortation_glyph);
320 }
321 }
322
323 $tpl->setVariable('COL_TITLE', $col_title);
324 $tpl->setVariable('COL_TYPE', strtolower($col->getType()));
325 $tpl->parseCurrentBlock();
326 }
327 }
328
329 protected function renderActionsHeader(
330 RendererInterface $default_renderer,
331 Component\Table\Table $component,
332 Template $tpl,
333 int $compensate_col_count,
334 ): void {
335 if ($component->hasSingleActions()) {
336 $tpl->setVariable('COL_INDEX_ACTION', (string) $component->getColumnCount() + $compensate_col_count);
337 $tpl->setVariable('COL_TITLE_ACTION', $this->txt('actions'));
338 }
339
340 if ($component->hasMultiActions()) {
341 $glyph_factory = $this->getUIFactory()->symbol()->glyph();
342 $signal = $component->getSelectionSignal();
343 $sig_all = clone $signal;
344 $sig_all->addOption('select', true);
345 $select_all = $glyph_factory->add()->withOnClick($sig_all);
346 $signal->addOption('select', false);
347 $select_none = $glyph_factory->close()->withOnClick($signal);
348 $tpl->setVariable('SELECTION_CONTROL_SELECT', $default_renderer->render($select_all));
349 $tpl->setVariable('SELECTION_CONTROL_DESELECT', $default_renderer->render($select_none));
350 }
351
352 if ($component instanceof Component\Table\Ordering) {
353 if (!$component->isOrderingDisabled() && !$component->hasMultiActions()) {
354 $tpl->touchBlock('header_rowselection_cell');
355 }
356 }
357 }
358
363 protected function renderFullWidthDataCell(Component\Table\Data $component, Template $tpl, string $content): void
364 {
365 $cell_tpl = $this->getTemplate('tpl.datacell.html', true, true);
366 $cell_tpl->setCurrentBlock('cell');
367 $cell_tpl->setVariable('CELL_CONTENT', $content);
368 $cell_tpl->setVariable('COL_SPAN', count($component->getVisibleColumns()));
369 $cell_tpl->setVariable('COL_TYPE', 'full-width');
370 $cell_tpl->setVariable('COL_INDEX', '1');
371 $cell_tpl->parseCurrentBlock();
372
373 $tpl->setCurrentBlock('row');
374 $tpl->setVariable('ALTERNATION', 'even');
375 $tpl->setVariable('CELLS', $cell_tpl->get());
376 $tpl->parseCurrentBlock();
377 }
378
379 protected function registerActions(Component\Table\Table $component): Component\Table\Table
380 {
381 $opt_action_id = Action::OPT_ACTIONID;
382 $opt_row_id = Action::OPT_ROWID;
383
384 $component = $component
385 ->withAdditionalOnLoadCode(
386 static fn($id): string =>
387 "il.UI.table.data.init('{$id}','{$opt_action_id}','{$opt_row_id}');"
388 )
389 ->withAdditionalOnLoadCode($this->getAsyncActionHandler($component->getAsyncActionSignal()))
390 ->withAdditionalOnLoadCode($this->getMultiActionHandler($component->getMultiActionSignal()))
391 ->withAdditionalOnLoadCode($this->getSelectionHandler($component->getSelectionSignal()));
392
393 $actions = [];
394 foreach ($component->getAllActions() as $action_id => $action) {
395 $component = $component->withAdditionalOnLoadCode($this->getActionRegistration((string) $action_id, $action));
396 if ($action->isAsync()) {
397 $signal = clone $component->getAsyncActionSignal();
398 $signal->addOption(Action::OPT_ACTIONID, $action_id);
399 $action = $action->withSignalTarget($signal);
400 }
401 $actions[$action_id] = $action;
402 }
403 $component = $component->withActions($actions);
404
405 if ($component->hasMultiActions()) {
406 $component = $component->withAdditionalOnLoadCode(
407 static fn($id): string => "il.UI.table.data.get('{$id}').selectAll(false);"
408 );
409 }
410
411 return $component;
412 }
413
414 protected function appendTableRows(
415 Template $tpl,
416 \Generator|array $rows,
417 RendererInterface $default_renderer
418 ): void {
419 $alternate = 'even';
420 foreach ($rows as $row) {
421 $row_contents = $default_renderer->render($row);
422 $alternate = ($alternate === 'odd') ? 'even' : 'odd';
423 $tpl->setCurrentBlock('row');
424 $tpl->setVariable('ALTERNATION', $alternate);
425 $tpl->setVariable('CELLS', $row_contents);
426 $tpl->parseCurrentBlock();
427 }
428 }
429
430 protected function renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content): void
431 {
432 $row_tpl = $this->getTemplate('tpl.presentationrow_empty.html', true, true);
433 $row_tpl->setVariable('CONTENT', $content);
434 $tpl->setVariable('ROW', $row_tpl->get());
435 }
436
441 array $actions,
442 string $table_id
443 ): \ILIAS\UI\Component\Modal\RoundTrip {
444 $f = $this->getUIFactory();
445
446 $msg = $f->messageBox()->confirmation($this->txt('datatable_multiactionmodal_msg'));
447
448 $select = $f->input()->field()->select(
449 $this->txt('datatable_multiactionmodal_actionlabel'),
450 array_map(
451 static fn($action): string => $action->getLabel(),
452 $actions
453 ),
454 ""
455 )->withRequired(true);
456 $submit = $f->button()->primary($this->txt('datatable_multiactionmodal_apply'), '')
457 ->withOnLoadCode(
458 static fn($id): string => "$('#{$id}').click(function() { il.UI.table.data.get('{$table_id}').doActionForAll(this); return false; });"
459 );
460 $modal = $f->modal()
461 ->roundtrip($this->txt('datatable_multiactionmodal_title'), [$msg, $select])
462 ->withActionButtons([$submit]);
463 return $modal;
464 }
465
466 protected function getUrlTooLongWarning(): \ILIAS\UI\Component\MessageBox\MessageBox
467 {
468 $f = $this->getUIFactory();
469 return $f->messageBox()->failure($this->txt('warning_url_too_long_msg'));
470 }
471
475 protected function buildMultiActionsDropdown(
476 array $actions,
477 Component\Signal $action_signal,
478 Component\Signal $modal_signal,
480 if ($actions === []) {
481 return null;
482 }
483 $f = $this->getUIFactory();
484 $glyph = $f->symbol()->glyph()->bulletlist();
485 $buttons = [];
486 $all_obj_buttons = [];
487 foreach ($actions as $action_id => $act) {
488 $signal = clone $action_signal;
489 $signal->addOption(Action::OPT_ACTIONID, $action_id);
490 $buttons[] = $f->button()->shy($act->getLabel(), $signal);
491 }
492
493 $buttons[] = $f->divider()->horizontal();
494 $buttons[] = $f->button()->shy($this->txt('datatable_multiactionmodal_listentry'), '#')->withOnClick($modal_signal);
495
496 return $f->dropdown()->standard($buttons)->withLabel($this->txt('datatable_multiaction_label'));
497 }
498
499 protected function getAsyncActionHandler(Component\Signal $action_signal): \Closure
500 {
501 return static function ($id) use ($action_signal): string {
502 return "
503 $(document).on('{$action_signal}', function(event, signal_data) {
504 il.UI.table.data.get('{$id}').doSingleAction(signal_data);
505 return false;
506 });";
507 };
508 }
509 protected function getMultiActionHandler(Component\Signal $action_signal): \Closure
510 {
511 return static function ($id) use ($action_signal): string {
512 return "
513 $(document).on('{$action_signal}', function(event, signal_data) {
514 il.UI.table.data.get('{$id}').doMultiAction(signal_data);
515 return false;
516 });";
517 };
518 }
519
520 protected function getSelectionHandler(Component\Signal $selection_signal): \Closure
521 {
522 return static function ($id) use ($selection_signal): string {
523 return "
524 $(document).on('{$selection_signal}', function(event, signal_data) {
525 il.UI.table.data.get('{$id}').selectAll(signal_data.options.select);
526 return false;
527 });
528 ";
529 };
530 }
531
532 protected function getActionRegistration(
533 string $action_id,
534 Action $action
535 ): \Closure {
536 $async = $action->isAsync() ? 'true' : 'false';
537 $url_builder_js = $action->getURLBuilderJS();
538 $tokens_js = $action->getURLBuilderTokensJS();
539
540 return static function ($id) use ($action_id, $async, $url_builder_js, $tokens_js): string {
541 return "
542 il.UI.table.data.get('{$id}').registerAction('{$action_id}', {$async}, {$url_builder_js}, {$tokens_js});
543 ";
544 };
545 }
546
547 public function renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer): string
548 {
549 $cell_tpl = $this->getTemplate("tpl.datacell.html", true, true);
550 $this->fillCells($component, $cell_tpl, $default_renderer);
551
552
553 return $cell_tpl->get();
554 }
555
556 public function renderOrderingRow(Component\Table\OrderingRow $component, RendererInterface $default_renderer): string
557 {
558 $cell_tpl = $this->getTemplate("tpl.orderingcell.html", true, true);
559 $this->fillCells($component, $cell_tpl, $default_renderer);
560
561
562 if ($component->isOrderingDisabled()) {
563 return $cell_tpl->get();
564 }
565
566 $namesource = new class () implements NameSource {
567 public function getNewName(): string
568 {
569 return '';
570 }
571 public function getNewDedicatedName(string $dedicated_name): string
572 {
573 return $dedicated_name;
574 }
575 };
576
577 $numeric_label = $this->txt("ui_table_order");
578 $input = $this->getUIFactory()->input()->field()->numeric($numeric_label)
579 ->withDedicatedName($component->getId())
580 ->withNameFrom($namesource)
581 ->withValue($component->getPosition() * 10);
582 $cell_tpl->setVariable('ORDER_INPUT', $default_renderer->render($input));
583
584 return $cell_tpl->get();
585 }
586
587
588 protected function fillCells(
590 Template $cell_tpl,
591 RendererInterface $default_renderer
592 ) {
593 $cols = $row->getColumns();
594 foreach ($cols as $col_id => $column) {
595 if ($column->isHighlighted()) {
596 $cell_tpl->touchBlock('highlighted');
597 }
598 $cell_tpl->setCurrentBlock('cell');
599 $cell_tpl->setVariable('COL_TYPE', strtolower($column->getType()));
600 $cell_content = $row->getCellContent($col_id);
601 if ($cell_content instanceof Component\Component) {
602 $cell_content = $default_renderer->render($cell_content);
603 }
604 $cell_tpl->setVariable('CELL_CONTENT', $cell_content);
605 $cell_tpl->setVariable('CELL_COL_TITLE', $row->getColumns()[$col_id]->getTitle());
606 $cell_tpl->parseCurrentBlock();
607 }
608
609 if ($row->tableHasMultiActions()) {
610 $cell_tpl->setVariable('ROW_ID', $row->getId());
611 }
612 if ($row->tableHasSingleActions()) {
613 $row_actions_dropdown = $this->getSingleActionsForRow(
614 $row->getId(),
615 $row->getActions()
616 );
617 $cell_tpl->setVariable('ACTION_CONTENT', $default_renderer->render($row_actions_dropdown));
618 }
619
620 if ($row instanceof Component\Table\OrderingRow) {
621 if (!$row->isOrderingDisabled()) {
622 $drag_handle = $this->getUIFactory()->symbol()->glyph()->dragHandle();
623 $drag_handle = $default_renderer->render($drag_handle);
624 $cell_tpl->setVariable('DRAG_HANDLE', $drag_handle);
625 }
626 }
627 }
628
632 protected function getSingleActionsForRow(string $row_id, array $actions): \ILIAS\UI\Component\Dropdown\Standard
633 {
634 $f = $this->getUIFactory();
635 $buttons = [];
636 foreach ($actions as $act) {
637 $act = $act->withRowId($row_id);
638 $target = $act->getTarget();
639 if ($target instanceof URI) {
640 $target = (string) $target;
641 }
642 $buttons[] = $f->button()->shy($act->getLabel(), $target);
643 }
644 return $f->dropdown()->standard($buttons);
645 }
646
647
648 public function renderOrderingTable(Component\Table\Ordering $component, RendererInterface $default_renderer): string
649 {
650 $tpl = $this->getTemplate("tpl.orderingtable.html", true, true);
651 $component = $this->registerActions($component);
652
653 [$component, $view_controls] = $component->applyViewControls();
654
655 $rows = $component->getDataBinding()->getRows(
656 $component->getRowBuilder(),
657 array_keys($component->getVisibleColumns()),
658 );
659
660
661 if (!$component->isOrderingDisabled()) {
662 $component = $component->withAdditionalOnLoadCode(
663 static fn($id): string => "il.UI.table.ordering.init('{$id}');"
664 );
665 }
666
667 // these column counts and index numbers are meant for aria attributes in the html tpl
668 $compensate_col_index = 1; // aria-colindex expects counting to start with 1, not 0
669 // for column with checkbox and/or drag handle before first data column which is numbered 1.
670 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
671 $compensate_col_index += 1; // checkbox & drag handle
672 }
673 if (!$component->isOrderingDisabled()) {
674 $compensate_col_index += 1; // Position input
675 }
676
677 $compensate_col_count = 0; // count starts with 1, only needs to be compensated for special columns
678 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
679 $compensate_col_count += 1; // checkbox & drag handle
680 }
681 if (!$component->isOrderingDisabled()) {
682 $compensate_col_count += 1; // Position input
683 }
684 if ($component->hasSingleActions()) { // adds column with action dropdown at the very end
685 $compensate_col_count += 1;
686 }
687
688 $tableid = $this->bindJavaScript($component) ?? $this->createId();
689
690 if (!$component->isOrderingDisabled()) {
691 $submit = $this->getUIFactory()->button()->standard($this->txt('sorting_save'), "")
692 ->withOnLoadCode(static fn($id) => "document.getElementById('$id').addEventListener('click',
693 function() {document.querySelector('#$tableid form.c-table-ordering__form').submit();return false;});");
694
695 $tpl->setVariable('FORM_BUTTONS', $default_renderer->render($submit));
696 $tpl->setVariable('POS_INPUT_TITLE', $this->txt('table_posinput_col_title'));
697
698 }
699
700 $tpl->setVariable('ID', $tableid);
701 $tpl->setVariable('TARGET_URL', $component->getTargetURL() ? $component->getTargetURL()->__toString() : '#');
702 $tpl->setVariable('TITLE', $component->getTitle());
703 $tpl->setVariable('COL_COUNT', (string) $component->getColumnCount() + $compensate_col_count);
704 $tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
705
706 $columns = $component->getVisibleColumns();
707 foreach ($columns as $col_id => $col) {
708 $col_title = $col->getTitle();
709 $tpl->setCurrentBlock('header_cell');
710 $tpl->setVariable('COL_INDEX', (string) $col->getIndex() + $compensate_col_index);
711 $tpl->setVariable('COL_TITLE', $col_title);
712 $tpl->setVariable('COL_TYPE', strtolower($col->getType()));
713 $tpl->parseCurrentBlock();
714 }
715
716 $rows = iterator_to_array($rows);
717 $r = [];
718 foreach ($rows as $idx => $row) {
719 $r[] = $row
720 ->withPosition($idx + 1)
721 ->withOrderingDisabled($component->isOrderingDisabled());
722 }
723
724 $this->renderActionsHeader($default_renderer, $component, $tpl, $compensate_col_count);
725 $this->appendTableRows($tpl, $r, $default_renderer);
726
727 if ($component->hasMultiActions()) {
728 $multi_actions = $component->getMultiActions();
729 $modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $tableid);
730 $multi_actions_dropdown = $this->buildMultiActionsDropdown(
731 $multi_actions,
732 $component->getMultiActionSignal(),
733 $modal->getShowSignal()
734 );
735
736 $tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
737 $tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
738 $tpl->setVariable('MULTI_ACTION_WARNING', $default_renderer->render($this->getUrlTooLongWarning()));
739 $tpl->setVariable('MULTI_ACTION_WARNING_BUTTON_CLOSE_LABEL', $this->txt('datatable_close_warning'));
740 }
741 if ($component->hasMultiActions() || !$component->isOrderingDisabled()) {
742 $multi_action_col_span = count($component->getVisibleColumns()) + $compensate_col_count;
743 $tpl->setVariable('MULTI_ACTION_SPAN', (string) $multi_action_col_span);
744 }
745
746 return $tpl->get();
747 }
748
752 public function registerResources(ResourceRegistry $registry): void
753 {
754 parent::registerResources($registry);
755 $registry->register('assets/js/table.min.js');
756 $registry->register('assets/js/modal.min.js');
757 }
758
760 {
761 $show = $component->getShowSignal();
762 $close = $component->getCloseSignal();
763 $toggle = $component->getToggleSignal();
764 $table_id = $component->getTableId();
765 return $component->withAdditionalOnLoadCode(
766 static fn($id): string =>
767 "$(document).on('$show', function() { il.UI.table.presentation.get('$table_id').expandRow('$id'); return false; });" .
768 "$(document).on('$close', function() { il.UI.table.presentation.get('$table_id').collapseRow('$id'); return false; });" .
769 "$(document).on('$toggle', function() { il.UI.table.presentation.get('$table_id').toggleRow('$id'); return false; });"
770 );
771 }
772}
$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:759
fillCells(Component\Table\DataRow $row, Template $cell_tpl, RendererInterface $default_renderer)
Definition: Renderer.php:588
registerActions(Component\Table\Table $component)
Definition: Renderer.php:379
renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:547
getAsyncActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:499
getMultiActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:509
renderOrderingTable(Component\Table\Ordering $component, RendererInterface $default_renderer)
Definition: Renderer.php:648
getActionRegistration(string $action_id, Action $action)
Definition: Renderer.php:532
buildMultiActionsAllObjectsModal(array $actions, string $table_id)
Definition: Renderer.php:440
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:475
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:363
getSingleActionsForRow(string $row_id, array $actions)
Definition: Renderer.php:632
renderActionsHeader(RendererInterface $default_renderer, Component\Table\Table $component, Template $tpl, int $compensate_col_count,)
Definition: Renderer.php:329
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:39
renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content)
Definition: Renderer.php:430
renderTableHeader(RendererInterface $default_renderer, Component\Table\Data $component, Template $tpl, ?Component\Signal $sortation_signal, int $compensate_col_index,)
Definition: Renderer.php:277
getSelectionHandler(Component\Signal $selection_signal)
Definition: Renderer.php:520
renderOrderingRow(Component\Table\OrderingRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:556
appendTableRows(Template $tpl, \Generator|array $rows, RendererInterface $default_renderer)
Definition: Renderer.php:414
renderDataTable(Component\Table\Data $component, RendererInterface $default_renderer)
Definition: Renderer.php:201
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:752
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'))