ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
Renderer.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
22 
29 
31 {
35  public function render(Component\Component $component, RendererInterface $default_renderer): string
36  {
37  $this->checkComponent($component);
38 
39  if ($component instanceof Component\Button\Close) {
40  return $this->renderClose($component);
41  } elseif ($component instanceof Component\Button\Minimize) {
42  return $this->renderMinimize($component);
43  } elseif ($component instanceof Component\Button\Toggle) {
44  return $this->renderToggle($component);
45  } elseif ($component instanceof Component\Button\Month) {
46  return $this->renderMonth($component);
47  } else {
51  return $this->renderButton($component, $default_renderer);
52  }
53  }
54 
55  protected function renderButton(Component\Button\Button $component, RendererInterface $default_renderer): string
56  {
57  $tpl_name = "";
58  if ($component instanceof Component\Button\Primary) {
59  $tpl_name = "tpl.primary.html";
60  }
61  if ($component instanceof Component\Button\Standard) {
62  $tpl_name = "tpl.standard.html";
63  }
64  if ($component instanceof Component\Button\Shy) {
65  $tpl_name = "tpl.shy.html";
66  }
67  if ($component instanceof Component\Button\Tag) {
68  $tpl_name = "tpl.tag.html";
69  }
70  if ($component instanceof Component\Button\Bulky) {
71  $tpl_name = "tpl.bulky.html";
72  }
73 
74  $tpl = $this->getTemplate($tpl_name, true, true);
75 
76  $action = $component->getAction();
77  // The action is always put in the data-action attribute to have it available
78  // on the client side, even if it is not available on rendering.
79  if (is_string($action)) {
80  $tpl->setCurrentBlock("with_data_action");
81  $tpl->setVariable("ACTION", $action);
82  $tpl->parseCurrentBlock();
83  }
84 
85  $label = $component->getLabel();
86  if ($label !== null) {
87  $tpl->setVariable("LABEL", $component->getLabel());
88  }
89  if ($component->isActive()) {
90  // The actions might also be a list of signals, these will be appended by
91  // bindJavascript in maybeRenderId.
92  if (is_string($action) && $action != "") {
93  $component = $component->withAdditionalOnLoadCode(function ($id) use ($action) {
94  $action = str_replace("&amp;", "&", $action);
95 
96  return "$('#$id').on('click', function(event) {
97  window.location = '$action';
98  return false;
99  });";
100  });
101  }
102 
103  if ($component instanceof Component\Button\LoadingAnimationOnClick && $component->hasLoadingAnimationOnClick()) {
104  $component = $component->withAdditionalOnLoadCode(fn ($id) => "$('#$id').click(function(e) { il.UI.button.activateLoadingAnimation('$id')});");
105  }
106  } else {
107  $tpl->touchBlock("disabled");
108  }
109  $aria_label = $component->getAriaLabel();
110  if ($aria_label != null) {
111  $tpl->setCurrentBlock("with_aria_label");
112  $tpl->setVariable("ARIA_LABEL", $aria_label);
113  $tpl->parseCurrentBlock();
114  }
115 
116  if ($component instanceof Component\Button\Engageable
117  && $component->isEngageable()
118  ) {
119  if ($component->isEngaged()) {
120  $tpl->touchBlock("engaged");
121  $aria_pressed = 'true';
122  } else {
123  $aria_pressed = 'false';
124  }
125 
126  //Note that Bulky Buttons need to handle aria_pressed seperatly due to possible aria_role conflicts
127  if (!($component instanceof Bulky)) {
128  $tpl->setCurrentBlock("with_aria_pressed");
129  $tpl->setVariable("ARIA_PRESSED", $aria_pressed);
130  $tpl->parseCurrentBlock();
131  }
132  }
133 
134  $tooltip_embedding = $this->getTooltipRenderer()->maybeGetTooltipEmbedding(...$component->getHelpTopics());
135  if ($tooltip_embedding) {
136  $component = $component->withAdditionalOnLoadCode($tooltip_embedding[1]);
137  }
138 
139  $this->maybeRenderId($component, $tpl);
140 
141  if ($component instanceof Component\Button\Tag) {
142  $this->additionalRenderTag($component, $tpl);
143  }
144 
145  if ($component instanceof Component\Button\Bulky) {
146  $this->additionalRenderBulky($component, $default_renderer, $tpl);
147  }
148 
149  if (!$tooltip_embedding) {
150  return $tpl->get();
151  }
152 
153  $tooltip_id = $this->createId();
154  $tpl->setCurrentBlock("with_aria_describedby");
155  $tpl->setVariable("ARIA_DESCRIBED_BY", $tooltip_id);
156  $tpl->parseCurrentBlock();
157 
158  return $tooltip_embedding[0]($tooltip_id, $tpl->get());
159  }
160 
164  public function registerResources(ResourceRegistry $registry): void
165  {
166  parent::registerResources($registry);
167  $registry->register('./src/UI/templates/js/Button/button.js');
168  $registry->register("./node_modules/moment/min/moment-with-locales.min.js");
169  $registry->register("./node_modules/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js");
170  }
171 
172  protected function renderClose(Component\Button\Close $component): string
173  {
174  $tpl = $this->getTemplate("tpl.close.html", true, true);
175  // This is required as the rendering seems to only create any output at all
176  // if any var was set or block was touched.
177  $tpl->setVariable("FORCE_RENDERING", "");
178  $tpl->setVariable("ARIA_LABEL", $this->txt("close"));
179  $this->maybeRenderId($component, $tpl);
180  return $tpl->get();
181  }
182 
183  protected function renderMinimize(Component\Button\Minimize $component): string
184  {
185  $tpl = $this->getTemplate("tpl.minimize.html", true, true);
186  $tpl->setVariable("ARIA_LABEL", $this->txt("minimize"));
187  $this->maybeRenderId($component, $tpl);
188  return $tpl->get();
189  }
190 
191  protected function renderToggle(Component\Button\Toggle $component): string
192  {
193  $tpl = $this->getTemplate("tpl.toggle.html", true, true);
194 
195  $on_action = $component->getActionOn();
196  $off_action = $component->getActionOff();
197 
198  $on_url = (is_string($on_action))
199  ? $on_action
200  : "";
201 
202  $off_url = (is_string($off_action))
203  ? $off_action
204  : "";
205 
206  $signals = [];
207 
208  foreach ($component->getTriggeredSignals() as $s) {
209  $signals[] = [
210  "signal_id" => $s->getSignal()->getId(),
211  "event" => $s->getEvent(),
212  "options" => $s->getSignal()->getOptions()
213  ];
214  }
215 
216  $signals = json_encode($signals);
217 
218  $button_status = 'off';
219  if ($component->isEngaged()) {
220  $button_status = 'on';
221  }
222 
223  if ($component->isActive()) {
224  $component = $component->withAdditionalOnLoadCode(fn ($id) => "$('#$id').on('click', function(event) {
225  il.UI.button.handleToggleClick(event, '$id', '$on_url', '$off_url', $signals);
226  return false; // stop event propagation
227  });");
228  $tpl->setCurrentBlock("with_on_off_label");
229  $tpl->setVariable("ON_LABEL", $this->txt("toggle_on"));
230  $tpl->setVariable("OFF_LABEL", $this->txt("toggle_off"));
231  $tpl->parseCurrentBlock();
232  } else {
233  $tpl->touchBlock("disabled");
234  $button_status = 'unavailable';
235  }
236 
237  $tpl->touchBlock($button_status);
238 
239  $label = $component->getLabel();
240  if (!empty($label)) {
241  $tpl->setCurrentBlock("with_label");
242  $tpl->setVariable("LABEL", $label);
243  $tpl->parseCurrentBlock();
244  }
245  $aria_label = $component->getAriaLabel();
246  if ($aria_label != null) {
247  $tpl->setCurrentBlock("with_aria_label");
248  $tpl->setVariable("ARIA_LABEL", $aria_label);
249  $tpl->parseCurrentBlock();
250  }
251 
252  $tooltip_embedding = $this->getTooltipRenderer()->maybeGetTooltipEmbedding(...$component->getHelpTopics());
253  if ($tooltip_embedding) {
254  $component = $component->withAdditionalOnLoadCode($tooltip_embedding[1]);
255  $tooltip_id = $this->createId();
256  $tpl->setCurrentBlock("with_aria_describedby");
257  $tpl->setVariable("ARIA_DESCRIBED_BY", $tooltip_id);
258  $tpl->parseCurrentBlock();
259 
260  $this->maybeRenderId($component, $tpl);
261  return $tooltip_embedding[0]($tooltip_id, $tpl->get());
262  }
263 
264  $this->maybeRenderId($component, $tpl);
265  return $tpl->get();
266  }
267 
268  protected function maybeRenderId(Component\JavaScriptBindable $component, Template $tpl): void
269  {
270  $id = $this->bindJavaScript($component);
271  if ($id !== null) {
272  $tpl->setCurrentBlock("with_id");
273  $tpl->setVariable("ID", $id);
274  $tpl->parseCurrentBlock();
275  }
276  }
277 
278  protected function renderMonth(Component\Button\Month $component): string
279  {
280  $def = $component->getDefault();
281 
282  for ($i = 1; $i <= 12; $i++) {
283  $this->toJS(array("month_" . str_pad((string) $i, 2, "0", STR_PAD_LEFT) . "_short"));
284  }
285 
286  $tpl = $this->getTemplate("tpl.month.html", true, true);
287 
288  $month = explode("-", $def);
289  $tpl->setVariable("DEFAULT_LABEL", $this->txt("month_" . str_pad($month[0], 2, "0", STR_PAD_LEFT) . "_short") . " " . $month[1]);
290  $tpl->setVariable("DEF_DATE", $month[0] . "/1/" . $month[1]);
291  // see https://github.com/moment/moment/tree/develop/locale
292  $lang_key = in_array($this->getLangKey(), array("ar", "bg", "cs", "da", "de", "el", "en", "es", "et", "fa", "fr", "hu", "it",
293  "ja", "ka", "lt", "nl", "pl", "pt", "ro", "ru", "sk", "sq", "sr", "tr", "uk", "vi", "zh"))
294  ? $this->getLangKey()
295  : "en";
296  if ($lang_key == "zh") {
297  $lang_key = "zh-cn";
298  }
299  $tpl->setVariable("LANG", $lang_key);
300 
301  $component = $component->withAdditionalOnLoadCode(fn ($id) => "il.UI.button.initMonth('$id');");
302  $id = $this->bindJavaScript($component);
303 
304  $tpl->setVariable("ID", $id);
305 
306  return $tpl->get();
307  }
308 
309  protected function additionalRenderTag(Component\Button\Tag $component, Template $tpl): void
310  {
311  $tpl->touchBlock('rel_' . $component->getRelevance());
312 
313  $classes = trim(join(' ', $component->getClasses()));
314  if ($classes !== '') {
315  $tpl->setVariable("CLASSES", $classes);
316  }
317 
318  $bgcol = $component->getBackgroundColor();
319  if ($bgcol) {
320  $tpl->setVariable("BGCOL", $bgcol->asHex());
321  }
322  $forecol = $component->getForegroundColor();
323  if ($forecol) {
324  $tpl->setVariable("FORECOL", $forecol->asHex());
325  }
326  }
327 
328  protected function additionalRenderBulky(
329  Component\Button\Button $component,
330  RendererInterface $default_renderer,
331  Template $tpl
332  ): void {
333  $renderer = $default_renderer->withAdditionalContext($component);
334  $tpl->setVariable("ICON_OR_GLYPH", $renderer->render($component->getIconOrGlyph()));
335  $label = $component->getLabel();
336  if ($label !== null) {
337  $tpl->setVariable("LABEL", $label);
338  }
339 
340  $aria_role = $component->getAriaRole();
341  if ($aria_role != null) {
342  $tpl->setCurrentBlock("with_aria_role");
343  $tpl->setVariable("ARIA_ROLE", $aria_role);
344  $tpl->parseCurrentBlock();
345  }
346  if ($component->isEngageable()) {
347  if ($aria_role == Bulky::MENUITEM) {
348  $tpl->touchBlock("with_aria_haspopup");
349  } else {
350  //Note that aria-role='menuitems MUST-NOT have Aria-pressed to true;
351  $tpl->setCurrentBlock("with_aria_pressed");
352  if ($component->isEngaged()) {
353  $tpl->setVariable("ARIA_PRESSED", "true");
354  } else {
355  $tpl->setVariable("ARIA_PRESSED", "false");
356  }
357  $tpl->parseCurrentBlock();
358  }
359  }
360  }
361 
365  protected function getComponentInterfaceName(): array
366  {
367  return array(Component\Button\Primary::class
368  , Component\Button\Standard::class
369  , Component\Button\Close::class
370  , Component\Button\Minimize::class
371  , Component\Button\Shy::class
372  , Component\Button\Month::class
373  , Component\Button\Tag::class
374  , Component\Button\Bulky::class
375  , Component\Button\Toggle::class
376  );
377  }
378 }
Registry for resources required by rendered output like Javascript or CSS.
checkComponent(Component $component)
Check if a given component fits this renderer and throw if that is not the case. ...
renderMinimize(Component\Button\Minimize $component)
Definition: Renderer.php:183
renderMonth(Component\Button\Month $component)
Definition: Renderer.php:278
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.
toJS($key)
Add language var to client side (il.Language)
setCurrentBlock(string $name)
Set the block to work on.
setVariable(string $name, $value)
Set a variable in the current block.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Bulky.php:21
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:164
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
renderToggle(Component\Button\Toggle $component)
Definition: Renderer.php:191
additionalRenderTag(Component\Button\Tag $component, Template $tpl)
Definition: Renderer.php:309
renderButton(Component\Button\Button $component, RendererInterface $default_renderer)
Definition: Renderer.php:55
register(string $name)
Add a dependency.
renderClose(Component\Button\Close $component)
Definition: Renderer.php:172
parseCurrentBlock()
Parse the block that is currently worked on.
render(Component $component, Renderer $default_renderer)
Render the component if possible and delegate additional rendering to the default_renderer.
touchBlock(string $name)
Touch a block without working further on it.
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
additionalRenderBulky(Component\Button\Button $component, RendererInterface $default_renderer, Template $tpl)
Definition: Renderer.php:328
maybeRenderId(Component\JavaScriptBindable $component, Template $tpl)
Definition: Renderer.php:268
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.