ILIAS  trunk Revision v12.0_alpha-377-g3641b37b9db
class.FormAdapterGUI.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
26
31{
33
34 protected const DEFAULT_SECTION = "@internal_default_section";
35 protected string $submit_caption = "";
36 protected \ilLanguage $lng;
37 protected const ASYNC_NONE = 0;
38 protected const ASYNC_MODAL = 1;
39 protected const ASYNC_ON = 2;
40 protected \ILIAS\Data\Factory $data;
41 protected \ilObjUser $user;
42 protected string $last_key = "";
43 protected \ILIAS\Refinery\Factory $refinery;
44
45 protected string $title = "";
46 protected array $values = [];
47 protected array $disable = [];
48
49 protected ?array $raw_data = null;
50 protected \ILIAS\HTTP\Services $http;
51 protected \ilCtrlInterface $ctrl;
52 protected \ILIAS\DI\UIServices $ui;
53 protected array $fields = [];
54 protected array $field_path = [];
55 protected array $sections = [self::DEFAULT_SECTION => ["title" => "", "description" => "", "fields" => []]];
57 protected array $section_of_field = [];
58 protected ?array $class_path;
59 protected string $cmd = self::DEFAULT_SECTION;
60 protected ?Form\Standard $form = null;
61 protected array $upload_handler = [];
63 protected \ilGlobalTemplateInterface $main_tpl;
64 protected ?array $current_switch = null;
65 protected ?array $current_optional = null;
66 protected ?array $current_group = null;
67 protected static bool $initialised = false;
68
72 public function __construct(
73 ?array $class_path,
74 string $cmd,
75 string $submit_caption = ""
76 ) {
77 global $DIC;
78 $this->class_path = $class_path;
79 $this->cmd = $cmd;
80 $this->ui = $DIC->ui();
81 $this->ctrl = $DIC->ctrl();
82 $this->http = $DIC->http();
83 $this->lng = $DIC->language();
84 $this->refinery = $DIC->refinery();
85 $this->main_tpl = $DIC->ui()->mainTemplate();
86 $this->user = $DIC->user();
87 $this->data = new \ILIAS\Data\Factory();
88 $this->submit_caption = $submit_caption;
90 $this->initStdObjProperties($DIC);
91 $this->lng->loadLanguageModule("rep");
92 }
93
94 public static function getOnLoadCode(): string
95 {
96 return "il.repository.ui.init();\n" .
97 "il.repository.core.init('" . ILIAS_HTTP_PATH . "')";
98 }
99
100 public static function initJavascript(): void
101 {
102 global $DIC;
103
104 if (!isset($DIC["ui.factory"])) {
105 return;
106 }
107
108 $f = $DIC->ui()->factory();
109 $r = $DIC->ui()->renderer();
110 if (!self::$initialised) {
111 $main_tpl = $DIC->ui()->mainTemplate();
112 $debug = false;
113 if ($debug) {
114 $main_tpl->addJavaScript("../components/ILIAS/Repository/resources/repository.js");
115 } else {
116 $main_tpl->addJavaScript("assets/js/repository.js");
117 }
118
120
121 // render dummy components to load the necessary .js needed for async processing
122 $d = [];
123 $d[] = $f->input()->field()->text("");
124 $r->render($d);
125 self::$initialised = true;
126 }
127 }
128
129 public function asyncModal(): self
130 {
131 $this->async_mode = self::ASYNC_MODAL;
132 return $this;
133 }
134
135 public function async(): self
136 {
137 $this->async_mode = self::ASYNC_ON;
138 return $this;
139 }
140
141 public function syncModal(): self
142 {
143 return $this;
144 }
145
146 public function isSentAsync(): bool
147 {
148 return ($this->async_mode !== self::ASYNC_NONE);
149 }
150
151 public function getTitle(): string
152 {
153 return $this->title;
154 }
155
156 public function section(
157 string $key,
158 string $title,
159 string $description = ""
160 ): self {
161 if ($this->title == "") {
162 $this->title = $title;
163 }
164
165 $this->sections[$key] = [
166 "title" => $title,
167 "description" => $description,
168 "fields" => []
169 ];
170 $this->current_section = $key;
171 return $this;
172 }
173
174 public function text(
175 string $key,
176 string $title,
177 string $description = "",
178 ?string $value = null
179 ): self {
180 $this->values[$key] = $value;
181 $field = $this->ui->factory()->input()->field()->text($title, $description);
182 if (!is_null($value)) {
183 $field = $field->withValue($value);
184 }
185 $this->addField($key, $field);
186 return $this;
187 }
188
189 public function checkbox(
190 string $key,
191 string $title,
192 string $description = "",
193 ?bool $value = null
194 ): self {
195 $this->values[$key] = $value;
196 $field = $this->ui->factory()->input()->field()->checkbox($title, $description);
197 if (!is_null($value)) {
198 $field = $field->withValue($value);
199 }
200 $this->addField($key, $field);
201 return $this;
202 }
203
204 public function hidden(
205 string $key,
206 string $value
207 ): self {
208 $this->values[$key] = $value;
209 $field = $this->ui->factory()->input()->field()->hidden();
210 $field = $field->withValue($value);
211 $this->addField($key, $field);
212 return $this;
213 }
214
215 public function required($required = true): self
216 {
217 if ($required && ($field = $this->getLastField())) {
218 if ($field instanceof \ILIAS\UI\Component\Input\Field\Text) {
219 $field = $field->withRequired(true, new NotEmpty(
220 new Factory(),
221 $this->lng
222 ));
223 } else {
224 $field = $field->withRequired(true);
225 }
226 $this->replaceLastField($field);
227 }
228 return $this;
229 }
230
231 public function disabled($disabled = true): self
232 {
233 if ($disabled && ($field = $this->getLastField())) {
234 $field = $field->withDisabled(true);
235 $this->disable[$this->last_key] = true;
236 $this->replaceLastField($field);
237 }
238 return $this;
239 }
240
241 public function textarea(
242 string $key,
243 string $title,
244 string $description = "",
245 ?string $value = null
246 ): self {
247 $this->values[$key] = $value;
248 $field = $this->ui->factory()->input()->field()->textarea($title, $description);
249 if (!is_null($value)) {
250 $field = $field->withValue($value);
251 }
252 $this->addField($key, $field);
253 return $this;
254 }
255
256 public function number(
257 string $key,
258 string $title,
259 string $description = "",
260 ?int $value = null,
261 ?int $min_value = null,
262 ?int $max_value = null
263 ): self {
264 $this->values[$key] = $value;
265 $trans = [];
266 if (!is_null($min_value)) {
267 $trans[] = $this->refinery->int()->isGreaterThanOrEqual($min_value);
268 }
269 if (!is_null($max_value)) {
270 $trans[] = $this->refinery->int()->isLessThanOrEqual($max_value);
271 }
272 $field = $this->ui->factory()->input()->field()->numeric($title, $description);
273 if (count($trans) > 0) {
274 $field = $field->withAdditionalTransformation($this->refinery->logical()->parallel($trans));
275 }
276 if (!is_null($value)) {
277 $field = $field->withValue($value);
278 }
279 $this->addField($key, $field);
280 return $this;
281 }
282
283 public function date(
284 string $key,
285 string $title,
286 string $description = "",
287 ?\ilDate $value = null
288 ): self {
289 $this->values[$key] = $value;
290 $field = $this->ui->factory()->input()->field()->dateTime($title, $description);
291
292 $format = $this->user->getDateFormat();
293 $dt_format = (string) $format;
294
295 $field = $field->withFormat($format);
296 if (!is_null($value)) {
297 $field = $field->withValue(
298 (new \DateTime($value->get(IL_CAL_DATE)))->format($dt_format)
299 );
300 }
301 $this->addField($key, $field);
302 return $this;
303 }
304
305 public function dateTime(
306 string $key,
307 string $title,
308 string $description = "",
309 ?\ilDateTime $value = null
310 ): self {
311 $this->values[$key] = $value;
312 $field = $this->ui->factory()->input()->field()->dateTime($title, $description)->withUseTime(true);
313
314 if ((int) $this->user->getTimeFormat() === \ilCalendarSettings::TIME_FORMAT_12) {
315 $dt_format = $this->data->dateFormat()->withTime12($this->user->getDateFormat());
316 } else {
317 $dt_format = $this->data->dateFormat()->withTime24($this->user->getDateFormat());
318 }
319 $field = $field->withFormat($dt_format);
320 if (!is_null($value) && !is_null($value->get(IL_CAL_DATETIME))) {
321 $field = $field->withValue(
322 (new \DateTime($value->get(IL_CAL_DATETIME)))->format(
323 ((string) $dt_format)
324 )
325 );
326 }
327 $this->addField($key, $field);
328 return $this;
329 }
330
331 public function dateTimeDuration(
332 string $key,
333 string $title,
334 string $description = "",
335 ?\ilDateTime $from = null,
336 ?\ilDateTime $to = null,
337 string $label_from = "",
338 string $label_to = ""
339 ): self {
340 $this->values[$key] = [$from, $to];
341 if ($label_from === "") {
342 $label_from = $this->lng->txt("rep_activation_limited_start");
343 }
344 if ($label_to === "") {
345 $label_to = $this->lng->txt("rep_activation_limited_end");
346 }
347 $field = $this->ui->factory()->input()->field()->duration($title, $description)->withUseTime(true)->withLabels($label_from, $label_to);
348
349 if ((int) $this->user->getTimeFormat() === \ilCalendarSettings::TIME_FORMAT_12) {
350 $dt_format = $this->data->dateFormat()->withTime12($this->user->getDateFormat());
351 } else {
352 $dt_format = $this->data->dateFormat()->withTime24($this->user->getDateFormat());
353 }
354 $field = $field->withFormat($dt_format);
355 $val_from = $val_to = null;
356 if (!is_null($from) && !is_null($from->get(IL_CAL_DATETIME))) {
357 $val_from = (new \DateTime(
358 $from->get(IL_CAL_DATETIME)
359 ))->format((string) $dt_format);
360 }
361 if (!is_null($to) && !is_null($to->get(IL_CAL_DATETIME))) {
362 $val_to = (new \DateTime(
363 $to->get(IL_CAL_DATETIME)
364 ))->format((string) $dt_format);
365 }
366 $field = $field->withValue([$val_from, $val_to]);
367 $this->addField($key, $field);
368 return $this;
369 }
370
371 protected function getDateTimeData(?\DateTimeImmutable $value, $use_time = false): \ilDate|\ilDateTime|null
372 {
373 if (is_null($value)) {
374 return null;
375 }
376 if ($use_time) {
377 return new \ilDateTime($value->format("Y-m-d H:i:s"), IL_CAL_DATETIME);
378 }
379 return new \ilDate($value->format("Y-m-d"), IL_CAL_DATE);
380 }
381
382 public function select(
383 string $key,
384 string $title,
385 array $options,
386 string $description = "",
387 ?string $value = null
388 ): self {
389 $this->values[$key] = $value;
390 $field = $this->ui->factory()->input()->field()->select($title, $options, $description);
391 if (!is_null($value)) {
392 $field = $field->withValue($value);
393 }
394 $this->addField(
395 $key,
396 $field
397 );
398 return $this;
399 }
400
401 public function radio(
402 string $key,
403 string $title,
404 string $description = "",
405 ?string $value = null
406 ): self {
407 $this->values[$key] = $value;
408 $field = $this->ui->factory()->input()->field()->radio($title, $description);
409 if (!is_null($value)) {
410 $field = $field->withOption($value, ""); // dummy to prevent exception, will be overwritten by radioOption
411 $field = $field->withValue($value);
412 }
413 $this->addField(
414 $key,
415 $field
416 );
417 return $this;
418 }
419
420 public function radioOption(string $value, string $title, string $description = ""): self
421 {
422 if ($field = $this->getLastField()) {
423 $field = $field->withOption($value, $title, $description);
424 $this->replaceLastField($field);
425 }
426 return $this;
427 }
428
429 public function switch(
430 string $key,
431 string $title,
432 string $description = "",
433 ?string $value = null
434 ): self {
435 $this->values[$key] = $value;
436 $this->current_switch = [
437 "key" => $key,
438 "title" => $title,
439 "description" => $description,
440 "value" => $value,
441 "groups" => []
442 ];
443 return $this;
444 }
445
446 public function optional(
447 string $key,
448 string $title,
449 string $description = "",
450 ?bool $value = null
451 ): self {
452 $this->values[$key] = $value;
453 $this->current_optional = [
454 "key" => $key,
455 "title" => $title,
456 "description" => $description,
457 "value" => $value,
458 "group" => []
459 ];
460 return $this;
461 }
462
463 public function group(string $key, string $title, string $description = "", $disabled = false): self
464 {
465 $this->endCurrentGroup();
466 $this->current_group = [
467 "key" => $key,
468 "title" => $title,
469 "description" => $description,
470 "disabled" => $disabled,
471 "fields" => []
472 ];
473 return $this;
474 }
475
476 protected function endCurrentGroup(): void
477 {
478 if (!is_null($this->current_group)) {
479 if (!is_null($this->current_switch)) {
480 $fields = [];
481 foreach ($this->current_group["fields"] as $key) {
482 $fields[$key] = $this->fields[$key];
483 }
484 $this->current_switch["groups"][$this->current_group["key"]] =
485 $this->ui->factory()->input()->field()->group(
486 $fields,
487 $this->current_group["title"]
488 )->withByline($this->current_group["description"]);
489 if ($this->current_group["disabled"]) {
490 $this->current_switch["groups"][$this->current_group["key"]] =
491 $this->current_switch["groups"][$this->current_group["key"]]
492 ->withDisabled(true);
493 }
494 }
495 }
496 $this->current_group = null;
497 }
498
499 public function end(): self
500 {
501 $this->endCurrentGroup();
502 if (!is_null($this->current_switch)) {
503 $field = $this->ui->factory()->input()->field()->switchableGroup(
504 $this->current_switch["groups"],
505 $this->current_switch["title"],
506 $this->current_switch["description"]
507 );
508 if (!is_null($this->current_switch["value"])) {
509 $cvalue = $this->current_switch["value"];
510 if (isset($this->current_switch["groups"][$cvalue])) {
511 $field = $field->withValue($cvalue);
512 }
513 }
514 $key = $this->current_switch["key"];
515 $this->current_switch = null;
516 $this->addField($key, $field);
517 }
518 if (!is_null($this->current_optional)) {
519 $field = $this->ui->factory()->input()->field()->optionalGroup(
520 $this->current_optional["fields"],
521 $this->current_optional["title"],
522 $this->current_optional["description"]
523 );
524 if ($this->current_optional["value"]) {
525 $value = [];
526 foreach ($this->current_optional["fields"] as $key => $input) {
527 $value[$key] = $input->getValue();
528 }
529 $field = $field->withValue($value);
530 } else {
531 $field = $field->withValue(null);
532 }
533 $key = $this->current_optional["key"];
534 $this->current_optional = null;
535 $this->addField($key, $field);
536 }
537 return $this;
538 }
539
540 public function file(
541 string $key,
542 string $title,
543 \Closure $result_handler,
544 string $id_parameter,
545 string $description = "",
546 ?int $max_files = null,
547 array $mime_types = [],
548 array $ctrl_path = [],
549 string $logger_id = ""
550 ): self {
551 $this->upload_handler[$key] = new \ilRepoStandardUploadHandlerGUI(
552 $result_handler,
553 $id_parameter,
554 $logger_id,
555 $ctrl_path
556 );
557
558 foreach (["application/x-compressed", "application/x-zip-compressed"] as $zipmime) {
559 if (in_array("application/zip", $mime_types) &&
560 !in_array($zipmime, $mime_types)) {
561 $mime_types[] = $zipmime;
562 }
563 }
564
565 if (count($mime_types) > 0) {
566 $description .= $this->lng->txt("rep_allowed_types") . ": " .
567 implode(", ", $mime_types);
568 }
569
570 $field = $this->ui->factory()->input()->field()->file(
571 $this->upload_handler[$key],
572 $title,
573 $description
574 );
575 // not necessary, see https://github.com/ILIAS-eLearning/ILIAS/pull/9314
576 //->withMaxFileSize((int) \ilFileUtils::getPhpUploadSizeLimitInBytes());
577 if (!is_null($max_files)) {
578 $field = $field->withMaxFiles($max_files);
579 }
580 if (count($mime_types) > 0) {
581 $field = $field->withAcceptedMimeTypes($mime_types);
582 }
583
584 $this->addField(
585 $key,
586 $field
587 );
588 return $this;
589 }
590
592 {
593 if (!isset($this->upload_handler[$key])) {
594 throw new \ilException("Unknown file upload field: " . $key);
595 }
596 return $this->upload_handler[$key];
597 }
598
599
600 protected function addField(string $key, FormInput $field, $supress_0_key = false): void
601 {
602 if ($key === "") {
603 throw new \ilException("Missing Input Key: " . $key);
604 }
605 if (isset($this->field[$key])) {
606 throw new \ilException("Duplicate Input Key: " . $key);
607 }
608 $field_path = [];
609 if ($this->current_section !== self::DEFAULT_SECTION) {
610 $field_path[] = $this->current_section;
611 }
612 if (!is_null($this->current_group)) {
613 $this->current_group["fields"][] = $key;
614 if (!is_null($this->current_switch)) {
615 $field_path[] = $this->current_switch["key"];
616 $field_path[] = 1; // the value of subitems in SwitchableGroup are in the 1 key of the raw data
617 $field_path[] = $key;
618 }
619 } elseif (!is_null($this->current_optional)) {
620 $field_path[] = $this->current_optional["key"];
621 $this->current_optional["fields"][$key] = $field;
622 $field_path[] = $key;
623 } else {
624 $this->sections[$this->current_section]["fields"][] = $key;
625 $field_path[] = $key;
626 if ($field instanceof \ILIAS\UI\Component\Input\Field\SwitchableGroup) {
627 $field_path[] = 0; // the value of the SwitchableGroup is in the 0 key of the raw data
628 }
629 if ($field instanceof \ILIAS\UI\Component\Input\Field\OptionalGroup) {
630 //$field_path[] = 0; // the value of the SwitchableGroup is in the 0 key of the raw data
631 }
632 if ($field instanceof \ILIAS\UI\Component\Input\Field\File) {
633 if (!$supress_0_key) { // needed for tiles, that come with a custom transformation omitting the 0
634 $field_path[] = 0; // the value of File Inputs is in the 0 key of the raw data
635 }
636 }
637 }
638 $this->fields[$key] = $field;
639 $this->field_path[$key] = $field_path;
640 $this->last_key = $key;
641 $this->form = null;
642 }
643
644 protected function getFieldForKey(string $key): FormInput
645 {
646 if (!isset($this->fields[$key])) {
647 throw new \ilException("Unknown Key: " . $key);
648 }
649 return $this->fields[$key];
650 }
651
652 protected function getLastField(): ?FormInput
653 {
654 return $this->fields[$this->last_key] ?? null;
655 }
656
657 protected function replaceLastField(FormInput $field): void
658 {
659 if ($this->last_key !== "") {
660 $this->fields[$this->last_key] = $field;
661 }
662 }
663
664 public function getForm(): Form\Standard
665 {
666 $ctrl = $this->ctrl;
667
668 if (is_null($this->form)) {
669 $async = ($this->async_mode !== self::ASYNC_NONE);
670 $action = "";
671 if (!is_null($this->class_path)) {
672 $action = $ctrl->getLinkTargetByClass($this->class_path, $this->cmd, "", $async);
673 }
674 $inputs = [];
675 foreach ($this->sections as $sec_key => $section) {
676 if ($sec_key === self::DEFAULT_SECTION) {
677 foreach ($this->sections[$sec_key]["fields"] as $f_key) {
678 $inputs[$f_key] = $this->getFieldForKey($f_key);
679 }
680 } elseif (count($this->sections[$sec_key]["fields"]) > 0) {
681 $sec_inputs = [];
682 foreach ($this->sections[$sec_key]["fields"] as $f_key) {
683 $sec_inputs[$f_key] = $this->getFieldForKey($f_key);
684 }
685 $inputs[$sec_key] = $this->ui->factory()->input()->field()->section(
686 $sec_inputs,
687 $section["title"],
688 $section["description"]
689 );
690 }
691 }
692 $this->form = $this->ui->factory()->input()->container()->form()->standard(
693 $action,
694 $inputs
695 );
696 if ($this->submit_caption !== "") {
697 $this->form = $this->form->withSubmitLabel($this->submit_caption);
698 }
699 }
700 return $this->form;
701 }
702
703 public function getSubmitLabel(): string
704 {
705 return $this->getForm()->getSubmitLabel() ?? $this->lng->txt("save");
706 }
707
708 protected function _getData(): void
709 {
710 if (is_null($this->raw_data)) {
711 $request = $this->http->request();
712 $this->form = $this->getForm()->withRequest($request);
713 $this->raw_data = $this->form->getData();
714 }
715 }
716
717 public function isValid(): bool
718 {
719 $this->_getData();
720 return !(is_null($this->raw_data));
721 }
722
726 public function getData(string $key): mixed
727 {
728 $this->_getData();
729
730 if (!isset($this->fields[$key])) {
731 return null;
732 }
733
734 if (isset($this->disable[$key])) {
735 return $this->values[$key];
736 }
737
738 $value = $this->raw_data;
739 foreach ($this->field_path[$key] as $path_key) {
740 if (!isset($value[$path_key])) {
741 return null;
742 }
743 $value = $value[$path_key];
744 }
745
746 $field = $this->getFieldForKey($key);
747
748 if ($field instanceof \ILIAS\UI\Component\Input\Field\DateTime) {
750 $value = $this->getDateTimeData($value, $field->getUseTime());
751 }
752
753 if ($field instanceof \ILIAS\UI\Component\Input\Field\Duration) {
755 $value = [
756 $this->getDateTimeData($value["start"], $field->getUseTime()),
757 $this->getDateTimeData($value["end"], $field->getUseTime()),
758 ];
759 }
760
761 if ($field instanceof \ILIAS\UI\Component\Input\Field\OptionalGroup) {
762 $value = is_array($value);
763 }
764
765 return $value;
766 }
767
768 public function render(): string
769 {
770 if ($this->async_mode === self::ASYNC_NONE && !$this->ctrl->isAsynch()) {
771 $html = $this->ui->renderer()->render($this->getForm());
772 } else {
773 $html = $this->ui->renderer()->renderAsync($this->getForm()) . "<script>" . $this->getOnLoadCode() . "</script>";
774 }
775 return $html;
776 }
777}
Builds data types.
Definition: Factory.php:36
radio(string $key, string $title, string $description="", ?string $value=null)
text(string $key, string $title, string $description="", ?string $value=null)
date(string $key, string $title, string $description="", ?\ilDate $value=null)
optional(string $key, string $title, string $description="", ?bool $value=null)
checkbox(string $key, string $title, string $description="", ?bool $value=null)
group(string $key, string $title, string $description="", $disabled=false)
number(string $key, string $title, string $description="", ?int $value=null, ?int $min_value=null, ?int $max_value=null)
getDateTimeData(?\DateTimeImmutable $value, $use_time=false)
select(string $key, string $title, array $options, string $description="", ?string $value=null)
dateTime(string $key, string $title, string $description="", ?\ilDateTime $value=null)
dateTimeDuration(string $key, string $title, string $description="", ?\ilDateTime $from=null, ?\ilDateTime $to=null, string $label_from="", string $label_to="")
section(string $key, string $title, string $description="")
__construct(?array $class_path, string $cmd, string $submit_caption="")
addField(string $key, FormInput $field, $supress_0_key=false)
textarea(string $key, string $title, string $description="", ?string $value=null)
file(string $key, string $title, \Closure $result_handler, string $id_parameter, string $description="", ?int $max_files=null, array $mime_types=[], array $ctrl_path=[], string $logger_id="")
radioOption(string $value, string $title, string $description="")
Definition: UI.php:24
const IL_CAL_DATE
const IL_CAL_DATETIME
@classDescription Date and time handling
Class for single dates.
addJavaScript(string $a_js_file, bool $a_add_version_parameter=true, int $a_batch=2)
Add a javascript file that should be included in the header.
addOnLoadCode(string $a_code, int $a_batch=2)
Add on load code.
This describes inputs that can be used in forms.
Definition: FormInput.php:33
This describes commonalities between all forms.
Definition: Form.php:34
This describes commonalities between all inputs.
Definition: Input.php:47
static http()
Fetches the global http state from ILIAS.
initStdObjProperties(Container $DIC)
form(?array $class_path, string $cmd, string $submit_caption="")
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Factory.php:21
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
if(!file_exists('../ilias.ini.php'))
global $DIC
Definition: shib_login.php:26