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