xref: /dokuwiki/inc/Form/Form.php (revision 5facb9bceb036e7074e65f767f7fed26f2bd07e3)
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     *
617ec97767SGerrit Uitslag     * @param string $name
627ec97767SGerrit Uitslag     * @param string $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    /**
82*5facb9bcSMichael Große     * Get the position of the element in the form or false if it is not in the form
83*5facb9bcSMichael Große     *
84*5facb9bcSMichael Große     * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
85*5facb9bcSMichael Große     *
86*5facb9bcSMichael Große     * @param Element $element
87*5facb9bcSMichael Große     *
88*5facb9bcSMichael Große     * @return false|int
89*5facb9bcSMichael Große     */
90*5facb9bcSMichael Große    public function getElementPosition(Element $element)
91*5facb9bcSMichael Große    {
92*5facb9bcSMichael Große        return array_search($element, $this->elements, true);
93*5facb9bcSMichael Große    }
94*5facb9bcSMichael Große
95*5facb9bcSMichael Große    /**
96ef0c211bSAndreas Gohr     * Returns a reference to the element at a position.
97ef0c211bSAndreas Gohr     * A position out-of-bounds will return either the
98ef0c211bSAndreas Gohr     * first (underflow) or last (overflow) element.
99ef0c211bSAndreas Gohr     *
1007ec97767SGerrit Uitslag     * @param int $pos
101ef0c211bSAndreas Gohr     * @return Element
102ef0c211bSAndreas Gohr     */
103ef0c211bSAndreas Gohr    public function getElementAt($pos) {
104ef0c211bSAndreas Gohr        if($pos < 0) $pos = count($this->elements) + $pos;
105ef0c211bSAndreas Gohr        if($pos < 0) $pos = 0;
106ef0c211bSAndreas Gohr        if($pos >= count($this->elements)) $pos = count($this->elements) - 1;
107ef0c211bSAndreas Gohr        return $this->elements[$pos];
108ef0c211bSAndreas Gohr    }
109ef0c211bSAndreas Gohr
110ef0c211bSAndreas Gohr    /**
111ef0c211bSAndreas Gohr     * Gets the position of the first of a type of element
112ef0c211bSAndreas Gohr     *
113ef0c211bSAndreas Gohr     * @param string $type Element type to look for.
114ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
115ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
116ef0c211bSAndreas Gohr     */
117ef0c211bSAndreas Gohr    public function findPositionByType($type, $offset = 0) {
118ef0c211bSAndreas Gohr        $len = $this->elementCount();
119ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
120ef0c211bSAndreas Gohr            if($this->elements[$pos]->getType() == $type) {
121ef0c211bSAndreas Gohr                return $pos;
122ef0c211bSAndreas Gohr            }
123ef0c211bSAndreas Gohr        }
124ef0c211bSAndreas Gohr        return false;
125ef0c211bSAndreas Gohr    }
126ef0c211bSAndreas Gohr
127ef0c211bSAndreas Gohr    /**
128ef0c211bSAndreas Gohr     * Gets the position of the first element matching the attribute
129ef0c211bSAndreas Gohr     *
130ef0c211bSAndreas Gohr     * @param string $name Name of the attribute
131ef0c211bSAndreas Gohr     * @param string $value Value the attribute should have
132ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
133ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
134ef0c211bSAndreas Gohr     */
135ef0c211bSAndreas Gohr    public function findPositionByAttribute($name, $value, $offset = 0) {
136ef0c211bSAndreas Gohr        $len = $this->elementCount();
137ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
138ef0c211bSAndreas Gohr            if($this->elements[$pos]->attr($name) == $value) {
139ef0c211bSAndreas Gohr                return $pos;
140ef0c211bSAndreas Gohr            }
141ef0c211bSAndreas Gohr        }
142ef0c211bSAndreas Gohr        return false;
143ef0c211bSAndreas Gohr    }
144ef0c211bSAndreas Gohr
145ef0c211bSAndreas Gohr    #endregion
146ef0c211bSAndreas Gohr
147ef0c211bSAndreas Gohr    #region Element positioning functions
148ef0c211bSAndreas Gohr
149ef0c211bSAndreas Gohr    /**
150ef0c211bSAndreas Gohr     * Adds or inserts an element to the form
15112a4e4d1SAndreas Gohr     *
15212a4e4d1SAndreas Gohr     * @param Element $element
15312a4e4d1SAndreas Gohr     * @param int $pos 0-based position in the form, -1 for at the end
15412a4e4d1SAndreas Gohr     * @return Element
15512a4e4d1SAndreas Gohr     */
15612a4e4d1SAndreas Gohr    public function addElement(Element $element, $pos = -1) {
157bb6d40dcSAndreas Gohr        if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
15812a4e4d1SAndreas Gohr        if($pos < 0) {
15912a4e4d1SAndreas Gohr            $this->elements[] = $element;
16012a4e4d1SAndreas Gohr        } else {
16112a4e4d1SAndreas Gohr            array_splice($this->elements, $pos, 0, array($element));
16212a4e4d1SAndreas Gohr        }
16312a4e4d1SAndreas Gohr        return $element;
16412a4e4d1SAndreas Gohr    }
16512a4e4d1SAndreas Gohr
16612a4e4d1SAndreas Gohr    /**
167ef0c211bSAndreas Gohr     * Replaces an existing element with a new one
168ef0c211bSAndreas Gohr     *
169ef0c211bSAndreas Gohr     * @param Element $element the new element
1707ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to replace
171ef0c211bSAndreas Gohr     */
172ef0c211bSAndreas Gohr    public function replaceElement(Element $element, $pos) {
173bb6d40dcSAndreas Gohr        if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
174ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1, array($element));
175ef0c211bSAndreas Gohr    }
176ef0c211bSAndreas Gohr
177ef0c211bSAndreas Gohr    /**
178ef0c211bSAndreas Gohr     * Remove an element from the form completely
179ef0c211bSAndreas Gohr     *
1807ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to remove
181ef0c211bSAndreas Gohr     */
182ef0c211bSAndreas Gohr    public function removeElement($pos) {
183ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1);
184ef0c211bSAndreas Gohr    }
185ef0c211bSAndreas Gohr
186ef0c211bSAndreas Gohr    #endregion
187ef0c211bSAndreas Gohr
188ef0c211bSAndreas Gohr    #region Element adding functions
189ef0c211bSAndreas Gohr
190ef0c211bSAndreas Gohr    /**
19112a4e4d1SAndreas Gohr     * Adds a text input field
19212a4e4d1SAndreas Gohr     *
1937ec97767SGerrit Uitslag     * @param string $name
1947ec97767SGerrit Uitslag     * @param string $label
19512a4e4d1SAndreas Gohr     * @param int $pos
19612a4e4d1SAndreas Gohr     * @return InputElement
19712a4e4d1SAndreas Gohr     */
198de19515fSAndreas Gohr    public function addTextInput($name, $label = '', $pos = -1) {
19912a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('text', $name, $label), $pos);
20012a4e4d1SAndreas Gohr    }
20112a4e4d1SAndreas Gohr
20212a4e4d1SAndreas Gohr    /**
20312a4e4d1SAndreas Gohr     * Adds a password input field
20412a4e4d1SAndreas Gohr     *
2057ec97767SGerrit Uitslag     * @param string $name
2067ec97767SGerrit Uitslag     * @param string $label
20712a4e4d1SAndreas Gohr     * @param int $pos
20812a4e4d1SAndreas Gohr     * @return InputElement
20912a4e4d1SAndreas Gohr     */
210de19515fSAndreas Gohr    public function addPasswordInput($name, $label = '', $pos = -1) {
21112a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('password', $name, $label), $pos);
21212a4e4d1SAndreas Gohr    }
21312a4e4d1SAndreas Gohr
21412a4e4d1SAndreas Gohr    /**
21512a4e4d1SAndreas Gohr     * Adds a radio button field
21612a4e4d1SAndreas Gohr     *
2177ec97767SGerrit Uitslag     * @param string $name
2187ec97767SGerrit Uitslag     * @param string $label
21912a4e4d1SAndreas Gohr     * @param int $pos
22012a4e4d1SAndreas Gohr     * @return CheckableElement
22112a4e4d1SAndreas Gohr     */
222de19515fSAndreas Gohr    public function addRadioButton($name, $label = '', $pos = -1) {
22312a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
22412a4e4d1SAndreas Gohr    }
22512a4e4d1SAndreas Gohr
22612a4e4d1SAndreas Gohr    /**
22712a4e4d1SAndreas Gohr     * Adds a checkbox field
22812a4e4d1SAndreas Gohr     *
2297ec97767SGerrit Uitslag     * @param string $name
2307ec97767SGerrit Uitslag     * @param string $label
23112a4e4d1SAndreas Gohr     * @param int $pos
23212a4e4d1SAndreas Gohr     * @return CheckableElement
23312a4e4d1SAndreas Gohr     */
234de19515fSAndreas Gohr    public function addCheckbox($name, $label = '', $pos = -1) {
23512a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
23612a4e4d1SAndreas Gohr    }
23712a4e4d1SAndreas Gohr
23812a4e4d1SAndreas Gohr    /**
2398638ead5SAndreas Gohr     * Adds a dropdown field
2408638ead5SAndreas Gohr     *
2417ec97767SGerrit Uitslag     * @param string $name
2428638ead5SAndreas Gohr     * @param array $options
2438638ead5SAndreas Gohr     * @param string $label
2448638ead5SAndreas Gohr     * @param int $pos
2458638ead5SAndreas Gohr     * @return DropdownElement
2468638ead5SAndreas Gohr     */
2478638ead5SAndreas Gohr    public function addDropdown($name, $options, $label = '', $pos = -1) {
2488638ead5SAndreas Gohr        return $this->addElement(new DropdownElement($name, $options, $label), $pos);
2498638ead5SAndreas Gohr    }
2508638ead5SAndreas Gohr
2518638ead5SAndreas Gohr    /**
25212a4e4d1SAndreas Gohr     * Adds a textarea field
25312a4e4d1SAndreas Gohr     *
2547ec97767SGerrit Uitslag     * @param string $name
2557ec97767SGerrit Uitslag     * @param string $label
25612a4e4d1SAndreas Gohr     * @param int $pos
25712a4e4d1SAndreas Gohr     * @return TextareaElement
25812a4e4d1SAndreas Gohr     */
259de19515fSAndreas Gohr    public function addTextarea($name, $label = '', $pos = -1) {
26012a4e4d1SAndreas Gohr        return $this->addElement(new TextareaElement($name, $label), $pos);
26112a4e4d1SAndreas Gohr    }
26212a4e4d1SAndreas Gohr
26312a4e4d1SAndreas Gohr    /**
2648f0df229SAndreas Gohr     * Adds a simple button, escapes the content for you
2658f0df229SAndreas Gohr     *
2668f0df229SAndreas Gohr     * @param string $name
2678f0df229SAndreas Gohr     * @param string $content
2688f0df229SAndreas Gohr     * @param int $pos
2698f0df229SAndreas Gohr     * @return Element
2708f0df229SAndreas Gohr     */
2718f0df229SAndreas Gohr    public function addButton($name, $content, $pos = -1) {
2728f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
2738f0df229SAndreas Gohr    }
2748f0df229SAndreas Gohr
2758f0df229SAndreas Gohr    /**
2768f0df229SAndreas Gohr     * Adds a simple button, allows HTML for content
2778f0df229SAndreas Gohr     *
2788f0df229SAndreas Gohr     * @param string $name
2798f0df229SAndreas Gohr     * @param string $html
2808f0df229SAndreas Gohr     * @param int $pos
2818f0df229SAndreas Gohr     * @return Element
2828f0df229SAndreas Gohr     */
2838f0df229SAndreas Gohr    public function addButtonHTML($name, $html, $pos = -1) {
2848f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, $html), $pos);
2858f0df229SAndreas Gohr    }
2868f0df229SAndreas Gohr
2878f0df229SAndreas Gohr    /**
288a453c16bSAndreas Gohr     * Adds a label referencing another input element, escapes the label for you
289a453c16bSAndreas Gohr     *
2907ec97767SGerrit Uitslag     * @param string $label
291a453c16bSAndreas Gohr     * @param string $for
292a453c16bSAndreas Gohr     * @param int $pos
293a453c16bSAndreas Gohr     * @return Element
294a453c16bSAndreas Gohr     */
295a453c16bSAndreas Gohr    public function addLabel($label, $for='', $pos = -1) {
296a453c16bSAndreas Gohr        return $this->addLabelHTML(hsc($label), $for, $pos);
297a453c16bSAndreas Gohr    }
298a453c16bSAndreas Gohr
299a453c16bSAndreas Gohr    /**
300a453c16bSAndreas Gohr     * Adds a label referencing another input element, allows HTML for content
301a453c16bSAndreas Gohr     *
302a453c16bSAndreas Gohr     * @param string $content
303a453c16bSAndreas Gohr     * @param string|Element $for
304a453c16bSAndreas Gohr     * @param int $pos
305a453c16bSAndreas Gohr     * @return Element
306a453c16bSAndreas Gohr     */
307a453c16bSAndreas Gohr    public function addLabelHTML($content, $for='', $pos = -1) {
308a453c16bSAndreas Gohr        $element = new LabelElement(hsc($content));
309a453c16bSAndreas Gohr
310a453c16bSAndreas Gohr        if(is_a($for, '\dokuwiki\Form\Element')) {
311a453c16bSAndreas Gohr            /** @var Element $for */
312a453c16bSAndreas Gohr            $for = $for->id();
313a453c16bSAndreas Gohr        }
314a453c16bSAndreas Gohr        $for = (string) $for;
315a453c16bSAndreas Gohr        if($for !== '') {
316a453c16bSAndreas Gohr            $element->attr('for', $for);
317a453c16bSAndreas Gohr        }
318a453c16bSAndreas Gohr
319a453c16bSAndreas Gohr        return $this->addElement($element, $pos);
320a453c16bSAndreas Gohr    }
321a453c16bSAndreas Gohr
322a453c16bSAndreas Gohr    /**
323de19515fSAndreas Gohr     * Add fixed HTML to the form
324de19515fSAndreas Gohr     *
3257ec97767SGerrit Uitslag     * @param string $html
326de19515fSAndreas Gohr     * @param int $pos
32764744a10SAndreas Gohr     * @return HTMLElement
328de19515fSAndreas Gohr     */
329de19515fSAndreas Gohr    public function addHTML($html, $pos = -1) {
330de19515fSAndreas Gohr        return $this->addElement(new HTMLElement($html), $pos);
331de19515fSAndreas Gohr    }
332de19515fSAndreas Gohr
33364744a10SAndreas Gohr    /**
33464744a10SAndreas Gohr     * Add a closed HTML tag to the form
33564744a10SAndreas Gohr     *
3367ec97767SGerrit Uitslag     * @param string $tag
33764744a10SAndreas Gohr     * @param int $pos
33864744a10SAndreas Gohr     * @return TagElement
33964744a10SAndreas Gohr     */
34064744a10SAndreas Gohr    public function addTag($tag, $pos = -1) {
34164744a10SAndreas Gohr        return $this->addElement(new TagElement($tag), $pos);
34264744a10SAndreas Gohr    }
34364744a10SAndreas Gohr
34464744a10SAndreas Gohr    /**
34564744a10SAndreas Gohr     * Add an open HTML tag to the form
34664744a10SAndreas Gohr     *
34764744a10SAndreas Gohr     * Be sure to close it again!
34864744a10SAndreas Gohr     *
3497ec97767SGerrit Uitslag     * @param string $tag
35064744a10SAndreas Gohr     * @param int $pos
35164744a10SAndreas Gohr     * @return TagOpenElement
35264744a10SAndreas Gohr     */
35364744a10SAndreas Gohr    public function addTagOpen($tag, $pos = -1) {
35464744a10SAndreas Gohr        return $this->addElement(new TagOpenElement($tag), $pos);
35564744a10SAndreas Gohr    }
35664744a10SAndreas Gohr
35764744a10SAndreas Gohr    /**
35864744a10SAndreas Gohr     * Add a closing HTML tag to the form
35964744a10SAndreas Gohr     *
36064744a10SAndreas Gohr     * Be sure it had been opened before
36164744a10SAndreas Gohr     *
3627ec97767SGerrit Uitslag     * @param string $tag
36364744a10SAndreas Gohr     * @param int $pos
36464744a10SAndreas Gohr     * @return TagCloseElement
36564744a10SAndreas Gohr     */
36664744a10SAndreas Gohr    public function addTagClose($tag, $pos = -1) {
36764744a10SAndreas Gohr        return $this->addElement(new TagCloseElement($tag), $pos);
36864744a10SAndreas Gohr    }
36964744a10SAndreas Gohr
37064744a10SAndreas Gohr    /**
37164744a10SAndreas Gohr     * Open a Fieldset
37264744a10SAndreas Gohr     *
3737ec97767SGerrit Uitslag     * @param string $legend
37464744a10SAndreas Gohr     * @param int $pos
37564744a10SAndreas Gohr     * @return FieldsetOpenElement
37664744a10SAndreas Gohr     */
37764744a10SAndreas Gohr    public function addFieldsetOpen($legend = '', $pos = -1) {
37864744a10SAndreas Gohr        return $this->addElement(new FieldsetOpenElement($legend), $pos);
37964744a10SAndreas Gohr    }
38064744a10SAndreas Gohr
38164744a10SAndreas Gohr    /**
38264744a10SAndreas Gohr     * Close a fieldset
38364744a10SAndreas Gohr     *
38464744a10SAndreas Gohr     * @param int $pos
38564744a10SAndreas Gohr     * @return TagCloseElement
38664744a10SAndreas Gohr     */
38764744a10SAndreas Gohr    public function addFieldsetClose($pos = -1) {
38864744a10SAndreas Gohr        return $this->addElement(new FieldsetCloseElement(), $pos);
38964744a10SAndreas Gohr    }
39064744a10SAndreas Gohr
39164744a10SAndreas Gohr    #endregion
39264744a10SAndreas Gohr
3931f5d8b65SAndreas Gohr    /**
3941f5d8b65SAndreas Gohr     * Adjust the elements so that fieldset open and closes are matching
3951f5d8b65SAndreas Gohr     */
396de19515fSAndreas Gohr    protected function balanceFieldsets() {
3971f5d8b65SAndreas Gohr        $lastclose = 0;
3981f5d8b65SAndreas Gohr        $isopen = false;
3991f5d8b65SAndreas Gohr        $len = count($this->elements);
4001f5d8b65SAndreas Gohr
4011f5d8b65SAndreas Gohr        for($pos = 0; $pos < $len; $pos++) {
4021f5d8b65SAndreas Gohr            $type = $this->elements[$pos]->getType();
4031f5d8b65SAndreas Gohr            if($type == 'fieldsetopen') {
4041f5d8b65SAndreas Gohr                if($isopen) {
40580b13baaSGerrit Uitslag                    //close previous fieldset
4061f5d8b65SAndreas Gohr                    $this->addFieldsetClose($pos);
4071f5d8b65SAndreas Gohr                    $lastclose = $pos + 1;
4081f5d8b65SAndreas Gohr                    $pos++;
4091f5d8b65SAndreas Gohr                    $len++;
4101f5d8b65SAndreas Gohr                }
4111f5d8b65SAndreas Gohr                $isopen = true;
4121f5d8b65SAndreas Gohr            } else if($type == 'fieldsetclose') {
4131f5d8b65SAndreas Gohr                if(!$isopen) {
4141f5d8b65SAndreas Gohr                    // make sure there was a fieldsetopen
4151f5d8b65SAndreas Gohr                    // either right after the last close or at the begining
4161f5d8b65SAndreas Gohr                    $this->addFieldsetOpen('', $lastclose);
4171f5d8b65SAndreas Gohr                    $len++;
4181f5d8b65SAndreas Gohr                    $pos++;
4191f5d8b65SAndreas Gohr                }
4201f5d8b65SAndreas Gohr                $lastclose = $pos;
4211f5d8b65SAndreas Gohr                $isopen = false;
4221f5d8b65SAndreas Gohr            }
4231f5d8b65SAndreas Gohr        }
4241f5d8b65SAndreas Gohr
4251f5d8b65SAndreas Gohr        // close open fieldset at the end
4261f5d8b65SAndreas Gohr        if($isopen) {
4271f5d8b65SAndreas Gohr            $this->addFieldsetClose();
4281f5d8b65SAndreas Gohr        }
429de19515fSAndreas Gohr    }
430de19515fSAndreas Gohr
431de19515fSAndreas Gohr    /**
43212a4e4d1SAndreas Gohr     * The HTML representation of the whole form
43312a4e4d1SAndreas Gohr     *
43412a4e4d1SAndreas Gohr     * @return string
43512a4e4d1SAndreas Gohr     */
43612a4e4d1SAndreas Gohr    public function toHTML() {
437de19515fSAndreas Gohr        $this->balanceFieldsets();
438de19515fSAndreas Gohr
43901e3f2b3SMichael Große        $html = '<form ' . buildAttributes($this->attrs()) . '>';
44012a4e4d1SAndreas Gohr
44112a4e4d1SAndreas Gohr        foreach($this->hidden as $name => $value) {
44201e3f2b3SMichael Große            $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />';
44312a4e4d1SAndreas Gohr        }
44412a4e4d1SAndreas Gohr
44512a4e4d1SAndreas Gohr        foreach($this->elements as $element) {
44601e3f2b3SMichael Große            $html .= $element->toHTML();
44712a4e4d1SAndreas Gohr        }
44812a4e4d1SAndreas Gohr
44901e3f2b3SMichael Große        $html .= '</form>';
45012a4e4d1SAndreas Gohr
45112a4e4d1SAndreas Gohr        return $html;
45212a4e4d1SAndreas Gohr    }
45312a4e4d1SAndreas Gohr}
454