ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
Renderer.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
29 use LogicException;
30 
36 {
37  public const MODE_ROLE = "group";
38 
39  public function render(Component\Component $component, RendererInterface $default_renderer): string
40  {
41  $this->checkComponent($component);
42 
43  if ($component instanceof Component\ViewControl\Mode) {
44  return $this->renderMode($component, $default_renderer);
45  }
46  if ($component instanceof Component\ViewControl\Section) {
47  return $this->renderSection($component, $default_renderer);
48  }
49  if ($component instanceof Component\ViewControl\Sortation) {
50  return $this->renderSortation($component, $default_renderer);
51  }
52  if ($component instanceof Component\ViewControl\Pagination) {
53  return $this->renderPagination($component, $default_renderer);
54  }
55  throw new LogicException("Component '{$component->getCanonicalName()}' isn't supported by this renderer.");
56  }
57 
58  protected function renderMode(Component\ViewControl\Mode $component, RendererInterface $default_renderer): string
59  {
60  $f = $this->getUIFactory();
61 
62  $tpl = $this->getTemplate("tpl.mode.html", true, true);
63 
64  $activate_first_item = false;
65  $active = $component->getActive();
66  if ($active == "") {
67  $activate_first_item = true;
68  }
69 
70  $tpl->setVariable("ARIA", $this->txt($component->getAriaLabel()));
71  $tpl->setVariable("ROLE", self::MODE_ROLE);
72  foreach ($component->getLabelledActions() as $label => $action) {
73  $tpl->setCurrentBlock("view_control");
74 
75  //At this point we don't have a specific text for the button aria label.
76  // component->getAriaLabel gets the main view control aria label.
77  $button = $f->button()->standard($label, $action)->withAriaLabel($label);
78  if ($activate_first_item) {
79  $button = $button->withEngagedState(true);
80  $activate_first_item = false;
81  } elseif ($active == $label) {
82  $button = $button->withEngagedState(true);
83  } else {
84  $button = $button->withEngagedState(false);
85  }
86  $tpl->setVariable("BUTTON", $default_renderer->render($button));
87  $tpl->parseCurrentBlock();
88  }
89 
90  return $tpl->get();
91  }
92 
93  protected function renderSection(
94  Component\ViewControl\Section $component,
95  RendererInterface $default_renderer
96  ): string {
97  $tpl = $this->getTemplate("tpl.section.html", true, true);
98 
99  // render middle button
100  $tpl->setVariable("BUTTON", $default_renderer->render($component->getSelectorButton()));
101 
102  // previous button
103  $this->renderSectionButton($component->getPreviousActions(), $tpl, "prev");
104 
105  // next button
106  $this->renderSectionButton($component->getNextActions(), $tpl, "next");
107 
108  return $tpl->get();
109  }
110 
111  protected function renderSectionButton(Component\Button\Button $component, Template $tpl, string $type): void
112  {
113  $uptype = strtoupper($type);
114 
115  $action = $component->getAction();
116  $tpl->setVariable($uptype . "_ACTION", $action);
117  $label = ($type == "next")
118  ? $this->txt("next")
119  : $this->txt("previous");
120  $tpl->setVariable($uptype . "_LABEL", $label);
121  if ($component->isActive()) {
122  $tpl->setCurrentBlock($type . "_with_href");
123  $tpl->setVariable($uptype . "_HREF", $action);
124  $tpl->parseCurrentBlock();
125  } else {
126  $tpl->touchBlock($type . "_disabled");
127  }
128  $this->renderId($component, $tpl, $type . "_with_id", $uptype . "_ID");
129  }
130 
131  protected function renderSortation(
132  Component\ViewControl\Sortation $component,
133  RendererInterface $default_renderer
134  ): string {
135  $f = $this->getUIFactory();
136 
137  $tpl = $this->getTemplate("tpl.sortation.html", true, true);
138 
139  $component = $component->withResetSignals();
140  $triggeredSignals = $component->getTriggeredSignals();
141  if ($triggeredSignals) {
142  $internal_signal = $component->getSelectSignal();
143  $signal = $triggeredSignals[0]->getSignal();
144 
145  $component = $component->withAdditionalOnLoadCode(fn($id) => "$(document).on('$internal_signal', function(event, signalData) {
146  il.UI.viewcontrol.sortation.onInternalSelect(event, signalData, '$signal', '$id');
147  return false;
148  })");
149  }
150 
151  $this->renderId($component, $tpl, "id", "ID");
152 
153  //setup entries
154  $options = $component->getOptions();
155  $init_label = $component->getLabel();
156  $items = array();
157  foreach ($options as $val => $label) {
158  if ($triggeredSignals) {
159  $shy = $f->button()->shy($label, $val)->withOnClick($internal_signal);
160  } else {
161  $url = $component->getTargetURL() ?? '';
162  $url .= (strpos($url, '?') === false) ? '?' : '&';
163  $url .= $component->getParameterName() . '=' . $val;
164  $shy = $f->button()->shy($label, $url);
165  }
166  $items[] = $shy;
167  }
168 
169  $dd = $f->dropdown()->standard($items)
170  ->withAriaLabel($init_label);
171 
172  $tpl->setVariable('SORTATION_DROPDOWN', $default_renderer->render($dd));
173  return $tpl->get();
174  }
175 
176  protected function renderPagination(
177  Component\ViewControl\Pagination $component,
178  RendererInterface $default_renderer
179  ): string {
180  $range = $this->getPaginationRange($component);
181 
182  if($component->getNumberOfPages() < 2) {
183  return '';
184  }
185 
186  $tpl = $this->getTemplate("tpl.pagination.html", true, true);
187  $component = $component->withResetSignals();
188  $triggeredSignals = $component->getTriggeredSignals();
189  if ($triggeredSignals) {
190  $internal_signal = $component->getInternalSignal();
191  $signal = $triggeredSignals[0]->getSignal();
192  $component = $component->withOnLoadCode(
193  fn($id) => "$(document).on('$internal_signal', function(event, signalData) {
194  il.UI.viewcontrol.pagination.onInternalSelect(event, signalData, '$signal', '$id');
195  return false;
196  })"
197  );
198  $id = $this->bindJavaScript($component);
199  $tpl->setVariable('ID', $id);
200  }
201 
202  $chunk_options = array();
203  foreach ($range as $entry) {
204  $shy = $this->getPaginationShyButton($entry, $component);
205  if ($entry === $component->getCurrentPage()) {
206  $shy = $shy->withEngagedState(true);
207  }
208  $chunk_options[] = $shy;
209  }
210 
211  if ($component->getDropdownAt() == null ||
212  $component->getDropdownAt() > $component->getNumberOfPages()) {
213  foreach ($chunk_options as $entry) {
214  $tpl->setCurrentBlock("entry");
215  $tpl->setVariable('BUTTON', $default_renderer->render($entry));
216  $tpl->parseCurrentBlock();
217  }
218  } else {
219  //if threshold is reached, render as dropdown
220  $f = $this->getUIFactory();
221 
222  $dd_label_template = $component->getDropdownLabel();
223  if ($dd_label_template === $component->getDefaultDropdownLabel()) {
224  $dd_label_template = $this->txt($dd_label_template);
225  }
226  $dd_label = sprintf(
227  $dd_label_template,
228  $component->getCurrentPage() + 1,
229  $component->getNumberOfPages()
230  );
231 
232  $dd = $f->dropdown()->standard($chunk_options)->withLabel($dd_label);
233  $tpl->setCurrentBlock("entry");
234  $tpl->setVariable('BUTTON', $default_renderer->render($dd));
235  $tpl->parseCurrentBlock();
236  }
237 
238  if ($component->getMaxPaginationButtons()) {
239  $this->setPaginationFirstLast($component, $range, $default_renderer, $tpl);
240  }
241 
242  $this->setPaginationBrowseControls($component, $default_renderer, $tpl);
243  return $tpl->get();
244  }
245 
251  protected function getPaginationRange(Component\ViewControl\Pagination $component): array
252  {
253  if (!$component->getMaxPaginationButtons()) {
254  $start = 0;
255  $stop = max($component->getNumberOfPages() - 1, 0);
256  } else {
257  //current page should be in the middle, so start is half the amount of max entries:
258  $start = (int) ($component->getCurrentPage() - floor($component->getMaxPaginationButtons() / 2));
259  $start = max($start, 0); //0, if negative
260  //stop is (calculated) start plus number of entries:
261  $stop = $start + $component->getMaxPaginationButtons() - 1;
262  //if stop exceeds max pages, recalculate both:
263  if ($stop > $component->getNumberOfPages() - 1) {
264  $stop = max($component->getNumberOfPages() - 1, 0); //0, if negative
265  $start = $stop - $component->getMaxPaginationButtons();
266  $start = max($start, 0); //0, if negative
267  }
268  }
269  return range($start, $stop);
270  }
271 
272  protected function getPaginationShyButton(
273  int $val,
274  Component\ViewControl\Pagination $component,
275  string $label = ''
276  ): Shy {
277  $f = $this->getUIFactory();
278 
279  if ($label === '') {
280  $label = (string) ($val + 1);
281  }
282 
283  if ($component->getTriggeredSignals()) {
284  $shy = $f->button()->shy($label, (string) $val)->withOnClick($component->getInternalSignal());
285  } else {
286  $url = $component->getTargetURL() ?? '';
287  if (strpos($url, '?') === false) {
288  $url .= '?' . $component->getParameterName() . '=' . $val;
289  } else {
290  $base = substr($url, 0, strpos($url, '?') + 1);
291  $query = parse_url($url, PHP_URL_QUERY);
292  parse_str($query, $params);
293  $params[$component->getParameterName()] = $val;
294  $url = $base . http_build_query($params);
295  }
296  $shy = $f->button()->shy($label, $url);
297  }
298  return $shy;
299  }
300 
304  protected function setPaginationBrowseControls(
305  Component\ViewControl\Pagination $component,
306  RendererInterface $default_renderer,
307  Template $tpl
308  ): void {
309  $prev = max(0, $component->getCurrentPage() - 1);
310  $next = $component->getCurrentPage() + 1;
311 
312  $f = $this->getUIFactory();
313 
314  if ($component->getTriggeredSignals()) {
315  $back = $f->symbol()->glyph()->back('')->withOnClick($component->getInternalSignal());
316  $forward = $f->symbol()->glyph()->next('')->withOnClick($component->getInternalSignal());
317  } else {
318  $url = $component->getTargetURL() ?? '';
319  if (strpos($url, '?') === false) {
320  $url_prev = $url . '?' . $component->getParameterName() . '=' . $prev;
321  $url_next = $url . '?' . $component->getParameterName() . '=' . $next;
322  } else {
323  $base = substr($url, 0, strpos($url, '?') + 1);
324  $query = parse_url($url, PHP_URL_QUERY);
325  parse_str($query, $params);
326 
327  $params[$component->getParameterName()] = $prev;
328  $url_prev = $base . http_build_query($params);
329  $params[$component->getParameterName()] = $next;
330  $url_next = $base . http_build_query($params);
331  }
332 
333  $back = $f->symbol()->glyph()->back($url_prev);
334  $forward = $f->symbol()->glyph()->next($url_next);
335  }
336 
337  if ($component->getCurrentPage() === 0) {
338  $back = $back->withUnavailableAction();
339  }
340  if ($component->getCurrentPage() >= $component->getNumberOfPages() - 1) {
341  $forward = $forward->withUnavailableAction();
342  }
343 
344  $tpl->setVariable('PREVIOUS', $default_renderer->render($back));
345  $tpl->setVariable('NEXT', $default_renderer->render($forward));
346  }
347 
353  protected function setPaginationFirstLast(
354  Component\ViewControl\Pagination $component,
355  array $range,
356  RendererInterface $default_renderer,
357  Template $tpl
358  ): void {
359  if (!in_array(0, $range)) {
360  $shy = $this->getPaginationShyButton(0, $component);
361  $tpl->setVariable('FIRST', $default_renderer->render($shy));
362  }
363  $last = max($component->getNumberOfPages() - 1, 0);
364  if (!in_array($last, $range)) {
365  $shy = $this->getPaginationShyButton($component->getNumberOfPages() - 1, $component);
366  $tpl->setVariable('LAST', $default_renderer->render($shy));
367  }
368  }
369 
373  public function registerResources(ResourceRegistry $registry): void
374  {
375  parent::registerResources($registry);
376  $registry->register('./src/UI/templates/js/ViewControl/sortation.js');
377  $registry->register('./src/UI/templates/js/ViewControl/pagination.js');
378  }
379 
380  protected function renderId(
381  Component\JavaScriptBindable $component,
382  Template $tpl,
383  string $block,
384  string $template_var
385  ): void {
386  $id = $this->bindJavaScript($component);
387  if (!$id) {
388  $id = $this->createId();
389  }
390  $tpl->setCurrentBlock($block);
391  $tpl->setVariable($template_var, $id);
392  $tpl->parseCurrentBlock();
393  }
394 
398  protected function getComponentInterfaceName(): array
399  {
400  return array(
401  Component\ViewControl\Mode::class,
402  Component\ViewControl\Section::class,
403  Component\ViewControl\Sortation::class,
404  Component\ViewControl\Pagination::class
405  );
406  }
407 }
renderSection(Component\ViewControl\Section $component, RendererInterface $default_renderer)
Definition: Renderer.php:93
Registry for resources required by rendered output like Javascript or CSS.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Factory.php:21
renderSortation(Component\ViewControl\Sortation $component, RendererInterface $default_renderer)
Definition: Renderer.php:131
checkComponent(Component $component)
Check if a given component fits this renderer and throw if that is not the case. ...
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:33
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
txt(string $id)
Get a text from the language file.
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:39
renderId(Component\JavaScriptBindable $component, Template $tpl, string $block, string $template_var)
Definition: Renderer.php:380
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:304
setCurrentBlock(string $name)
Set the block to work on.
getPaginationShyButton(int $val, Component\ViewControl\Pagination $component, string $label='')
Definition: Renderer.php:272
setVariable(string $name, $value)
Set a variable in the current block.
getPaginationRange(Component\ViewControl\Pagination $component)
Get the range of pagination-buttons to show.
Definition: Renderer.php:251
renderMode(Component\ViewControl\Mode $component, RendererInterface $default_renderer)
Definition: Renderer.php:58
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
$url
Definition: ltiregstart.php:35
register(string $name)
Add a dependency.
setPaginationFirstLast(Component\ViewControl\Pagination $component, array $range, RendererInterface $default_renderer, Template $tpl)
Add quick-access to first/last pages in pagination.
Definition: Renderer.php:353
parseCurrentBlock()
Parse the block that is currently worked on.
renderPagination(Component\ViewControl\Pagination $component, RendererInterface $default_renderer)
Definition: Renderer.php:176
touchBlock(string $name)
Touch a block without working further on it.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:373
renderSectionButton(Component\Button\Button $component, Template $tpl, string $type)
Definition: Renderer.php:111
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.