19declare(strict_types=1);
41use ILIAS\UI\Implementation\Component\ComponentHelper;
103 case ($component instanceof
F\
Section):
109 case ($component instanceof
F\
Link):
112 case ($component instanceof
F\
Group):
113 return $default_renderer->render($component->getInputs());
115 case ($component instanceof
F\
Text):
118 case ($component instanceof
F\
Numeric):
124 case ($component instanceof
F\
Tag):
130 case ($component instanceof
F\
Select):
134 return $this->renderMarkdownField($component, $default_renderer);
137 return $this->renderTextareaField($component, $default_renderer);
139 case ($component instanceof
F\
Radio):
148 case ($component instanceof
F\
Image):
151 case ($component instanceof
F\
File):
154 case ($component instanceof
F\
Url):
157 case ($component instanceof
F\
Hidden):
163 case ($component instanceof
F\
Rating):
181 ?
string $id_for_label =
null,
182 ?
string $dependant_group_html =
null
186 $tpl->setVariable(
"LABEL", $label);
187 $tpl->setVariable(
"INPUT", $input_html);
189 $tpl->setVariable(
"INPUT_NAME", $component->getName());
193 $tpl->setVariable(
"BINDING_ID", $binding_id);
197 $tpl->setCurrentBlock(
'for');
198 $tpl->setVariable(
"ID", $id_for_label);
199 $tpl->parseCurrentBlock();
201 $tpl->touchBlock(
'tabindex');
206 $tpl->setVariable(
"BYLINE", $byline);
211 $tpl->setCurrentBlock(
'required');
212 $tpl->setVariable(
"REQUIRED_ARIA", $this->
txt(
'required_field'));
213 $tpl->parseCurrentBlock();
217 $tpl->touchBlock(
"disabled");
220 $error = $component->getError();
223 $tpl->setVariable(
"ERROR_LABEL", $this->
txt(
"ui_error"));
224 $tpl->setVariable(
"ERROR_ID", $error_id);
225 $tpl->setVariable(
"ERROR",
$error);
227 $tpl->setVariable(
"ERROR_FOR_ID", $id_for_label);
231 if ($dependant_group_html) {
232 $tpl->setVariable(
"DEPENDANT_GROUP", $dependant_group_html);
239 $name = $component->getName();
246 $id = $this->bindJavaScript($component) ?? $this->createId();
262 if (!is_null($escape)) {
263 $value = $escape($value);
265 if (isset($value) && $value !==
'') {
272 return function ($v) {
276 return htmlspecialchars((
string) $v, ENT_QUOTES,
'utf-8',
false);
282 return function ($v) {
286 return htmlentities((
string) $v, ENT_QUOTES,
'utf-8',
false);
292 $input_html = $default_renderer->render($component->getInputs());
293 return $this->wrapInFormContext(
295 $component->getLabel(),
302 $tpl = $this->getTemplate(
"tpl.text.html",
true,
true);
303 $this->applyName($component, $tpl);
305 if ($component->getMaxLength()) {
306 $tpl->setVariable(
"MAX_LENGTH", $component->getMaxLength());
309 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
311 $label_id = $this->createId();
312 $tpl->setVariable(
'ID', $label_id);
313 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
318 $tpl = $this->getTemplate(
"tpl.numeric.html",
true,
true);
319 $this->applyName($component, $tpl);
320 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
322 $tpl->setVariable(
"STEPSIZE", $component->getStepSize());
324 $label_id = $this->createId();
325 $tpl->setVariable(
'ID', $label_id);
326 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
331 $tpl = $this->getTemplate(
"tpl.checkbox.html",
true,
true);
332 $this->applyName($component, $tpl);
334 if ($component->getValue()) {
335 $tpl->touchBlock(
"value");
338 $label_id = $this->createId();
339 $tpl->setVariable(
'ID', $label_id);
340 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
345 $tpl = $this->getTemplate(
"tpl.optionalgroup_label.html",
true,
true);
346 $tpl->setVariable(
'LABEL', $component->getLabel());
347 $tpl->setVariable(
"NAME", $component->getName());
348 if ($component->getValue()) {
349 $tpl->setVariable(
"CHECKED",
'checked="checked"');
352 $label_id = $this->createId();
353 $tpl->setVariable(
'ID', $label_id);
355 $label = $tpl->get();
356 $input_html = $default_renderer->render($component->getInputs());
358 return $this->wrapInFormContext($component, $label, $input_html, $label_id);
364 if ($component->getValue() !==
null) {
365 list($value, ) = $component->getValue();
369 foreach ($component->getInputs() as $key => $group) {
370 $tpl = $this->getTemplate(
"tpl.switchablegroup_label.html",
true,
true);
371 $tpl->setVariable(
'LABEL', $group->getLabel());
372 $tpl->setVariable(
"NAME", $component->getName());
373 $tpl->setVariable(
"VALUE", $key);
375 $label_id = $this->createId();
376 $tpl->setVariable(
'ID', $label_id);
378 if ($key == $value) {
379 $tpl->setVariable(
"CHECKED",
'checked="checked"');
382 $input_html .= $this->wrapInFormContext(
385 $default_renderer->render($group),
391 return $this->wrapInFormContext(
393 $component->getLabel(),
400 $tpl = $this->getTemplate(
"tpl.tag_input.html",
true,
true);
401 $this->applyName($component, $tpl);
403 $configuration = $component->getConfiguration();
404 $value = $component->getValue();
409 return [
'value' => rawurlencode($v),
'display' => $v];
415 $autocomplete_endpoint =
'undefined';
416 $autocomplete_token =
'undefined';
417 if ($component->getAsyncAutocompleteEndpoint() !==
null) {
418 $autocomplete_endpoint = $component->getAsyncAutocompleteEndpoint()
419 ->renderObject([$component->getAsyncAutocompleteToken()]);
420 $autocomplete_token = $component->getAsyncAutocompleteToken()->render();
423 $component = $component->withAdditionalOnLoadCode(
424 function (
$id) use ($configuration, $value, $autocomplete_endpoint, $autocomplete_token) {
425 $encoded = json_encode($configuration);
426 $value = json_encode($value);
427 return 'il.UI.Input.tagInput.init(document.querySelector('
428 .
"'#{$id} .c-field-tag'), {$encoded}, {$value},"
429 .
" {$autocomplete_endpoint}, "
430 .
" {$autocomplete_token}"
435 if ($component->isDisabled()) {
436 $tpl->setVariable(
"DISABLED",
"disabled");
437 $tpl->setVariable(
"READONLY",
"readonly");
440 $label_id = $this->createId();
441 $tpl->setVariable(
'ID', $label_id);
442 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
447 $tpl = $this->getTemplate(
"tpl.password.html",
true,
true);
448 $this->applyName($component, $tpl);
450 if ($component->getRevelation()) {
451 $component = $component->withResetSignals();
452 $sig_reveal = $component->getRevealSignal();
453 $sig_mask = $component->getMaskSignal();
454 $component = $component->withAdditionalOnLoadCode(
function (
$id) use ($sig_reveal, $sig_mask) {
456 "$(document).on('$sig_reveal', function() {
457 const fieldContainer = document.querySelector('#$id .c-input__field .c-field-password');
458 fieldContainer.classList.add('revealed');
459 fieldContainer.getElementsByTagName('input').item(0).type='text';
461 "$(document).on('$sig_mask', function() {
462 const fieldContainer = document.querySelector('#$id .c-input__field .c-field-password');
463 fieldContainer.classList.remove('revealed');
464 fieldContainer.getElementsByTagName('input').item(0).type='password';
468 $f = $this->getUIFactory();
469 $btn_reveal =
$f->button()->shy(
'',
'')->withSymbol(
$f->symbol()->glyph()->eyeopen())
470 ->withOnClick($sig_reveal);
471 $btn_mask =
$f->button()->shy(
'',
'')->withSymbol(
$f->symbol()->glyph()->eyeclosed())
472 ->withOnClick($sig_mask);
474 $tpl->setVariable(
'PASSWORD_REVEAL', $default_renderer->render($btn_reveal));
475 $tpl->setVariable(
'PASSWORD_MASK', $default_renderer->render($btn_mask));
478 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
480 $label_id = $this->createId();
481 $tpl->setVariable(
'ID', $label_id);
482 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
487 $tpl = $this->getTemplate(
"tpl.select.html",
true,
true);
488 $this->applyName($component, $tpl);
490 $value = $component->getValue();
491 $value_is_empty = $value ===
null || $value ===
'';
493 $tpl->setCurrentBlock(
"options");
494 if ($value_is_empty) {
495 $tpl->setVariable(
"SELECTED",
'selected="selected"');
497 if ($value_is_empty && $component->isRequired()) {
498 $tpl->setVariable(
"DISABLED_OPTION",
"disabled");
499 $tpl->setVariable(
"HIDDEN",
"hidden");
502 if ($value_is_empty || !$component->isRequired()) {
503 $tpl->setVariable(
"VALUE",
null);
504 $tpl->setVariable(
"VALUE_STR", $component->isRequired() ? $this->txt(
'ui_select_dropdown_label') :
'-');
505 $tpl->parseCurrentBlock();
508 foreach ($component->getOptions() as $option_key => $option_value) {
509 $tpl->setCurrentBlock(
"options");
510 if (!$value_is_empty && $value == $option_key) {
511 $tpl->setVariable(
"SELECTED",
'selected="selected"');
513 $tpl->setVariable(
"VALUE", $option_key);
514 $tpl->setVariable(
"VALUE_STR", $option_value);
515 $tpl->parseCurrentBlock();
518 $label_id = $this->createId();
519 $tpl->setVariable(
'ID', $label_id);
520 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
523 protected function renderMarkdownField(
F\
Markdown $component, RendererInterface $default_renderer): string
525 [$textarea_tpl, $component] = $this->getPreparedTextareaTemplate($component);
528 $component = $component->withAdditionalOnLoadCode(
529 static function (
$id) use ($component):
string {
531 il.UI.Input.markdown.init(
532 document.querySelector('#$id .c-input__field textarea')?.id,
533 '{$component->getMarkdownRenderer()->getAsyncUrl()}',
534 '{$component->getMarkdownRenderer()->getParameterName()}'
540 $textarea_id = $this->createId();
541 $textarea_tpl->setVariable(
'ID', $textarea_id);
543 $markdown_tpl = $this->getTemplate(
"tpl.markdown.html",
true,
true);
544 $markdown_tpl->setVariable(
'TEXTAREA', $textarea_tpl->get());
546 $markdown_tpl->setVariable(
548 $component->getMarkdownRenderer()->render(
549 $this->htmlEntities()($component->getValue() ??
'')
553 $markdown_tpl->setVariable(
555 $default_renderer->render(
556 $this->getUIFactory()->viewControl()->mode([
557 $this->txt(
'ui_md_input_edit') =>
'#',
558 $this->txt(
'ui_md_input_view') =>
'#',
564 $markdown_actions_glyphs = [
565 'ACTION_HEADING' => $this->getUIFactory()->symbol()->glyph()->header(),
566 'ACTION_LINK' => $this->getUIFactory()->symbol()->glyph()->link(),
567 'ACTION_BOLD' => $this->getUIFactory()->symbol()->glyph()->bold(),
568 'ACTION_ITALIC' => $this->getUIFactory()->symbol()->glyph()->italic(),
569 'ACTION_ORDERED_LIST' => $this->getUIFactory()->symbol()->glyph()->numberedlist(),
570 'ACTION_UNORDERED_LIST' => $this->getUIFactory()->symbol()->glyph()->bulletlist()
573 foreach ($markdown_actions_glyphs as $tpl_variable => $glyph) {
574 $action = $this->getUIFactory()->button()->standard(
'',
'#')->withSymbol($glyph);
576 if ($component->isDisabled()) {
577 $action = $action->withUnavailableAction();
580 $markdown_tpl->setVariable($tpl_variable, $default_renderer->render($action));
583 return $this->wrapInFormContext($component, $component->getLabel(), $markdown_tpl->get());
586 protected function renderTextareaField(
F\Textarea $component, RendererInterface $default_renderer): string
588 [$tpl, $component] = $this->getPreparedTextareaTemplate($component);
591 $component = $component->withAdditionalOnLoadCode(
592 static fn(
$id) =>
"il.UI.Input.textarea.init(document.querySelector('#$id .c-input__field textarea')?.id);",
595 $label_id = $this->createId();
596 $tpl->setVariable(
'ID', $label_id);
597 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
605 $tpl = $this->getTemplate(
"tpl.textarea.html",
true,
true);
607 if (0 < $component->getMaxLimit()) {
608 $tpl->setVariable(
'REMAINDER_TEXT', $this->txt(
'ui_chars_remaining'));
609 $tpl->setVariable(
'REMAINDER', $component->getMaxLimit() - strlen($component->getValue() ??
''));
610 $tpl->setVariable(
'MAX_LIMIT', $component->getMaxLimit());
613 if (
null !== $component->getMinLimit()) {
614 $tpl->setVariable(
'MIN_LIMIT', $component->getMinLimit());
617 [$mustache_variable_html, $component] = $this->renderMustacheVariables($component);
618 $tpl->setVariable(
'MUSTACHE_VARIABLES_HTML', $mustache_variable_html);
620 $this->applyName($component, $tpl);
624 $this->mustacheVariableEntities()
626 return [$tpl, $component];
636 return [
'', $component];
639 $template = $this->getTemplate(
'tpl.mustache_variables.html',
true,
true);
640 $template->setVariable(
'MUSTACHE_VARIABLE_USAGE_INFO', $this->txt(
'ui_mustache_variables_usage_info'));
648 $template->setCurrentBlock(
'with_mustache_variable_definition');
649 $template->setVariable(
'VARIABLE_NAME', $variable_name);
650 $template->setVariable(
'VARIABLE_DESCRIPTION', $description);
651 $template->parseCurrentBlock();
655 $enriched_component = $component->withAdditionalOnLoadCode(
static fn(
$id) =>
"
656 il.UI.Input.mustacheVariables.init(
657 il.UI.Input.textarea.get(document.querySelector('#$id .c-input__field textarea')?.id) ??
658 il.UI.Input.markdown.get(document.querySelector('#$id .c-input__field textarea')?.id),
659 document.getElementById('$id'),
663 return [$template->get(), $enriched_component];
668 $tpl = $this->getTemplate(
"tpl.radio.html",
true,
true);
669 $id = $this->createId();
671 foreach ($component->getOptions() as $value => $label) {
673 if ($component->hasOptionFilter()) {
674 $tpl->touchBlock(
'is_filter_option');
677 $opt_id =
$id .
'_' . $value .
'_opt';
679 $tpl->setCurrentBlock(
'optionblock');
680 $tpl->setVariable(
"NAME", $component->getName());
681 $tpl->setVariable(
"OPTIONID", $opt_id);
682 $tpl->setVariable(
"VALUE", $value);
683 $tpl->setVariable(
"LABEL", $label);
685 if ($component->getValue() !==
null && $component->getValue() == $value) {
686 $tpl->setVariable(
"CHECKED",
'checked="checked"');
688 if ($component->isDisabled()) {
689 $tpl->setVariable(
"DISABLED",
'disabled="disabled"');
692 $byline = $component->getBylineFor((
string) $value);
693 if (!empty($byline)) {
694 $tpl->setVariable(
"BYLINE", $byline);
697 $tpl->parseCurrentBlock();
700 if ($component->hasOptionFilter()) {
702 $tpl->touchBlock(
"has_option_filter");
703 $field_html = $tpl->get();
704 [$field_html, $component] = $this->renderOptionFilter($field_html, $component, $default_renderer);
706 $field_html = $tpl->get();
709 return $this->wrapInFormContext($component, $component->getLabel(), $field_html);
714 $tpl = $this->getTemplate(
"tpl.multiselect.html",
true,
true);
716 $options = $component->getOptions();
718 $value = $component->getValue();
719 $name = $this->applyName($component, $tpl);
720 foreach (
$options as $opt_value => $opt_label) {
722 if ($component->hasOptionFilter()) {
723 $tpl->touchBlock(
'is_filter_option');
726 $tpl->setCurrentBlock(
"option");
727 $tpl->setVariable(
"NAME", $name);
728 $tpl->setVariable(
"VALUE", $opt_value);
729 $tpl->setVariable(
"LABEL", $opt_label);
731 if ($value && in_array($opt_value, $value)) {
732 $tpl->setVariable(
"CHECKED",
'checked="checked"');
735 $tpl->parseCurrentBlock();
738 $tpl->touchBlock(
"no_options");
741 if ($component->hasOptionFilter()) {
743 $tpl->touchBlock(
"has_option_filter");
744 $field_html = $tpl->get();
745 [$field_html, $component] = $this->renderOptionFilter($field_html, $component, $default_renderer);
747 $field_html = $tpl->get();
750 return $this->wrapInFormContext($component, $component->getLabel(), $field_html);
755 list($component, $tpl) = $this->internalRenderDateTimeField($component, $default_renderer);
756 $label_id = $this->createId();
757 $tpl->setVariable(
'ID', $label_id);
758 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
766 $tpl = $this->getTemplate(
"tpl.datetime.html",
true,
true);
767 $this->applyName($component, $tpl);
769 if ($component->getTimeOnly() ===
true) {
770 $format = $component::TIME_FORMAT;
771 $dt_type = self::TYPE_TIME;
773 $dt_type = self::TYPE_DATE;
774 $format = $this->getTransformedDateFormat(
775 $component->getFormat(),
776 self::DATEPICKER_FORMAT_MAPPING
779 if ($component->getUseTime() ===
true) {
780 $format .=
' ' . $component::TIME_FORMAT;
781 $dt_type = self::TYPE_DATETIME;
785 $tpl->setVariable(
"DTTYPE", $dt_type);
787 $min_max_format = self::DATE_DATEPICKER_MINMAX_FORMAT;
788 if ($dt_type === self::TYPE_DATETIME) {
789 $min_max_format = self::DATETIME_DATEPICKER_MINMAX_FORMAT;
792 $min_date = $component->getMinValue();
793 if (!is_null($min_date)) {
794 $tpl->setVariable(
"MIN_DATE", date_format($min_date, $min_max_format));
796 $max_date = $component->getMaxValue();
797 if (!is_null($max_date)) {
798 $tpl->setVariable(
"MAX_DATE", date_format($max_date, $min_max_format));
801 $this->applyValue($component, $tpl,
function (?
string $value) use ($dt_type) {
802 if ($value !==
null) {
803 $value = new \DateTimeImmutable($value);
804 return $value->format(match ($dt_type) {
805 self::TYPE_DATETIME => self::HTML5_NATIVE_DATETIME_FORMAT,
806 self::TYPE_DATE => self::HTML5_NATIVE_DATE_FORMAT,
807 self::TYPE_TIME => self::HTML5_NATIVE_TIME_FORMAT,
812 return [$component, $tpl];
817 $inputs = $component->getInputs();
820 list($input, $tpl) = $this->internalRenderDateTimeField($input, $default_renderer);
822 $from_input_id = $this->createId();
823 $tpl->setVariable(
'ID', $from_input_id);
824 $input_html = $this->wrapInFormContext($input, $input->getLabel(), $tpl->get(), $from_input_id);
827 ->withAdditionalPickerconfig([
'useCurrent' =>
false]);
828 list($input, $tpl) = $this->internalRenderDateTimeField($input, $default_renderer);
829 $until_input_id = $this->createId();
830 $tpl->setVariable(
'ID', $until_input_id);
831 $input_html .= $this->wrapInFormContext($input, $input->getLabel(), $tpl->get(), $until_input_id);
833 $tpl = $this->getTemplate(
"tpl.duration.html",
true,
true);
834 $tpl->setVariable(
'DURATION', $input_html);
835 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());
840 $inputs_html = $default_renderer->render($section->getInputs());
842 $headline_tpl = $this->getTemplate(
"tpl.headlines.html",
true,
true);
843 $headline_tpl->setVariable(
"HEADLINE", $section->getLabel());
844 $nesting_level = $section->getNestingLevel() + 2;
845 if ($nesting_level > 6) {
848 $headline_tpl->setVariable(
"LEVEL", $nesting_level);
850 $headline_html = $headline_tpl->get();
852 return $this->wrapInFormContext($section, $headline_html, $inputs_html);
857 $tpl = $this->getTemplate(
"tpl.url.html",
true,
true);
858 $this->applyName($component, $tpl);
859 $this->applyValue($component, $tpl, $this->escapeSpecialChars());
861 $label_id = $this->createId();
862 $tpl->setVariable(
'ID', $label_id);
863 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
868 return $this->renderFileField($input, $default_renderer);
873 $template = $this->getTemplate(
'tpl.file.html',
true,
true);
874 foreach ($input->getGeneratedDynamicInputs() as $metadata_input) {
876 if (
null !== (
$data = $metadata_input->getValue())) {
877 $file_id = (!$input->hasMetadataInputs()) ?
$data :
$data[0] ??
null;
879 if (
null !== $file_id) {
880 $file_info = $input->getUploadHandler()->getInfoResult($file_id);
884 $template = $this->renderFilePreview(
893 $file_preview_template = $this->getTemplate(
'tpl.file.html',
true,
true);
894 $file_preview_template = $this->renderFilePreview(
896 $input->getTemplateForDynamicInputs(),
899 $file_preview_template
902 $template->setVariable(
'FILE_PREVIEW_TEMPLATE', $file_preview_template->get(
'block_file_preview'));
904 $this->setHelpBlockForFileField($template, $input);
906 $input = $this->initClientsideFileInput($input);
909 $template->setVariable(
'ACTION_BUTTON', $default_renderer->render(
910 $this->getUIFactory()->button()->shy(
911 $input->getMaxFiles() <= 1
912 ? $this->txt(
'select_file_from_computer')
913 : $this->txt(
'select_files_from_computer'),
918 return $this->wrapInFormContext(
927 $template = $this->getTemplate(
'tpl.hidden.html',
true,
true);
928 $this->applyName($input, $template);
929 $this->applyValue($input, $template, $this->escapeSpecialChars());
930 if ($input->isDisabled()) {
931 $template->setVariable(
"DISABLED",
'disabled="disabled"');
933 $this->bindJSandApplyId($input, $template);
934 return $template->get();
942 parent::registerResources($registry);
943 $registry->
register(
'assets/css/tagify.css');
944 $registry->
register(
'assets/js/tagInput.js');
946 $registry->
register(
'assets/js/dropzone.min.js');
947 $registry->
register(
'assets/js/dropzone.js');
948 $registry->
register(
'assets/js/input.js');
949 $registry->
register(
'assets/js/core.js');
950 $registry->
register(
'assets/js/file.js');
952 $registry->
register(
'assets/js/drilldown.min.js');
953 $registry->
register(
'assets/js/input.factory.min.js');
963 foreach ($input->getTriggeredSignals() as $s) {
965 "signal_id" => $s->getSignal()->getId(),
966 "event" => $s->getEvent(),
967 "options" => $s->getSignal()->getOptions()
970 if ($signals !==
null) {
971 $signals = json_encode($signals);
973 $input = $input->withAdditionalOnLoadCode($input->getUpdateOnLoadCode());
975 $input = $input->withAdditionalOnLoadCode(
static function (
$id) use ($signals) {
976 $code =
"il.UI.input.setSignalsForId('$id', $signals);";
994 foreach ($origin->toArray() as $element) {
995 if (array_key_exists($element, $mapping)) {
996 $ret .= $mapping[$element];
1007 RendererInterface $default_renderer,
1011 $f = $this->getUIFactory();
1013 $template->
setVariable(
'REMOVAL_GLYPH', $default_renderer->render(
1014 $f->button()->shy(
'',
'')->withSymbol(
$f->symbol()->glyph()->close())
1017 if (
null !== $file_info) {
1027 if ($file_input->hasMetadataInputs()) {
1028 $template->
setVariable(
'EXPAND_GLYPH', $default_renderer->render(
1029 $f->button()->shy(
'',
'')->withSymbol(
$f->symbol()->glyph()->expand())
1031 $template->
setVariable(
'COLLAPSE_GLYPH', $default_renderer->render(
1032 $f->button()->shy(
'',
'')->withSymbol(
$f->symbol()->glyph()->collapse())
1036 $template->
setVariable(
'METADATA_INPUTS', $default_renderer->render($metadata_input));
1046 function (
$id) use ($input) {
1047 $current_file_count = count($input->getGeneratedDynamicInputs());
1048 $translations = json_encode($input->getTranslations());
1049 $is_disabled = ($input->isDisabled()) ?
'true' :
'false';
1050 $php_upload_limit = $this->getUploadLimitResolver()->getPhpUploadLimitInBytes();
1051 $should_upload_be_chunked = ($input->getMaxFileSize() > $php_upload_limit) ?
'true' :
'false';
1052 $chunk_size = (
int) floor($php_upload_limit * self::FILE_UPLOAD_CHUNK_SIZE_FACTOR);
1054 $(document).ready(function () {
1055 il.UI.Input.File.init(
1057 '{$input->getUploadHandler()->getUploadURL()}',
1058 '{$input->getUploadHandler()->getFileRemovalURL()}',
1059 '{$input->getUploadHandler()->getFileIdentifierParameterName()}',
1060 $current_file_count,
1061 {$input->getMaxFiles()},
1062 {$input->getMaxFileSize()},
1063 '{$this->prepareDropzoneJsMimeTypes($input->getAcceptedMimeTypes())}',
1066 $should_upload_be_chunked,
1082 $mime_type_string =
'';
1083 foreach ($mime_types as $index => $mime_type) {
1084 $mime_type_string .= (isset($mime_types[$index + 1])) ?
"$mime_type," : $mime_type;
1087 return $mime_type_string;
1092 $tpl = $this->getTemplate(
"tpl.color_select.html",
true,
true);
1093 $this->applyName($component, $tpl);
1094 $tpl->setVariable(
'VALUE', $component->getValue());
1096 $label_id = $this->createId();
1097 $tpl->setVariable(
'ID', $label_id);
1098 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get(), $label_id);
1103 $tpl = $this->getTemplate(
"tpl.rating.html",
true,
true);
1104 $id = $this->createId();
1105 $aria_description_id =
$id .
'_desc';
1106 $tpl->setVariable(
'DESCRIPTION_SRC_ID', $aria_description_id);
1108 $option_count = count(FiveStarRatingScale::cases()) - 1;
1110 foreach (range($option_count, 1, -1) as $option) {
1111 $tpl->setCurrentBlock(
'scaleoption');
1112 $tpl->setVariable(
'ARIALABEL', $this->txt($option .
'stars'));
1113 $tpl->setVariable(
'OPT_VALUE', (
string) $option);
1114 $tpl->setVariable(
'OPT_ID',
$id .
'-' . $option);
1115 $tpl->setVariable(
'NAME', $component->getName());
1116 $tpl->setVariable(
'DESCRIPTION_ID', $aria_description_id);
1119 $tpl->setVariable(
"SELECTED",
' checked="checked"');
1121 if ($component->isDisabled()) {
1122 $tpl->setVariable(
"DISABLED",
'disabled="disabled"');
1124 $tpl->parseCurrentBlock();
1127 if (!$component->isRequired()) {
1128 $tpl->setVariable(
'NEUTRAL_ID',
$id .
'-0');
1129 $tpl->setVariable(
'NEUTRAL_NAME', $component->getName());
1130 $tpl->setVariable(
'NEUTRAL_LABEL', $this->txt(
'reset_stars'));
1131 $tpl->setVariable(
'NEUTRAL_DESCRIPTION_ID', $aria_description_id);
1133 if ($component->getValue() === FiveStarRatingScale::NONE || is_null($component->getValue())) {
1134 $tpl->setVariable(
'NEUTRAL_SELECTED',
' checked="checked"');
1138 if ($txt = $component->getAdditionalText()) {
1139 $tpl->setVariable(
'TEXT', $txt);
1142 if ($component->isDisabled()) {
1143 $tpl->touchBlock(
'disabled');
1145 if ($average = $component->getCurrentAverage()) {
1146 $average_title = sprintf($this->txt(
'rating_average'), $average);
1147 $tpl->setVariable(
'AVERAGE_VALUE', $average_title);
1148 $tpl->setVariable(
'AVERAGE_VALUE_PERCENT', $average / $option_count * self::CENTUM);
1151 return $this->wrapInFormContext($component, $component->getLabel(), $tpl->get());
1156 $template = $this->prepareTreeSelectTemplate($component, $default_renderer);
1158 if ($component->canSelectChildNodes()) {
1159 $select_child_nodes =
'true';
1161 $select_child_nodes =
'false';
1164 $enriched_component = $component->withAdditionalOnLoadCode(
1165 static fn(
$id) =>
"il.UI.Input.treeSelect.initTreeMultiSelect('$id', $select_child_nodes);"
1168 $id = $this->bindJSandApplyId($enriched_component, $template);
1170 return $this->wrapInFormContext($component, $component->getLabel(), $template->
get(),
$id);
1175 $template = $this->prepareTreeSelectTemplate($component, $default_renderer);
1177 $enriched_component = $component->withAdditionalOnLoadCode(
1178 static fn(
$id) =>
"il.UI.Input.treeSelect.initTreeSelect('$id');"
1181 $id = $this->bindJSandApplyId($enriched_component, $template);
1183 return $this->wrapInFormContext($component, $component->getLabel(), $template->
get(),
$id);
1186 protected function prepareTreeSelectTemplate(
1188 RendererInterface $default_renderer,
1190 $template = $this->getTemplate(
'tpl.tree_select.html',
true,
true);
1196 $template->
setVariable(
'SELECT_LABEL', $this->txt(
'select'));
1197 $template->
setVariable(
'CLOSE_LABEL', $this->txt(
'close'));
1200 $template->
setVariable(
'INPUT_TEMPLATE', $default_renderer->render(
1203 $template->
setVariable(
'BREADCRUMB_TEMPLATE', $default_renderer->render(
1204 $this->getUIFactory()->breadcrumbs([$this->getUIFactory()->link()->standard(
'label',
'#')])
1206 $template->
setVariable(
'BREADCRUMBS', $default_renderer->render(
1207 $this->getUIFactory()->breadcrumbs([])
1214 $this->getUIFactory()->input()->field()->node(),
1215 $this->getUIFactory()->symbol()->icon(),
1219 $lockstep_iterator = $this->iterateGeneratorsInLockstep($leaf_generator, $dynamic_inputs_generator);
1220 $sync_node_id_whitelst = [];
1222 foreach ($lockstep_iterator as [$leaf, $dynamic_input]) {
1225 $this->checkArgInstanceOf(
'leaf', $leaf, Node\Leaf::class);
1227 $value_template = $this->getTemplate(
'tpl.tree_select.html',
true,
true);
1228 $value_template->setCurrentBlock(
'with_value_template');
1229 $value_template->setVariable(
'NODE_ID', (
string) ($leaf->getId()));
1230 $value_template->setVariable(
'NODE_NAME', $leaf->getName());
1231 $value_template->setVariable(
'INPUT_TEMPLATE', $default_renderer->render($dynamic_input));
1232 $value_template->setVariable(
'UNSELECT_NODE_LABEL', sprintf($this->txt(
'unselect_node'), $leaf->getName()));
1233 $value_template->parseCurrentBlock();
1236 $template->
setVariable(
'VALUE', $value_template->get(
'with_value_template'));
1239 foreach ($leaf->getFullPath() as $node_id) {
1241 $sync_node_id_whitelst[$node_id] = $node_id;
1245 $node_factory = $this->getUIFactory()->input()->field()->node();
1248 $this->getUIFactory()->
symbol()->icon(),
1249 array_values($sync_node_id_whitelst),
1254 foreach ($node_generator as $node) {
1256 $this->checkArgInstanceOf(
'node', $node,
Component\Input\Field\Node\Node::class);
1260 $template->
setVariable(
'DRILLDOWN', $default_renderer->render(
1261 $this->getUIFactory()->menu()->drilldown($component->
getLabel(), $nodes)
1264 $this->toJS(
'unselect_node');
1265 $this->toJS(
'select_node');
1279 while (
$a->valid() &&
$b->valid()) {
1280 yield [
$a->current(),
$b->current()];
1284 if (
$a->valid() ||
$b->valid()) {
1285 throw new LogicException(
'Generators do not have equal lenghts.');
1294 $template->
setVariable(
'FILE_SIZE_LABEL', $this->txt(
'file_notice'));
1295 $template->
setVariable(
'FILE_SIZE_VALUE',
new DataSize($input->getMaxFileSize(), DataSize::Byte));
1299 $template->
setVariable(
'FILES_LABEL', $this->txt(
'ui_file_upload_max_nr'));
1300 $template->
setVariable(
'FILES_VALUE', $input->getMaxFiles());
1315 $option_filter_template = $this->getTemplate(
"tpl.option_filter.html",
true,
true);
1316 $option_filter_template->setVariable(
'INPUT', $input_html);
1318 $search_input_id = $this->createId();
1319 $search_input_label_id = $this->createId();
1320 $search_input_description_id = $this->createId();
1321 $list_id = $this->createId();
1323 $option_filter_template->setVariable(
'SEARCH_INPUT_ID', $search_input_id);
1324 $option_filter_template->setVariable(
'SEARCH_INPUT_LABEL_ID', $search_input_label_id);
1325 $option_filter_template->setVariable(
'SEARCH_INPUT_DESCRIPTION_ID', $search_input_description_id);
1326 $option_filter_template->setVariable(
'LIST_ID', $list_id);
1328 $no_selection_text = $this->txt(
'ui_field_option_filter_no_selection');
1329 $option_filter_template->setVariable(
'NOTHING_SELECTED', $no_selection_text);
1330 $option_filter_template->setVariable(
'ARIA_FILTERED_RESULTS', $this->txt(
'ui_field_option_filter_filtered_results_aria_label'));
1332 $option_filter_template->setVariable(
'SEARCH_LABEL', $this->txt(
"ui_field_option_filter_search_in"));
1333 $option_filter_template->setVariable(
'SCREEN_READER_HINT', $this->txt(
'ui_field_option_filter_screen_reader_hint'));
1334 $option_filter_template->setVariable(
'NO_MATCH', $this->txt(
'ui_field_option_filter_no_match'));
1335 $option_filter_template->setVariable(
'OPTIONS_SHOWN', $this->txt(
'ui_field_option_filter_options_shown'));
1337 $expand_icon = $default_renderer->render($this->getUIFactory()->
symbol()->glyph()->
expand()->withLabel(
''));
1338 $option_filter_template->setVariable(
'EXPAND_TEXT', $expand_icon . $this->txt(
'ui_field_option_filter_show_all_options'));
1340 $collapse_icon = $default_renderer->render($this->getUIFactory()->
symbol()->glyph()->collapseHorizontal()->withLabel(
''));
1341 $option_filter_template->setVariable(
'COLLAPSE_TEXT', $collapse_icon . $this->txt(
'ui_field_option_filter_show_less'));
1343 $remove_icon = $default_renderer->render($this->getUIFactory()->
symbol()->glyph()->
remove()->withLabel(
''));
1344 $option_filter_template->setVariable(
'CLEAR_SEARCH_BTN', $remove_icon . $this->txt(
'ui_field_option_filter_clear_search'));
1346 $component = $component->withAdditionalOnLoadCode(
1347 static fn(
$id):
string =>
"il.UI.Input.optionFilter.init(document.getElementById('$id'));",
1350 return [$option_filter_template->get(), $component];
1355 return function ($val) {
1356 $val = htmlentities((
string) $val);
1357 return str_replace(
'{{',
'{{', str_replace(
'}}',
'}}', $val));
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
This class provides the data size with additional information to remove the work to calculate the siz...
A Link is the often used combination of a label and an URL.
A password is used as part of credentials for authentication.
The scope of this class is split ilias-conform URI's into components.
FiveStarRatingScale
This is the scale for the Rating Input.
This implements the text input.
This implements the textarea input.
Base class for all component renderers.
cannotHandleComponent(Component $component)
This method MUST be called by derived component renderers, if.
txt(string $id)
Get a text from the language file.
createId()
Get a fresh unique id.
bindJavaScript(JavaScriptBindable $component)
Bind the component to JavaScript.
addTriggererOnLoadCode(Triggerer $triggerer)
Add onload-code for triggerer.
getComponentCanonicalNameAttribute(Component $component)
getTemplate(string $name, bool $purge_unfilled_vars, bool $purge_unused_blocks)
Get template of component this renderer is made for.
Interface FileInfoResult.
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.
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.
trait JavaScriptBindable
Trait for components implementing JavaScriptBindable providing standard implementation.
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples