ILIAS  trunk Revision v11.0_alpha-1731-gff9cd7e2bd3
All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
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  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  if ($component->getTriggeredSignals()) {
337  $back = $f->symbol()->glyph()->back('')->withOnClick($component->getInternalSignal());
338  $forward = $f->symbol()->glyph()->next('')->withOnClick($component->getInternalSignal());
339  } else {
340  $url = $component->getTargetURL() ?? '';
341  if (strpos($url, '?') === false) {
342  $url_prev = $url . '?' . $component->getParameterName() . '=' . $prev;
343  $url_next = $url . '?' . $component->getParameterName() . '=' . $next;
344  } else {
345  $base = substr($url, 0, strpos($url, '?') + 1);
346  $query = parse_url($url, PHP_URL_QUERY);
347  parse_str($query, $params);
348 
349  $params[$component->getParameterName()] = $prev;
350  $url_prev = $base . http_build_query($params);
351  $params[$component->getParameterName()] = $next;
352  $url_next = $base . http_build_query($params);
353  }
354 
355  $back = $f->symbol()->glyph()->back($url_prev);
356  $forward = $f->symbol()->glyph()->next($url_next);
357  }
358 
359  if ($component->getCurrentPage() === 0) {
360  $back = $back->withUnavailableAction();
361  }
362  if ($component->getCurrentPage() >= $component->getNumberOfPages() - 1) {
363  $forward = $forward->withUnavailableAction();
364  }
365 
366  $tpl->setVariable('PREVIOUS', $default_renderer->render($back));
367  $tpl->setVariable('NEXT', $default_renderer->render($forward));
368  }
369 
375  protected function setPaginationFirstLast(
376  Component\ViewControl\Pagination $component,
377  array $range,
378  RendererInterface $default_renderer,
379  Template $tpl
380  ): void {
381  if (!in_array(0, $range)) {
382  $shy = $this->getPaginationShyButton(0, $component);
383  $tpl->setVariable('FIRST', $default_renderer->render($shy));
384  }
385  $last = max($component->getNumberOfPages() - 1, 0);
386  if (!in_array($last, $range)) {
387  $shy = $this->getPaginationShyButton($component->getNumberOfPages() - 1, $component);
388  $tpl->setVariable('LAST', $default_renderer->render($shy));
389  }
390  }
391 
395  public function registerResources(ResourceRegistry $registry): void
396  {
397  parent::registerResources($registry);
398  $registry->register('assets/js/viewcontrols.min.js');
399  $registry->register('assets/js/dropdown.js');
400  }
401 
402  protected function renderId(
403  Component\JavaScriptBindable $component,
404  Template $tpl,
405  string $block,
406  string $template_var
407  ): void {
408  $id = $this->bindJavaScript($component);
409  if (!$id) {
410  $id = $this->createId();
411  }
412  $tpl->setCurrentBlock($block);
413  $tpl->setVariable($template_var, $id);
414  $tpl->parseCurrentBlock();
415  }
416 }
renderSection(Component\ViewControl\Section $component, RendererInterface $default_renderer)
Definition: Renderer.php:89
Registry for resources required by rendered output like Javascript or CSS.
renderSortation(Component\ViewControl\Sortation $component, RendererInterface $default_renderer)
Definition: Renderer.php:127
if(! $DIC->user() ->getId()||!ilLTIConsumerAccess::hasCustomProviderCreationAccess()) $params
Definition: ltiregstart.php:31
txt(string $id)
Get a text from the language file.
$url
Definition: shib_logout.php:66
renderId(Component\JavaScriptBindable $component, Template $tpl, string $block, string $template_var)
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:326
setCurrentBlock(string $name)
Set the block to work on.
getPaginationShyButton(int $val, Component\ViewControl\Pagination $component, string $label='')
Definition: Renderer.php:294
setVariable(string $name, $value)
Set a variable in the current block.
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
getPaginationRange(Component\ViewControl\Pagination $component)
Get the range of pagination-buttons to show.
Definition: Renderer.php:273
renderMode(Component\ViewControl\Mode $component, RendererInterface $default_renderer)
Definition: Renderer.php:56
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:39
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:375
parseCurrentBlock()
Parse the block that is currently worked on.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
renderPagination(Component\ViewControl\Pagination $component, RendererInterface $default_renderer)
Definition: Renderer.php:194
touchBlock(string $name)
Touch a block without working further on it.
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:395
renderSectionButton(Component\Button\Button $component, Template $tpl, string $type)
Definition: Renderer.php:107
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.