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
24use ILIAS\UI\Renderer as RendererInterface;
29use LogicException;
30
36{
37 public const MODE_ROLE = "group";
38
39 public function render(Component\Component $component, RendererInterface $default_renderer): string
40 {
41 if ($component instanceof Component\ViewControl\Mode) {
42 return $this->renderMode($component, $default_renderer);
43 }
44 if ($component instanceof Component\ViewControl\Section) {
45 return $this->renderSection($component, $default_renderer);
46 }
47 if ($component instanceof Component\ViewControl\Sortation) {
48 return $this->renderSortation($component, $default_renderer);
49 }
50 if ($component instanceof Component\ViewControl\Pagination) {
51 return $this->renderPagination($component, $default_renderer);
52 }
53 $this->cannotHandleComponent($component);
54 }
55
56 protected function renderMode(Component\ViewControl\Mode $component, RendererInterface $default_renderer): string
57 {
58 $f = $this->getUIFactory();
59
60 $tpl = $this->getTemplate("tpl.mode.html", true, true);
61
62 $activate_first_item = false;
63 $active = $component->getActive();
64 if ($active == "") {
65 $activate_first_item = true;
66 }
67
68 $tpl->setVariable("ARIA", $this->txt($component->getAriaLabel()));
69 $tpl->setVariable("ROLE", self::MODE_ROLE);
70 foreach ($component->getLabelledActions() as $label => $action) {
71 $tpl->setCurrentBlock("view_control");
72
73 $button = $f->button()->standard($label, $action);
74 if ($activate_first_item) {
75 $button = $button->withEngagedState(true);
76 $activate_first_item = false;
77 } elseif ($active == $label) {
78 $button = $button->withEngagedState(true);
79 } else {
80 $button = $button->withEngagedState(false);
81 }
82 $tpl->setVariable("BUTTON", $default_renderer->render($button));
83 $tpl->parseCurrentBlock();
84 }
85
86 return $tpl->get();
87 }
88
89 protected function renderSection(
90 Component\ViewControl\Section $component,
91 RendererInterface $default_renderer
92 ): string {
93 $tpl = $this->getTemplate("tpl.section.html", true, true);
94
95 // render middle button
96 $tpl->setVariable("BUTTON", $default_renderer->render($component->getSelectorButton()));
97
98 // previous button
99 $this->renderSectionButton($component->getPreviousActions(), $tpl, "prev");
100
101 // next button
102 $this->renderSectionButton($component->getNextActions(), $tpl, "next");
103
104 return $tpl->get();
105 }
106
107 protected function renderSectionButton(Component\Button\Button $component, Template $tpl, string $type): void
108 {
109 $uptype = strtoupper($type);
110
111 $action = $component->getAction();
112 $tpl->setVariable($uptype . "_ACTION", $action);
113 $label = ($type == "next")
114 ? $this->txt("next")
115 : $this->txt("previous");
116 $tpl->setVariable($uptype . "_LABEL", $label);
117 if ($component->isActive()) {
118 $tpl->setCurrentBlock($type . "_with_href");
119 $tpl->setVariable($uptype . "_HREF", $action);
120 $tpl->parseCurrentBlock();
121 } else {
122 $tpl->touchBlock($type . "_disabled");
123 }
124 $this->renderId($component, $tpl, $type . "_with_id", $uptype . "_ID");
125 }
126
127 protected function renderSortation(
128 Component\ViewControl\Sortation $component,
129 RendererInterface $default_renderer
130 ): string {
131 $f = $this->getUIFactory();
132
133 $tpl = $this->getTemplate("tpl.sortation.html", true, true);
134 $label_prefix = $component->getLabelPrefix() ?? $this->txt('vc_sort');
135
136 $component = $component->withResetSignals();
137 $triggeredSignals = $component->getTriggeredSignals();
138 if ($triggeredSignals) {
139 $internal_signal = $component->getSelectSignal();
140 $internal_signal->addOption('label_prefix', $label_prefix);
141 $signal = $triggeredSignals[0]->getSignal();
142 $component = $component
143 ->withAdditionalOnLoadCode(
144 fn($id) => "$(document).on('$internal_signal', function(event, signalData) {
145 il.UI.viewcontrol.sortation.get('$id').onInternalSelect(event, signalData, '$signal');
146 return false;
147 })"
148 );
149 }
150
151 $component = $component
152 ->withAdditionalOnLoadCode(
153 fn($id) => "il.UI.viewcontrol.sortation.init('$id');"
154 )
155 ->withAdditionalOnLoadCode(
156 fn($id) =>
157 "il.UI.dropdown.init(document.getElementById('{$id}'));"
158 );
159
160 $id = $this->bindJavaScript($component);
161 $tpl->setVariable("ID", $id);
162 $tpl->setVariable("ID_MENU", $id . '_ctrl');
163
164 $options = $component->getOptions();
165 $items = [];
166
167 $selected = $component->getSelected();
168 foreach ($options as $val => $label) {
169 $tpl->setCurrentBlock('option');
170
171 if ($val === $selected) {
172 $tpl->touchBlock('selected');
173 $tpl->setCurrentBlock('option');
174 }
175
176 if ($triggeredSignals) {
177 $shy = $f->button()->shy($label, $val)->withOnClick($internal_signal);
178 } else {
179 $url = $component->getTargetURL() ?? '';
180 $url .= (strpos($url, '?') === false) ? '?' : '&';
181 $url .= $component->getParameterName() . '=' . $val;
182 $shy = $f->button()->shy($label, $url);
183 }
184 $items[] = $shy;
185 $tpl->setVariable('OPTION', $default_renderer->render($shy));
186 $tpl->parseCurrentBlock();
187 }
188
189 $tpl->setVariable('LABEL', $label_prefix . ' ' . $options[$selected] . ' ');
190 $tpl->setVariable("ARIA_LABEL", $this->txt("sortation"));
191 return $tpl->get();
192 }
193
194 protected function renderPagination(
195 Component\ViewControl\Pagination $component,
196 RendererInterface $default_renderer
197 ): string {
198 $range = $this->getPaginationRange($component);
199
200 if ($component->getNumberOfPages() < 2) {
201 return '';
202 }
203
204 $tpl = $this->getTemplate("tpl.pagination.html", true, true);
205 $component = $component->withResetSignals();
206 $triggeredSignals = $component->getTriggeredSignals();
207 if ($triggeredSignals) {
208 $internal_signal = $component->getInternalSignal();
209 $signal = $triggeredSignals[0]->getSignal();
210 $component = $component
211 ->withAdditionalOnLoadCode(
212 fn($id) => "il.UI.viewcontrol.pagination.init('$id');"
213 )
214 ->withAdditionalOnLoadCode(
215 fn($id) => "$(document).on('$internal_signal', function(event, signalData) {
216 il.UI.viewcontrol.pagination.get('$id').onInternalSelect(event, signalData, '$signal');
217 return false;
218 })"
219 );
220 $id = $this->bindJavaScript($component);
221 $tpl->setVariable('ID', $id);
222 }
223
224 $chunk_options = array();
225 foreach ($range as $entry) {
226 $shy = $this->getPaginationShyButton($entry, $component);
227 if ($entry === $component->getCurrentPage()) {
228 $shy = $shy->withEngagedState(true);
229 }
230 $chunk_options[] = $shy;
231 }
232
233 if ($component->getDropdownAt() == null ||
234 $component->getDropdownAt() > $component->getNumberOfPages()) {
235 foreach ($chunk_options as $entry) {
236 $tpl->setCurrentBlock("entry");
237 $tpl->setVariable('BUTTON', $default_renderer->render($entry));
238 $tpl->parseCurrentBlock();
239 }
240 } else {
241 //if threshold is reached, render as dropdown
242 $f = $this->getUIFactory();
243
244 $dd_label_template = $component->getDropdownLabel();
245 if ($dd_label_template === $component->getDefaultDropdownLabel()) {
246 $dd_label_template = $this->txt($dd_label_template);
247 }
248 $dd_label = sprintf(
249 $dd_label_template,
250 $component->getCurrentPage() + 1,
251 $component->getNumberOfPages()
252 );
253
254 $dd = $f->dropdown()->standard($chunk_options)->withLabel($dd_label);
255 $tpl->setCurrentBlock("entry");
256 $tpl->setVariable('BUTTON', $default_renderer->render($dd));
257 $tpl->parseCurrentBlock();
258 }
259
260 if ($component->getMaxPaginationButtons()) {
261 $this->setPaginationFirstLast($component, $range, $default_renderer, $tpl);
262 }
263
264 $this->setPaginationBrowseControls($component, $default_renderer, $tpl);
265 return $tpl->get();
266 }
267
273 protected function getPaginationRange(Component\ViewControl\Pagination $component): array
274 {
275 if (!$component->getMaxPaginationButtons()) {
276 $start = 0;
277 $stop = max($component->getNumberOfPages() - 1, 0);
278 } else {
279 //current page should be in the middle, so start is half the amount of max entries:
280 $start = (int) ($component->getCurrentPage() - floor($component->getMaxPaginationButtons() / 2));
281 $start = max($start, 0); //0, if negative
282 //stop is (calculated) start plus number of entries:
283 $stop = $start + $component->getMaxPaginationButtons() - 1;
284 //if stop exceeds max pages, recalculate both:
285 if ($stop > $component->getNumberOfPages() - 1) {
286 $stop = max($component->getNumberOfPages() - 1, 0); //0, if negative
287 $start = $stop - $component->getMaxPaginationButtons();
288 $start = max($start, 0); //0, if negative
289 }
290 }
291 return range($start, $stop);
292 }
293
294 protected function getPaginationShyButton(
295 int $val,
296 Component\ViewControl\Pagination $component,
297 string $label = ''
298 ): Shy {
299 $f = $this->getUIFactory();
300
301 if ($label === '') {
302 $label = (string) ($val + 1);
303 }
304
305 if ($component->getTriggeredSignals()) {
306 $shy = $f->button()->shy($label, (string) $val)->withOnClick($component->getInternalSignal());
307 } else {
308 $url = $component->getTargetURL() ?? '';
309 if (strpos($url, '?') === false) {
310 $url .= '?' . $component->getParameterName() . '=' . $val;
311 } else {
312 $base = substr($url, 0, strpos($url, '?') + 1);
313 $query = parse_url($url, PHP_URL_QUERY);
314 parse_str($query, $params);
315 $params[$component->getParameterName()] = $val;
316 $url = $base . http_build_query($params);
317 }
318 $shy = $f->button()->shy($label, $url);
319 }
320 return $shy;
321 }
322
326 protected function setPaginationBrowseControls(
327 Component\ViewControl\Pagination $component,
328 RendererInterface $default_renderer,
329 Template $tpl
330 ): void {
331 $prev = max(0, $component->getCurrentPage() - 1);
332 $next = $component->getCurrentPage() + 1;
333
334 $f = $this->getUIFactory();
335
336 $back_btn = $f->button()->standard("", "");
337 $forward_btn = $f->button()->standard("", "");
338
339 if ($component->getTriggeredSignals()) {
340 $back_btn = $back_btn->withOnClick($component->getInternalSignal());
341 $forward_btn = $forward_btn->withOnClick($component->getInternalSignal());
342 } else {
343 $url = $component->getTargetURL() ?? '';
344 if (strpos($url, '?') === false) {
345 $url_prev = $url . '?' . $component->getParameterName() . '=' . $prev;
346 $url_next = $url . '?' . $component->getParameterName() . '=' . $next;
347 } else {
348 $base = substr($url, 0, strpos($url, '?') + 1);
349 $query = parse_url($url, PHP_URL_QUERY);
350 parse_str($query, $params);
351
352 $params[$component->getParameterName()] = $prev;
353 $url_prev = $base . http_build_query($params);
354 $params[$component->getParameterName()] = $next;
355 $url_next = $base . http_build_query($params);
356 }
357
358 $back_btn = $f->button()->standard("", $url_prev);
359 $forward_btn = $f->button()->standard("", $url_next);
360 }
361
362 if ($component->getCurrentPage() != 0) {
363 $back_glyph = $f->symbol()->glyph()->back();
364 $back_btn = $back_btn->withSymbol($back_glyph);
365 $tpl->setVariable('PREVIOUS', $default_renderer->render($back_btn));
366 }
367 if ($component->getCurrentPage() != $component->getNumberOfPages() - 1) {
368 $forward_glyph = $f->symbol()->glyph()->next();
369 $forward_btn = $forward_btn->withSymbol($forward_glyph);
370 $tpl->setVariable('NEXT', $default_renderer->render($forward_btn));
371 }
372 }
373
379 protected function setPaginationFirstLast(
380 Component\ViewControl\Pagination $component,
381 array $range,
382 RendererInterface $default_renderer,
383 Template $tpl
384 ): void {
385 if (!in_array(0, $range)) {
386 $shy = $this->getPaginationShyButton(0, $component);
387 $tpl->setVariable('FIRST', $default_renderer->render($shy));
388 }
389 $last = max($component->getNumberOfPages() - 1, 0);
390 if (!in_array($last, $range)) {
391 $shy = $this->getPaginationShyButton($component->getNumberOfPages() - 1, $component);
392 $tpl->setVariable('LAST', $default_renderer->render($shy));
393 }
394 }
395
399 public function registerResources(ResourceRegistry $registry): void
400 {
401 parent::registerResources($registry);
402 $registry->register('assets/js/viewcontrols.min.js');
403 $registry->register('assets/js/dropdown.js');
404 }
405
406 protected function renderId(
407 Component\JavaScriptBindable $component,
408 Template $tpl,
409 string $block,
410 string $template_var
411 ): void {
412 $id = $this->bindJavaScript($component);
413 if (!$id) {
414 $id = $this->createId();
415 }
416 $tpl->setCurrentBlock($block);
417 $tpl->setVariable($template_var, $id);
418 $tpl->parseCurrentBlock();
419 }
420}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
renderMode(Component\ViewControl\Mode $component, RendererInterface $default_renderer)
Definition: Renderer.php:56
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:399
setPaginationBrowseControls(Component\ViewControl\Pagination $component, RendererInterface $default_renderer, Template $tpl)
Add back/next-glyphs to the template for left/right browsing in pagination.
Definition: Renderer.php:326
renderPagination(Component\ViewControl\Pagination $component, RendererInterface $default_renderer)
Definition: Renderer.php:194
renderSortation(Component\ViewControl\Sortation $component, RendererInterface $default_renderer)
Definition: Renderer.php:127
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:39
getPaginationRange(Component\ViewControl\Pagination $component)
Get the range of pagination-buttons to show.
Definition: Renderer.php:273
renderSectionButton(Component\Button\Button $component, Template $tpl, string $type)
Definition: Renderer.php:107
renderSection(Component\ViewControl\Section $component, RendererInterface $default_renderer)
Definition: Renderer.php:89
getPaginationShyButton(int $val, Component\ViewControl\Pagination $component, string $label='')
Definition: Renderer.php:294
setPaginationFirstLast(Component\ViewControl\Pagination $component, array $range, RendererInterface $default_renderer, Template $tpl)
Add quick-access to first/last pages in pagination.
Definition: Renderer.php:379
renderId(Component\JavaScriptBindable $component, Template $tpl, string $block, string $template_var)
Definition: Renderer.php:406
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
return true
Interface to be extended by components that have the possibility to bind to Javascript.
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.
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
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Bulky.php:21
if(!file_exists('../ilias.ini.php'))
$url
Definition: shib_logout.php:68