ILIAS  trunk Revision v13.0_alpha-62-g3b72d83f2e5
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);
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);
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);
143 }
144 }
145
146 $tooltip_embedding = $this->getTooltipRenderer()->getHelpTopicTooltipEmbedding(...$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);
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 }
181
182 protected function renderClose(Component\Button\Close $component): string
183 {
184 $tpl = $this->getTemplate("tpl.close.html", true, true);
185 // This is required as the rendering seems to only create any output at all
186 // if any var was set or block was touched.
187 $tpl->setVariable("FORCE_RENDERING", "");
188 $tpl->setVariable("ARIA_LABEL", $this->txt("close"));
189 $this->maybeRenderId($component, $tpl);
190 return $tpl->get();
191 }
192
193 protected function renderMinimize(Component\Button\Minimize $component): string
194 {
195 $tpl = $this->getTemplate("tpl.minimize.html", true, true);
196 $tpl->setVariable("ARIA_LABEL", $this->txt("minimize"));
197 $this->maybeRenderId($component, $tpl);
198 return $tpl->get();
199 }
200
201 protected function renderToggle(Component\Button\Toggle $component): string
202 {
203 $tpl = $this->getTemplate("tpl.toggle.html", true, true);
204
205 $this->renderButtonType($component, $tpl);
206
207 $on_action = $component->getActionOn();
208 $off_action = $component->getActionOff();
209
210 $on_url = (is_string($on_action))
211 ? $on_action
212 : "";
213
214 $off_url = (is_string($off_action))
215 ? $off_action
216 : "";
217
218 $signals = [];
219
220 foreach ($component->getTriggeredSignals() as $s) {
221 $signals[] = [
222 "signal_id" => $s->getSignal()->getId(),
223 "event" => $s->getEvent(),
224 "options" => $s->getSignal()->getOptions()
225 ];
226 }
227
228 $signals = json_encode($signals);
229
230 $button_status = 'off';
231 if ($component->isEngaged()) {
232 $button_status = 'on';
233 }
234
235 if ($component->isActive()) {
236 $component = $component->withAdditionalOnLoadCode(
237 fn($id) =>
238 "$('#$id').on('click', function(event) {
239 il.UI.button.handleToggleClick(event, '$id', '$on_url', '$off_url', $signals);
240 return false; // stop event propagation
241 });"
242 );
243 $tpl->setCurrentBlock("with_on_off_label");
244 $tpl->setVariable("ON_LABEL", $this->txt("toggle_on"));
245 $tpl->setVariable("OFF_LABEL", $this->txt("toggle_off"));
247 } else {
248 $tpl->touchBlock("disabled");
249 $button_status = 'unavailable';
250 }
251
252 $tpl->touchBlock($button_status);
253
254 $label = $component->getLabel();
255 if (!empty($label)) {
256 $tpl->setCurrentBlock("with_label");
257 $tpl->setVariable("LABEL", $label);
259 }
260 $aria_label = $component->getAriaLabel();
261 if ($aria_label != null) {
262 $tpl->setCurrentBlock("with_aria_label");
263 $tpl->setVariable("ARIA_LABEL", $aria_label);
265 }
266
267 $tooltip_embedding = $this->getTooltipRenderer()->getHelpTopicTooltipEmbedding(...$component->getHelpTopics());
268 if ($tooltip_embedding) {
269 $component = $component->withAdditionalOnLoadCode($tooltip_embedding[1]);
270 $tooltip_id = $this->createId();
271 $tpl->setCurrentBlock("with_aria_describedby");
272 $tpl->setVariable("ARIA_DESCRIBED_BY", $tooltip_id);
274
275 $this->maybeRenderId($component, $tpl);
276 return $tooltip_embedding[0]($tooltip_id, $tpl->get());
277 }
278
279 $this->maybeRenderId($component, $tpl);
280 return $tpl->get();
281 }
282
283 protected function maybeRenderId(Component\JavaScriptBindable $component, Template $tpl): void
284 {
285 $id = $this->bindJavaScript($component);
286 if ($id !== null) {
287 $tpl->setCurrentBlock("with_id");
288 $tpl->setVariable("ID", $id);
290 }
291 }
292
293 protected function renderMonth(Component\Button\Month $component): string
294 {
295 $tpl = $this->getTemplate("tpl.month.html", true, true);
296
297 $component = $component->withAdditionalOnLoadCode(fn($id) => "il.UI.button.initMonth('$id');");
298 $id = $this->bindJavaScript($component);
299 $tpl->setVariable("ID", $id);
300
301 $def = $component->getDefault();
302 $value = implode('-', array_reverse(explode("-", $def)));
303 $tpl->setVariable("DEFAULT", $value);
304
305 return $tpl->get();
306 }
307
308 protected function additionalRenderTag(Component\Button\Tag $component, Template $tpl): void
309 {
310 $tpl->touchBlock('rel_' . $component->getRelevance());
311
312 $classes = trim(join(' ', $component->getClasses()));
313 if ($classes !== '') {
314 $tpl->setVariable("CLASSES", $classes);
315 }
316
317 $bgcol = $component->getBackgroundColor();
318 if ($bgcol) {
319 $tpl->setVariable("BGCOL", $bgcol->asHex());
320 }
321 $forecol = $component->getForegroundColor();
322 if ($forecol) {
323 $tpl->setVariable("FORECOL", $forecol->asHex());
324 }
325 }
326
327 protected function additionalRenderBulky(
328 Component\Button\Button $component,
329 RendererInterface $default_renderer,
331 ): void {
332 $aria_role = $component->getAriaRole();
333 if ($aria_role != null) {
334 $tpl->setCurrentBlock("with_aria_role");
335 $tpl->setVariable("ARIA_ROLE", $aria_role);
337 }
338 if ($component->isEngageable()) {
339 if ($aria_role == Bulky::MENUITEM) {
340 $tpl->touchBlock("with_aria_haspopup");
341 } else {
342 //Note that aria-role='menuitems MUST-NOT have Aria-pressed to true;
343 $tpl->setCurrentBlock("with_aria_pressed");
344 if ($component->isEngaged()) {
345 $tpl->setVariable("ARIA_PRESSED", "true");
346 } else {
347 $tpl->setVariable("ARIA_PRESSED", "false");
348 }
350 }
351 }
352 }
353
354 protected function renderButtonType(Component\Button\Button $component, Template $tpl): void
355 {
356 // omit type attribute by default
357 }
358
359 protected function getSymbolLabel(Symbol $symbol): string
360 {
361 if (!$symbol instanceof Glyph) {
362 return $symbol->getLabel();
363 }
364 // @todo: this breaks custom labels, translation should happen in factory...
365 $label = $this->txt($symbol->getLabel());
366 foreach ($symbol->getCounters() as $counter) {
367 if ($counter->getNumber() > 0) {
368 $label .= $this->txt("counter_" . $counter->getType()) . " " . $counter->getNumber() . "; ";
369 }
370 }
371 return trim($label);
372 }
373}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
additionalRenderBulky(Component\Button\Button $component, RendererInterface $default_renderer, Template $tpl)
Definition: Renderer.php:327
renderToggle(Component\Button\Toggle $component)
Definition: Renderer.php:201
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:38
additionalRenderTag(Component\Button\Tag $component, Template $tpl)
Definition: Renderer.php:308
renderClose(Component\Button\Close $component)
Definition: Renderer.php:182
renderButtonType(Component\Button\Button $component, Template $tpl)
Definition: Renderer.php:354
maybeRenderId(Component\JavaScriptBindable $component, Template $tpl)
Definition: Renderer.php:283
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:176
renderMonth(Component\Button\Month $component)
Definition: Renderer.php:293
renderButton(Component\Button\Button $component, RendererInterface $default_renderer)
Definition: Renderer.php:57
renderMinimize(Component\Button\Minimize $component)
Definition: Renderer.php:193
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...
setVariable(string $variable, $value='')
Sets the given variable to the given value.
touchBlock(string $block)
overwrites ITX::touchBlock.
parseCurrentBlock(string $block_name=self::DEFAULT_BLOCK)
Parses the given block.
setCurrentBlock(string $part=self::DEFAULT_BLOCK)
Sets the template to the given block.
get(string $part=self::DEFAULT_BLOCK)
Renders the given block and returns the html string.
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
An entity that renders components to a string output.
Definition: Renderer.php:31
readonly ilGlobalTemplateInterface $tpl
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