ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.FormAdapterGUI.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
22 
26 
31 {
32  protected const DEFAULT_SECTION = "@internal_default_section";
33  protected bool $in_modal = false;
34  protected string $submit_caption = "";
35  protected \ilLanguage $lng;
36  protected const ASYNC_NONE = 0;
37  protected const ASYNC_MODAL = 1;
38  protected const ASYNC_ON = 2;
39  protected \ILIAS\Data\Factory $data;
40  protected \ilObjUser $user;
41  protected string $last_key = "";
42  protected \ILIAS\Refinery\Factory $refinery;
43 
44  protected string $title = "";
45 
46 
50  protected $raw_data = null;
51  protected \ILIAS\HTTP\Services $http;
52  protected \ilCtrlInterface $ctrl;
53  protected \ILIAS\DI\UIServices $ui;
54  protected array $fields = [];
55  protected array $field_path = [];
56  protected array $sections = [self::DEFAULT_SECTION => ["title" => "", "description" => "", "fields" => []]];
57  protected string $current_section = self::DEFAULT_SECTION;
58  protected array $section_of_field = [];
59  protected $class_path;
60  protected string $cmd = self::DEFAULT_SECTION;
61  protected ?Form\Standard $form = null;
62  protected array $upload_handler = [];
63  protected int $async_mode = self::ASYNC_NONE;
64  protected \ilGlobalTemplateInterface $main_tpl;
65  protected ?array $current_switch = null;
66  protected ?array $current_group = null;
67  protected static bool $initialised = false;
68 
72  public function __construct(
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->lng = $DIC->language();
86  $this->main_tpl = $DIC->ui()->mainTemplate();
87  $this->user = $DIC->user();
88  $this->data = new \ILIAS\Data\Factory();
89  $this->submit_caption = $submit_caption;
90  self::initJavascript();
91  }
92 
93  public static function getOnLoadCode(): string
94  {
95  return "il.repository.ui.init();\n" .
96  "il.repository.core.init('" . ILIAS_HTTP_PATH . "')";
97  }
98 
99  public static function initJavascript(): void
100  {
101  global $DIC;
102 
103  if (!isset($DIC["ui.factory"])) {
104  return;
105  }
106 
107  $f = $DIC->ui()->factory();
108  $r = $DIC->ui()->renderer();
109  if (!self::$initialised) {
110  $main_tpl = $DIC->ui()->mainTemplate();
111  $main_tpl->addJavaScript("./Services/Repository/js/repository.js");
112  $main_tpl->addOnLoadCode(self::getOnLoadCode());
113 
114  // render dummy components to load the necessary .js needed for async processing
115  $d = [];
116  $d[] = $f->input()->field()->text("");
117  $r->render($d);
118  self::$initialised = true;
119  }
120  }
121 
122  public function asyncModal(): self
123  {
124  $this->async_mode = self::ASYNC_MODAL;
125  $this->in_modal = true;
126  return $this;
127  }
128 
129  public function async(): self
130  {
131  $this->async_mode = self::ASYNC_ON;
132  return $this;
133  }
134 
135  public function syncModal(): self
136  {
137  $this->in_modal = true;
138  return $this;
139  }
140 
141  public function isSentAsync(): bool
142  {
143  return ($this->async_mode !== self::ASYNC_NONE);
144  }
145 
146  public function getTitle(): string
147  {
148  return $this->title;
149  }
150 
151  public function section(
152  string $key,
153  string $title,
154  string $description = ""
155  ): self {
156  if ($this->title == "") {
157  $this->title = $title;
158  }
159 
160  $this->sections[$key] = [
161  "title" => $title,
162  "description" => $description,
163  "fields" => []
164  ];
165  $this->current_section = $key;
166  return $this;
167  }
168 
169  public function text(
170  string $key,
171  string $title,
172  string $description = "",
173  ?string $value = null
174  ): self {
175  $field = $this->ui->factory()->input()->field()->text($title, $description);
176  if (!is_null($value)) {
177  $field = $field->withValue($value);
178  }
179  $this->addField($key, $field);
180  return $this;
181  }
182 
183  public function checkbox(
184  string $key,
185  string $title,
186  string $description = "",
187  ?bool $value = null
188  ): self {
189  $field = $this->ui->factory()->input()->field()->checkbox($title, $description);
190  if (!is_null($value)) {
191  $field = $field->withValue($value);
192  }
193  $this->addField($key, $field);
194  return $this;
195  }
196 
197  public function hidden(
198  string $key,
199  string $value
200  ): self {
201  $field = $this->ui->factory()->input()->field()->hidden();
202  $field = $field->withValue($value);
203  $this->addField($key, $field);
204  return $this;
205  }
206 
207  public function required(): self
208  {
209  if ($field = $this->getLastField()) {
210  if ($field instanceof \ILIAS\UI\Component\Input\Field\Text) {
211  $field = $field->withRequired(true, new NotEmpty(
212  new Factory(),
213  $this->lng
214  ));
215  } else {
216  $field = $field->withRequired(true);
217  }
218  $this->replaceLastField($field);
219  }
220  return $this;
221  }
222 
223  public function textarea(
224  string $key,
225  string $title,
226  string $description = "",
227  ?string $value = null
228  ): self {
229  $field = $this->ui->factory()->input()->field()->textarea($title, $description);
230  if (!is_null($value)) {
231  $field = $field->withValue($value);
232  }
233  $this->addField($key, $field);
234  return $this;
235  }
236 
237  public function number(
238  string $key,
239  string $title,
240  string $description = "",
241  ?int $value = null,
242  ?int $min_value = null,
243  ?int $max_value = null
244  ): self {
245  $trans = [];
246  if (!is_null($min_value)) {
247  $trans[] = $this->refinery->int()->isGreaterThanOrEqual($min_value);
248  }
249  if (!is_null($max_value)) {
250  $trans[] = $this->refinery->int()->isLessThanOrEqual($max_value);
251  }
252  $field = $this->ui->factory()->input()->field()->numeric($title, $description);
253  if (count($trans) > 0) {
254  $field = $field->withAdditionalTransformation($this->refinery->logical()->parallel($trans));
255  }
256  if (!is_null($value)) {
257  $field = $field->withValue($value);
258  }
259  $this->addField($key, $field);
260  return $this;
261  }
262 
263  public function date(
264  string $key,
265  string $title,
266  string $description = "",
267  ?\ilDate $value = null
268  ): self {
269  $field = $this->ui->factory()->input()->field()->dateTime($title, $description);
270 
271  $format = $this->user->getDateFormat();
272  $dt_format = (string) $format;
273  /*
274  switch ((int) $this->user->getDateFormat()) {
275  case \ilCalendarSettings::DATE_FORMAT_DMY:
276  $format = $this->data->dateFormat()->germanShort();
277  $dt_format = "d.m.Y";
278  break;
279  case \ilCalendarSettings::DATE_FORMAT_MDY:
280  $format = $this->data->dateFormat()->custom()->month()->slash()->day()->slash()->year();
281  $dt_format = "m/d/Y";
282  break;
283  default:
284  $format = $this->data->dateFormat()->standard();
285  $dt_format = "Y-m-d";
286  break;
287  }*/
288 
289  $field = $field->withFormat($format);
290  if (!is_null($value)) {
291  $field = $field->withValue(
292  (new \DateTime($value->get(IL_CAL_DATE)))->format($dt_format)
293  );
294  }
295  $this->addField($key, $field);
296  return $this;
297  }
298 
302  protected function getDateTimeData(?\DateTimeImmutable $value, $use_time = false)
303  {
304  if (is_null($value)) {
305  return null;
306  }
307  if ($use_time) {
308  return new \ilDateTime($value->format("Y-m-d H:i:s"), IL_CAL_DATETIME);
309  }
310  return new \ilDate($value->format("Y-m-d"), IL_CAL_DATE);
311  }
312 
313  public function select(
314  string $key,
315  string $title,
316  array $options,
317  string $description = "",
318  ?string $value = null
319  ): self {
320  $field = $this->ui->factory()->input()->field()->select($title, $options, $description);
321  if (!is_null($value)) {
322  $field = $field->withValue($value);
323  }
324  $this->addField(
325  $key,
326  $field
327  );
328  return $this;
329  }
330 
331  public function radio(
332  string $key,
333  string $title,
334  string $description = "",
335  ?string $value = null
336  ): self {
337  $field = $this->ui->factory()->input()->field()->radio($title, $description);
338  if (!is_null($value)) {
339  $field = $field->withOption($value, ""); // dummy to prevent exception, will be overwritten by radioOption
340  $field = $field->withValue($value);
341  }
342  $this->addField(
343  $key,
344  $field
345  );
346  return $this;
347  }
348 
349  public function radioOption(string $value, string $title, string $description = ""): self
350  {
351  if ($field = $this->getLastField()) {
352  $field = $field->withOption($value, $title, $description);
353  $this->replaceLastField($field);
354  }
355  return $this;
356  }
357 
358  public function switch(
359  string $key,
360  string $title,
361  string $description = "",
362  ?string $value = null
363  ): self {
364  $this->current_switch = [
365  "key" => $key,
366  "title" => $title,
367  "description" => $description,
368  "value" => $value,
369  "groups" => []
370  ];
371  return $this;
372  }
373 
374  public function group(string $key, string $title, string $description = ""): self
375  {
376  $this->endCurrentGroup();
377  $this->current_group = [
378  "key" => $key,
379  "title" => $title,
380  "description" => $description,
381  "fields" => []
382  ];
383  return $this;
384  }
385 
386  protected function endCurrentGroup(): void
387  {
388  if (!is_null($this->current_group)) {
389  if (!is_null($this->current_switch)) {
390  $this->current_switch["groups"][$this->current_group["key"]] =
391  $this->ui->factory()->input()->field()->group(
392  $this->current_group["fields"],
393  $this->current_group["title"]
394  )->withByline($this->current_group["description"]);
395  }
396  }
397  $this->current_group = null;
398  }
399 
400  public function end(): self
401  {
402  $this->endCurrentGroup();
403  if (!is_null($this->current_switch)) {
404  $field = $this->ui->factory()->input()->field()->switchableGroup(
405  $this->current_switch["groups"],
406  $this->current_switch["title"],
407  $this->current_switch["description"]
408  );
409  if (!is_null($this->current_switch["value"])) {
410  $field = $field->withValue($this->current_switch["value"]);
411  }
412  $key = $this->current_switch["key"];
413  $this->current_switch = null;
414  $this->addField($key, $field);
415  }
416  return $this;
417  }
418 
419  public function file(
420  string $key,
421  string $title,
422  \Closure $result_handler,
423  string $id_parameter,
424  string $description = "",
425  ?int $max_files = null,
426  array $mime_types = [],
427  array $ctrl_path = [],
428  string $logger_id = ""
429  ): self {
430  $this->upload_handler[$key] = new \ilRepoStandardUploadHandlerGUI(
431  $result_handler,
432  $id_parameter,
433  $logger_id,
434  $ctrl_path
435  );
436 
437  foreach (["application/x-compressed", "application/x-zip-compressed"] as $zipmime) {
438  if (in_array("application/zip", $mime_types) &&
439  !in_array($zipmime, $mime_types)) {
440  $mime_types[] = $zipmime;
441  }
442  }
443 
444  if (count($mime_types) > 0) {
445  $description .= $this->lng->txt("rep_allowed_types") . ": " .
446  implode(", ", $mime_types);
447  }
448 
449  $field = $this->ui->factory()->input()->field()->file(
450  $this->upload_handler[$key],
451  $title,
452  $description
453  );
454  // not necessary, see https://github.com/ILIAS-eLearning/ILIAS/pull/9314
455  //->withMaxFileSize((int) \ilFileUtils::getPhpUploadSizeLimitInBytes());
456  if (!is_null($max_files)) {
457  $field = $field->withMaxFiles($max_files);
458  }
459  if (count($mime_types) > 0) {
460  $field = $field->withAcceptedMimeTypes($mime_types);
461  }
462 
463  $this->addField(
464  $key,
465  $field
466  );
467  return $this;
468  }
469 
471  {
472  if (!isset($this->upload_handler[$key])) {
473  throw new \ilException("Unknown file upload field: " . $key);
474  }
475  return $this->upload_handler[$key];
476  }
477 
478 
479  protected function addField(string $key, FormInput $field): void
480  {
481  if ($key === "") {
482  throw new \ilException("Missing Input Key: " . $key);
483  }
484  if (isset($this->field[$key])) {
485  throw new \ilException("Duplicate Input Key: " . $key);
486  }
487  $field_path = [];
488  if ($this->current_section !== self::DEFAULT_SECTION) {
489  $field_path[] = $this->current_section;
490  }
491  if (!is_null($this->current_group)) {
492  $this->current_group["fields"][$key] = $field;
493  if (!is_null($this->current_switch)) {
494  $field_path[] = $this->current_switch["key"];
495  $field_path[] = 1; // the value of subitems in SwitchableGroup are in the 1 key of the raw data
496  $field_path[] = $key;
497  }
498  } else {
499  $this->sections[$this->current_section]["fields"][] = $key;
500  $field_path[] = $key;
501  if ($field instanceof \ILIAS\UI\Component\Input\Field\SwitchableGroup) {
502  $field_path[] = 0; // the value of the SwitchableGroup is in the 0 key of the raw data
503  }
504  if ($field instanceof \ILIAS\UI\Component\Input\Field\File) {
505  $field_path[] = 0; // the value of File Inputs is in the 0 key of the raw data
506  }
507  }
508  $this->fields[$key] = $field;
509  $this->field_path[$key] = $field_path;
510  $this->last_key = $key;
511  $this->form = null;
512  }
513 
514  protected function getFieldForKey(string $key): FormInput
515  {
516  if (!isset($this->fields[$key])) {
517  throw new \ilException("Unknown Key: " . $key);
518  }
519  return $this->fields[$key];
520  }
521 
522  protected function getLastField(): ?FormInput
523  {
524  return $this->fields[$this->last_key] ?? null;
525  }
526 
527  protected function replaceLastField(FormInput $field): void
528  {
529  if ($this->last_key !== "") {
530  $this->fields[$this->last_key] = $field;
531  }
532  }
533 
534  protected function getForm(): Form\Standard
535  {
536  $ctrl = $this->ctrl;
537 
538  if (is_null($this->form)) {
539  $async = ($this->async_mode !== self::ASYNC_NONE);
540  $action = "";
541  if (!is_null($this->class_path)) {
542  $action = $ctrl->getLinkTargetByClass($this->class_path, $this->cmd, "", $async);
543  }
544  $inputs = [];
545  foreach ($this->sections as $sec_key => $section) {
546  if ($sec_key === self::DEFAULT_SECTION) {
547  foreach ($this->sections[$sec_key]["fields"] as $f_key) {
548  $inputs[$f_key] = $this->getFieldForKey($f_key);
549  }
550  } elseif (count($this->sections[$sec_key]["fields"]) > 0) {
551  $sec_inputs = [];
552  foreach ($this->sections[$sec_key]["fields"] as $f_key) {
553  $sec_inputs[$f_key] = $this->getFieldForKey($f_key);
554  }
555  $inputs[$sec_key] = $this->ui->factory()->input()->field()->section(
556  $sec_inputs,
557  $section["title"],
558  $section["description"]
559  );
560  }
561  }
562  $this->form = $this->ui->factory()->input()->container()->form()->standard(
563  $action,
564  $inputs
565  );
566  if ($this->submit_caption !== "") {
567  $this->form = $this->form->withSubmitLabel($this->submit_caption);
568  }
569  }
570  return $this->form;
571  }
572 
573  public function getSubmitLabel(): string
574  {
575  return $this->getForm()->getSubmitLabel() ?? $this->lng->txt("save");
576  }
577 
578  protected function _getData(): void
579  {
580  if (is_null($this->raw_data)) {
581  $request = $this->http->request();
582  $this->form = $this->getForm()->withRequest($request);
583  $this->raw_data = $this->form->getData();
584  }
585  }
586 
587  public function isValid(): bool
588  {
589  $this->_getData();
590  return !(is_null($this->raw_data));
591  }
592 
596  public function getData(string $key)
597  {
598  $this->_getData();
599 
600  if (!isset($this->fields[$key])) {
601  throw new \ilException("Unknown Key: " . $key);
602  }
603 
604  $value = $this->raw_data;
605  foreach ($this->field_path[$key] as $path_key) {
606  if (!isset($value[$path_key])) {
607  return null;
608  }
609  $value = $value[$path_key];
610  }
611 
612  $field = $this->getFieldForKey($key);
613 
614  if ($field instanceof \ILIAS\UI\Component\Input\Field\DateTime) {
616  $value = $this->getDateTimeData($value, $field->getUseTime());
617  }
618 
619  return $value;
620  }
621 
622  public function render(): string
623  {
624  if ($this->async_mode === self::ASYNC_NONE && !$this->ctrl->isAsynch()) {
625  $html = $this->ui->renderer()->render($this->getForm());
626  } else {
627  $html = $this->ui->renderer()->renderAsync($this->getForm()) . "<script>" . $this->getOnLoadCode() . "</script>";
628  }
629  if ($this->in_modal) {
630  if ($this->async_mode === self::ASYNC_MODAL) {
631  $html = str_replace("<form ", "<form data-rep-modal-form='async' ", $html);
632  } else {
633  $html = str_replace("<form ", "<form data-rep-modal-form='sync' ", $html);
634  }
635  }
636  return $html;
637  }
638 }
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="")
This describes commonalities between all forms.
Definition: Form.php:32
const IL_CAL_DATETIME
Class ChatMainBarProvider .
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
addField(string $key, FormInput $field)
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: Factory.php:21
global $DIC
Definition: feed.php:28
radio(string $key, string $title, string $description="", ?string $value=null)
section(string $key, string $title, string $description="")
radioOption(string $value, string $title, string $description="")
string $key
Consumer key/client ID value.
Definition: System.php:193
textarea(string $key, string $title, string $description="", ?string $value=null)
getLinkTargetByClass( $a_class, string $a_cmd=null, string $a_anchor=null, bool $is_async=false, bool $has_xml_style=false)
Returns a link target for the given information.
form( $class_path, string $cmd, string $submit_caption="")
checkbox(string $key, string $title, string $description="", ?bool $value=null)
const IL_CAL_DATE
text(string $key, string $title, string $description="", ?string $value=null)
__construct( $class_path, string $cmd, string $submit_caption="")
group(string $key, string $title, string $description="")
select(string $key, string $title, array $options, string $description="", ?string $value=null)
This describes commonalities between all inputs.
Definition: Input.php:46
number(string $key, string $title, string $description="", ?int $value=null, ?int $min_value=null, ?int $max_value=null)
This describes inputs that can be used in forms.
Definition: FormInput.php:31
getDateTimeData(?\DateTimeImmutable $value, $use_time=false)
$r
date(string $key, string $title, string $description="", ?\ilDate $value=null)