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