ILIAS  trunk Revision v12.0_alpha-1227-g7ff6d300864
Renderer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
25use ILIAS\UI\Renderer as RendererInterface;
32
34{
38 public function render(Component\Component $component, RendererInterface $default_renderer): string
39 {
40 if ($component instanceof Component\Triggerer) {
41 $component = $this->addTriggererOnLoadCode($component);
42 }
43 if ($component instanceof Component\Button\Close) {
44 return $this->renderClose($component);
45 } elseif ($component instanceof Component\Button\Minimize) {
46 return $this->renderMinimize($component);
47 } elseif ($component instanceof Component\Button\Toggle) {
48 return $this->renderToggle($component);
49 } elseif ($component instanceof Component\Button\Month) {
50 return $this->renderMonth($component);
51 } elseif ($component instanceof Component\Button\Button) {
52 return $this->renderButton($component, $default_renderer);
53 }
54 $this->cannotHandleComponent($component);
55 }
56
57 protected function renderButton(Component\Button\Button $component, RendererInterface $default_renderer): string
58 {
59 $tpl_name = "";
60 if ($component instanceof Component\Button\Primary) {
61 $tpl_name = "tpl.primary.html";
62 }
63 if ($component instanceof Component\Button\Standard) {
64 $tpl_name = "tpl.standard.html";
65 }
66 if ($component instanceof Component\Button\Shy) {
67 $tpl_name = "tpl.shy.html";
68 }
69 if ($component instanceof Component\Button\Tag) {
70 $tpl_name = "tpl.tag.html";
71 }
72 if ($component instanceof Component\Button\Bulky) {
73 $tpl_name = "tpl.bulky.html";
74 }
75
76 $tpl = $this->getTemplate($tpl_name, true, true);
77
78 $this->renderButtonType($component, $tpl);
79
80 $action = $component->getAction();
81 // The action is always put in the data-action attribute to have it available
82 // on the client side, even if it is not available on rendering.
83 if (is_string($action)) {
84 $tpl->setCurrentBlock("with_data_action");
85 $tpl->setVariable("ACTION", $action);
86 $tpl->parseCurrentBlock();
87 }
88
89 $tpl->setVariable("LABEL", $component->getLabel());
90 $symbol = $component->getSymbol();
91 if ($symbol !== null) {
92 // use label of symbol as aria-label if the button does not provide one
93 if ('' === $component->getLabel() && '' === $component->getAriaLabel()) {
94 $component = $component->withAriaLabel($this->getSymbolLabel($symbol));
95 }
96 // unset the symbol label, button provide label (semantic meaning)
97 $symbol = $symbol->withLabel('');
98 $tpl->setVariable("SYMBOL", $default_renderer->render($symbol));
99 }
100
101 if ($component->isActive()) {
102 // The actions might also be a list of signals, these will be appended by
103 // bindJavascript in maybeRenderId.
104 if (is_string($action) && $action != "") {
105 $component = $component->withAdditionalOnLoadCode(function ($id) use ($action) {
106 $action = str_replace("&amp;", "&", $action);
107
108 return "$('#$id').on('click', function(event) {
109 window.location = '$action';
110 return false;
111 });";
112 });
113 }
114
115 if ($component instanceof Component\Button\LoadingAnimationOnClick && $component->hasLoadingAnimationOnClick()) {
116 $component = $component->withAdditionalOnLoadCode(fn($id) => "$('#$id').click(function(e) { il.UI.button.activateLoadingAnimation('$id')});");
117 }
118 } else {
119 $tpl->touchBlock("disabled");
120 }
121 $aria_label = $component->getAriaLabel();
122 if ($aria_label != null) {
123 $tpl->setCurrentBlock("with_aria_label");
124 $tpl->setVariable("ARIA_LABEL", $aria_label);
125 $tpl->parseCurrentBlock();
126 }
127
128 if ($component instanceof Component\Button\Engageable
129 && $component->isEngageable()
130 ) {
131 if ($component->isEngaged()) {
132 $tpl->touchBlock("engaged");
133 $aria_pressed = 'true';
134 } else {
135 $aria_pressed = 'false';
136 }
137
138 //Note that Bulky Buttons need to handle aria_pressed seperatly due to possible aria_role conflicts
139 if (!($component instanceof Bulky)) {
140 $tpl->setCurrentBlock("with_aria_pressed");
141 $tpl->setVariable("ARIA_PRESSED", $aria_pressed);
142 $tpl->parseCurrentBlock();
143 }
144 }
145
146 $tooltip_embedding = $this->getTooltipRenderer()->maybeGetTooltipEmbedding(...$component->getHelpTopics());
147 if ($tooltip_embedding) {
148 $component = $component->withAdditionalOnLoadCode($tooltip_embedding[1]);
149 }
150
151 $this->maybeRenderId($component, $tpl);
152
153 if ($component instanceof Component\Button\Tag) {
154 $this->additionalRenderTag($component, $tpl);
155 }
156
157 if ($component instanceof Component\Button\Bulky) {
158 $this->additionalRenderBulky($component, $default_renderer, $tpl);
159 }
160
161 if (!$tooltip_embedding) {
162 return $tpl->get();
163 }
164
165 $tooltip_id = $this->createId();
166 $tpl->setCurrentBlock("with_aria_describedby");
167 $tpl->setVariable("ARIA_DESCRIBED_BY", $tooltip_id);
168 $tpl->parseCurrentBlock();
169
170 return $tooltip_embedding[0]($tooltip_id, $tpl->get());
171 }
172
176 public function registerResources(ResourceRegistry $registry): void
177 {
178 parent::registerResources($registry);
179 $registry->register('assets/js/button.js');
180 $registry->register("./assets/js/moment-with-locales.min.js");
181 }
182
183 protected function renderClose(Component\Button\Close $component): string
184 {
185 $tpl = $this->getTemplate("tpl.close.html", true, true);
186 // This is required as the rendering seems to only create any output at all
187 // if any var was set or block was touched.
188 $tpl->setVariable("FORCE_RENDERING", "");
189 $tpl->setVariable("ARIA_LABEL", $this->txt("close"));
190 $this->maybeRenderId($component, $tpl);
191 return $tpl->get();
192 }
193
194 protected function renderMinimize(Component\Button\Minimize $component): string
195 {
196 $tpl = $this->getTemplate("tpl.minimize.html", true, true);
197 $tpl->setVariable("ARIA_LABEL", $this->txt("minimize"));
198 $this->maybeRenderId($component, $tpl);
199 return $tpl->get();
200 }
201
202 protected function renderToggle(Component\Button\Toggle $component): string
203 {
204 $tpl = $this->getTemplate("tpl.toggle.html", true, true);
205
206 $this->renderButtonType($component, $tpl);
207
208 $on_action = $component->getActionOn();
209 $off_action = $component->getActionOff();
210
211 $on_url = (is_string($on_action))
212 ? $on_action
213 : "";
214
215 $off_url = (is_string($off_action))
216 ? $off_action
217 : "";
218
219 $signals = [];
220
221 foreach ($component->getTriggeredSignals() as $s) {
222 $signals[] = [
223 "signal_id" => $s->getSignal()->getId(),
224 "event" => $s->getEvent(),
225 "options" => $s->getSignal()->getOptions()
226 ];
227 }
228
229 $signals = json_encode($signals);
230
231 $button_status = 'off';
232 if ($component->isEngaged()) {
233 $button_status = 'on';
234 }
235
236 if ($component->isActive()) {
237 $component = $component->withAdditionalOnLoadCode(
238 fn($id) =>
239 "$('#$id').on('click', function(event) {
240 il.UI.button.handleToggleClick(event, '$id', '$on_url', '$off_url', $signals);
241 return false; // stop event propagation
242 });"
243 );
244 $tpl->setCurrentBlock("with_on_off_label");
245 $tpl->setVariable("ON_LABEL", $this->txt("toggle_on"));
246 $tpl->setVariable("OFF_LABEL", $this->txt("toggle_off"));
247 $tpl->parseCurrentBlock();
248 } else {
249 $tpl->touchBlock("disabled");
250 $button_status = 'unavailable';
251 }
252
253 $tpl->touchBlock($button_status);
254
255 $label = $component->getLabel();
256 if (!empty($label)) {
257 $tpl->setCurrentBlock("with_label");
258 $tpl->setVariable("LABEL", $label);
259 $tpl->parseCurrentBlock();
260 }
261 $aria_label = $component->getAriaLabel();
262 if ($aria_label != null) {
263 $tpl->setCurrentBlock("with_aria_label");
264 $tpl->setVariable("ARIA_LABEL", $aria_label);
265 $tpl->parseCurrentBlock();
266 }
267
268 $tooltip_embedding = $this->getTooltipRenderer()->maybeGetTooltipEmbedding(...$component->getHelpTopics());
269 if ($tooltip_embedding) {
270 $component = $component->withAdditionalOnLoadCode($tooltip_embedding[1]);
271 $tooltip_id = $this->createId();
272 $tpl->setCurrentBlock("with_aria_describedby");
273 $tpl->setVariable("ARIA_DESCRIBED_BY", $tooltip_id);
274 $tpl->parseCurrentBlock();
275
276 $this->maybeRenderId($component, $tpl);
277 return $tooltip_embedding[0]($tooltip_id, $tpl->get());
278 }
279
280 $this->maybeRenderId($component, $tpl);
281 return $tpl->get();
282 }
283
284 protected function maybeRenderId(Component\JavaScriptBindable $component, Template $tpl): void
285 {
286 $id = $this->bindJavaScript($component);
287 if ($id !== null) {
288 $tpl->setCurrentBlock("with_id");
289 $tpl->setVariable("ID", $id);
290 $tpl->parseCurrentBlock();
291 }
292 }
293
294 protected function renderMonth(Component\Button\Month $component): string
295 {
296 $tpl = $this->getTemplate("tpl.month.html", true, true);
297
298 $component = $component->withAdditionalOnLoadCode(fn($id) => "il.UI.button.initMonth('$id');");
299 $id = $this->bindJavaScript($component);
300 $tpl->setVariable("ID", $id);
301
302 $def = $component->getDefault();
303 $value = implode('-', array_reverse(explode("-", $def)));
304 $tpl->setVariable("DEFAULT", $value);
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 $aria_role = $component->getAriaRole();
334 if ($aria_role != null) {
335 $tpl->setCurrentBlock("with_aria_role");
336 $tpl->setVariable("ARIA_ROLE", $aria_role);
337 $tpl->parseCurrentBlock();
338 }
339 if ($component->isEngageable()) {
340 if ($aria_role == Bulky::MENUITEM) {
341 $tpl->touchBlock("with_aria_haspopup");
342 } else {
343 //Note that aria-role='menuitems MUST-NOT have Aria-pressed to true;
344 $tpl->setCurrentBlock("with_aria_pressed");
345 if ($component->isEngaged()) {
346 $tpl->setVariable("ARIA_PRESSED", "true");
347 } else {
348 $tpl->setVariable("ARIA_PRESSED", "false");
349 }
350 $tpl->parseCurrentBlock();
351 }
352 }
353 }
354
355 protected function renderButtonType(Component\Button\Button $component, Template $tpl): void
356 {
357 // omit type attribute by default
358 }
359
360 protected function getSymbolLabel(Symbol $symbol): string
361 {
362 if (!$symbol instanceof Glyph) {
363 return $symbol->getLabel();
364 }
365 // @todo: this breaks custom labels, translation should happen in factory...
366 $label = $this->txt($symbol->getLabel());
367 foreach ($symbol->getCounters() as $counter) {
368 if ($counter->getNumber() > 0) {
369 $label .= $this->txt("counter_" . $counter->getType()) . " " . $counter->getNumber() . "; ";
370 }
371 }
372 return trim($label);
373 }
374}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
additionalRenderBulky(Component\Button\Button $component, RendererInterface $default_renderer, Template $tpl)
Definition: Renderer.php:328
renderToggle(Component\Button\Toggle $component)
Definition: Renderer.php:202
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:38
additionalRenderTag(Component\Button\Tag $component, Template $tpl)
Definition: Renderer.php:309
renderClose(Component\Button\Close $component)
Definition: Renderer.php:183
renderButtonType(Component\Button\Button $component, Template $tpl)
Definition: Renderer.php:355
maybeRenderId(Component\JavaScriptBindable $component, Template $tpl)
Definition: Renderer.php:284
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:176
renderMonth(Component\Button\Month $component)
Definition: Renderer.php:294
renderButton(Component\Button\Button $component, RendererInterface $default_renderer)
Definition: Renderer.php:57
renderMinimize(Component\Button\Minimize $component)
Definition: Renderer.php:194
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
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.
This class is supposed to unify rendering of tooltips over all components and should also be usable b...
Engageable Components have an "engaged" state and will be displayed accordingly.
Definition: Engageable.php:29
isEngageable()
Returns whether the button is stateful or not.
Interface for buttons with loading animation on click.
hasLoadingAnimationOnClick()
Return whether loading animation has been activated.
Interface to be extended by components that have the possibility to bind to Javascript.
This describes how an icon could be modified during construction of UI.
Definition: Icon.php:29
This describes a symbol.
Definition: Symbol.php:30
getLabel()
Get the label of this icon.
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
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Bulky.php:21
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Bulky.php:21
$counter