ILIAS  release_8 Revision v8.23
Renderer.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
34 use ILIAS\Data\URI;
36 use LogicException;
37 
39 {
40  public const BLOCK_MAINBAR_ENTRIES = 'trigger_item';
41  public const BLOCK_MAINBAR_TOOLS = 'tool_trigger_item';
42  public const BLOCK_METABAR_ENTRIES = 'meta_element';
43 
44  private array $trigger_signals = [];
45 
49  public function render(Component\Component $component, RendererInterface $default_renderer): string
50  {
51  $this->checkComponent($component);
52 
53  if ($component instanceof MainBar) {
54  return $this->renderMainbar($component, $default_renderer);
55  }
56  if ($component instanceof MetaBar) {
57  return $this->renderMetabar($component, $default_renderer);
58  }
59  if ($component instanceof Footer) {
60  return $this->renderFooter($component, $default_renderer);
61  }
62  if ($component instanceof ModeInfo) {
63  return $this->renderModeInfo($component, $default_renderer);
64  }
65  if ($component instanceof Component\MainControls\SystemInfo) {
66  return $this->renderSystemInfo($component, $default_renderer);
67  }
68  throw new LogicException("Cannot render: " . get_class($component));
69  }
70 
71  protected function calculateMainBarTreePosition($pos, $slate)
72  {
73  if (!$slate instanceof Slate && !$slate instanceof MainBar) {
74  return $slate;
75  }
76  return $slate
77  ->withMainBarTreePosition($pos)
78  ->withMappedSubNodes(
79  function ($num, $slate, $is_tool = false) use ($pos) {
80  if ($is_tool) {
81  $pos = 'T';
82  }
83  return $this->calculateMainBarTreePosition("$pos:$num", $slate);
84  }
85  );
86  }
87 
88  protected function renderToolEntry(
89  string $entry_id,
90  string $mb_id,
91  MainBar $component,
93  RendererInterface $default_renderer
94  ): string {
95  $hidden = $component->getInitiallyHiddenToolIds();
96  $close_buttons = $component->getCloseButtons();
97 
98  $is_removeable = array_key_exists($entry_id, $close_buttons);
99  $is_hidden = in_array($entry_id, $hidden);
100 
101  if ($is_removeable) {
102  $trigger_signal = $component->getTriggerSignal($mb_id, $component::ENTRY_ACTION_REMOVE);
103  $this->trigger_signals[] = $trigger_signal;
104  $btn_removetool = $close_buttons[$entry_id]
105  ->withAdditionalOnloadCode(
106  fn ($id) => "il.UI.maincontrols.mainbar.addPartIdAndEntry('$mb_id', 'remover', '$id', true);"
107  )
108  ->withOnClick($trigger_signal);
109 
110  $tpl->setCurrentBlock("tool_removal");
111  $tpl->setVariable("REMOVE_TOOL", $default_renderer->render($btn_removetool));
112  $tpl->parseCurrentBlock();
113  }
114 
115  $is_removeable = $is_removeable ? 'true' : 'false';
116  $is_hidden = $is_hidden ? 'true' : 'false';
117  return "il.UI.maincontrols.mainbar.addToolEntry('$mb_id', $is_removeable, $is_hidden, '$entry_id');";
118  }
119 
120  protected function renderMainbarEntry(
121  array $entries,
122  string $block,
123  MainBar $component,
125  RendererInterface $default_renderer
126  ): void {
127  $f = $this->getUIFactory();
128  foreach ($entries as $k => $entry) {
129  $button = $entry;
130  $slate = null;
131  $js = '';
132 
133  if ($entry instanceof Slate) {
134  $slate = $entry;
135  $mb_id = $entry->getMainBarTreePosition();
136  $is_tool = $block === static::BLOCK_MAINBAR_TOOLS;
137  if ($is_tool) {
138  $js = $this->renderToolEntry($k, $mb_id, $component, $tpl, $default_renderer);
139  }
140 
141  $trigger_signal = $component->getTriggerSignal($mb_id, $component::ENTRY_ACTION_TRIGGER);
142  $this->trigger_signals[] = $trigger_signal;
143  $button = $f->button()->bulky($entry->getSymbol(), $entry->getName(), '#')
144  ->withOnClick($trigger_signal);
145  } else {
146  //add Links/Buttons as toplevel entries
147  $pos = array_search($k, array_keys($entries));
148  $mb_id = '0:' . $pos;
149  $is_tool = false;
150  }
151 
152  $button = $button->withAdditionalOnLoadCode(
153  function ($id) use ($js, $mb_id, $k, $is_tool): string {
154  $add_as_tool = $is_tool ? 'true' : 'false';
155  $js .= "
156  il.UI.maincontrols.mainbar.addPartIdAndEntry('$mb_id', 'triggerer', '$id', $add_as_tool);
157  il.UI.maincontrols.mainbar.addMapping('$k','$mb_id');
158  ";
159  return $js;
160  }
161  )->withAriaRole(IBulky::MENUITEM);
162 
163  $tpl->setCurrentBlock($block);
164  $tpl->setVariable("BUTTON", $default_renderer->render($button));
165  $tpl->parseCurrentBlock();
166 
167  if ($slate) {
168  $entry = $entry->withAriaRole(ISlate::MENU);
169 
170  $tpl->setCurrentBlock("slate_item");
171  $tpl->setVariable("SLATE", $default_renderer->render($entry));
172  $tpl->parseCurrentBlock();
173  }
174  }
175  }
176 
177  protected function renderMainbar(MainBar $component, RendererInterface $default_renderer): string
178  {
179  $f = $this->getUIFactory();
180  $tpl = $this->getTemplate("tpl.mainbar.html", true, true);
181 
182  $tpl->setVariable("ARIA_LABEL", $this->txt('mainbar_aria_label'));
183  $more_btn_label = $this->txt('mainbar_more_label');
187  $more_slate = $f->mainControls()->slate()->combined(
188  $more_btn_label,
189  $f->symbol()->glyph()->more()
190  );
191  $more_slate = $more_slate->withAriaRole(ISlate::MENU);
192  $component = $component->withAdditionalEntry(
193  '_mb_more_entry',
194  $more_slate
195  );
196  $component = $this->calculateMainBarTreePosition("0", $component);
197 
198  $mb_entries = [
199  static::BLOCK_MAINBAR_ENTRIES => $component->getEntries(),
200  static::BLOCK_MAINBAR_TOOLS => $component->getToolEntries()
201  ];
202 
203  foreach ($mb_entries as $block => $entries) {
204  $this->renderMainbarEntry(
205  $entries,
206  $block,
207  $component,
208  $tpl,
209  $default_renderer
210  );
211  }
212 
213  //tools-section trigger
214  if (count($component->getToolEntries()) > 0) {
215  $btn_tools = $component->getToolsButton()
216  ->withOnClick($component->getToggleToolsSignal());
217 
218  $tpl->setCurrentBlock("tools_trigger");
219  $tpl->setVariable("BUTTON", $default_renderer->render($btn_tools));
220  $tpl->parseCurrentBlock();
221  }
222 
223  //disengage all, close slates
224  $btn_disengage = $f->button()->bulky($f->symbol()->glyph()->collapseHorizontal("#"), $this->txt('close'), "#")
225  ->withOnClick($component->getDisengageAllSignal());
226  $tpl->setVariable("CLOSE_SLATES", $default_renderer->render($btn_disengage));
227 
228 
229  $id = $this->bindMainbarJS($component);
230  $tpl->setVariable('ID', $id);
231 
232  return $tpl->get();
233  }
234 
235  protected function renderMetabar(MetaBar $component, RendererInterface $default_renderer): string
236  {
237  $f = $this->getUIFactory();
238  $tpl = $this->getTemplate("tpl.metabar.html", true, true);
239  $active = '';
240  $signals = [
241  'entry' => $component->getEntryClickSignal(),
242  'close_slates' => $component->getDisengageAllSignal()
243  ];
244  $entries = $component->getEntries();
245 
246  $more_label = $this->txt('show_more');
247  $more_symbol = $f->symbol()->glyph()->disclosure()
248  ->withCounter($f->counter()->novelty(0))
249  ->withCounter($f->counter()->status(0));
253  $more_slate = $f->mainControls()->slate()->combined($more_label, $more_symbol);
254  $more_slate = $more_slate->withAriaRole(ISlate::MENU);
255  $entries[] = $more_slate;
256 
258  $tpl,
259  $default_renderer,
260  $signals['entry'],
261  static::BLOCK_METABAR_ENTRIES,
262  $entries,
263  $active
264  );
265 
266  $component = $component->withOnLoadCode(
267  function ($id) use ($signals) {
268  $entry_signal = $signals['entry'];
269  $close_slates_signal = $signals['close_slates'];
270  return "
271  il.UI.maincontrols.metabar.registerSignals(
272  '$id',
273  '$entry_signal',
274  '$close_slates_signal',
275  );
276  il.UI.maincontrols.metabar.init();
277  $(window).resize(il.UI.maincontrols.metabar.init);
278  ";
279  }
280  );
281  $tpl->setVariable('ARIA_LABEL', $this->txt('metabar_aria_label'));
282 
283  $id = $this->bindJavaScript($component);
284  $tpl->setVariable('ID', $id);
285  return $tpl->get();
286  }
287 
288  protected function renderModeInfo(ModeInfo $component, RendererInterface $default_renderer): string
289  {
290  $tpl = $this->getTemplate("tpl.mode_info.html", true, true);
291  $tpl->setVariable('MODE_TITLE', $component->getModeTitle());
292  $base_URI = $component->getCloseAction()->getBaseURI();
293  $query = $component->getCloseAction()->getQuery();
294  $action = $base_URI . '?' . $query;
295  $close = $this->getUIFactory()->symbol()->glyph()->close($action);
296  $tpl->setVariable('CLOSE_GLYPH', $default_renderer->render($close));
297 
298  return $tpl->get();
299  }
300 
301  protected function renderSystemInfo(
302  Component\MainControls\SystemInfo $component,
303  RendererInterface $default_renderer
304  ): string {
305  $tpl = $this->getTemplate("tpl.system_info.html", true, true);
306  $tpl->setVariable('HEADLINE', $component->getHeadLine());
307  $tpl->setVariable('BODY', $component->getInformationText());
308  $tpl->setVariable('DENOTATION', $component->getDenotation());
309  switch ($component->getDenotation()) {
312  $tpl->setVariable('LIVE', 'aria-live="polite"');
313  break;
315  $tpl->setVariable('ROLE', 'role="alert"');
316  break;
317  }
318  if ($component->isDismissable()) {
319  $close = $this->getUIFactory()->symbol()->glyph()->close("#");
320  $signal = $component->getCloseSignal();
321  $close = $close->withOnClick($signal);
322  $tpl->setVariable('CLOSE_BUTTON', $default_renderer->render($close));
323  $tpl->setVariable('CLOSE_URI', (string) $component->getDismissAction());
324  $component = $component->withAdditionalOnLoadCode(fn ($id) => "$(document).on('$signal', function() { il.UI.maincontrols.system_info.close('$id'); });");
325  }
326 
327  $more = $this->getUIFactory()->symbol()->glyph()->more("#");
328  $tpl->setVariable('MORE_BUTTON', $default_renderer->render($more));
329 
330  $component = $component->withAdditionalOnLoadCode(fn ($id) => "il.UI.maincontrols.system_info.init('$id')");
331 
332  $id = $this->bindJavaScript($component);
333  $tpl->setVariable('ID', $id);
334  $tpl->setVariable('ID_HEADLINE', $id . "_headline");
335  $tpl->setVariable('ID_DESCRIPTION', $id . "_description");
336 
337  return $tpl->get();
338  }
339 
340 
341  protected function renderTriggerButtonsAndSlates(
343  RendererInterface $default_renderer,
344  Signal $entry_signal,
345  string $block,
346  array $entries,
347  string $active = null
348  ): void {
349  foreach ($entries as $id => $entry) {
350  $use_block = $block;
351  $engaged = (string) $id === $active;
352  $slate = null;
353  if ($entry instanceof Slate) {
354  $f = $this->getUIFactory();
355  $secondary_signal = $entry->getToggleSignal();
356  $clickable = $f->button()->bulky($entry->getSymbol(), $entry->getName(), '#')
358  ->withOnClick($entry_signal)
359  ->appendOnClick($secondary_signal)
360  ->withAriaRole(IBulky::MENUITEM);
361 
362  $slate = $entry;
363  } elseif ($entry instanceof IBulky) {
364  $clickable = $entry;
365  $clickable = $clickable->withAriaRole(IBulky::MENUITEM);
366  $slate = null;
367  } else {
368  $clickable = $entry;
369  }
370 
371  $clickable_html = $default_renderer->render($clickable);
372 
373  if ($slate) {
374  $tpl->setCurrentBlock("slate_item");
375  $tpl->setVariable("SLATE", $default_renderer->render($slate));
376  $tpl->parseCurrentBlock();
377  }
378 
379  $tpl->setCurrentBlock($use_block);
380  $tpl->setVariable("BUTTON", $clickable_html);
381  $tpl->parseCurrentBlock();
382  }
383  }
384 
385  protected function bindMainbarJS(MainBar $component): ?string
386  {
387  $trigger_signals = $this->trigger_signals;
388 
389  $inititally_active = $component->getActive();
390 
391  $component = $component->withOnLoadCode(
392  function ($id) use ($component, $trigger_signals, $inititally_active): string {
393  $disengage_all_signal = $component->getDisengageAllSignal();
394  $tools_toggle_signal = $component->getToggleToolsSignal();
395 
396  $js = "il.UI.maincontrols.mainbar.addTriggerSignal('$disengage_all_signal');";
397  $js .= "il.UI.maincontrols.mainbar.addTriggerSignal('$tools_toggle_signal');";
398 
399  foreach ($trigger_signals as $signal) {
400  $js .= "il.UI.maincontrols.mainbar.addTriggerSignal('$signal');";
401  }
402 
403  foreach ($component->getToolEntries() as $k => $tool) {
404  $signal = $component->getEngageToolSignal($k);
405  $js .= "il.UI.maincontrols.mainbar.addTriggerSignal('$signal');";
406  }
407 
408  $js .= "
409  window.addEventListener('resize', il.UI.maincontrols.mainbar.adjustToScreenSize);
410  il.UI.maincontrols.mainbar.init('$inititally_active');
411  ";
412  return $js;
413  }
414  );
415 
416  return $this->bindJavaScript($component);
417  }
418 
419  protected function renderFooter(Footer $component, RendererInterface $default_renderer): string
420  {
421  $tpl = $this->getTemplate("tpl.footer.html", true, true);
422  $links = $component->getLinks();
423  $modalsWithTriggers = $component->getModals();
424  $links = array_merge($links, array_column($modalsWithTriggers, 1));
425 
426  if ($links) {
427  $link_list = $this->getUIFactory()->listing()->unordered($links);
428  $tpl->setVariable('LINKS', $default_renderer->render($link_list));
429  }
430 
431  if ($modalsWithTriggers !== []) {
432  $tpl->setVariable('MODALS', $default_renderer->render(
433  array_column($modalsWithTriggers, 0)
434  ));
435  }
436 
437  $tpl->setVariable('TEXT', $component->getText());
438 
439  $perm_url = $component->getPermanentURL();
440  if ($perm_url instanceof URI) {
441  $url = $perm_url->__toString();
442  $link = $this->getUIFactory()
443  ->link()
444  ->standard($this->txt('perma_link'), $url);
445  $tpl->setVariable('PERMANENT', $default_renderer->render($link));
446  }
447  return $tpl->get();
448  }
449 
453  public function registerResources(ResourceRegistry $registry): void
454  {
455  parent::registerResources($registry);
456  $registry->register('./src/UI/templates/js/MainControls/dist/mainbar.js');
457  $registry->register('./src/UI/templates/js/MainControls/metabar.js');
458  $registry->register('./src/GlobalScreen/Client/dist/GS.js');
459  $registry->register('./src/UI/templates/js/MainControls/system_info.js');
460  }
461 
465  protected function getComponentInterfaceName(): array
466  {
467  return array(
468  MetaBar::class,
469  MainBar::class,
470  Footer::class,
471  ModeInfo::class,
472  Component\MainControls\SystemInfo::class
473  );
474  }
475 }
Registry for resources required by rendered output like Javascript or CSS.
This describes the MainBar.
Definition: MainBar.php:33
checkComponent(Component $component)
Check if a given component fits this renderer and throw if that is not the case. ...
getDisengageAllSignal()
This signal disengages all slates when triggered.
getInitiallyHiddenToolIds()
There are tools that are rendered invisible before first activation.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
renderToolEntry(string $entry_id, string $mb_id, MainBar $component, UITemplateWrapper $tpl, RendererInterface $default_renderer)
Definition: Renderer.php:88
txt(string $id)
Get a text from the language file.
getEntryClickSignal()
The Signal is triggered when any Entry is being clicked.
renderFooter(Footer $component, RendererInterface $default_renderer)
Definition: Renderer.php:419
getDisengageAllSignal()
This signal disengages all slates when triggered.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
renderSystemInfo(Component\MainControls\SystemInfo $component, RendererInterface $default_renderer)
Definition: Renderer.php:301
renderModeInfo(ModeInfo $component, RendererInterface $default_renderer)
Definition: Renderer.php:288
The scope of this class is split ilias-conform URI&#39;s into components.
Definition: URI.php:34
$query
withAdditionalEntry(string $id, $entry)
Append an entry.
getToolsButton()
Returns the button of the tools-trigger.
register(string $name)
Add a dependency.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
This describes the MetaBar.
Definition: MetaBar.php:32
renderMainbarEntry(array $entries, string $block, MainBar $component, UITemplateWrapper $tpl, RendererInterface $default_renderer)
Definition: Renderer.php:120
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:49
$url
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Factory.php:21
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:453
getToggleToolsSignal()
Signal to toggle the tools-section.
if($DIC->http() ->request() ->getMethod()=="GET" &&isset($DIC->http() ->request() ->getQueryParams()['tex'])) $tpl
Definition: latex.php:41
getEngageToolSignal(string $tool_id)
Signal to engage a tool from outside the MainBar.
getCloseButtons()
Buttons to close tools; maybe configure with callback.
This describes the Footer.
Definition: Footer.php:32
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Combined.php:20
renderTriggerButtonsAndSlates(UITemplateWrapper $tpl, RendererInterface $default_renderer, Signal $entry_signal, string $block, array $entries, string $active=null)
Definition: Renderer.php:341
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.