ILIAS  release_8 Revision v8.24
Renderer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
25use ILIAS\UI\Renderer as RendererInterface;
32
34{
38 public function render(Component\Component $component, RendererInterface $default_renderer): string
39 {
40 $this->checkComponent($component);
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 return $this->renderDataRow($component, $default_renderer);
52 }
53 throw new \LogicException(self::class . " cannot render component '" . get_class($component) . "'.");
54 }
55
56 protected function renderPresentationTable(
57 Component\Table\Presentation $component,
58 RendererInterface $default_renderer
59 ): string {
60 $tpl = $this->getTemplate("tpl.presentationtable.html", true, true);
61 $tpl->setVariable("TITLE", $component->getTitle());
62
63 $vcs = $component->getViewControls();
64 if ($vcs) {
65 $tpl->touchBlock("viewcontrols");
66 foreach ($vcs as $vc) {
67 $tpl->setCurrentBlock("vc");
68 $tpl->setVariable("VC", $default_renderer->render($vc));
69 $tpl->parseCurrentBlock();
70 }
71 }
72
73 $component = $component->withAdditionalOnLoadCode(
74 static fn ($id) => "il.UI.table.presentation.init('{$id}');"
75 );
76 $id = $this->bindJavaScript($component);
77 $tpl->setVariable("ID", $id);
78
79 $row_mapping = $component->getRowMapping();
80 $data = $component->getData();
81 $component_id = $id;
82
83 foreach ($data as $record) {
84 $row = $row_mapping(
85 new PresentationRow($component->getSignalGenerator(), $component_id),
86 $record,
87 $this->getUIFactory(),
88 $component->getEnvironment()
89 );
90
91 $tpl->setCurrentBlock("row");
92 $tpl->setVariable("ROW", $default_renderer->render($row));
93 $tpl->parseCurrentBlock();
94 }
95
96 return $tpl->get();
97 }
98
99 protected function renderPresentationRow(
101 RendererInterface $default_renderer
102 ): string {
103 $f = $this->getUIFactory();
104 $tpl = $this->getTemplate("tpl.presentationrow.html", true, true);
105
106 $component = $this->registerSignals($component->withResetSignals());
107 $sig_show = $component->getShowSignal();
108 $sig_hide = $component->getCloseSignal();
109 $sig_toggle = $component->getToggleSignal();
110 $id = $this->bindJavaScript($component);
111
112 $expander = $f->symbol()->glyph()->expand("#")
113 ->withOnClick($sig_show);
114 $collapser = $f->symbol()->glyph()->collapse("#")
115 ->withOnClick($sig_hide);
116 $shy_expander = $f->button()->shy($this->txt("presentation_table_more"), "#")
117 ->withOnClick($sig_show);
118
119 $tpl->setVariable("ID", $id);
120 $tpl->setVariable("EXPANDER", $default_renderer->render($expander));
121 $tpl->setVariable("COLLAPSER", $default_renderer->render($collapser));
122 $tpl->setVariable("SHY_EXPANDER", $default_renderer->render($shy_expander));
123
124 $tpl->setVariable("HEADLINE", $component->getHeadline());
125 $tpl->setVariable("TOGGLE_SIGNAL", $sig_toggle);
126 $subheadline = $component->getSubheadline();
127 if ($subheadline) {
128 $tpl->setVariable("SUBHEADLINE", $subheadline);
129 }
130
131 foreach ($component->getImportantFields() as $label => $value) {
132 $tpl->setCurrentBlock("important_field");
133 if (is_string($label)) {
134 $tpl->setVariable("IMPORTANT_FIELD_LABEL", $label);
135 }
136 $tpl->setVariable("IMPORTANT_FIELD_VALUE", $value);
137 $tpl->parseCurrentBlock();
138 }
139
140 $tpl->setVariable("DESCLIST", $default_renderer->render($component->getContent()));
141
142 $further_fields_headline = $component->getFurtherFieldsHeadline();
143 $further_fields = $component->getFurtherFields();
144
145 if (count($further_fields) > 0) {
146 $tpl->touchBlock("has_further_fields");
147
148 if ($further_fields_headline) {
149 $tpl->setVariable("FURTHER_FIELDS_HEADLINE", $further_fields_headline);
150 }
151
152 foreach ($further_fields as $label => $value) {
153 $tpl->setCurrentBlock("further_field");
154 if (is_string($label)) {
155 $tpl->setVariable("FIELD_LABEL", $label);
156 }
157 $tpl->setVariable("FIELD_VALUE", $value);
158 $tpl->parseCurrentBlock();
159 }
160 }
161
162 $action = $component->getAction();
163 if (!is_null($action)) {
164 $tpl->setCurrentBlock("button");
165 $tpl->setVariable("BUTTON", $default_renderer->render($action));
166 $tpl->parseCurrentBlock();
167 }
168
169 return $tpl->get();
170 }
171
172 public function renderDataTable(Component\Table\Data $component, RendererInterface $default_renderer): string
173 {
174 $tpl = $this->getTemplate("tpl.datatable.html", true, true);
175
176 $opt_action_id = Action::OPT_ACTIONID;
177 $opt_row_id = Action::OPT_ROWID;
178 $component = $component
179 ->withAdditionalOnLoadCode(
180 static fn ($id): string =>
181 "il.UI.table.data.init('{$id}','{$opt_action_id}','{$opt_row_id}');"
182 )
183 ->withAdditionalOnLoadCode($this->getAsyncActionHandler($component->getAsyncActionSignal()))
184 ->withAdditionalOnLoadCode($this->getMultiActionHandler($component->getMultiActionSignal()))
185 ->withAdditionalOnLoadCode($this->getSelectionHandler($component->getSelectionSignal()));
186
187 $actions = [];
188 foreach ($component->getAllActions() as $action_id => $action) {
189 $component = $component->withAdditionalOnLoadCode($this->getActionRegistration((string) $action_id, $action));
190 if ($action->isAsync()) {
191 $signal = clone $component->getAsyncActionSignal();
192 $signal->addOption(Action::OPT_ACTIONID, $action_id);
193 $action = $action->withSignalTarget($signal);
194 }
195 $actions[$action_id] = $action;
196 }
197 $component = $component->withActions($actions);
198
199 if ($component->hasMultiActions()) {
200 $component = $component->withAdditionalOnLoadCode(
201 static fn ($id): string => "il.UI.table.data.get('{$id}').selectAll(false);"
202 );
203 }
204
205 [$component, $view_controls] = $component->applyViewControls(
206 $component->getFilter() ?? [],
207 $component->getAdditionalParameters()
208 );
209
210 $tpl->setVariable('VIEW_CONTROLS', $default_renderer->render($view_controls));
211
212 $rows = $component->getDataRetrieval()->getRows(
213 $component->getRowBuilder(),
214 array_keys($component->getVisibleColumns()),
215 $component->getRange(),
216 $component->getOrder(),
217 $component->getFilter(),
218 $component->getAdditionalParameters()
219 );
220
221 $id = $this->bindJavaScript($component);
222 $tpl->setVariable('ID', $id);
223 $tpl->setVariable('TITLE', $component->getTitle());
224 $tpl->setVariable('COL_COUNT', (string) $component->getColumnCount());
225
226 $sortation_signal = null;
227 // if the generator is empty, and thus invalid, we render an empty row.
228 if (!$rows->valid()) {
229 $this->renderFullWidthDataCell($component, $tpl, $this->txt('ui_table_no_records'));
230 } else {
231 $this->appendTableRows($tpl, $rows, $default_renderer);
232
233 if ($component->hasMultiActions()) {
234 $multi_actions = $component->getMultiActions();
235 $modal = $this->buildMultiActionsAllObjectsModal($multi_actions, $id);
236 $multi_actions_dropdown = $this->buildMultiActionsDropdown(
237 $multi_actions,
238 $component->getMultiActionSignal(),
239 $modal->getShowSignal()
240 );
241 $tpl->setVariable('MULTI_ACTION_TRIGGERER', $default_renderer->render($multi_actions_dropdown));
242 $tpl->setVariable('MULTI_ACTION_ALL_MODAL', $default_renderer->render($modal));
243 }
244
245 $sortation_signal = null;
246 $sortation_view_control = array_filter(
247 $view_controls->getInputs(),
248 static fn ($i): bool => $i instanceof Component\Input\ViewControl\Sortation
249 );
250 if($sortation_view_control) {
251 $sortation_signal = array_shift($sortation_view_control)->getInternalSignal();
252 $sortation_signal->addOption('parent_container', $id);
253 }
254 }
255
256 $this->renderTableHeader($default_renderer, $component, $tpl, $sortation_signal);
257 return $tpl->get();
258 }
259
260 protected function renderTableHeader(
261 RendererInterface $default_renderer,
262 Component\Table\Data $component,
264 ?Component\Signal $sortation_signal
265 ): void {
266 $order = $component->getOrder();
267 $glyph_factory = $this->getUIFactory()->symbol()->glyph();
268 $sort_col = key($order->get());
269 $sort_direction = current($order->get());
270 $columns = $component->getVisibleColumns();
271
272 foreach ($columns as $col_id => $col) {
273 $param_sort_direction = Order::ASC;
274 $col_title = $col->getTitle();
275 if ($col_id === $sort_col) {
276 if ($sort_direction === Order::ASC) {
277 $sortation = $this->txt('order_option_generic_ascending');
278 $sortation_glyph = $glyph_factory->sortAscending("#");
279 $param_sort_direction = Order::DESC;
280 }
281 if ($sort_direction === Order::DESC) {
282 $sortation = $this->txt('order_option_generic_descending');
283 $sortation_glyph = $glyph_factory->sortDescending("#");
284 }
285 }
286
287 $tpl->setCurrentBlock('header_cell');
288 $tpl->setVariable('COL_INDEX', (string) $col->getIndex());
289
290 if ($col->isSortable() && ! is_null($sortation_signal)) {
291 $sort_signal = clone $sortation_signal;
292 $sort_signal->addOption('value', "$col_id:$param_sort_direction");
293 $col_title = $default_renderer->render(
294 $this->getUIFactory()->button()->shy($col_title, $sort_signal)
295 );
296
297 if ($col_id === $sort_col) {
298 $sortation_glyph = $default_renderer->render($sortation_glyph->withOnClick($sort_signal));
299 $tpl->setVariable('COL_SORTATION', $sortation);
300 $tpl->setVariable('COL_SORTATION_GLYPH', $sortation_glyph);
301 }
302 }
303
304 $tpl->setVariable('COL_TITLE', $col_title);
305 $tpl->setVariable('COL_TYPE', strtolower($col->getType()));
306 $tpl->parseCurrentBlock();
307 }
308
309 if ($component->hasSingleActions()) {
310 $tpl->setVariable('COL_INDEX_ACTION', (string) count($columns));
311 $tpl->setVariable('COL_TITLE_ACTION', $this->txt('actions'));
312
313 }
314 if ($component->hasMultiActions()) {
315 $signal = $component->getSelectionSignal();
316 $sig_all = clone $signal;
317 $sig_all->addOption('select', true);
318 $select_all = $glyph_factory->add()->withOnClick($sig_all);
319 $signal->addOption('select', false);
320 $select_none = $glyph_factory->close()->withOnClick($signal);
321 $tpl->setVariable('SELECTION_CONTROL_SELECT', $default_renderer->render($select_all));
322 $tpl->setVariable('SELECTION_CONTROL_DESELECT', $default_renderer->render($select_none));
323 }
324 }
325
330 protected function renderFullWidthDataCell(Component\Table\Data $component, Template $tpl, string $content): void
331 {
332 $cell_tpl = $this->getTemplate('tpl.datacell.html', true, true);
333 $cell_tpl->setCurrentBlock('cell');
334 $cell_tpl->setVariable('CELL_CONTENT', $content);
335 $cell_tpl->setVariable('COL_SPAN', count($component->getVisibleColumns()));
336 $cell_tpl->setVariable('COL_TYPE', 'full-width');
337 $cell_tpl->setVariable('COL_INDEX', '1');
338 $cell_tpl->parseCurrentBlock();
339
340 $tpl->setCurrentBlock('row');
341 $tpl->setVariable('ALTERNATION', 'even');
342 $tpl->setVariable('CELLS', $cell_tpl->get());
343 $tpl->parseCurrentBlock();
344 }
345
346 protected function appendTableRows(
348 \Generator $rows,
349 RendererInterface $default_renderer
350 ): void {
351 $alternate = 'even';
352 foreach ($rows as $row) {
353 $row_contents = $default_renderer->render($row);
354 $alternate = ($alternate === 'odd') ? 'even' : 'odd';
355 $tpl->setCurrentBlock('row');
356 $tpl->setVariable('ALTERNATION', $alternate);
357 $tpl->setVariable('CELLS', $row_contents);
358 $tpl->parseCurrentBlock();
359 }
360 }
361
362 protected function renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content): void
363 {
364 $row_tpl = $this->getTemplate('tpl.presentationrow_empty.html', true, true);
365 $row_tpl->setVariable('CONTENT', $content);
366 $tpl->setVariable('ROW', $row_tpl->get());
367 }
368
373 array $actions,
374 string $table_id
375 ): \ILIAS\UI\Component\Modal\RoundTrip {
376 $f = $this->getUIFactory();
377
378 $msg = $f->messageBox()->confirmation($this->txt('datatable_multiactionmodal_msg'));
379
380 $select = $f->input()->field()->select(
381 $this->txt('datatable_multiactionmodal_actionlabel'),
382 array_map(
383 static fn ($action): string => $action->getLabel(),
384 $actions
385 ),
386 ""
387 );
388 $submit = $f->button()->primary($this->txt('datatable_multiactionmodal_buttonlabel'), '')
389 ->withOnLoadCode(
390 static fn ($id): string => "$('#{$id}').click(function() { il.UI.table.data.get('{$table_id}').doActionForAll(this); return false; });"
391 );
392 $modal = $f->modal()
393 ->roundtrip($this->txt('datatable_multiactionmodal_title'), [$msg, $select])
394 ->withActionButtons([$submit]);
395 return $modal;
396 }
397
401 protected function buildMultiActionsDropdown(
402 array $actions,
403 Component\Signal $action_signal,
404 Component\Signal $modal_signal
406 if ($actions === []) {
407 return null;
408 }
409 $f = $this->getUIFactory();
410 $glyph = $f->symbol()->glyph()->bulletlist();
411 $buttons = [];
412 $all_obj_buttons = [];
413 foreach ($actions as $action_id => $act) {
414 $signal = clone $action_signal;
415 $signal->addOption(Action::OPT_ACTIONID, $action_id);
416 $buttons[] = $f->button()->shy($act->getLabel(), $signal);
417 }
418
419 $buttons[] = $f->divider()->horizontal();
420 $buttons[] = $f->button()->shy($this->txt('datatable_multiactionmodal_listentry'), '#')->withOnClick($modal_signal);
421
422 return $f->dropdown()->standard($buttons);
423 }
424
425 protected function getAsyncActionHandler(Component\Signal $action_signal): \Closure
426 {
427 return static function ($id) use ($action_signal): string {
428 return "
429 $(document).on('{$action_signal}', function(event, signal_data) {
430 il.UI.table.data.get('{$id}').doSingleAction(signal_data);
431 return false;
432 });";
433 };
434 }
435 protected function getMultiActionHandler(Component\Signal $action_signal): \Closure
436 {
437 return static function ($id) use ($action_signal): string {
438 return "
439 $(document).on('{$action_signal}', function(event, signal_data) {
440 il.UI.table.data.get('{$id}').doMultiAction(signal_data);
441 return false;
442 });";
443 };
444 }
445
446 protected function getSelectionHandler(Component\Signal $selection_signal): \Closure
447 {
448 return static function ($id) use ($selection_signal): string {
449 return "
450 $(document).on('{$selection_signal}', function(event, signal_data) {
451 il.UI.table.data.get('{$id}').selectAll(signal_data.options.select);
452 return false;
453 });
454 ";
455 };
456 }
457
458 protected function getActionRegistration(
459 string $action_id,
460 Action $action
461 ): \Closure {
462 $async = $action->isAsync() ? 'true' : 'false';
463 $url_builder_js = $action->getURLBuilderJS();
464 $tokens_js = $action->getURLBuilderTokensJS();
465
466 return static function ($id) use ($action_id, $async, $url_builder_js, $tokens_js): string {
467 return "
468 il.UI.table.data.get('{$id}').registerAction('{$action_id}', {$async}, {$url_builder_js}, {$tokens_js});
469 ";
470 };
471 }
472
473 public function renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer): string
474 {
475 $cell_tpl = $this->getTemplate("tpl.datacell.html", true, true);
476 $cols = $component->getColumns();
477
478 foreach ($cols as $col_id => $column) {
479 if ($column->isHighlighted()) {
480 $cell_tpl->touchBlock('highlighted');
481 }
482 $cell_tpl->setCurrentBlock('cell');
483 $cell_tpl->setVariable('COL_TYPE', strtolower($column->getType()));
484 $cell_tpl->setVariable('COL_INDEX', $column->getIndex());
485 $cell_content = $component->getCellContent($col_id);
486 if ($cell_content instanceof Component\Component) {
487 $cell_content = $default_renderer->render($cell_content);
488 }
489 $cell_tpl->setVariable('CELL_CONTENT', $cell_content);
490 $cell_tpl->setVariable('CELL_COL_TITLE', $component->getColumns()[$col_id]->getTitle());
491 $cell_tpl->parseCurrentBlock();
492 }
493
494 if ($component->tableHasMultiActions()) {
495 $cell_tpl->setVariable('ROW_ID', $component->getId());
496 }
497 if ($component->tableHasSingleActions()) {
498 $row_actions_dropdown = $this->getSingleActionsForRow(
499 $component->getId(),
500 $component->getActions()
501 );
502 $cell_tpl->setVariable('ACTION_CONTENT', $default_renderer->render($row_actions_dropdown));
503 }
504
505 return $cell_tpl->get();
506 }
507
511 protected function getSingleActionsForRow(string $row_id, array $actions): \ILIAS\UI\Component\Dropdown\Standard
512 {
513 $f = $this->getUIFactory();
514 $buttons = [];
515 foreach ($actions as $act) {
516 $act = $act->withRowId($row_id);
517 $target = $act->getTarget();
518 if ($target instanceof URI) {
519 $target = (string) $target;
520 }
521 $buttons[] = $f->button()->shy($act->getLabel(), $target);
522 }
523 return $f->dropdown()->standard($buttons);
524 }
525
529 public function registerResources(ResourceRegistry $registry): void
530 {
531 parent::registerResources($registry);
532 $registry->register('./src/UI/templates/js/Table/dist/table.min.js');
533 $registry->register('./src/UI/templates/js/Modal/modal.js');
534 }
535
537 {
538 $show = $component->getShowSignal();
539 $close = $component->getCloseSignal();
540 $toggle = $component->getToggleSignal();
541 $table_id = $component->getTableId();
542 return $component->withAdditionalOnLoadCode(
543 static fn ($id): string =>
544 "$(document).on('$show', function() { il.UI.table.presentation.get('$table_id').expandRow('$id'); return false; });" .
545 "$(document).on('$close', function() { il.UI.table.presentation.get('$table_id').collapseRow('$id'); return false; });" .
546 "$(document).on('$toggle', function() { il.UI.table.presentation.get('$table_id').toggleRow('$id'); return false; });"
547 );
548 }
549
553 protected function getComponentInterfaceName(): array
554 {
555 return [
556 Component\Table\PresentationRow::class,
557 Component\Table\Presentation::class,
558 Component\Table\Data::class,
559 Component\Table\DataRow::class
560 ];
561 }
562}
$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:13
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
registerSignals(Component\Table\PresentationRow $component)
Definition: Renderer.php:536
renderDataRow(Component\Table\DataRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:473
getAsyncActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:425
getComponentInterfaceName()
Get the name of the component-interface this renderer is supposed to render.ATTENTION: Fully qualifie...
Definition: Renderer.php:553
getMultiActionHandler(Component\Signal $action_signal)
Definition: Renderer.php:435
getActionRegistration(string $action_id, Action $action)
Definition: Renderer.php:458
buildMultiActionsAllObjectsModal(array $actions, string $table_id)
Definition: Renderer.php:372
renderPresentationRow(Component\Table\PresentationRow $component, RendererInterface $default_renderer)
Definition: Renderer.php:99
renderPresentationTable(Component\Table\Presentation $component, RendererInterface $default_renderer)
Definition: Renderer.php:56
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:330
getSingleActionsForRow(string $row_id, array $actions)
Definition: Renderer.php:511
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:38
renderTableHeader(RendererInterface $default_renderer, Component\Table\Data $component, Template $tpl, ?Component\Signal $sortation_signal)
Definition: Renderer.php:260
renderEmptyPresentationRow(Template $tpl, RendererInterface $default_renderer, string $content)
Definition: Renderer.php:362
getSelectionHandler(Component\Signal $selection_signal)
Definition: Renderer.php:446
appendTableRows(Template $tpl, \Generator $rows, RendererInterface $default_renderer)
Definition: Renderer.php:346
renderDataTable(Component\Table\Data $component, RendererInterface $default_renderer)
Definition: Renderer.php:172
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:529
buildMultiActionsDropdown(array $actions, Component\Signal $action_signal, Component\Signal $modal_signal)
Definition: Renderer.php:401
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
checkComponent(Component $component)
Check if a given component fits this renderer and throw \LogicError if that is not the case.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
return true
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
This describes commonalities between all types of Dropdowns.
Definition: Dropdown.php:35
This describes a Sortation View Control.
Definition: Sortation.php:29
This is a legacy support of Component\Input\ViewControl\ViewControl that has been moved to Component\...
Definition: ViewControl.php:32
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.
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
An entity that renders components to a string output.
Definition: Renderer.php:31
if($DIC->http() ->request() ->getMethod()=="GET" &&isset($DIC->http() ->request() ->getQueryParams()['tex'])) $tpl
Definition: latex.php:41
$i
Definition: metadata.php:41
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ChatMainBarProvider \MainMenu\Provider.
Class Factory.
$cols
Definition: xhr_table.php:11
$rows
Definition: xhr_table.php:10