xref: /dokuwiki/inc/Form/Form.php (revision a453c16b3290eabdf35778c54498101c737544e1)
112a4e4d1SAndreas Gohr<?php
212a4e4d1SAndreas Gohrnamespace dokuwiki\Form;
312a4e4d1SAndreas Gohr
412a4e4d1SAndreas Gohr/**
512a4e4d1SAndreas Gohr * Class Form
612a4e4d1SAndreas Gohr *
712a4e4d1SAndreas Gohr * Represents the whole Form. This is what you work on, and add Elements to
812a4e4d1SAndreas Gohr *
912a4e4d1SAndreas Gohr * @package dokuwiki\Form
1012a4e4d1SAndreas Gohr */
1112a4e4d1SAndreas Gohrclass Form extends Element {
1212a4e4d1SAndreas Gohr
1312a4e4d1SAndreas Gohr    /**
1412a4e4d1SAndreas Gohr     * @var array name value pairs for hidden values
1512a4e4d1SAndreas Gohr     */
1612a4e4d1SAndreas Gohr    protected $hidden = array();
1712a4e4d1SAndreas Gohr
1812a4e4d1SAndreas Gohr    /**
1912a4e4d1SAndreas Gohr     * @var Element[] the elements of the form
2012a4e4d1SAndreas Gohr     */
2112a4e4d1SAndreas Gohr    protected $elements = array();
2212a4e4d1SAndreas Gohr
2312a4e4d1SAndreas Gohr    /**
2412a4e4d1SAndreas Gohr     * Creates a new, empty form with some default attributes
2512a4e4d1SAndreas Gohr     *
2612a4e4d1SAndreas Gohr     * @param array $attributes
2712a4e4d1SAndreas Gohr     */
2812a4e4d1SAndreas Gohr    public function __construct($attributes = array()) {
2912a4e4d1SAndreas Gohr        global $ID;
3012a4e4d1SAndreas Gohr
3112a4e4d1SAndreas Gohr        parent::__construct('form', $attributes);
3212a4e4d1SAndreas Gohr
3312a4e4d1SAndreas Gohr        // use the current URL as default action
3412a4e4d1SAndreas Gohr        if(!$this->attr('action')) {
3512a4e4d1SAndreas Gohr            $get = $_GET;
3612a4e4d1SAndreas Gohr            if(isset($get['id'])) unset($get['id']);
376d0ceaf9SAndreas Gohr            $self = wl($ID, $get, false, '&'); //attributes are escaped later
3812a4e4d1SAndreas Gohr            $this->attr('action', $self);
3912a4e4d1SAndreas Gohr        }
4012a4e4d1SAndreas Gohr
4112a4e4d1SAndreas Gohr        // post is default
4212a4e4d1SAndreas Gohr        if(!$this->attr('method')) {
4312a4e4d1SAndreas Gohr            $this->attr('method', 'post');
4412a4e4d1SAndreas Gohr        }
4512a4e4d1SAndreas Gohr
4612a4e4d1SAndreas Gohr        // we like UTF-8
4712a4e4d1SAndreas Gohr        if(!$this->attr('accept-charset')) {
4812a4e4d1SAndreas Gohr            $this->attr('accept-charset', 'utf-8');
4912a4e4d1SAndreas Gohr        }
5012a4e4d1SAndreas Gohr
5112a4e4d1SAndreas Gohr        // add the security token by default
5212a4e4d1SAndreas Gohr        $this->setHiddenField('sectok', getSecurityToken());
5312a4e4d1SAndreas Gohr
546d0ceaf9SAndreas Gohr        // identify this as a new form based form in HTML
556d0ceaf9SAndreas Gohr        $this->addClass('doku_form');
5612a4e4d1SAndreas Gohr    }
5712a4e4d1SAndreas Gohr
5812a4e4d1SAndreas Gohr    /**
5912a4e4d1SAndreas Gohr     * Sets a hidden field
6012a4e4d1SAndreas Gohr     *
6112a4e4d1SAndreas Gohr     * @param $name
6212a4e4d1SAndreas Gohr     * @param $value
6312a4e4d1SAndreas Gohr     * @return $this
6412a4e4d1SAndreas Gohr     */
6512a4e4d1SAndreas Gohr    public function setHiddenField($name, $value) {
6612a4e4d1SAndreas Gohr        $this->hidden[$name] = $value;
6712a4e4d1SAndreas Gohr        return $this;
6812a4e4d1SAndreas Gohr    }
6912a4e4d1SAndreas Gohr
70ef0c211bSAndreas Gohr    #region element query function
7164744a10SAndreas Gohr
7212a4e4d1SAndreas Gohr    /**
73ef0c211bSAndreas Gohr     * Returns the numbers of elements in the form
74ef0c211bSAndreas Gohr     *
75ef0c211bSAndreas Gohr     * @return int
76ef0c211bSAndreas Gohr     */
77ef0c211bSAndreas Gohr    public function elementCount() {
78ef0c211bSAndreas Gohr        return count($this->elements);
79ef0c211bSAndreas Gohr    }
80ef0c211bSAndreas Gohr
81ef0c211bSAndreas Gohr    /**
82ef0c211bSAndreas Gohr     * Returns a reference to the element at a position.
83ef0c211bSAndreas Gohr     * A position out-of-bounds will return either the
84ef0c211bSAndreas Gohr     * first (underflow) or last (overflow) element.
85ef0c211bSAndreas Gohr     *
86ef0c211bSAndreas Gohr     * @param $pos
87ef0c211bSAndreas Gohr     * @return Element
88ef0c211bSAndreas Gohr     */
89ef0c211bSAndreas Gohr    public function getElementAt($pos) {
90ef0c211bSAndreas Gohr        if($pos < 0) $pos = count($this->elements) + $pos;
91ef0c211bSAndreas Gohr        if($pos < 0) $pos = 0;
92ef0c211bSAndreas Gohr        if($pos >= count($this->elements)) $pos = count($this->elements) - 1;
93ef0c211bSAndreas Gohr        return $this->elements[$pos];
94ef0c211bSAndreas Gohr    }
95ef0c211bSAndreas Gohr
96ef0c211bSAndreas Gohr    /**
97ef0c211bSAndreas Gohr     * Gets the position of the first of a type of element
98ef0c211bSAndreas Gohr     *
99ef0c211bSAndreas Gohr     * @param string $type Element type to look for.
100ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
101ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
102ef0c211bSAndreas Gohr     */
103ef0c211bSAndreas Gohr    public function findPositionByType($type, $offset = 0) {
104ef0c211bSAndreas Gohr        $len = $this->elementCount();
105ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
106ef0c211bSAndreas Gohr            if($this->elements[$pos]->getType() == $type) {
107ef0c211bSAndreas Gohr                return $pos;
108ef0c211bSAndreas Gohr            }
109ef0c211bSAndreas Gohr        }
110ef0c211bSAndreas Gohr        return false;
111ef0c211bSAndreas Gohr    }
112ef0c211bSAndreas Gohr
113ef0c211bSAndreas Gohr    /**
114ef0c211bSAndreas Gohr     * Gets the position of the first element matching the attribute
115ef0c211bSAndreas Gohr     *
116ef0c211bSAndreas Gohr     * @param string $name Name of the attribute
117ef0c211bSAndreas Gohr     * @param string $value Value the attribute should have
118ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
119ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
120ef0c211bSAndreas Gohr     */
121ef0c211bSAndreas Gohr    public function findPositionByAttribute($name, $value, $offset = 0) {
122ef0c211bSAndreas Gohr        $len = $this->elementCount();
123ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
124ef0c211bSAndreas Gohr            if($this->elements[$pos]->attr($name) == $value) {
125ef0c211bSAndreas Gohr                return $pos;
126ef0c211bSAndreas Gohr            }
127ef0c211bSAndreas Gohr        }
128ef0c211bSAndreas Gohr        return false;
129ef0c211bSAndreas Gohr    }
130ef0c211bSAndreas Gohr
131ef0c211bSAndreas Gohr    #endregion
132ef0c211bSAndreas Gohr
133ef0c211bSAndreas Gohr    #region Element positioning functions
134ef0c211bSAndreas Gohr
135ef0c211bSAndreas Gohr    /**
136ef0c211bSAndreas Gohr     * Adds or inserts an element to the form
13712a4e4d1SAndreas Gohr     *
13812a4e4d1SAndreas Gohr     * @param Element $element
13912a4e4d1SAndreas Gohr     * @param int $pos 0-based position in the form, -1 for at the end
14012a4e4d1SAndreas Gohr     * @return Element
14112a4e4d1SAndreas Gohr     */
14212a4e4d1SAndreas Gohr    public function addElement(Element $element, $pos = -1) {
143ef0c211bSAndreas Gohr        if(is_a($element, '\dokuwiki\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
14412a4e4d1SAndreas Gohr        if($pos < 0) {
14512a4e4d1SAndreas Gohr            $this->elements[] = $element;
14612a4e4d1SAndreas Gohr        } else {
14712a4e4d1SAndreas Gohr            array_splice($this->elements, $pos, 0, array($element));
14812a4e4d1SAndreas Gohr        }
14912a4e4d1SAndreas Gohr        return $element;
15012a4e4d1SAndreas Gohr    }
15112a4e4d1SAndreas Gohr
15212a4e4d1SAndreas Gohr    /**
153ef0c211bSAndreas Gohr     * Replaces an existing element with a new one
154ef0c211bSAndreas Gohr     *
155ef0c211bSAndreas Gohr     * @param Element $element the new element
156ef0c211bSAndreas Gohr     * @param $pos 0-based position of the element to replace
157ef0c211bSAndreas Gohr     */
158ef0c211bSAndreas Gohr    public function replaceElement(Element $element, $pos) {
159ef0c211bSAndreas Gohr        if(is_a($element, '\dokuwiki\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
160ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1, array($element));
161ef0c211bSAndreas Gohr    }
162ef0c211bSAndreas Gohr
163ef0c211bSAndreas Gohr    /**
164ef0c211bSAndreas Gohr     * Remove an element from the form completely
165ef0c211bSAndreas Gohr     *
166ef0c211bSAndreas Gohr     * @param $pos 0-based position of the element to remove
167ef0c211bSAndreas Gohr     */
168ef0c211bSAndreas Gohr    public function removeElement($pos) {
169ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1);
170ef0c211bSAndreas Gohr    }
171ef0c211bSAndreas Gohr
172ef0c211bSAndreas Gohr    #endregion
173ef0c211bSAndreas Gohr
174ef0c211bSAndreas Gohr    #region Element adding functions
175ef0c211bSAndreas Gohr
176ef0c211bSAndreas Gohr    /**
17712a4e4d1SAndreas Gohr     * Adds a text input field
17812a4e4d1SAndreas Gohr     *
17912a4e4d1SAndreas Gohr     * @param $name
18012a4e4d1SAndreas Gohr     * @param $label
18112a4e4d1SAndreas Gohr     * @param int $pos
18212a4e4d1SAndreas Gohr     * @return InputElement
18312a4e4d1SAndreas Gohr     */
184de19515fSAndreas Gohr    public function addTextInput($name, $label = '', $pos = -1) {
18512a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('text', $name, $label), $pos);
18612a4e4d1SAndreas Gohr    }
18712a4e4d1SAndreas Gohr
18812a4e4d1SAndreas Gohr    /**
18912a4e4d1SAndreas Gohr     * Adds a password input field
19012a4e4d1SAndreas Gohr     *
19112a4e4d1SAndreas Gohr     * @param $name
19212a4e4d1SAndreas Gohr     * @param $label
19312a4e4d1SAndreas Gohr     * @param int $pos
19412a4e4d1SAndreas Gohr     * @return InputElement
19512a4e4d1SAndreas Gohr     */
196de19515fSAndreas Gohr    public function addPasswordInput($name, $label = '', $pos = -1) {
19712a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('password', $name, $label), $pos);
19812a4e4d1SAndreas Gohr    }
19912a4e4d1SAndreas Gohr
20012a4e4d1SAndreas Gohr    /**
20112a4e4d1SAndreas Gohr     * Adds a radio button field
20212a4e4d1SAndreas Gohr     *
20312a4e4d1SAndreas Gohr     * @param $name
20412a4e4d1SAndreas Gohr     * @param $label
20512a4e4d1SAndreas Gohr     * @param int $pos
20612a4e4d1SAndreas Gohr     * @return CheckableElement
20712a4e4d1SAndreas Gohr     */
208de19515fSAndreas Gohr    public function addRadioButton($name, $label = '', $pos = -1) {
20912a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
21012a4e4d1SAndreas Gohr    }
21112a4e4d1SAndreas Gohr
21212a4e4d1SAndreas Gohr    /**
21312a4e4d1SAndreas Gohr     * Adds a checkbox field
21412a4e4d1SAndreas Gohr     *
21512a4e4d1SAndreas Gohr     * @param $name
21612a4e4d1SAndreas Gohr     * @param $label
21712a4e4d1SAndreas Gohr     * @param int $pos
21812a4e4d1SAndreas Gohr     * @return CheckableElement
21912a4e4d1SAndreas Gohr     */
220de19515fSAndreas Gohr    public function addCheckbox($name, $label = '', $pos = -1) {
22112a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
22212a4e4d1SAndreas Gohr    }
22312a4e4d1SAndreas Gohr
22412a4e4d1SAndreas Gohr    /**
22512a4e4d1SAndreas Gohr     * Adds a textarea field
22612a4e4d1SAndreas Gohr     *
22712a4e4d1SAndreas Gohr     * @param $name
22812a4e4d1SAndreas Gohr     * @param $label
22912a4e4d1SAndreas Gohr     * @param int $pos
23012a4e4d1SAndreas Gohr     * @return TextareaElement
23112a4e4d1SAndreas Gohr     */
232de19515fSAndreas Gohr    public function addTextarea($name, $label = '', $pos = -1) {
23312a4e4d1SAndreas Gohr        return $this->addElement(new TextareaElement($name, $label), $pos);
23412a4e4d1SAndreas Gohr    }
23512a4e4d1SAndreas Gohr
23612a4e4d1SAndreas Gohr    /**
2378f0df229SAndreas Gohr     * Adds a simple button, escapes the content for you
2388f0df229SAndreas Gohr     *
2398f0df229SAndreas Gohr     * @param string $name
2408f0df229SAndreas Gohr     * @param string $content
2418f0df229SAndreas Gohr     * @param int $pos
2428f0df229SAndreas Gohr     * @return Element
2438f0df229SAndreas Gohr     */
2448f0df229SAndreas Gohr    public function addButton($name, $content, $pos = -1) {
2458f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
2468f0df229SAndreas Gohr    }
2478f0df229SAndreas Gohr
2488f0df229SAndreas Gohr    /**
2498f0df229SAndreas Gohr     * Adds a simple button, allows HTML for content
2508f0df229SAndreas Gohr     *
2518f0df229SAndreas Gohr     * @param string $name
2528f0df229SAndreas Gohr     * @param string $html
2538f0df229SAndreas Gohr     * @param int $pos
2548f0df229SAndreas Gohr     * @return Element
2558f0df229SAndreas Gohr     */
2568f0df229SAndreas Gohr    public function addButtonHTML($name, $html, $pos = -1) {
2578f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, $html), $pos);
2588f0df229SAndreas Gohr    }
2598f0df229SAndreas Gohr
2608f0df229SAndreas Gohr    /**
261*a453c16bSAndreas Gohr     * Adds a label referencing another input element, escapes the label for you
262*a453c16bSAndreas Gohr     *
263*a453c16bSAndreas Gohr     * @param $label
264*a453c16bSAndreas Gohr     * @param string $for
265*a453c16bSAndreas Gohr     * @param int $pos
266*a453c16bSAndreas Gohr     * @return Element
267*a453c16bSAndreas Gohr     */
268*a453c16bSAndreas Gohr    public function addLabel($label, $for='', $pos = -1) {
269*a453c16bSAndreas Gohr        return $this->addLabelHTML(hsc($label), $for, $pos);
270*a453c16bSAndreas Gohr    }
271*a453c16bSAndreas Gohr
272*a453c16bSAndreas Gohr    /**
273*a453c16bSAndreas Gohr     * Adds a label referencing another input element, allows HTML for content
274*a453c16bSAndreas Gohr     *
275*a453c16bSAndreas Gohr     * @param string $content
276*a453c16bSAndreas Gohr     * @param string|Element $for
277*a453c16bSAndreas Gohr     * @param int $pos
278*a453c16bSAndreas Gohr     * @return Element
279*a453c16bSAndreas Gohr     */
280*a453c16bSAndreas Gohr    public function addLabelHTML($content, $for='', $pos = -1) {
281*a453c16bSAndreas Gohr        $element = new LabelElement(hsc($content));
282*a453c16bSAndreas Gohr
283*a453c16bSAndreas Gohr        if(is_a($for, '\dokuwiki\Form\Element')) {
284*a453c16bSAndreas Gohr            /** @var Element $for */
285*a453c16bSAndreas Gohr            $for = $for->id();
286*a453c16bSAndreas Gohr        }
287*a453c16bSAndreas Gohr        $for = (string) $for;
288*a453c16bSAndreas Gohr        if($for !== '') {
289*a453c16bSAndreas Gohr            $element->attr('for', $for);
290*a453c16bSAndreas Gohr        }
291*a453c16bSAndreas Gohr
292*a453c16bSAndreas Gohr        return $this->addElement($element, $pos);
293*a453c16bSAndreas Gohr    }
294*a453c16bSAndreas Gohr
295*a453c16bSAndreas Gohr    /**
296de19515fSAndreas Gohr     * Add fixed HTML to the form
297de19515fSAndreas Gohr     *
298de19515fSAndreas Gohr     * @param $html
299de19515fSAndreas Gohr     * @param int $pos
30064744a10SAndreas Gohr     * @return HTMLElement
301de19515fSAndreas Gohr     */
302de19515fSAndreas Gohr    public function addHTML($html, $pos = -1) {
303de19515fSAndreas Gohr        return $this->addElement(new HTMLElement($html), $pos);
304de19515fSAndreas Gohr    }
305de19515fSAndreas Gohr
30664744a10SAndreas Gohr    /**
30764744a10SAndreas Gohr     * Add a closed HTML tag to the form
30864744a10SAndreas Gohr     *
30964744a10SAndreas Gohr     * @param $tag
31064744a10SAndreas Gohr     * @param int $pos
31164744a10SAndreas Gohr     * @return TagElement
31264744a10SAndreas Gohr     */
31364744a10SAndreas Gohr    public function addTag($tag, $pos = -1) {
31464744a10SAndreas Gohr        return $this->addElement(new TagElement($tag), $pos);
31564744a10SAndreas Gohr    }
31664744a10SAndreas Gohr
31764744a10SAndreas Gohr    /**
31864744a10SAndreas Gohr     * Add an open HTML tag to the form
31964744a10SAndreas Gohr     *
32064744a10SAndreas Gohr     * Be sure to close it again!
32164744a10SAndreas Gohr     *
32264744a10SAndreas Gohr     * @param $tag
32364744a10SAndreas Gohr     * @param int $pos
32464744a10SAndreas Gohr     * @return TagOpenElement
32564744a10SAndreas Gohr     */
32664744a10SAndreas Gohr    public function addTagOpen($tag, $pos = -1) {
32764744a10SAndreas Gohr        return $this->addElement(new TagOpenElement($tag), $pos);
32864744a10SAndreas Gohr    }
32964744a10SAndreas Gohr
33064744a10SAndreas Gohr    /**
33164744a10SAndreas Gohr     * Add a closing HTML tag to the form
33264744a10SAndreas Gohr     *
33364744a10SAndreas Gohr     * Be sure it had been opened before
33464744a10SAndreas Gohr     *
33564744a10SAndreas Gohr     * @param $tag
33664744a10SAndreas Gohr     * @param int $pos
33764744a10SAndreas Gohr     * @return TagCloseElement
33864744a10SAndreas Gohr     */
33964744a10SAndreas Gohr    public function addTagClose($tag, $pos = -1) {
34064744a10SAndreas Gohr        return $this->addElement(new TagCloseElement($tag), $pos);
34164744a10SAndreas Gohr    }
34264744a10SAndreas Gohr
34364744a10SAndreas Gohr    /**
34464744a10SAndreas Gohr     * Open a Fieldset
34564744a10SAndreas Gohr     *
34664744a10SAndreas Gohr     * @param $legend
34764744a10SAndreas Gohr     * @param int $pos
34864744a10SAndreas Gohr     * @return FieldsetOpenElement
34964744a10SAndreas Gohr     */
35064744a10SAndreas Gohr    public function addFieldsetOpen($legend = '', $pos = -1) {
35164744a10SAndreas Gohr        return $this->addElement(new FieldsetOpenElement($legend), $pos);
35264744a10SAndreas Gohr    }
35364744a10SAndreas Gohr
35464744a10SAndreas Gohr    /**
35564744a10SAndreas Gohr     * Close a fieldset
35664744a10SAndreas Gohr     *
35764744a10SAndreas Gohr     * @param int $pos
35864744a10SAndreas Gohr     * @return TagCloseElement
35964744a10SAndreas Gohr     */
36064744a10SAndreas Gohr    public function addFieldsetClose($pos = -1) {
36164744a10SAndreas Gohr        return $this->addElement(new FieldsetCloseElement(), $pos);
36264744a10SAndreas Gohr    }
36364744a10SAndreas Gohr
36464744a10SAndreas Gohr    #endregion
36564744a10SAndreas Gohr
3661f5d8b65SAndreas Gohr    /**
3671f5d8b65SAndreas Gohr     * Adjust the elements so that fieldset open and closes are matching
3681f5d8b65SAndreas Gohr     */
369de19515fSAndreas Gohr    protected function balanceFieldsets() {
3701f5d8b65SAndreas Gohr        $lastclose = 0;
3711f5d8b65SAndreas Gohr        $isopen = false;
3721f5d8b65SAndreas Gohr        $len = count($this->elements);
3731f5d8b65SAndreas Gohr
3741f5d8b65SAndreas Gohr        for($pos = 0; $pos < $len; $pos++) {
3751f5d8b65SAndreas Gohr            $type = $this->elements[$pos]->getType();
3761f5d8b65SAndreas Gohr            if($type == 'fieldsetopen') {
3771f5d8b65SAndreas Gohr                if($isopen) {
37880b13baaSGerrit Uitslag                    //close previous fieldset
3791f5d8b65SAndreas Gohr                    $this->addFieldsetClose($pos);
3801f5d8b65SAndreas Gohr                    $lastclose = $pos + 1;
3811f5d8b65SAndreas Gohr                    $pos++;
3821f5d8b65SAndreas Gohr                    $len++;
3831f5d8b65SAndreas Gohr                }
3841f5d8b65SAndreas Gohr                $isopen = true;
3851f5d8b65SAndreas Gohr            } else if($type == 'fieldsetclose') {
3861f5d8b65SAndreas Gohr                if(!$isopen) {
3871f5d8b65SAndreas Gohr                    // make sure there was a fieldsetopen
3881f5d8b65SAndreas Gohr                    // either right after the last close or at the begining
3891f5d8b65SAndreas Gohr                    $this->addFieldsetOpen('', $lastclose);
3901f5d8b65SAndreas Gohr                    $len++;
3911f5d8b65SAndreas Gohr                    $pos++;
3921f5d8b65SAndreas Gohr                }
3931f5d8b65SAndreas Gohr                $lastclose = $pos;
3941f5d8b65SAndreas Gohr                $isopen = false;
3951f5d8b65SAndreas Gohr            }
3961f5d8b65SAndreas Gohr        }
3971f5d8b65SAndreas Gohr
3981f5d8b65SAndreas Gohr        // close open fieldset at the end
3991f5d8b65SAndreas Gohr        if($isopen) {
4001f5d8b65SAndreas Gohr            $this->addFieldsetClose();
4011f5d8b65SAndreas Gohr        }
402de19515fSAndreas Gohr    }
403de19515fSAndreas Gohr
404de19515fSAndreas Gohr    /**
40512a4e4d1SAndreas Gohr     * The HTML representation of the whole form
40612a4e4d1SAndreas Gohr     *
40712a4e4d1SAndreas Gohr     * @return string
40812a4e4d1SAndreas Gohr     */
40912a4e4d1SAndreas Gohr    public function toHTML() {
410de19515fSAndreas Gohr        $this->balanceFieldsets();
411de19515fSAndreas Gohr
41212a4e4d1SAndreas Gohr        $html = '<form ' . buildAttributes($this->attrs()) . '>' . DOKU_LF;
41312a4e4d1SAndreas Gohr
41412a4e4d1SAndreas Gohr        foreach($this->hidden as $name => $value) {
41512a4e4d1SAndreas Gohr            $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />' . DOKU_LF;
41612a4e4d1SAndreas Gohr        }
41712a4e4d1SAndreas Gohr
41812a4e4d1SAndreas Gohr        foreach($this->elements as $element) {
41912a4e4d1SAndreas Gohr            $html .= $element->toHTML() . DOKU_LF;
42012a4e4d1SAndreas Gohr        }
42112a4e4d1SAndreas Gohr
42212a4e4d1SAndreas Gohr        $html .= '</form>' . DOKU_LF;
42312a4e4d1SAndreas Gohr
42412a4e4d1SAndreas Gohr        return $html;
42512a4e4d1SAndreas Gohr    }
42612a4e4d1SAndreas Gohr}
427