1<?php
2/* Copyright (c) 1998-2009 ILIAS open source, Extended GPL, see docs/LICENSE */
3
4include_once("./Services/Form/classes/class.ilFormGUI.php");
5
6// please do not add any more includes here if things are not really
7// highly re-used
8include_once("./Services/Form/classes/class.ilFormPropertyGUI.php");
9include_once("./Services/Form/classes/class.ilSubEnabledFormPropertyGUI.php");
10include_once("./Services/Form/classes/class.ilCheckboxInputGUI.php");
11include_once("./Services/Form/classes/class.ilCustomInputGUI.php");
12include_once("./Services/Form/classes/class.ilDateTimeInputGUI.php");
13include_once("./Services/Form/classes/class.ilFileInputGUI.php");
14include_once("./Services/Form/classes/class.ilImageFileInputGUI.php");
15include_once('./Services/Form/classes/class.ilFlashFileInputGUI.php');
16include_once("./Services/Form/classes/class.ilLocationInputGUI.php");
17include_once("./Services/Form/classes/class.ilRadioGroupInputGUI.php");
18include_once("./Services/Form/classes/class.ilCheckboxGroupInputGUI.php");
19include_once("./Services/Form/classes/class.ilFormSectionHeaderGUI.php");
20include_once("./Services/Form/classes/class.ilSelectInputGUI.php");
21include_once("./Services/Form/classes/class.ilTextAreaInputGUI.php");
22include_once("./Services/Form/classes/class.ilTextInputGUI.php");
23include_once("./Services/Form/classes/class.ilDurationInputGUI.php");
24include_once("./Services/Form/classes/class.ilFeedUrlInputGUI.php");
25include_once("./Services/Form/classes/class.ilNonEditableValueGUI.php");
26include_once("./Services/Form/classes/class.ilRegExpInputGUI.php");
27include_once('./Services/Form/classes/class.ilColorPickerInputGUI.php');
28include_once('./Services/Form/classes/class.ilPasswordInputGUI.php');
29include_once('./Services/Form/classes/class.ilUserLoginInputGUI.php');
30include_once('./Services/Form/classes/class.ilEMailInputGUI.php');
31include_once('./Services/Form/classes/class.ilHiddenInputGUI.php');
32include_once('./Services/Form/classes/class.ilNumberInputGUI.php');
33include_once('./Services/Form/classes/class.ilCSSRectInputGUI.php');
34include_once('./Services/Form/classes/class.ilTextWizardInputGUI.php');
35include_once './Services/Form/classes/class.ilFileWizardInputGUI.php';
36include_once './Services/Form/classes/class.ilFormulaInputGUI.php';
37include_once './Services/Form/classes/class.ilBirthdayInputGUI.php';
38
39/**
40* This class represents a property form user interface
41*
42* @author Alex Killing <alex.killing@gmx.de>
43* @version $Id$
44* @ilCtrl_Calls ilPropertyFormGUI: ilFormPropertyDispatchGUI
45* @ingroup	ServicesForm
46*/
47class ilPropertyFormGUI extends ilFormGUI
48{
49    /**
50     * @var ilLanguage
51     */
52    protected $lng;
53
54    /**
55     * @var ilCtrl
56     */
57    protected $ctrl;
58
59    /**
60     * @var ilTemplate
61     */
62    protected $tpl;
63
64    /**
65     * @var ilObjUser
66     */
67    protected $user;
68
69    /**
70     * @var ilSetting
71     */
72    protected $settings;
73
74    private $buttons = array();
75    private $items = array();
76    protected $mode = "std";
77    protected $check_input_called = false;
78    protected $disable_standard_message = false;
79    protected $top_anchor = "il_form_top";
80    protected $titleicon = false;
81    protected $description = "";
82    protected $tbl_width = false;
83    protected $show_top_buttons = true;
84    protected $hide_labels = false;
85
86    protected $force_top_buttons = false;
87
88    /**
89    * Constructor
90    *
91    * @param
92    */
93    public function __construct()
94    {
95        global $DIC;
96
97        $this->lng = $DIC->language();
98        $this->ctrl = $DIC->ctrl();
99
100        $this->user = null;
101        if (isset($DIC["ilUser"])) {
102            $this->user = $DIC["ilUser"];
103        }
104
105        $this->settings = null;
106        if (isset($DIC["ilSetting"])) {
107            $this->settings = $DIC["ilSetting"];
108        }
109
110        $lng = $DIC->language();
111
112        $lng->loadLanguageModule("form");
113
114        // avoid double submission
115        $this->setPreventDoubleSubmission(true);
116
117        // do it as early as possible
118        $this->rebuildUploadedFiles();
119    }
120
121    /**
122    * Execute command.
123    */
124    public function executeCommand()
125    {
126        $ilCtrl = $this->ctrl;
127
128        $next_class = $ilCtrl->getNextClass($this);
129        $cmd = $ilCtrl->getCmd();
130
131        switch ($next_class) {
132            case 'ilformpropertydispatchgui':
133                $ilCtrl->saveParameter($this, 'postvar');
134                include_once './Services/Form/classes/class.ilFormPropertyDispatchGUI.php';
135                $form_prop_dispatch = new ilFormPropertyDispatchGUI();
136                $item = $this->getItemByPostVar($_REQUEST["postvar"]);
137                $form_prop_dispatch->setItem($item);
138                return $ilCtrl->forwardCommand($form_prop_dispatch);
139                break;
140
141        }
142        return false;
143    }
144
145    /**
146     * Set table width
147     *
148     * @access public
149     * @param string table width
150     *
151     */
152    final public function setTableWidth($a_width)
153    {
154        $this->tbl_width = $a_width;
155    }
156
157    /**
158     * get table width
159     *
160     * @access public
161     *
162     */
163    final public function getTableWidth()
164    {
165        return $this->tbl_width;
166    }
167
168    /**
169    * Set Mode ('std', 'subform').
170    *
171    * @param	string	$a_mode	Mode ('std', 'subform')
172    */
173    public function setMode($a_mode)
174    {
175        $this->mode = $a_mode;
176    }
177
178    /**
179    * Get Mode ('std', 'subform').
180    *
181    * @return	string	Mode ('std', 'subform')
182    */
183    public function getMode()
184    {
185        return $this->mode;
186    }
187
188    /**
189    * Set Title.
190    *
191    * @param	string	$a_title	Title
192    */
193    public function setTitle($a_title)
194    {
195        $this->title = $a_title;
196    }
197
198    /**
199    * Get Title.
200    *
201    * @return	string	Title
202    */
203    public function getTitle()
204    {
205        return $this->title;
206    }
207
208    /**
209    * Set TitleIcon.
210    *
211    * @param	string	$a_titleicon	TitleIcon
212    */
213    public function setTitleIcon($a_titleicon)
214    {
215        $this->titleicon = $a_titleicon;
216    }
217
218    /**
219    * Get TitleIcon.
220    *
221    * @return	string	TitleIcon
222    */
223    public function getTitleIcon()
224    {
225        return $this->titleicon;
226    }
227
228    /**
229    * Set description
230    *
231    * @param	string	description
232    */
233    public function setDescription($a_val)
234    {
235        $this->description = $a_val;
236    }
237
238    /**
239    * Get description
240    *
241    * @return	string	description
242    */
243    public function getDescription()
244    {
245        return $this->description;
246    }
247
248    /**
249     * Set top anchor
250     *
251     * @param	string	top anchor
252     * @deprecated
253     */
254    public function setTopAnchor($a_val)
255    {
256        $this->top_anchor = $a_val;
257    }
258
259    /**
260    * Get top anchor
261    *
262    * @return	string	top anchor
263    */
264    public function getTopAnchor()
265    {
266        return $this->top_anchor;
267    }
268
269    /**
270     * Get show top buttons
271     */
272    public function setShowTopButtons($a_val)
273    {
274        $this->show_top_buttons = $a_val;
275    }
276
277    /**
278     * Set show top buttons
279     */
280    public function getShowTopButtons()
281    {
282        return $this->show_top_buttons;
283    }
284
285    /**
286     * Set force top buttons
287     *
288     * @param bool $a_val force top buttons
289     */
290    public function setForceTopButtons($a_val)
291    {
292        $this->force_top_buttons = $a_val;
293    }
294
295    /**
296     * Get force top buttons
297     *
298     * @return bool force top buttons
299     */
300    public function getForceTopButtons()
301    {
302        return $this->force_top_buttons;
303    }
304
305
306    /**
307    * Add Item (Property, SectionHeader).
308    *
309    * @param	object	$a_property		Item object
310    */
311    public function addItem($a_item)
312    {
313        $a_item->setParentForm($this);
314        return $this->items[] = $a_item;
315    }
316
317    /**
318    * Remove Item.
319    *
320    * @param	string	$a_postvar		Post Var
321    */
322    public function removeItemByPostVar($a_post_var, $a_remove_unused_headers = false)
323    {
324        foreach ($this->items as $key => $item) {
325            if (method_exists($item, "getPostVar") && $item->getPostVar() == $a_post_var) {
326                unset($this->items[$key]);
327            }
328        }
329
330        // remove section headers if they do not contain any items anymore
331        if ($a_remove_unused_headers) {
332            $unset_keys = array();
333            $last_item = null;
334            $last_key = null;
335            foreach ($this->items as $key => $item) {
336                if ($item instanceof ilFormSectionHeaderGUI && $last_item instanceof ilFormSectionHeaderGUI) {
337                    $unset_keys[] = $last_key;
338                }
339                $last_item = $item;
340                $last_key = $key;
341            }
342            if ($last_item instanceof ilFormSectionHeaderGUI) {
343                $unset_keys[] = $last_key;
344            }
345            foreach ($unset_keys as $key) {
346                unset($this->items[$key]);
347            }
348        }
349    }
350
351    /**
352    * Get Item by POST variable.
353    *
354    * @param	string	$a_postvar		Post Var
355    */
356    public function getItemByPostVar($a_post_var)
357    {
358        foreach ($this->items as $key => $item) {
359            if ($item->getType() != "section_header") {
360                //if ($item->getPostVar() == $a_post_var)
361                $ret = $item->getItemByPostVar($a_post_var);
362                if (is_object($ret)) {
363                    return $ret;
364                }
365            }
366        }
367
368        return false;
369    }
370
371    /**
372    * Set Items
373    *
374    * @param	array	$a_items	array of item objects
375    */
376    public function setItems($a_items)
377    {
378        $this->items = $a_items;
379    }
380
381    /**
382    * Get Items
383    *
384    * @return	array	array of item objects
385    */
386    public function getItems()
387    {
388        return $this->items;
389    }
390
391    /**
392     * returns a flat array of all input items including
393     * the possibly existing subitems recursively
394     *
395     * @return array
396     */
397    public function getInputItemsRecursive()
398    {
399        $inputItems = array();
400
401        foreach ($this->items as $item) {
402            if ($item->getType() == 'section_header') {
403                continue;
404            }
405
406            $inputItems[] = $item;
407
408            if ($item instanceof ilSubEnabledFormPropertyGUI) {
409                $inputItems = array_merge($inputItems, $item->getSubInputItemsRecursive());
410            }
411        }
412
413        return $inputItems;
414    }
415
416    /**
417    * Set disable standard message
418    *
419    * @param	boolean		disable standard message
420    */
421    public function setDisableStandardMessage($a_val)
422    {
423        $this->disable_standard_message = $a_val;
424    }
425
426    /**
427    * Get disable standard message
428    *
429    * @return	boolean		disable standard message
430    */
431    public function getDisableStandardMessage()
432    {
433        return $this->disable_standard_message;
434    }
435
436    /**
437    * Get a value indicating whether the labels should be hidden or not.
438    *
439    * @return	boolean		true, to hide the labels; otherwise, false.
440    */
441    public function getHideLabels()
442    {
443        return $this->hide_labels;
444    }
445
446    /**
447    * Set a value indicating whether the labels should be hidden or not.
448    *
449    * @param	boolean	$a_value	Indicates whether the labels should be hidden.
450    */
451    public function setHideLabels($a_value = true)
452    {
453        $this->hide_labels = $a_value;
454    }
455
456    /**
457    * Set form values from an array
458    *
459    * @param	array	$a_values	Value array (key is post variable name, value is value)
460    */
461    public function setValuesByArray($a_values, $a_restrict_to_value_keys = false)
462    {
463        foreach ($this->items as $item) {
464            if (!($a_restrict_to_value_keys) ||
465                in_array($item->getPostVar(), array_keys($a_values))) {
466                $item->setValueByArray($a_values);
467            }
468        }
469    }
470
471    /**
472    * Set form values from POST values
473    *
474    */
475    public function setValuesByPost()
476    {
477        foreach ($this->items as $item) {
478            $item->setValueByArray($_POST);
479        }
480    }
481
482    /**
483    * Check Post Input. This method also strips slashes and html from
484    * input and sets the alert texts for the items, if the input was not ok.
485    *
486    * @return	boolean		ok true/false
487    */
488    public function checkInput()
489    {
490        global $DIC;
491
492        if ($this->check_input_called) {
493            die("Error: ilPropertyFormGUI->checkInput() called twice.");
494        }
495
496        $ok = true;
497        foreach ($this->items as $item) {
498            $item_ok = $item->checkInput();
499            if (!$item_ok) {
500                $ok = false;
501            }
502        }
503
504        // check if POST is missint completely (if post_max_size exceeded)
505        if (count($this->items) > 0 && !is_array($_POST)) {
506            $ok = false;
507        }
508
509        $this->check_input_called = true;
510
511
512
513        // try to keep uploads for another try
514        if (!$ok && $_POST["ilfilehash"] && sizeof($_FILES)) {
515            $hash = $_POST["ilfilehash"];
516
517            foreach ($_FILES as $field => $data) {
518                // only try to keep files that are ok
519                // see 25484: Wrong error handling when uploading icon instead of tile
520                $item = $this->getItemByPostVar($field);
521                if (is_bool($item) || !$item->checkInput()) {
522                    continue;
523                }
524                // we support up to 2 nesting levels (see test/assesment)
525                if (is_array($data["tmp_name"])) {
526                    foreach ($data["tmp_name"] as $idx => $upload) {
527                        if (is_array($upload)) {
528                            foreach ($upload as $idx2 => $file) {
529                                if ($file && is_uploaded_file($file)) {
530                                    $file_name = $data["name"][$idx][$idx2];
531                                    $file_type = $data["type"][$idx][$idx2];
532                                    $this->keepFileUpload($hash, $field, $file, $file_name, $file_type, $idx, $idx2);
533                                }
534                            }
535                        } elseif ($upload && is_uploaded_file($upload)) {
536                            $file_name = $data["name"][$idx];
537                            $file_type = $data["type"][$idx];
538                            $this->keepFileUpload($hash, $field, $upload, $file_name, $file_type, $idx);
539                        }
540                    }
541                } else {
542                    $this->keepFileUpload($hash, $field, $data["tmp_name"], $data["name"], $data["type"]);
543                }
544            }
545        }
546        $http = $DIC->http();
547        $txt = $DIC->language()->txt("form_input_not_valid");
548        switch ($http->request()->getHeaderLine('Accept')) {
549            // When JS asks for a valid JSON-Response, we send the success and message as JSON
550            case 'application/json':
551                $stream = \ILIAS\Filesystem\Stream\Streams::ofString(json_encode([
552                    'success' => $ok,
553                    'message' => $txt,
554                ]));
555                $http->saveResponse($http->response()->withBody($stream));
556
557                return $ok;
558
559            // Otherwise we send it using ilUtil and it will be rendered in the Template
560            default:
561
562                if (!$ok && !$this->getDisableStandardMessage()) {
563                    ilUtil::sendFailure($txt);
564                }
565
566                return $ok;
567        }
568    }
569
570    /**
571     *
572     * Returns the value of a HTTP-POST variable, identified by the passed id
573     *
574     * @param	string	The key used for value determination
575     * @param	boolean	A flag whether the form input has to be validated before calling this method
576     * @return	string	The value of a HTTP-POST variable, identified by the passed id
577     * @access	public
578     *
579     */
580    public function getInput($a_post_var, $ensureValidation = true)
581    {
582        // this check ensures, that checkInput has been called (incl. stripSlashes())
583        if (!$this->check_input_called && $ensureValidation) {
584            die("Error: ilPropertyFormGUI->getInput() called without calling checkInput() first.");
585        }
586
587        return $_POST[$a_post_var];
588    }
589
590    /**
591    * Add a custom property.
592    *
593    * @param	string		Title
594    * @param	string		HTML.
595    * @param	string		Info text.
596    * @param	string		Alert text.
597    * @param	boolean		Required field. (Default false)
598    */
599    public function addCustomProperty(
600        $a_title,
601        $a_html,
602        $a_info = "",
603        $a_alert = "",
604        $a_required = false
605    ) {
606        $this->properties[] = array("type" => "custom",
607            "title" => $a_title,
608            "html" => $a_html,
609            "info" => $a_info);
610    }
611
612    /**
613    * Add Command button
614    *
615    * @param	string	Command
616    * @param	string	Text
617    */
618    public function addCommandButton($a_cmd, $a_text, $a_id = "")
619    {
620        $this->buttons[] = array("cmd" => $a_cmd, "text" => $a_text, "id" => $a_id);
621    }
622
623
624    /**
625     * Return all Command buttons
626     *
627     * @return array
628     */
629    public function getCommandButtons()
630    {
631        return $this->buttons;
632    }
633
634    /**
635    * Remove all command buttons
636    */
637    public function clearCommandButtons()
638    {
639        $this->buttons = array();
640    }
641
642    /**
643    * Get Content.
644    */
645    public function getContent()
646    {
647        global $DIC;
648        $lng = $this->lng;
649        $tpl = $DIC["tpl"];
650        $ilSetting = $this->settings;
651
652        include_once("./Services/YUI/classes/class.ilYuiUtil.php");
653        ilYuiUtil::initEvent();
654        ilYuiUtil::initDom();
655        ilYuiUtil::initAnimation();
656
657        $tpl->addJavaScript("./Services/JavaScript/js/Basic.js");
658        $tpl->addJavaScript("Services/Form/js/Form.js");
659
660        $this->tpl = new ilTemplate("tpl.property_form.html", true, true, "Services/Form");
661
662        // check if form has not title and first item is a section header
663        // -> use section header for title and remove section header
664        // -> command buttons are presented on top
665        $fi = $this->items[0];
666        if ($this->getMode() == "std" &&
667            $this->getTitle() == "" &&
668            is_object($fi) && $fi->getType() == "section_header"
669            ) {
670            $this->setTitle($fi->getTitle());
671            unset($this->items[0]);
672        }
673
674
675        // title icon
676        if ($this->getTitleIcon() != "" && @is_file($this->getTitleIcon())) {
677            $this->tpl->setCurrentBlock("title_icon");
678            $this->tpl->setVariable("IMG_ICON", $this->getTitleIcon());
679            $this->tpl->parseCurrentBlock();
680        }
681
682        // title
683        if ($this->getTitle() != "") {
684            // commands on top
685            if (count($this->buttons) > 0 && $this->getShowTopButtons() && (count($this->items) > 2 || $this->force_top_buttons)) {
686                // command buttons
687                foreach ($this->buttons as $button) {
688                    $this->tpl->setCurrentBlock("cmd2");
689                    $this->tpl->setVariable("CMD", $button["cmd"]);
690                    $this->tpl->setVariable("CMD_TXT", $button["text"]);
691                    if ($button["id"] != "") {
692                        $this->tpl->setVariable("CMD2_ID", " id='" . $button["id"] . "_top'");
693                    }
694                    $this->tpl->parseCurrentBlock();
695                }
696                $this->tpl->setCurrentBlock("commands2");
697                $this->tpl->parseCurrentBlock();
698            }
699
700            if (is_object($ilSetting)) {
701                if ($ilSetting->get('char_selector_availability') > 0) {
702                    require_once 'Services/UIComponent/CharSelector/classes/class.ilCharSelectorGUI.php';
703                    if (ilCharSelectorGUI::_isAllowed()) {
704                        $char_selector = ilCharSelectorGUI::_getCurrentGUI();
705                        if ($char_selector->getConfig()->getAvailability() == ilCharSelectorConfig::ENABLED) {
706                            $char_selector->addToPage();
707                            $this->tpl->TouchBlock('char_selector');
708                        }
709                    }
710                }
711            }
712
713            $this->tpl->setCurrentBlock("header");
714            $this->tpl->setVariable("TXT_TITLE", $this->getTitle());
715            //$this->tpl->setVariable("LABEL", $this->getTopAnchor());
716            $this->tpl->setVariable("TXT_DESCRIPTION", $this->getDescription());
717            $this->tpl->parseCurrentBlock();
718        }
719        $this->tpl->touchBlock("item");
720
721        // properties
722        $this->required_text = false;
723        foreach ($this->items as $item) {
724            if ($item->getType() != "hidden") {
725                $this->insertItem($item);
726            }
727        }
728
729        // required
730        if ($this->required_text && $this->getMode() == "std") {
731            $this->tpl->setCurrentBlock("required_text");
732            $this->tpl->setVariable("TXT_REQUIRED", $lng->txt("required_field"));
733            $this->tpl->parseCurrentBlock();
734        }
735
736        // command buttons
737        foreach ($this->buttons as $button) {
738            $this->tpl->setCurrentBlock("cmd");
739            $this->tpl->setVariable("CMD", $button["cmd"]);
740            $this->tpl->setVariable("CMD_TXT", $button["text"]);
741
742            if ($button["id"] != "") {
743                $this->tpl->setVariable("CMD_ID", " id='" . $button["id"] . "'");
744            }
745
746            $this->tpl->parseCurrentBlock();
747        }
748
749        // #18808
750        if ($this->getMode() != "subform") {
751            // try to keep uploads even if checking input fails
752            if ($this->getMultipart()) {
753                $hash = $_POST["ilfilehash"];
754                if (!$hash) {
755                    $hash = md5(uniqid(mt_rand(), true));
756                }
757                $fhash = new ilHiddenInputGUI("ilfilehash");
758                $fhash->setValue($hash);
759                $this->addItem($fhash);
760            }
761        }
762
763        // hidden properties
764        $hidden_fields = false;
765        foreach ($this->items as $item) {
766            if ($item->getType() == "hidden") {
767                $item->insert($this->tpl);
768                $hidden_fields = true;
769            }
770        }
771
772        if ($this->required_text || count($this->buttons) > 0 || $hidden_fields) {
773            $this->tpl->setCurrentBlock("commands");
774            $this->tpl->parseCurrentBlock();
775        }
776
777
778        if ($this->getMode() == "subform") {
779            $this->tpl->touchBlock("sub_table");
780        } else {
781            $this->tpl->touchBlock("std_table");
782            $this->tpl->setVariable('STD_TABLE_WIDTH', $this->getTableWidth());
783        }
784
785        return $this->tpl->get();
786    }
787
788    protected function hideRequired($a_type)
789    {
790        // #15818
791        return in_array($a_type, array("non_editable_value"));
792    }
793
794    public function insertItem($item, $a_sub_item = false)
795    {
796        global $DIC;
797        $tpl = $DIC["tpl"];
798        ;
799        $lng = $this->lng;
800
801
802        $cfg = array();
803
804        //if(method_exists($item, "getMulti") && $item->getMulti())
805        if ($item instanceof ilMultiValuesItem && $item->getMulti()) {
806            $tpl->addJavascript("./Services/Form/js/ServiceFormMulti.js");
807
808            $this->tpl->setCurrentBlock("multi_in");
809            $this->tpl->setVariable("ID", $item->getFieldId());
810            $this->tpl->parseCurrentBlock();
811
812            $this->tpl->touchBlock("multi_out");
813
814
815            // add hidden item to enable preset multi items
816            // not used yet, should replace hidden field stuff
817            $multi_values = $item->getMultiValues();
818            if (is_array($multi_values) && sizeof($multi_values) > 1) {
819                $multi_value = new ilHiddenInputGUI("ilMultiValues~" . $item->getPostVar());
820                $multi_value->setValue(implode("~", $multi_values));
821                $this->addItem($multi_value);
822            }
823            $cfg["multi_values"] = $multi_values;
824        }
825
826        $item->insert($this->tpl);
827
828        if ($item->getType() == "file" || $item->getType() == "image_file") {
829            $this->setMultipart(true);
830        }
831
832        if ($item->getType() != "section_header") {
833            $cfg["id"] = $item->getFieldId();
834
835            // info text
836            if ($item->getInfo() != "") {
837                $this->tpl->setCurrentBlock("description");
838                $this->tpl->setVariable(
839                    "PROPERTY_DESCRIPTION",
840                    $item->getInfo()
841                );
842                $this->tpl->parseCurrentBlock();
843            }
844
845            if ($this->getMode() == "subform") {
846                // required
847                if (!$this->hideRequired($item->getType())) {
848                    if ($item->getRequired()) {
849                        $this->tpl->touchBlock("sub_required");
850                        $this->required_text = true;
851                    }
852                }
853
854                // hidden title (for accessibility, e.g. file upload)
855                if ($item->getHiddenTitle() != "") {
856                    $this->tpl->setCurrentBlock("sub_hid_title");
857                    $this->tpl->setVariable(
858                        "SPHID_TITLE",
859                        $item->getHiddenTitle()
860                    );
861                    $this->tpl->parseCurrentBlock();
862                }
863
864                $this->tpl->setCurrentBlock("sub_prop_start");
865                $this->tpl->setVariable("PROPERTY_TITLE", $item->getTitle());
866                $this->tpl->setVariable("PROPERTY_CLASS", "il_" . $item->getType());
867                if ($item->getType() != "non_editable_value" && $item->getFormLabelFor() != "") {
868                    $this->tpl->setVariable("FOR_ID", ' for="'.$item->getFormLabelFor().'" ');
869                }
870                $this->tpl->setVariable("LAB_ID", $item->getFieldId());
871                $this->tpl->parseCurrentBlock();
872            } else {
873                // required
874                if (!$this->hideRequired($item->getType())) {
875                    if ($item->getRequired()) {
876                        $this->tpl->touchBlock("required");
877                        $this->required_text = true;
878                    }
879                }
880
881                // hidden title (for accessibility, e.g. file upload)
882                if ($item->getHiddenTitle() != "") {
883                    $this->tpl->setCurrentBlock("std_hid_title");
884                    $this->tpl->setVariable(
885                        "PHID_TITLE",
886                        $item->getHiddenTitle()
887                    );
888                    $this->tpl->parseCurrentBlock();
889                }
890
891                $this->tpl->setCurrentBlock("std_prop_start");
892                $this->tpl->setVariable("PROPERTY_TITLE", $item->getTitle());
893                if ($item->getType() != "non_editable_value" && $item->getFormLabelFor() != "") {
894                    $this->tpl->setVariable("FOR_ID", ' for="'.$item->getFormLabelFor().'" ');
895                }
896                $this->tpl->setVariable("LAB_ID", $item->getFieldId());
897                if ($this->getHideLabels()) {
898                    $this->tpl->setVariable("HIDE_LABELS_STYLE", " ilFormOptionHidden");
899                }
900                $this->tpl->parseCurrentBlock();
901            }
902
903            // alert
904            if ($item->getType() != "non_editable_value" && $item->getAlert() != "") {
905                $this->tpl->setCurrentBlock("alert");
906                $this->tpl->setVariable(
907                    "IMG_ALERT",
908                    ilUtil::getImagePath("icon_alert.svg")
909                );
910                $this->tpl->setVariable(
911                    "ALT_ALERT",
912                    $lng->txt("alert")
913                );
914                $this->tpl->setVariable(
915                    "TXT_ALERT",
916                    $item->getAlert()
917                );
918                $this->tpl->parseCurrentBlock();
919            }
920
921            // subitems
922            $sf = null;
923            if ($item->getType() != "non_editable_value" or 1) {
924                $sf = $item->getSubForm();
925                if ($item->hideSubForm() && is_object($sf)) {
926                    $this->tpl->setCurrentBlock("sub_form_hide");
927                    $this->tpl->setVariable("DSFID", $item->getFieldId());
928                    $this->tpl->parseCurrentBlock();
929                }
930            }
931
932
933            $sf_content = "";
934            if (is_object($sf)) {
935                $sf_content = $sf->getContent();
936                if ($sf->getMultipart()) {
937                    $this->setMultipart(true);
938                }
939                $this->tpl->setCurrentBlock("sub_form");
940                $this->tpl->setVariable("PROP_SUB_FORM", $sf_content);
941                $this->tpl->setVariable("SFID", $item->getFieldId());
942                $this->tpl->parseCurrentBlock();
943            }
944
945            $this->tpl->setCurrentBlock("prop");
946            /* not used yet
947            $this->tpl->setVariable("ID", $item->getFieldId());
948            $this->tpl->setVariable("CFG", ilJsonUtil::encode($cfg));*/
949            $this->tpl->parseCurrentBlock();
950        }
951
952
953        $this->tpl->touchBlock("item");
954    }
955
956    public function getHTML()
957    {
958        $html = parent::getHTML();
959
960        // #13531 - get content that has to reside outside of the parent form tag, e.g. panels/layers
961        foreach ($this->items as $item) {
962            // #13536 - ilFormSectionHeaderGUI does NOT extend ilFormPropertyGUI ?!
963            if (method_exists($item, "getContentOutsideFormTag")) {
964                $outside = $item->getContentOutsideFormTag();
965                if ($outside) {
966                    $html .= $outside;
967                }
968            }
969        }
970
971        return $html;
972    }
973
974
975    //
976    // UPLOAD HANDLING
977    //
978
979    /**
980     * Import upload into temp directory
981     *
982     * @param string $a_hash unique form hash
983     * @param string $a_field form field
984     * @param string $a_tmp_name temp file name
985     * @param string $a_name original file name
986     * @param string $a_type file mime type
987     * @param mixed $a_index form field index (if array)
988     * @param mixed $a_sub_index form field subindex (if array)
989     * @return bool
990     */
991    protected function keepFileUpload($a_hash, $a_field, $a_tmp_name, $a_name, $a_type, $a_index = null, $a_sub_index = null)
992    {
993        if (trim($a_tmp_name) == "") {
994            return;
995        }
996
997        $a_name = ilUtil::getAsciiFileName($a_name);
998
999        $tmp_file_name = implode("~~", array(session_id(),
1000            $a_hash,
1001            $a_field,
1002            $a_index,
1003            $a_sub_index,
1004            str_replace("/", "~~", $a_type),
1005            str_replace("~~", "_", $a_name)));
1006
1007        // make sure temp directory exists
1008        $temp_path = ilUtil::getDataDir() . "/temp";
1009        if (!is_dir($temp_path)) {
1010            ilUtil::createDirectory($temp_path);
1011        }
1012
1013        ilUtil::moveUploadedFile($a_tmp_name, $tmp_file_name, $temp_path . "/" . $tmp_file_name);
1014
1015        /** @var ilFileInputGUI $file_input */
1016        $file_input = $this->getItemByPostVar($a_field);
1017        $file_input->setPending($a_name);
1018    }
1019
1020    /**
1021     * Get file upload data
1022     *
1023     * @param string $a_field form field
1024     * @param mixed $a_index form field index (if array)
1025     * @param mixed $a_sub_index form field subindex (if array)
1026     * @return array (tmp_name, name, type, error, size, is_upload)
1027     */
1028    public function getFileUpload($a_field, $a_index = null, $a_sub_index = null)
1029    {
1030        $res = array();
1031        if ($a_index) {
1032            if ($_FILES[$a_field]["tmp_name"][$a_index][$a_sub_index]) {
1033                $res = array(
1034                    "tmp_name" => $_FILES[$a_field]["tmp_name"][$a_index][$a_sub_index],
1035                    "name" => $_FILES[$a_field]["name"][$a_index][$a_sub_index],
1036                    "type" => $_FILES[$a_field]["type"][$a_index][$a_sub_index],
1037                    "error" => $_FILES[$a_field]["error"][$a_index][$a_sub_index],
1038                    "size" => $_FILES[$a_field]["size"][$a_index][$a_sub_index],
1039                    "is_upload" => true
1040                );
1041            }
1042        } elseif ($a_sub_index) {
1043            if ($_FILES[$a_field]["tmp_name"][$a_index]) {
1044                $res = array(
1045                    "tmp_name" => $_FILES[$a_field]["tmp_name"][$a_index],
1046                    "name" => $_FILES[$a_field]["name"][$a_index],
1047                    "type" => $_FILES[$a_field]["type"][$a_index],
1048                    "error" => $_FILES[$a_field]["error"][$a_index],
1049                    "size" => $_FILES[$a_field]["size"][$a_index],
1050                    "is_upload" => true
1051                );
1052            }
1053        } else {
1054            if ($_FILES[$a_field]["tmp_name"]) {
1055                $res = array(
1056                    "tmp_name" => $_FILES[$a_field]["tmp_name"],
1057                    "name" => $_FILES[$a_field]["name"],
1058                    "type" => $_FILES[$a_field]["type"],
1059                    "error" => $_FILES[$a_field]["error"],
1060                    "size" => $_FILES[$a_field]["size"],
1061                    "is_upload" => true
1062                );
1063            }
1064        }
1065        return $res;
1066    }
1067
1068    /**
1069     * Was any file uploaded?
1070     *
1071     * @param string $a_field form field
1072     * @param mixed $a_index form field index (if array)
1073     * @param mixed $a_sub_index form field subindex (if array)
1074     * @return bool
1075     */
1076    public function hasFileUpload($a_field, $a_index = null, $a_sub_index = null)
1077    {
1078        $data = $this->getFileUpload($a_field, $a_index, $a_sub_index);
1079        return (bool) $data["tmp_name"];
1080    }
1081
1082    /**
1083     * Move upload to target directory
1084     *
1085     * @param string $a_target_directory target directory (without filename!)
1086     * @param string $a_field form field
1087     * @param string $a_target_name target file name (if different from uploaded file)
1088     * @param mixed $a_index form field index (if array)
1089     * @param mixed $a_sub_index form field subindex (if array)
1090     * @return string target file name incl. path
1091     */
1092    public function moveFileUpload($a_target_directory, $a_field, $a_target_name = null, $a_index = null, $a_sub_index = null)
1093    {
1094        if (!is_dir($a_target_directory)) {
1095            return;
1096        }
1097
1098        $data = $this->getFileUpload($a_field, $a_index, $a_sub_index);
1099        if ($data["tmp_name"] && file_exists($data["tmp_name"])) {
1100            if ($a_target_name) {
1101                $data["name"] = $a_target_name;
1102            }
1103
1104            $target_file = $a_target_directory . "/" . $data["name"];
1105            $target_file = str_replace("//", "/", $target_file);
1106
1107            if ($data["is_upload"]) {
1108                if (!ilUtil::moveUploadedFile($data["tmp_name"], $data["name"], $target_file)) {
1109                    return;
1110                }
1111            } else {
1112                if (!rename($data["tmp_name"], $target_file)) {
1113                    return;
1114                }
1115            }
1116
1117            return $target_file;
1118        }
1119    }
1120
1121    /**
1122     * try to rebuild files
1123     */
1124    protected function rebuildUploadedFiles()
1125    {
1126        if (isset($_POST["ilfilehash"]) && $_POST["ilfilehash"]) {
1127            $temp_path = ilUtil::getDataDir() . "/temp";
1128            if (is_dir($temp_path)) {
1129                $reload = array();
1130
1131                $temp_files = glob($temp_path . "/" . session_id() . "~~" . $_POST["ilfilehash"] . "~~*");
1132                if (is_array($temp_files)) {
1133                    foreach ($temp_files as $full_file) {
1134                        $file = explode("~~", basename($full_file));
1135                        $field = $file[2];
1136                        $idx = $file[3];
1137                        $idx2 = $file[4];
1138                        $type = $file[5] . "/" . $file[6];
1139                        $name = $file[7];
1140
1141                        if ($idx2 != "") {
1142                            if (!$_FILES[$field]["tmp_name"][$idx][$idx2]) {
1143                                $_FILES[$field]["tmp_name"][$idx][$idx2] = $full_file;
1144                                $_FILES[$field]["name"][$idx][$idx2] = $name;
1145                                $_FILES[$field]["type"][$idx][$idx2] = $type;
1146                                $_FILES[$field]["error"][$idx][$idx2] = 0;
1147                                $_FILES[$field]["size"][$idx][$idx2] = filesize($full_file);
1148                            }
1149                        } elseif ($idx != "") {
1150                            if (!$_FILES[$field]["tmp_name"][$idx]) {
1151                                $_FILES[$field]["tmp_name"][$idx] = $full_file;
1152                                $_FILES[$field]["name"][$idx] = $name;
1153                                $_FILES[$field]["type"][$idx] = $type;
1154                                $_FILES[$field]["error"][$idx] = 0;
1155                                $_FILES[$field]["size"][$idx] = filesize($full_file);
1156                            }
1157                        } else {
1158                            if (!$_FILES[$field]["tmp_name"]) {
1159                                $_FILES[$field]["tmp_name"] = $full_file;
1160                                $_FILES[$field]["name"] = $name;
1161                                $_FILES[$field]["type"] = $type;
1162                                $_FILES[$field]["error"] = 0;
1163                                $_FILES[$field]["size"] = filesize($full_file);
1164                            }
1165                        }
1166                    }
1167                }
1168            }
1169        }
1170    }
1171}
1172