ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
Renderer.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
30use ILIAS\UI\Renderer as RendererInterface;
32use LogicException;
33use Closure;
41use ILIAS\UI\Implementation\Component\ComponentHelper;
42
48{
49 use ComponentHelper;
50
51 public const DATETIME_DATEPICKER_MINMAX_FORMAT = 'Y-m-d\Th:m';
52 public const DATE_DATEPICKER_MINMAX_FORMAT = 'Y-m-d';
53 public const TYPE_DATE = 'date';
54 public const TYPE_DATETIME = 'datetime-local';
55 public const TYPE_TIME = 'time';
56 public const HTML5_NATIVE_DATETIME_FORMAT = 'Y-m-d H:i';
57 public const HTML5_NATIVE_DATE_FORMAT = 'Y-m-d';
58 public const HTML5_NATIVE_TIME_FORMAT = 'H:i';
59
61 'd' => 'DD',
62 'jS' => 'Do',
63 'l' => 'dddd',
64 'D' => 'dd',
65 'S' => 'o',
66 'i' => 'mm',
67 'W' => '',
68 'm' => 'MM',
69 'F' => 'MMMM',
70 'M' => 'MMM',
71 'Y' => 'YYYY',
72 'y' => 'YY'
73 ];
74
81 protected const FILE_UPLOAD_CHUNK_SIZE_FACTOR = 0.9;
82
83 private const CENTUM = 100;
84
88 public function render(Component\Component $component, RendererInterface $default_renderer): string
89 {
90 $component = $this->setSignals($component);
91
92 switch (true) {
93 case ($component instanceof F\OptionalGroup):
94 return $this->renderOptionalGroup($component, $default_renderer);
95
96 case ($component instanceof F\SwitchableGroup):
97 return $this->renderSwitchableGroup($component, $default_renderer);
98
99 case ($component instanceof F\Section):
100 return $this->renderSection($component, $default_renderer);
101
102 case ($component instanceof F\Duration):
103 return $this->renderDurationField($component, $default_renderer);
104
105 case ($component instanceof F\Link):
106 return $this->renderLinkField($component, $default_renderer);
107
108 case ($component instanceof F\Group):
109 return $default_renderer->render($component->getInputs());
110
111 case ($component instanceof F\Text):
112 return $this->renderTextField($component, $default_renderer);
113
114 case ($component instanceof F\Numeric):
115 return $this->renderNumericField($component, $default_renderer);
116
117 case ($component instanceof F\Checkbox):
118 return $this->renderCheckboxField($component, $default_renderer);
119
120 case ($component instanceof F\Tag):
121 return $this->renderTagField($component, $default_renderer);
122
123 case ($component instanceof F\Password):
124 return $this->renderPasswordField($component, $default_renderer);
125
126 case ($component instanceof F\Select):
127 return $this->renderSelectField($component, $default_renderer);
128
129 case ($component instanceof F\Markdown):
130 return $this->renderMarkdownField($component, $default_renderer);
131
132 case ($component instanceof F\Textarea):
133 return $this->renderTextareaField($component, $default_renderer);
134
135 case ($component instanceof F\Radio):
136 return $this->renderRadioField($component, $default_renderer);
137
138 case ($component instanceof F\MultiSelect):
139 return $this->renderMultiSelectField($component, $default_renderer);
140
141 case ($component instanceof F\DateTime):
142 return $this->renderDateTimeField($component, $default_renderer);
143
144 case ($component instanceof F\Image):
145 return $this->renderImageField($component, $default_renderer);
146
147 case ($component instanceof F\File):
148 return $this->renderFileField($component, $default_renderer);
149
150 case ($component instanceof F\Url):
151 return $this->renderUrlField($component, $default_renderer);
152
153 case ($component instanceof F\Hidden):
154 return $this->renderHiddenField($component);
155
156 case ($component instanceof F\ColorSelect):
157 return $this->renderColorSelectField($component, $default_renderer);
158
159 case ($component instanceof F\Rating):
160 return $this->renderRatingField($component, $default_renderer);
161
162 case ($component instanceof F\TreeMultiSelect):
163 return $this->renderTreeMultiSelectField($component, $default_renderer);
164
165 case ($component instanceof F\TreeSelect):
166 return $this->renderTreeSelectField($component, $default_renderer);
167
168 default:
169 $this->cannotHandleComponent($component);
170 }
171 }
172
173 protected function wrapInFormContext(
174 FormInput $component,
175 string $label,
176 string $input_html,
177 ?string $id_for_label = null,
178 ?string $dependant_group_html = null
179 ): string {
180 $tpl = $this->getTemplate("tpl.context_form.html", true, true);
181
182 $tpl->setVariable("LABEL", $label);
183 $tpl->setVariable("INPUT", $input_html);
184 $tpl->setVariable("UI_COMPONENT_NAME", $this->getComponentCanonicalNameAttribute($component));
185 $tpl->setVariable("INPUT_NAME", $component->getName());
186
187 if ($component->getOnLoadCode() !== null) {
188 $binding_id = $this->bindJavaScript($component) ?? $this->createId();
189 $tpl->setVariable("BINDING_ID", $binding_id);
190 }
191
192 if ($id_for_label) {
193 $tpl->setCurrentBlock('for');
194 $tpl->setVariable("ID", $id_for_label);
195 $tpl->parseCurrentBlock();
196 } else {
197 $tpl->touchBlock('tabindex');
198 }
199
200 $byline = $component->getByline();
201 if ($byline) {
202 $tpl->setVariable("BYLINE", $byline);
203 }
204
205 $required = $component->isRequired();
206 if ($required) {
207 $tpl->setCurrentBlock('required');
208 $tpl->setVariable("REQUIRED_ARIA", $this->txt('required_field'));
209 $tpl->parseCurrentBlock();
210 }
211
212 if ($component->isDisabled()) {
213 $tpl->touchBlock("disabled");
214 }
215
216 $error = $component->getError();
217 if ($error) {
218 $error_id = $this->createId();
219 $tpl->setVariable("ERROR_LABEL", $this->txt("ui_error"));
220 $tpl->setVariable("ERROR_ID", $error_id);
221 $tpl->setVariable("ERROR", $error);
222 if ($id_for_label) {
223 $tpl->setVariable("ERROR_FOR_ID", $id_for_label);
224 }
225 }
226
227 if ($dependant_group_html) {
228 $tpl->setVariable("DEPENDANT_GROUP", $dependant_group_html);
229 }
230 return $tpl->get();
231 }
232
233 protected function applyName(FormInput $component, Template $tpl): ?string
234 {
235 $name = $component->getName();
236 $tpl->setVariable("NAME", $name);
237 return $name;
238 }
239
240 protected function bindJSandApplyId(Component\JavaScriptBindable $component, Template $tpl): string
241 {
242 $id = $this->bindJavaScript($component) ?? $this->createId();
243 $tpl->setVariable("ID", $id);
244 return $id;
245 }
246
255 protected function applyValue(FormInput $component, Template $tpl, ?callable $escape = null): void
256 {
257 $value = $component->getValue();
258 if (!is_null($escape)) {
259 $value = $escape($value);
260 }
261 if (isset($value) && $value !== '') {
262 $tpl->setVariable("VALUE", $value);
263 }
264 }
265
266 protected function escapeSpecialChars(): Closure
267 {
268 return function ($v) {
269 // with declare(strict_types=1) in place,
270 // htmlspecialchars will not silently convert to string anymore;
271 // therefore, the typecast must be explicit
272 return htmlspecialchars((string) $v, ENT_QUOTES, 'utf-8', false);
273 };
274 }
275
276 protected function htmlEntities(): Closure
277 {
278 return function ($v) {
279 // with declare(strict_types=1) in place,
280 // htmlentities will not silently convert to string anymore;
281 // therefore, the typecast must be explicit
282 return htmlentities((string) $v, ENT_QUOTES, 'utf-8', false);
283 };
284 }
285
286 protected function renderLinkField(F\Link $component, RendererInterface $default_renderer): string
287 {
288 $input_html = $default_renderer->render($component->getInputs());
289 return $this->wrapInFormContext(
290 $component,
291 $component->getLabel(),
292 $input_html,
293 );
294 }
295
296 protected function renderTextField(F\Text $component): string
297 {
298 $tpl = $this->getTemplate("tpl.text.html", true, true);
299 $this->applyName($component, $tpl);
300
301 if ($component->getMaxLength()) {
302 $tpl->setVariable("MAX_LENGTH", $component->getMaxLength());
303 }
304
305 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
306
307 $label_id = $this->createId();
308 $tpl->setVariable('ID', $label_id);
309 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
310 }
311
312 protected function renderNumericField(F\Numeric $component, RendererInterface $default_renderer): string
313 {
314 $tpl = $this->getTemplate("tpl.numeric.html", true, true);
315 $this->applyName($component, $tpl);
316 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
317
318 $tpl->setVariable("STEPSIZE", $component->getStepSize());
319
320 $label_id = $this->createId();
321 $tpl->setVariable('ID', $label_id);
322 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
323 }
324
325 protected function renderCheckboxField(F\Checkbox $component, RendererInterface $default_renderer): string
326 {
327 $tpl = $this->getTemplate("tpl.checkbox.html", true, true);
328 $this->applyName($component, $tpl);
329
330 if ($component->getValue()) {
331 $tpl->touchBlock("value");
332 }
333
334 $label_id = $this->createId();
335 $tpl->setVariable('ID', $label_id);
336 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
337 }
338
339 protected function renderOptionalGroup(F\OptionalGroup $component, RendererInterface $default_renderer): string
340 {
341 $tpl = $this->getTemplate("tpl.optionalgroup_label.html", true, true);
342 $tpl->setVariable('LABEL', $component->getLabel());
343 $tpl->setVariable("NAME", $component->getName());
344 if ($component->getValue()) {
345 $tpl->setVariable("CHECKED", 'checked="checked"');
346 }
347
348 $label_id = $this->createId();
349 $tpl->setVariable('ID', $label_id);
350
351 $label = $tpl->get();
352 $input_html = $default_renderer->render($component->getInputs());
353
354 return $this->wrapInFormContext($component, $label, $input_html, $label_id);
355 }
356
357 protected function renderSwitchableGroup(F\SwitchableGroup $component, RendererInterface $default_renderer): string
358 {
359 $value = null;
360 if ($component->getValue() !== null) {
361 list($value, ) = $component->getValue();
362 }
363
364 $input_html = '';
365 foreach ($component->getInputs() as $key => $group) {
366 $tpl = $this->getTemplate("tpl.switchablegroup_label.html", true, true);
367 $tpl->setVariable('LABEL', $group->getLabel());
368 $tpl->setVariable("NAME", $component->getName());
369 $tpl->setVariable("VALUE", $key);
370
371 $label_id = $this->createId();
372 $tpl->setVariable('ID', $label_id);
373
374 if ($key == $value) {
375 $tpl->setVariable("CHECKED", 'checked="checked"');
376 }
377
378 $input_html .= $this->wrapInFormContext(
379 $group,
380 $tpl->get(),
381 $default_renderer->render($group),
382 $label_id
383 );
384 }
385
386
387 return $this->wrapInFormContext(
388 $component,
389 $component->getLabel(),
390 $input_html
391 );
392 }
393
394 protected function renderTagField(F\Tag $component, RendererInterface $default_renderer): string
395 {
396 $tpl = $this->getTemplate("tpl.tag_input.html", true, true);
397 $this->applyName($component, $tpl);
398
399 $configuration = $component->getConfiguration();
400 $value = $component->getValue();
401
402 if ($value) {
403 $value = array_map(
404 function ($v) {
405 return ['value' => rawurlencode($v), 'display' => $v];
406 },
407 $value
408 );
409 }
410
411 $autocomplete_endpoint = 'undefined';
412 $autocomplete_token = 'undefined';
413 if ($component->getAsyncAutocompleteEndpoint() !== null) {
414 $autocomplete_endpoint = $component->getAsyncAutocompleteEndpoint()
415 ->renderObject([$component->getAsyncAutocompleteToken()]);
416 $autocomplete_token = $component->getAsyncAutocompleteToken()->render();
417 }
418
419 $component = $component->withAdditionalOnLoadCode(
420 function ($id) use ($configuration, $value, $autocomplete_endpoint, $autocomplete_token) {
421 $encoded = json_encode($configuration);
422 $value = json_encode($value);
423 return 'il.UI.Input.tagInput.init(document.querySelector('
424 . "'#{$id} .c-field-tag'), {$encoded}, {$value},"
425 . " {$autocomplete_endpoint}, "
426 . " {$autocomplete_token}"
427 . ");";
428 }
429 );
430
431 if ($component->isDisabled()) {
432 $tpl->setVariable("DISABLED", "disabled");
433 $tpl->setVariable("READONLY", "readonly");
434 }
435
436 $label_id = $this->createId();
437 $tpl->setVariable('ID', $label_id);
438 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
439 }
440
441 protected function renderPasswordField(F\Password $component, RendererInterface $default_renderer): string
442 {
443 $tpl = $this->getTemplate("tpl.password.html", true, true);
444 $this->applyName($component, $tpl);
445
446 if ($component->getRevelation()) {
447 $component = $component->withResetSignals();
448 $sig_reveal = $component->getRevealSignal();
449 $sig_mask = $component->getMaskSignal();
450 $component = $component->withAdditionalOnLoadCode(function ($id) use ($sig_reveal, $sig_mask) {
451 return
452 "$(document).on('$sig_reveal', function() {
453 const fieldContainer = document.querySelector('#$id .c-input__field .c-field-password');
454 fieldContainer.classList.add('revealed');
455 fieldContainer.getElementsByTagName('input').item(0).type='text';
456 });" .
457 "$(document).on('$sig_mask', function() {
458 const fieldContainer = document.querySelector('#$id .c-input__field .c-field-password');
459 fieldContainer.classList.remove('revealed');
460 fieldContainer.getElementsByTagName('input').item(0).type='password';
461 });";
462 });
463
464 $f = $this->getUIFactory();
465 $glyph_reveal = $f->symbol()->glyph()->eyeopen("#")
466 ->withOnClick($sig_reveal);
467 $glyph_mask = $f->symbol()->glyph()->eyeclosed("#")
468 ->withOnClick($sig_mask);
469
470 $tpl->setVariable('PASSWORD_REVEAL', $default_renderer->render($glyph_reveal));
471 $tpl->setVariable('PASSWORD_MASK', $default_renderer->render($glyph_mask));
472 }
473
474 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
475
476 $label_id = $this->createId();
477 $tpl->setVariable('ID', $label_id);
478 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
479 }
480
481 public function renderSelectField(F\Select $component, RendererInterface $default_renderer): string
482 {
483 $tpl = $this->getTemplate("tpl.select.html", true, true);
484 $this->applyName($component, $tpl);
485
486 $value = $component->getValue();
487 //disable first option if required.
488 $tpl->setCurrentBlock("options");
489 if (!$value) {
490 $tpl->setVariable("SELECTED", 'selected="selected"');
491 }
492 if ($component->isRequired() && !$value) {
493 $tpl->setVariable("DISABLED_OPTION", "disabled");
494 $tpl->setVariable("HIDDEN", "hidden");
495 }
496
497 if (!($value && $component->isRequired())) {
498 $tpl->setVariable("VALUE", null);
499 $tpl->setVariable("VALUE_STR", $component->isRequired() ? $this->txt('ui_select_dropdown_label') : '-');
500 $tpl->parseCurrentBlock();
501 }
502
503 foreach ($component->getOptions() as $option_key => $option_value) {
504 $tpl->setCurrentBlock("options");
505 if ($value == $option_key) {
506 $tpl->setVariable("SELECTED", 'selected="selected"');
507 }
508 $tpl->setVariable("VALUE", $option_key);
509 $tpl->setVariable("VALUE_STR", $option_value);
510 $tpl->parseCurrentBlock();
511 }
512
513 $label_id = $this->createId();
514 $tpl->setVariable('ID', $label_id);
515 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
516 }
517
518 protected function renderMarkdownField(F\Markdown $component, RendererInterface $default_renderer): string
519 {
521 $component = $component->withAdditionalOnLoadCode(
522 static function ($id) use ($component): string {
523 return "
524 const id = document.querySelector('#$id .c-input__field textarea')?.id;
525 il.UI.Input.markdown.init(
526 id,
527 '{$component->getMarkdownRenderer()->getAsyncUrl()}',
528 '{$component->getMarkdownRenderer()->getParameterName()}'
529 );
530 ";
531 }
532 );
533
534 $textarea_id = $this->createId();
535 $textarea_tpl = $this->getPreparedTextareaTemplate($component);
536 $textarea_tpl->setVariable('ID', $textarea_id);
537
538 $markdown_tpl = $this->getTemplate("tpl.markdown.html", true, true);
539 $markdown_tpl->setVariable('TEXTAREA', $textarea_tpl->get());
540
541 $markdown_tpl->setVariable(
542 'PREVIEW',
543 $component->getMarkdownRenderer()->render(
544 $this->htmlEntities()($component->getValue() ?? '')
545 )
546 );
547
548 $markdown_tpl->setVariable(
549 'VIEW_CONTROLS',
550 $default_renderer->render(
551 $this->getUIFactory()->viewControl()->mode([
552 $this->txt('ui_md_input_edit') => '#',
553 $this->txt('ui_md_input_view') => '#',
554 ], "")
555 )
556 );
557
559 $markdown_actions_glyphs = [
560 'ACTION_HEADING' => $this->getUIFactory()->symbol()->glyph()->header(),
561 'ACTION_LINK' => $this->getUIFactory()->symbol()->glyph()->link(),
562 'ACTION_BOLD' => $this->getUIFactory()->symbol()->glyph()->bold(),
563 'ACTION_ITALIC' => $this->getUIFactory()->symbol()->glyph()->italic(),
564 'ACTION_ORDERED_LIST' => $this->getUIFactory()->symbol()->glyph()->numberedlist(),
565 'ACTION_UNORDERED_LIST' => $this->getUIFactory()->symbol()->glyph()->bulletlist()
566 ];
567
568 foreach ($markdown_actions_glyphs as $tpl_variable => $glyph) {
569 if ($component->isDisabled()) {
570 $glyph = $glyph->withUnavailableAction();
571 }
572
573 $action = $this->getUIFactory()->button()->standard('', '#')->withSymbol($glyph);
574
575 if ($component->isDisabled()) {
576 $action = $action->withUnavailableAction();
577 }
578
579 $markdown_tpl->setVariable($tpl_variable, $default_renderer->render($action));
580 }
581
582 return $this->wrapInFormContext($component, $component->getLabel(), $markdown_tpl->get());
583 }
584
585 protected function renderTextareaField(F\Textarea $component, RendererInterface $default_renderer): string
586 {
588 $component = $component->withAdditionalOnLoadCode(
589 static function ($id): string {
590 return "
591 taId = document.querySelector('#$id .c-input__field textarea')?.id;
592 il.UI.Input.textarea.init(taId);
593 ";
594 }
595 );
596
597 $tpl = $this->getPreparedTextareaTemplate($component);
598
599 $label_id = $this->createId();
600 $tpl->setVariable('ID', $label_id);
601 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
602 }
603
604 protected function getPreparedTextareaTemplate(F\Textarea $component): Template
605 {
606 $tpl = $this->getTemplate("tpl.textarea.html", true, true);
607
608 if (0 < $component->getMaxLimit()) {
609 $tpl->setVariable('REMAINDER_TEXT', $this->txt('ui_chars_remaining'));
610 $tpl->setVariable('REMAINDER', $component->getMaxLimit() - strlen($component->getValue() ?? ''));
611 $tpl->setVariable('MAX_LIMIT', $component->getMaxLimit());
612 }
613
614 if (null !== $component->getMinLimit()) {
615 $tpl->setVariable('MIN_LIMIT', $component->getMinLimit());
616 }
617
618 $this->applyName($component, $tpl);
619 $this->applyValue($component, $tpl, $this->htmlEntities());
620 return $tpl;
621 }
622
623 protected function renderRadioField(F\Radio $component, RendererInterface $default_renderer): string
624 {
625 $tpl = $this->getTemplate("tpl.radio.html", true, true);
626 $id = $this->createId();
627
628 foreach ($component->getOptions() as $value => $label) {
629 $opt_id = $id . '_' . $value . '_opt';
630
631 $tpl->setCurrentBlock('optionblock');
632 $tpl->setVariable("NAME", $component->getName());
633 $tpl->setVariable("OPTIONID", $opt_id);
634 $tpl->setVariable("VALUE", $value);
635 $tpl->setVariable("LABEL", $label);
636
637 if ($component->getValue() !== null && $component->getValue() == $value) {
638 $tpl->setVariable("CHECKED", 'checked="checked"');
639 }
640 if ($component->isDisabled()) {
641 $tpl->setVariable("DISABLED", 'disabled="disabled"');
642 }
643
644 $byline = $component->getBylineFor((string) $value);
645 if (!empty($byline)) {
646 $tpl->setVariable("BYLINE", $byline);
647 }
648
649 $tpl->parseCurrentBlock();
650 }
651
652 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());
653 }
654
655 protected function renderMultiSelectField(F\MultiSelect $component, RendererInterface $default_renderer): string
656 {
657 $tpl = $this->getTemplate("tpl.multiselect.html", true, true);
658
659 $options = $component->getOptions();
660 if (count($options) > 0) {
661 $value = $component->getValue();
662 $name = $this->applyName($component, $tpl);
663 foreach ($options as $opt_value => $opt_label) {
664 $tpl->setCurrentBlock("option");
665 $tpl->setVariable("NAME", $name);
666 $tpl->setVariable("VALUE", $opt_value);
667 $tpl->setVariable("LABEL", $opt_label);
668
669 if ($value && in_array($opt_value, $value)) {
670 $tpl->setVariable("CHECKED", 'checked="checked"');
671 }
672 $tpl->parseCurrentBlock();
673 }
674 } else {
675 $tpl->touchBlock("no_options");
676 }
677
678 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());
679 }
680
681 protected function renderDateTimeField(F\DateTime $component, RendererInterface $default_renderer): string
682 {
683 list($component, $tpl) = $this->internalRenderDateTimeField($component, $default_renderer);
684 $label_id = $this->createId();
685 $tpl->setVariable('ID', $label_id);
686 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
687 }
688
692 protected function internalRenderDateTimeField(F\DateTime $component, RendererInterface $default_renderer): array
693 {
694 $tpl = $this->getTemplate("tpl.datetime.html", true, true);
695 $this->applyName($component, $tpl);
696
697 if ($component->getTimeOnly() === true) {
698 $format = $component::TIME_FORMAT;
699 $dt_type = self::TYPE_TIME;
700 } else {
701 $dt_type = self::TYPE_DATE;
702 $format = $this->getTransformedDateFormat(
703 $component->getFormat(),
704 self::DATEPICKER_FORMAT_MAPPING
705 );
706
707 if ($component->getUseTime() === true) {
708 $format .= ' ' . $component::TIME_FORMAT;
709 $dt_type = self::TYPE_DATETIME;
710 }
711 }
712
713 $tpl->setVariable("DTTYPE", $dt_type);
714
715 $min_max_format = self::DATE_DATEPICKER_MINMAX_FORMAT;
716 if ($dt_type === self::TYPE_DATETIME) {
717 $min_max_format = self::DATETIME_DATEPICKER_MINMAX_FORMAT;
718 }
719
720 $min_date = $component->getMinValue();
721 if (!is_null($min_date)) {
722 $tpl->setVariable("MIN_DATE", date_format($min_date, $min_max_format));
723 }
724 $max_date = $component->getMaxValue();
725 if (!is_null($max_date)) {
726 $tpl->setVariable("MAX_DATE", date_format($max_date, $min_max_format));
727 }
728
729 $this->applyValue($component, $tpl, function (?string $value) use ($dt_type) {
730 if ($value !== null) {
731 $value = new \DateTimeImmutable($value);
732 return $value->format(match ($dt_type) {
733 self::TYPE_DATETIME => self::HTML5_NATIVE_DATETIME_FORMAT,
734 self::TYPE_DATE => self::HTML5_NATIVE_DATE_FORMAT,
735 self::TYPE_TIME => self::HTML5_NATIVE_TIME_FORMAT,
736 });
737 }
738 return null;
739 });
740 return [$component, $tpl];
741 }
742
743 protected function renderDurationField(F\Duration $component, RendererInterface $default_renderer): string
744 {
745 $inputs = $component->getInputs();
746
747 $input = array_shift($inputs); //from
748 list($input, $tpl) = $this->internalRenderDateTimeField($input, $default_renderer);
749
750 $from_input_id = $this->createId();
751 $tpl->setVariable('ID', $from_input_id);
752 $input_html = $this->wrapInFormContext($input, $input->getLabel(), $tpl->get(), $from_input_id);
753
754 $input = array_shift($inputs) //until
755 ->withAdditionalPickerconfig(['useCurrent' => false]);
756 list($input, $tpl) = $this->internalRenderDateTimeField($input, $default_renderer);
757 $until_input_id = $this->createId();
758 $tpl->setVariable('ID', $until_input_id);
759 $input_html .= $this->wrapInFormContext($input, $input->getLabel(), $tpl->get(), $until_input_id);
760
761 $tpl = $this->getTemplate("tpl.duration.html", true, true);
762 $tpl->setVariable('DURATION', $input_html);
763 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());//, $from_input_id);
764 }
765
766 protected function renderSection(F\Section $section, RendererInterface $default_renderer): string
767 {
768 $inputs_html = $default_renderer->render($section->getInputs());
769
770 $headline_tpl = $this->getTemplate("tpl.headlines.html", true, true);
771 $headline_tpl->setVariable("HEADLINE", $section->getLabel());
772 $nesting_level = $section->getNestingLevel() + 2;
773 if ($nesting_level > 6) {
774 $nesting_level = 6;
775 };
776 $headline_tpl->setVariable("LEVEL", $nesting_level);
777
778 $headline_html = $headline_tpl->get();
779
780 return $this->wrapInFormContext($section, $headline_html, $inputs_html);
781 }
782
783 protected function renderUrlField(F\Url $component, RendererInterface $default_renderer): string
784 {
785 $tpl = $this->getTemplate("tpl.url.html", true, true);
786 $this->applyName($component, $tpl);
787 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
788
789 $label_id = $this->createId();
790 $tpl->setVariable('ID', $label_id);
791 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
792 }
793
794 protected function renderImageField(F\Image $input, RendererInterface $default_renderer): string
795 {
796 return $this->renderFileField($input, $default_renderer);
797 }
798
799 protected function renderFileField(F\File $input, RendererInterface $default_renderer): string
800 {
801 $template = $this->getTemplate('tpl.file.html', true, true);
802 foreach ($input->getGeneratedDynamicInputs() as $metadata_input) {
803 $file_info = null;
804 if (null !== ($data = $metadata_input->getValue())) {
805 $file_id = (!$input->hasMetadataInputs()) ? $data : $data[0] ?? null;
806
807 if (null !== $file_id) {
808 $file_info = $input->getUploadHandler()->getInfoResult($file_id);
809 }
810 }
811
812 $template = $this->renderFilePreview(
813 $input,
814 $metadata_input,
815 $default_renderer,
816 $file_info,
817 $template
818 );
819 }
820
821 $file_preview_template = $this->getTemplate('tpl.file.html', true, true);
822 $file_preview_template = $this->renderFilePreview(
823 $input,
824 $input->getTemplateForDynamicInputs(),
825 $default_renderer,
826 null,
827 $file_preview_template
828 );
829
830 $template->setVariable('FILE_PREVIEW_TEMPLATE', $file_preview_template->get('block_file_preview'));
831
832 $this->setHelpBlockForFileField($template, $input);
833
834 $input = $this->initClientsideFileInput($input);
835
836 // display the action button (to choose files).
837 $template->setVariable('ACTION_BUTTON', $default_renderer->render(
838 $this->getUIFactory()->button()->shy(
839 $this->txt('select_files_from_computer'),
840 '#'
841 )
842 ));
843
844 return $this->wrapInFormContext(
845 $input,
846 $input->getLabel(),
847 $template->get(),
848 );
849 }
850
851 protected function renderHiddenField(F\Hidden $input): string
852 {
853 $template = $this->getTemplate('tpl.hidden.html', true, true);
854 $this->applyName($input, $template);
855 $this->applyValue($input, $template, $this->escapeSpecialChars());
856 if ($input->isDisabled()) {
857 $template->setVariable("DISABLED", 'disabled="disabled"');
858 }
859 $this->bindJSandApplyId($input, $template);
860 return $template->get();
861 }
862
866 public function registerResources(ResourceRegistry $registry): void
867 {
868 parent::registerResources($registry);
869 $registry->register('assets/css/tagify.css');
870 $registry->register('assets/js/tagInput.js');
871
872 $registry->register('assets/js/dropzone.min.js');
873 $registry->register('assets/js/dropzone.js');
874 $registry->register('assets/js/input.js');
875 $registry->register('assets/js/core.js');
876 $registry->register('assets/js/file.js');
877 // workaround to manipulate the order of scripts
878 $registry->register('assets/js/drilldown.min.js');
879 $registry->register('assets/js/input.factory.min.js');
880 }
881
886 protected function setSignals(F\FormInput $input)
887 {
888 $signals = null;
889 foreach ($input->getTriggeredSignals() as $s) {
890 $signals[] = [
891 "signal_id" => $s->getSignal()->getId(),
892 "event" => $s->getEvent(),
893 "options" => $s->getSignal()->getOptions()
894 ];
895 }
896 if ($signals !== null) {
897 $signals = json_encode($signals);
898
899 $input = $input->withAdditionalOnLoadCode(function ($id) use ($signals) {
900 $code = "il.UI.input.setSignalsForId('$id', $signals);";
901 return $code;
902 });
903
904 $input = $input->withAdditionalOnLoadCode($input->getUpdateOnLoadCode());
905 }
906 return $input;
907 }
908
915 protected function getTransformedDateFormat(
916 DateFormat\DateFormat $origin,
917 array $mapping
918 ): string {
919 $ret = '';
920 foreach ($origin->toArray() as $element) {
921 if (array_key_exists($element, $mapping)) {
922 $ret .= $mapping[$element];
923 } else {
924 $ret .= $element;
925 }
926 }
927 return $ret;
928 }
929
930 protected function renderFilePreview(
931 FI\File $file_input,
932 FormInput $metadata_input,
933 RendererInterface $default_renderer,
934 ?FileInfoResult $file_info,
935 Template $template
936 ): Template {
937 $template->setCurrentBlock('block_file_preview');
938 $template->setVariable('REMOVAL_GLYPH', $default_renderer->render(
939 $this->getUIFactory()->symbol()->glyph()->close()->withAction("#")
940 ));
941
942 if (null !== $file_info) {
943 $template->setVariable('FILE_NAME', $file_info->getName());
944 $template->setVariable(
945 'FILE_SIZE',
946 (string) (new DataSize($file_info->getSize(), DataSize::Byte))
947 );
948 }
949
950 // only render expansion toggles if the input
951 // contains actual (unhidden) inputs.
952 if ($file_input->hasMetadataInputs()) {
953 $template->setVariable('EXPAND_GLYPH', $default_renderer->render(
954 $this->getUIFactory()->symbol()->glyph()->expand()->withAction("#")
955 ));
956 $template->setVariable('COLLAPSE_GLYPH', $default_renderer->render(
957 $this->getUIFactory()->symbol()->glyph()->collapse()->withAction("#")
958 ));
959 }
960
961 $template->setVariable('METADATA_INPUTS', $default_renderer->render($metadata_input));
962
963 $template->parseCurrentBlock();
964
965 return $template;
966 }
967
968 protected function initClientsideFileInput(FI\File $input): FI\File
969 {
970 return $input->withAdditionalOnLoadCode(
971 function ($id) use ($input) {
972 $current_file_count = count($input->getGeneratedDynamicInputs());
973 $translations = json_encode($input->getTranslations());
974 $is_disabled = ($input->isDisabled()) ? 'true' : 'false';
975 $php_upload_limit = $this->getUploadLimitResolver()->getPhpUploadLimitInBytes();
976 $should_upload_be_chunked = ($input->getMaxFileSize() > $php_upload_limit) ? 'true' : 'false';
977 $chunk_size = (int) floor($php_upload_limit * self::FILE_UPLOAD_CHUNK_SIZE_FACTOR);
978 return "
979 $(document).ready(function () {
980 il.UI.Input.File.init(
981 '$id',
982 '{$input->getUploadHandler()->getUploadURL()}',
983 '{$input->getUploadHandler()->getFileRemovalURL()}',
984 '{$input->getUploadHandler()->getFileIdentifierParameterName()}',
985 $current_file_count,
986 {$input->getMaxFiles()},
987 {$input->getMaxFileSize()},
988 '{$this->prepareDropzoneJsMimeTypes($input->getAcceptedMimeTypes())}',
989 $is_disabled,
990 $translations,
991 $should_upload_be_chunked,
992 $chunk_size
993 );
994 });
995 ";
996 }
997 );
998 }
999
1005 protected function prepareDropzoneJsMimeTypes(array $mime_types): string
1006 {
1007 $mime_type_string = '';
1008 foreach ($mime_types as $index => $mime_type) {
1009 $mime_type_string .= (isset($mime_types[$index + 1])) ? "$mime_type," : $mime_type;
1010 }
1011
1012 return $mime_type_string;
1013 }
1014
1015 protected function renderColorSelectField(F\ColorSelect $component, RendererInterface $default_renderer): string
1016 {
1017 $tpl = $this->getTemplate("tpl.color_select.html", true, true);
1018 $this->applyName($component, $tpl);
1019 $tpl->setVariable('VALUE', $component->getValue());
1020
1021 $label_id = $this->createId();
1022 $tpl->setVariable('ID', $label_id);
1023 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
1024 }
1025
1026 protected function renderRatingField(F\Rating $component, RendererInterface $default_renderer): string
1027 {
1028 $tpl = $this->getTemplate("tpl.rating.html", true, true);
1029 $id = $this->createId();
1030 $aria_description_id = $id . '_desc';
1031 $tpl->setVariable('DESCRIPTION_SRC_ID', $aria_description_id);
1032
1033 $option_count = count(FiveStarRatingScale::cases()) - 1;
1034
1035 foreach (range($option_count, 1, -1) as $option) {
1036 $tpl->setCurrentBlock('scaleoption');
1037 $tpl->setVariable('ARIALABEL', $this->txt($option . 'stars'));
1038 $tpl->setVariable('OPT_VALUE', (string) $option);
1039 $tpl->setVariable('OPT_ID', $id . '-' . $option);
1040 $tpl->setVariable('NAME', $component->getName());
1041 $tpl->setVariable('DESCRIPTION_ID', $aria_description_id);
1042
1043 if ($component->getValue() === FiveStarRatingScale::from((int) $option)) {
1044 $tpl->setVariable("SELECTED", ' checked="checked"');
1045 }
1046 if ($component->isDisabled()) {
1047 $tpl->setVariable("DISABLED", 'disabled="disabled"');
1048 }
1049 $tpl->parseCurrentBlock();
1050 }
1051
1052 if (!$component->isRequired()) {
1053 $tpl->setVariable('NEUTRAL_ID', $id . '-0');
1054 $tpl->setVariable('NEUTRAL_NAME', $component->getName());
1055 $tpl->setVariable('NEUTRAL_LABEL', $this->txt('reset_stars'));
1056 $tpl->setVariable('NEUTRAL_DESCRIPTION_ID', $aria_description_id);
1057
1058 if ($component->getValue() === FiveStarRatingScale::NONE || is_null($component->getValue())) {
1059 $tpl->setVariable('NEUTRAL_SELECTED', ' checked="checked"');
1060 }
1061 }
1062
1063 if ($txt = $component->getAdditionalText()) {
1064 $tpl->setVariable('TEXT', $txt);
1065 }
1066
1067 if ($component->isDisabled()) {
1068 $tpl->touchBlock('disabled');
1069 }
1070 if ($average = $component->getCurrentAverage()) {
1071 $average_title = sprintf($this->txt('rating_average'), $average);
1072 $tpl->setVariable('AVERAGE_VALUE', $average_title);
1073 $tpl->setVariable('AVERAGE_VALUE_PERCENT', $average / $option_count * self::CENTUM);
1074 }
1075
1076 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());
1077 }
1078
1079 protected function renderTreeMultiSelectField(F\TreeMultiSelect $component, RendererInterface $default_renderer): string
1080 {
1081 $template = $this->prepareTreeSelectTemplate($component, $default_renderer);
1082
1083 if ($component->canSelectChildNodes()) {
1084 $select_child_nodes = 'true';
1085 } else {
1086 $select_child_nodes = 'false';
1087 }
1088
1089 $enriched_component = $component->withAdditionalOnLoadCode(
1090 static fn($id) => "il.UI.Input.treeSelect.initTreeMultiSelect('$id', $select_child_nodes);"
1091 );
1092
1093 $id = $this->bindJSandApplyId($enriched_component, $template);
1094
1095 return $this->wrapInFormContext($component, $component->getLabel(), $template->get(), $id);
1096 }
1097
1098 protected function renderTreeSelectField(F\TreeSelect $component, RendererInterface $default_renderer): string
1099 {
1100 $template = $this->prepareTreeSelectTemplate($component, $default_renderer);
1101
1102 $enriched_component = $component->withAdditionalOnLoadCode(
1103 static fn($id) => "il.UI.Input.treeSelect.initTreeSelect('$id');"
1104 );
1105
1106 $id = $this->bindJSandApplyId($enriched_component, $template);
1107
1108 return $this->wrapInFormContext($component, $component->getLabel(), $template->get(), $id);
1109 }
1110
1111 protected function prepareTreeSelectTemplate(
1112 TreeSelect|TreeMultiSelect $component,
1113 RendererInterface $default_renderer,
1114 ): Template {
1115 $template = $this->getTemplate('tpl.tree_select.html', true, true);
1116
1117 if ($component->isDisabled()) {
1118 $template->setVariable('DISABLED', 'disabled');
1119 }
1120
1121 $template->setVariable('SELECT_LABEL', $this->txt('select'));
1122 $template->setVariable('CLOSE_LABEL', $this->txt('close'));
1123 $template->setVariable('LABEL', $component->getLabel());
1124
1125 $template->setVariable('INPUT_TEMPLATE', $default_renderer->render(
1126 $component->getTemplateForDynamicInputs()
1127 ));
1128 $template->setVariable('BREADCRUMB_TEMPLATE', $default_renderer->render(
1129 $this->getUIFactory()->breadcrumbs([$this->getUIFactory()->link()->standard('label', '#')])
1130 ));
1131 $template->setVariable('BREADCRUMBS', $default_renderer->render(
1132 $this->getUIFactory()->breadcrumbs([])
1133 ));
1134
1135 $node_factory = $this->getUIFactory()->input()->field()->node();
1136 $node_generator = $component->getNodeRetrieval()->getNodes(
1137 $node_factory,
1138 $this->getUIFactory()->symbol()->icon(),
1139 );
1140
1141 $nodes = [];
1142 foreach ($node_generator as $node) {
1143 // check against public interface, will be delegated to rendering chain.
1144 $this->checkArgInstanceOf('node', $node, Component\Input\Field\Node\Node::class);
1145 $nodes[] = $node;
1146 }
1147
1148 $template->setVariable('DRILLDOWN', $default_renderer->render(
1149 $this->getUIFactory()->menu()->drilldown($component->getLabel(), $nodes)
1150 ));
1151
1153 $dynamic_inputs_generator = (static fn() => yield from $component->getGeneratedDynamicInputs())();
1154
1155 $leaf_generator = $component->getNodeRetrieval()->getNodesAsLeaf(
1156 $this->getUIFactory()->input()->field()->node(),
1157 $this->getUIFactory()->symbol()->icon(),
1158 $component->getValue(),
1159 );
1160
1161 $lockstep_iterator = $this->iterateGeneratorsInLockstep($leaf_generator, $dynamic_inputs_generator);
1162
1163 foreach ($lockstep_iterator as [$leaf, $dynamic_input]) {
1164 // check against internal interface, will not be delegated to rendering chain.
1166 $this->checkArgInstanceOf('leaf', $leaf, Node\Leaf::class);
1167
1168 $value_template = $this->getTemplate('tpl.tree_select.html', true, true);
1169 $value_template->setCurrentBlock('with_value_template');
1170 $value_template->setVariable('NODE_ID', (string) ($leaf->getId()));
1171 $value_template->setVariable('NODE_NAME', $leaf->getName());
1172 $value_template->setVariable('INPUT_TEMPLATE', $default_renderer->render($dynamic_input));
1173 $value_template->setVariable('UNSELECT_NODE_LABEL', sprintf($this->txt('unselect_node'), $leaf->getName()));
1174 $value_template->parseCurrentBlock();
1175
1176 $template->setCurrentBlock('with_value');
1177 $template->setVariable('VALUE', $value_template->get('with_value_template'));
1178 $template->parseCurrentBlock();
1179 }
1180
1181 $this->toJS('unselect_node');
1182 $this->toJS('select_node');
1183
1184 return $template;
1185 }
1186
1194 protected function iterateGeneratorsInLockstep(\Generator $a, \Generator $b): \Generator
1195 {
1196 while ($a->valid() && $b->valid()) {
1197 yield [$a->current(), $b->current()];
1198 $a->next();
1199 $b->next();
1200 }
1201 if ($a->valid() || $b->valid()) {
1202 throw new LogicException('Generators do not have equal lenghts.');
1203 }
1204 }
1205
1206 private function setHelpBlockForFileField(Template $template, FI\File $input): void
1207 {
1208 $template->setCurrentBlock('HELP_BLOCK');
1209
1210 $template->setCurrentBlock('MAX_FILE_SIZE');
1211 $template->setVariable('FILE_SIZE_LABEL', $this->txt('file_notice'));
1212 $template->setVariable('FILE_SIZE_VALUE', new DataSize($input->getMaxFileSize(), DataSize::Byte));
1213 $template->parseCurrentBlock();
1214
1215 $template->setCurrentBlock('MAX_FILES');
1216 $template->setVariable('FILES_LABEL', $this->txt('ui_file_upload_max_nr'));
1217 $template->setVariable('FILES_VALUE', $input->getMaxFiles());
1218 $template->parseCurrentBlock();
1219
1220 $template->parseCurrentBlock();
1221 }
1222}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
This class provides the data size with additional information to remove the work to calculate the siz...
Definition: DataSize.php:31
A Date Format provides a format definition akin to PHP's date formatting options, but stores the sing...
Definition: DateFormat.php:27
A password is used as part of credentials for authentication.
Definition: Password.php:31
The scope of this class is split ilias-conform URI's into components.
Definition: URI.php:35
FiveStarRatingScale
This is the scale for the Rating Input.
An internal class for the clickable, non-editable Input Fields within Filters.
This implements the checkbox input.
Definition: Checkbox.php:36
This implements the duration input group.
Definition: Duration.php:38
This implements the group input.
Definition: Group.php:41
getValue()
Get the value that is displayed in the input client side.
getTemplateForDynamicInputs()
Returns an Input Field which will be used to generate similar inputs on both server and client.
getGeneratedDynamicInputs()
Returns serverside generated dynamic Inputs, which happens when providing values with.
This implements the multi-select input.
Definition: MultiSelect.php:32
This implements the numeric input.
Definition: Numeric.php:34
This implements the radio input.
Definition: Radio.php:35
renderPasswordField(F\Password $component, RendererInterface $default_renderer)
Definition: Renderer.php:441
renderImageField(F\Image $input, RendererInterface $default_renderer)
Definition: Renderer.php:794
bindJSandApplyId(Component\JavaScriptBindable $component, Template $tpl)
Definition: Renderer.php:240
renderDateTimeField(F\DateTime $component, RendererInterface $default_renderer)
Definition: Renderer.php:681
renderCheckboxField(F\Checkbox $component, RendererInterface $default_renderer)
Definition: Renderer.php:325
renderSelectField(F\Select $component, RendererInterface $default_renderer)
Definition: Renderer.php:481
renderRadioField(F\Radio $component, RendererInterface $default_renderer)
Definition: Renderer.php:623
renderTagField(F\Tag $component, RendererInterface $default_renderer)
Definition: Renderer.php:394
setHelpBlockForFileField(Template $template, FI\File $input)
Definition: Renderer.php:1206
renderMultiSelectField(F\MultiSelect $component, RendererInterface $default_renderer)
Definition: Renderer.php:655
renderNumericField(F\Numeric $component, RendererInterface $default_renderer)
Definition: Renderer.php:312
renderColorSelectField(F\ColorSelect $component, RendererInterface $default_renderer)
Definition: Renderer.php:1015
renderOptionalGroup(F\OptionalGroup $component, RendererInterface $default_renderer)
Definition: Renderer.php:339
renderSection(F\Section $section, RendererInterface $default_renderer)
Definition: Renderer.php:766
applyName(FormInput $component, Template $tpl)
Definition: Renderer.php:233
renderUrlField(F\Url $component, RendererInterface $default_renderer)
Definition: Renderer.php:783
internalRenderDateTimeField(F\DateTime $component, RendererInterface $default_renderer)
Definition: Renderer.php:692
renderFileField(F\File $input, RendererInterface $default_renderer)
Definition: Renderer.php:799
renderDurationField(F\Duration $component, RendererInterface $default_renderer)
Definition: Renderer.php:743
wrapInFormContext(FormInput $component, string $label, string $input_html, ?string $id_for_label=null, ?string $dependant_group_html=null)
Definition: Renderer.php:173
renderSwitchableGroup(F\SwitchableGroup $component, RendererInterface $default_renderer)
Definition: Renderer.php:357
getTransformedDateFormat(DateFormat\DateFormat $origin, array $mapping)
Return the datetime format in a form fit for the JS-component of this input.
Definition: Renderer.php:915
prepareDropzoneJsMimeTypes(array $mime_types)
Appends all given mime-types to a comma-separated string.
Definition: Renderer.php:1005
renderRatingField(F\Rating $component, RendererInterface $default_renderer)
Definition: Renderer.php:1026
registerResources(ResourceRegistry $registry)
Announce resources this renderer requires.
Definition: Renderer.php:866
renderTreeSelectField(F\TreeSelect $component, RendererInterface $default_renderer)
Definition: Renderer.php:1098
renderFilePreview(FI\File $file_input, FormInput $metadata_input, RendererInterface $default_renderer, ?FileInfoResult $file_info, Template $template)
Definition: Renderer.php:930
render(Component\Component $component, RendererInterface $default_renderer)
Definition: Renderer.php:88
renderTreeMultiSelectField(F\TreeMultiSelect $component, RendererInterface $default_renderer)
Definition: Renderer.php:1079
iterateGeneratorsInLockstep(\Generator $a, \Generator $b)
Iterates over two Generators in lockstep, yielding their current values as paired arrays which can be...
Definition: Renderer.php:1194
applyValue(FormInput $component, Template $tpl, ?callable $escape=null)
Escape values for rendering with a Callable "$escape" In order to prevent XSS-attacks,...
Definition: Renderer.php:255
renderLinkField(F\Link $component, RendererInterface $default_renderer)
Definition: Renderer.php:286
This implements the section input.
Definition: Section.php:34
This implements the text input.
Definition: Text.php:33
This implements the textarea input.
Definition: Textarea.php:34
This implements the URL input.
Definition: Url.php:35
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
ilErrorHandling $error
Definition: class.ilias.php:69
return true
$txt
Definition: error.php:31
This describes inputs that can be used in forms.
Definition: FormInput.php:33
This describes commonalities between all inputs.
Definition: Input.php:47
getValue()
Get the value that is displayed in the input client side.
Interface to be extended by components that have the possibility to bind to Javascript.
withAdditionalOnLoadCode(Closure $binder)
Add some onload-code to the component instead of replacing the existing one.
getOnLoadCode()
Get the currently bound on load code.
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.
get(?string $block=null)
Get the rendered template or a specific block.
setCurrentBlock(string $name)
Set the block to work on.
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: Checkbox.php:21
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Checkbox.php:21
trait JavaScriptBindable
Trait for components implementing JavaScriptBindable providing standard implementation.
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples