xref: /dokuwiki/inc/Form/Form.php (revision 7fa270bc10b622a41cd4931fd81eb6c43f3e0db4)
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
27*7fa270bcSMichael Große     * @param bool  $unsafe     if true, then the security token is ommited
2812a4e4d1SAndreas Gohr     */
29*7fa270bcSMichael Große    public function __construct($attributes = array(), $unsafe = false) {
3012a4e4d1SAndreas Gohr        global $ID;
3112a4e4d1SAndreas Gohr
3212a4e4d1SAndreas Gohr        parent::__construct('form', $attributes);
3312a4e4d1SAndreas Gohr
3412a4e4d1SAndreas Gohr        // use the current URL as default action
3512a4e4d1SAndreas Gohr        if(!$this->attr('action')) {
3612a4e4d1SAndreas Gohr            $get = $_GET;
3712a4e4d1SAndreas Gohr            if(isset($get['id'])) unset($get['id']);
386d0ceaf9SAndreas Gohr            $self = wl($ID, $get, false, '&'); //attributes are escaped later
3912a4e4d1SAndreas Gohr            $this->attr('action', $self);
4012a4e4d1SAndreas Gohr        }
4112a4e4d1SAndreas Gohr
4212a4e4d1SAndreas Gohr        // post is default
4312a4e4d1SAndreas Gohr        if(!$this->attr('method')) {
4412a4e4d1SAndreas Gohr            $this->attr('method', 'post');
4512a4e4d1SAndreas Gohr        }
4612a4e4d1SAndreas Gohr
4712a4e4d1SAndreas Gohr        // we like UTF-8
4812a4e4d1SAndreas Gohr        if(!$this->attr('accept-charset')) {
4912a4e4d1SAndreas Gohr            $this->attr('accept-charset', 'utf-8');
5012a4e4d1SAndreas Gohr        }
5112a4e4d1SAndreas Gohr
5212a4e4d1SAndreas Gohr        // add the security token by default
53*7fa270bcSMichael Große        if (!$unsafe) {
5412a4e4d1SAndreas Gohr            $this->setHiddenField('sectok', getSecurityToken());
55*7fa270bcSMichael Große        }
5612a4e4d1SAndreas Gohr
576d0ceaf9SAndreas Gohr        // identify this as a new form based form in HTML
586d0ceaf9SAndreas Gohr        $this->addClass('doku_form');
5912a4e4d1SAndreas Gohr    }
6012a4e4d1SAndreas Gohr
6112a4e4d1SAndreas Gohr    /**
6212a4e4d1SAndreas Gohr     * Sets a hidden field
6312a4e4d1SAndreas Gohr     *
647ec97767SGerrit Uitslag     * @param string $name
657ec97767SGerrit Uitslag     * @param string $value
6612a4e4d1SAndreas Gohr     * @return $this
6712a4e4d1SAndreas Gohr     */
6812a4e4d1SAndreas Gohr    public function setHiddenField($name, $value) {
6912a4e4d1SAndreas Gohr        $this->hidden[$name] = $value;
7012a4e4d1SAndreas Gohr        return $this;
7112a4e4d1SAndreas Gohr    }
7212a4e4d1SAndreas Gohr
73ef0c211bSAndreas Gohr    #region element query function
7464744a10SAndreas Gohr
7512a4e4d1SAndreas Gohr    /**
76ef0c211bSAndreas Gohr     * Returns the numbers of elements in the form
77ef0c211bSAndreas Gohr     *
78ef0c211bSAndreas Gohr     * @return int
79ef0c211bSAndreas Gohr     */
80ef0c211bSAndreas Gohr    public function elementCount() {
81ef0c211bSAndreas Gohr        return count($this->elements);
82ef0c211bSAndreas Gohr    }
83ef0c211bSAndreas Gohr
84ef0c211bSAndreas Gohr    /**
855facb9bcSMichael Große     * Get the position of the element in the form or false if it is not in the form
865facb9bcSMichael Große     *
875facb9bcSMichael 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.
885facb9bcSMichael Große     *
895facb9bcSMichael Große     * @param Element $element
905facb9bcSMichael Große     *
915facb9bcSMichael Große     * @return false|int
925facb9bcSMichael Große     */
935facb9bcSMichael Große    public function getElementPosition(Element $element)
945facb9bcSMichael Große    {
955facb9bcSMichael Große        return array_search($element, $this->elements, true);
965facb9bcSMichael Große    }
975facb9bcSMichael Große
985facb9bcSMichael Große    /**
99ef0c211bSAndreas Gohr     * Returns a reference to the element at a position.
100ef0c211bSAndreas Gohr     * A position out-of-bounds will return either the
101ef0c211bSAndreas Gohr     * first (underflow) or last (overflow) element.
102ef0c211bSAndreas Gohr     *
1037ec97767SGerrit Uitslag     * @param int $pos
104ef0c211bSAndreas Gohr     * @return Element
105ef0c211bSAndreas Gohr     */
106ef0c211bSAndreas Gohr    public function getElementAt($pos) {
107ef0c211bSAndreas Gohr        if($pos < 0) $pos = count($this->elements) + $pos;
108ef0c211bSAndreas Gohr        if($pos < 0) $pos = 0;
109ef0c211bSAndreas Gohr        if($pos >= count($this->elements)) $pos = count($this->elements) - 1;
110ef0c211bSAndreas Gohr        return $this->elements[$pos];
111ef0c211bSAndreas Gohr    }
112ef0c211bSAndreas Gohr
113ef0c211bSAndreas Gohr    /**
114ef0c211bSAndreas Gohr     * Gets the position of the first of a type of element
115ef0c211bSAndreas Gohr     *
116ef0c211bSAndreas Gohr     * @param string $type Element type to look for.
117ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
118ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
119ef0c211bSAndreas Gohr     */
120ef0c211bSAndreas Gohr    public function findPositionByType($type, $offset = 0) {
121ef0c211bSAndreas Gohr        $len = $this->elementCount();
122ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
123ef0c211bSAndreas Gohr            if($this->elements[$pos]->getType() == $type) {
124ef0c211bSAndreas Gohr                return $pos;
125ef0c211bSAndreas Gohr            }
126ef0c211bSAndreas Gohr        }
127ef0c211bSAndreas Gohr        return false;
128ef0c211bSAndreas Gohr    }
129ef0c211bSAndreas Gohr
130ef0c211bSAndreas Gohr    /**
131ef0c211bSAndreas Gohr     * Gets the position of the first element matching the attribute
132ef0c211bSAndreas Gohr     *
133ef0c211bSAndreas Gohr     * @param string $name Name of the attribute
134ef0c211bSAndreas Gohr     * @param string $value Value the attribute should have
135ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
136ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
137ef0c211bSAndreas Gohr     */
138ef0c211bSAndreas Gohr    public function findPositionByAttribute($name, $value, $offset = 0) {
139ef0c211bSAndreas Gohr        $len = $this->elementCount();
140ef0c211bSAndreas Gohr        for($pos = $offset; $pos < $len; $pos++) {
141ef0c211bSAndreas Gohr            if($this->elements[$pos]->attr($name) == $value) {
142ef0c211bSAndreas Gohr                return $pos;
143ef0c211bSAndreas Gohr            }
144ef0c211bSAndreas Gohr        }
145ef0c211bSAndreas Gohr        return false;
146ef0c211bSAndreas Gohr    }
147ef0c211bSAndreas Gohr
148ef0c211bSAndreas Gohr    #endregion
149ef0c211bSAndreas Gohr
150ef0c211bSAndreas Gohr    #region Element positioning functions
151ef0c211bSAndreas Gohr
152ef0c211bSAndreas Gohr    /**
153ef0c211bSAndreas Gohr     * Adds or inserts an element to the form
15412a4e4d1SAndreas Gohr     *
15512a4e4d1SAndreas Gohr     * @param Element $element
15612a4e4d1SAndreas Gohr     * @param int $pos 0-based position in the form, -1 for at the end
15712a4e4d1SAndreas Gohr     * @return Element
15812a4e4d1SAndreas Gohr     */
15912a4e4d1SAndreas Gohr    public function addElement(Element $element, $pos = -1) {
160bb6d40dcSAndreas Gohr        if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
16112a4e4d1SAndreas Gohr        if($pos < 0) {
16212a4e4d1SAndreas Gohr            $this->elements[] = $element;
16312a4e4d1SAndreas Gohr        } else {
16412a4e4d1SAndreas Gohr            array_splice($this->elements, $pos, 0, array($element));
16512a4e4d1SAndreas Gohr        }
16612a4e4d1SAndreas Gohr        return $element;
16712a4e4d1SAndreas Gohr    }
16812a4e4d1SAndreas Gohr
16912a4e4d1SAndreas Gohr    /**
170ef0c211bSAndreas Gohr     * Replaces an existing element with a new one
171ef0c211bSAndreas Gohr     *
172ef0c211bSAndreas Gohr     * @param Element $element the new element
1737ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to replace
174ef0c211bSAndreas Gohr     */
175ef0c211bSAndreas Gohr    public function replaceElement(Element $element, $pos) {
176bb6d40dcSAndreas Gohr        if(is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException('You can\'t add a form to a form');
177ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1, array($element));
178ef0c211bSAndreas Gohr    }
179ef0c211bSAndreas Gohr
180ef0c211bSAndreas Gohr    /**
181ef0c211bSAndreas Gohr     * Remove an element from the form completely
182ef0c211bSAndreas Gohr     *
1837ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to remove
184ef0c211bSAndreas Gohr     */
185ef0c211bSAndreas Gohr    public function removeElement($pos) {
186ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1);
187ef0c211bSAndreas Gohr    }
188ef0c211bSAndreas Gohr
189ef0c211bSAndreas Gohr    #endregion
190ef0c211bSAndreas Gohr
191ef0c211bSAndreas Gohr    #region Element adding functions
192ef0c211bSAndreas Gohr
193ef0c211bSAndreas Gohr    /**
19412a4e4d1SAndreas Gohr     * Adds a text input field
19512a4e4d1SAndreas Gohr     *
1967ec97767SGerrit Uitslag     * @param string $name
1977ec97767SGerrit Uitslag     * @param string $label
19812a4e4d1SAndreas Gohr     * @param int $pos
19912a4e4d1SAndreas Gohr     * @return InputElement
20012a4e4d1SAndreas Gohr     */
201de19515fSAndreas Gohr    public function addTextInput($name, $label = '', $pos = -1) {
20212a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('text', $name, $label), $pos);
20312a4e4d1SAndreas Gohr    }
20412a4e4d1SAndreas Gohr
20512a4e4d1SAndreas Gohr    /**
20612a4e4d1SAndreas Gohr     * Adds a password input field
20712a4e4d1SAndreas Gohr     *
2087ec97767SGerrit Uitslag     * @param string $name
2097ec97767SGerrit Uitslag     * @param string $label
21012a4e4d1SAndreas Gohr     * @param int $pos
21112a4e4d1SAndreas Gohr     * @return InputElement
21212a4e4d1SAndreas Gohr     */
213de19515fSAndreas Gohr    public function addPasswordInput($name, $label = '', $pos = -1) {
21412a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('password', $name, $label), $pos);
21512a4e4d1SAndreas Gohr    }
21612a4e4d1SAndreas Gohr
21712a4e4d1SAndreas Gohr    /**
21812a4e4d1SAndreas Gohr     * Adds a radio button field
21912a4e4d1SAndreas Gohr     *
2207ec97767SGerrit Uitslag     * @param string $name
2217ec97767SGerrit Uitslag     * @param string $label
22212a4e4d1SAndreas Gohr     * @param int $pos
22312a4e4d1SAndreas Gohr     * @return CheckableElement
22412a4e4d1SAndreas Gohr     */
225de19515fSAndreas Gohr    public function addRadioButton($name, $label = '', $pos = -1) {
22612a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
22712a4e4d1SAndreas Gohr    }
22812a4e4d1SAndreas Gohr
22912a4e4d1SAndreas Gohr    /**
23012a4e4d1SAndreas Gohr     * Adds a checkbox field
23112a4e4d1SAndreas Gohr     *
2327ec97767SGerrit Uitslag     * @param string $name
2337ec97767SGerrit Uitslag     * @param string $label
23412a4e4d1SAndreas Gohr     * @param int $pos
23512a4e4d1SAndreas Gohr     * @return CheckableElement
23612a4e4d1SAndreas Gohr     */
237de19515fSAndreas Gohr    public function addCheckbox($name, $label = '', $pos = -1) {
23812a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
23912a4e4d1SAndreas Gohr    }
24012a4e4d1SAndreas Gohr
24112a4e4d1SAndreas Gohr    /**
2428638ead5SAndreas Gohr     * Adds a dropdown field
2438638ead5SAndreas Gohr     *
2447ec97767SGerrit Uitslag     * @param string $name
2458638ead5SAndreas Gohr     * @param array $options
2468638ead5SAndreas Gohr     * @param string $label
2478638ead5SAndreas Gohr     * @param int $pos
2488638ead5SAndreas Gohr     * @return DropdownElement
2498638ead5SAndreas Gohr     */
2508638ead5SAndreas Gohr    public function addDropdown($name, $options, $label = '', $pos = -1) {
2518638ead5SAndreas Gohr        return $this->addElement(new DropdownElement($name, $options, $label), $pos);
2528638ead5SAndreas Gohr    }
2538638ead5SAndreas Gohr
2548638ead5SAndreas Gohr    /**
25512a4e4d1SAndreas Gohr     * Adds a textarea field
25612a4e4d1SAndreas Gohr     *
2577ec97767SGerrit Uitslag     * @param string $name
2587ec97767SGerrit Uitslag     * @param string $label
25912a4e4d1SAndreas Gohr     * @param int $pos
26012a4e4d1SAndreas Gohr     * @return TextareaElement
26112a4e4d1SAndreas Gohr     */
262de19515fSAndreas Gohr    public function addTextarea($name, $label = '', $pos = -1) {
26312a4e4d1SAndreas Gohr        return $this->addElement(new TextareaElement($name, $label), $pos);
26412a4e4d1SAndreas Gohr    }
26512a4e4d1SAndreas Gohr
26612a4e4d1SAndreas Gohr    /**
2678f0df229SAndreas Gohr     * Adds a simple button, escapes the content for you
2688f0df229SAndreas Gohr     *
2698f0df229SAndreas Gohr     * @param string $name
2708f0df229SAndreas Gohr     * @param string $content
2718f0df229SAndreas Gohr     * @param int $pos
2728f0df229SAndreas Gohr     * @return Element
2738f0df229SAndreas Gohr     */
2748f0df229SAndreas Gohr    public function addButton($name, $content, $pos = -1) {
2758f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
2768f0df229SAndreas Gohr    }
2778f0df229SAndreas Gohr
2788f0df229SAndreas Gohr    /**
2798f0df229SAndreas Gohr     * Adds a simple button, allows HTML for content
2808f0df229SAndreas Gohr     *
2818f0df229SAndreas Gohr     * @param string $name
2828f0df229SAndreas Gohr     * @param string $html
2838f0df229SAndreas Gohr     * @param int $pos
2848f0df229SAndreas Gohr     * @return Element
2858f0df229SAndreas Gohr     */
2868f0df229SAndreas Gohr    public function addButtonHTML($name, $html, $pos = -1) {
2878f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, $html), $pos);
2888f0df229SAndreas Gohr    }
2898f0df229SAndreas Gohr
2908f0df229SAndreas Gohr    /**
291a453c16bSAndreas Gohr     * Adds a label referencing another input element, escapes the label for you
292a453c16bSAndreas Gohr     *
2937ec97767SGerrit Uitslag     * @param string $label
294a453c16bSAndreas Gohr     * @param string $for
295a453c16bSAndreas Gohr     * @param int $pos
296a453c16bSAndreas Gohr     * @return Element
297a453c16bSAndreas Gohr     */
298a453c16bSAndreas Gohr    public function addLabel($label, $for='', $pos = -1) {
299a453c16bSAndreas Gohr        return $this->addLabelHTML(hsc($label), $for, $pos);
300a453c16bSAndreas Gohr    }
301a453c16bSAndreas Gohr
302a453c16bSAndreas Gohr    /**
303a453c16bSAndreas Gohr     * Adds a label referencing another input element, allows HTML for content
304a453c16bSAndreas Gohr     *
305a453c16bSAndreas Gohr     * @param string $content
306a453c16bSAndreas Gohr     * @param string|Element $for
307a453c16bSAndreas Gohr     * @param int $pos
308a453c16bSAndreas Gohr     * @return Element
309a453c16bSAndreas Gohr     */
310a453c16bSAndreas Gohr    public function addLabelHTML($content, $for='', $pos = -1) {
311a453c16bSAndreas Gohr        $element = new LabelElement(hsc($content));
312a453c16bSAndreas Gohr
313a453c16bSAndreas Gohr        if(is_a($for, '\dokuwiki\Form\Element')) {
314a453c16bSAndreas Gohr            /** @var Element $for */
315a453c16bSAndreas Gohr            $for = $for->id();
316a453c16bSAndreas Gohr        }
317a453c16bSAndreas Gohr        $for = (string) $for;
318a453c16bSAndreas Gohr        if($for !== '') {
319a453c16bSAndreas Gohr            $element->attr('for', $for);
320a453c16bSAndreas Gohr        }
321a453c16bSAndreas Gohr
322a453c16bSAndreas Gohr        return $this->addElement($element, $pos);
323a453c16bSAndreas Gohr    }
324a453c16bSAndreas Gohr
325a453c16bSAndreas Gohr    /**
326de19515fSAndreas Gohr     * Add fixed HTML to the form
327de19515fSAndreas Gohr     *
3287ec97767SGerrit Uitslag     * @param string $html
329de19515fSAndreas Gohr     * @param int $pos
33064744a10SAndreas Gohr     * @return HTMLElement
331de19515fSAndreas Gohr     */
332de19515fSAndreas Gohr    public function addHTML($html, $pos = -1) {
333de19515fSAndreas Gohr        return $this->addElement(new HTMLElement($html), $pos);
334de19515fSAndreas Gohr    }
335de19515fSAndreas Gohr
33664744a10SAndreas Gohr    /**
33764744a10SAndreas Gohr     * Add a closed HTML tag to the form
33864744a10SAndreas Gohr     *
3397ec97767SGerrit Uitslag     * @param string $tag
34064744a10SAndreas Gohr     * @param int $pos
34164744a10SAndreas Gohr     * @return TagElement
34264744a10SAndreas Gohr     */
34364744a10SAndreas Gohr    public function addTag($tag, $pos = -1) {
34464744a10SAndreas Gohr        return $this->addElement(new TagElement($tag), $pos);
34564744a10SAndreas Gohr    }
34664744a10SAndreas Gohr
34764744a10SAndreas Gohr    /**
34864744a10SAndreas Gohr     * Add an open HTML tag to the form
34964744a10SAndreas Gohr     *
35064744a10SAndreas Gohr     * Be sure to close it again!
35164744a10SAndreas Gohr     *
3527ec97767SGerrit Uitslag     * @param string $tag
35364744a10SAndreas Gohr     * @param int $pos
35464744a10SAndreas Gohr     * @return TagOpenElement
35564744a10SAndreas Gohr     */
35664744a10SAndreas Gohr    public function addTagOpen($tag, $pos = -1) {
35764744a10SAndreas Gohr        return $this->addElement(new TagOpenElement($tag), $pos);
35864744a10SAndreas Gohr    }
35964744a10SAndreas Gohr
36064744a10SAndreas Gohr    /**
36164744a10SAndreas Gohr     * Add a closing HTML tag to the form
36264744a10SAndreas Gohr     *
36364744a10SAndreas Gohr     * Be sure it had been opened before
36464744a10SAndreas Gohr     *
3657ec97767SGerrit Uitslag     * @param string $tag
36664744a10SAndreas Gohr     * @param int $pos
36764744a10SAndreas Gohr     * @return TagCloseElement
36864744a10SAndreas Gohr     */
36964744a10SAndreas Gohr    public function addTagClose($tag, $pos = -1) {
37064744a10SAndreas Gohr        return $this->addElement(new TagCloseElement($tag), $pos);
37164744a10SAndreas Gohr    }
37264744a10SAndreas Gohr
37364744a10SAndreas Gohr    /**
37464744a10SAndreas Gohr     * Open a Fieldset
37564744a10SAndreas Gohr     *
3767ec97767SGerrit Uitslag     * @param string $legend
37764744a10SAndreas Gohr     * @param int $pos
37864744a10SAndreas Gohr     * @return FieldsetOpenElement
37964744a10SAndreas Gohr     */
38064744a10SAndreas Gohr    public function addFieldsetOpen($legend = '', $pos = -1) {
38164744a10SAndreas Gohr        return $this->addElement(new FieldsetOpenElement($legend), $pos);
38264744a10SAndreas Gohr    }
38364744a10SAndreas Gohr
38464744a10SAndreas Gohr    /**
38564744a10SAndreas Gohr     * Close a fieldset
38664744a10SAndreas Gohr     *
38764744a10SAndreas Gohr     * @param int $pos
38864744a10SAndreas Gohr     * @return TagCloseElement
38964744a10SAndreas Gohr     */
39064744a10SAndreas Gohr    public function addFieldsetClose($pos = -1) {
39164744a10SAndreas Gohr        return $this->addElement(new FieldsetCloseElement(), $pos);
39264744a10SAndreas Gohr    }
39364744a10SAndreas Gohr
39464744a10SAndreas Gohr    #endregion
39564744a10SAndreas Gohr
3961f5d8b65SAndreas Gohr    /**
3971f5d8b65SAndreas Gohr     * Adjust the elements so that fieldset open and closes are matching
3981f5d8b65SAndreas Gohr     */
399de19515fSAndreas Gohr    protected function balanceFieldsets() {
4001f5d8b65SAndreas Gohr        $lastclose = 0;
4011f5d8b65SAndreas Gohr        $isopen = false;
4021f5d8b65SAndreas Gohr        $len = count($this->elements);
4031f5d8b65SAndreas Gohr
4041f5d8b65SAndreas Gohr        for($pos = 0; $pos < $len; $pos++) {
4051f5d8b65SAndreas Gohr            $type = $this->elements[$pos]->getType();
4061f5d8b65SAndreas Gohr            if($type == 'fieldsetopen') {
4071f5d8b65SAndreas Gohr                if($isopen) {
40880b13baaSGerrit Uitslag                    //close previous fieldset
4091f5d8b65SAndreas Gohr                    $this->addFieldsetClose($pos);
4101f5d8b65SAndreas Gohr                    $lastclose = $pos + 1;
4111f5d8b65SAndreas Gohr                    $pos++;
4121f5d8b65SAndreas Gohr                    $len++;
4131f5d8b65SAndreas Gohr                }
4141f5d8b65SAndreas Gohr                $isopen = true;
4151f5d8b65SAndreas Gohr            } else if($type == 'fieldsetclose') {
4161f5d8b65SAndreas Gohr                if(!$isopen) {
4171f5d8b65SAndreas Gohr                    // make sure there was a fieldsetopen
4181f5d8b65SAndreas Gohr                    // either right after the last close or at the begining
4191f5d8b65SAndreas Gohr                    $this->addFieldsetOpen('', $lastclose);
4201f5d8b65SAndreas Gohr                    $len++;
4211f5d8b65SAndreas Gohr                    $pos++;
4221f5d8b65SAndreas Gohr                }
4231f5d8b65SAndreas Gohr                $lastclose = $pos;
4241f5d8b65SAndreas Gohr                $isopen = false;
4251f5d8b65SAndreas Gohr            }
4261f5d8b65SAndreas Gohr        }
4271f5d8b65SAndreas Gohr
4281f5d8b65SAndreas Gohr        // close open fieldset at the end
4291f5d8b65SAndreas Gohr        if($isopen) {
4301f5d8b65SAndreas Gohr            $this->addFieldsetClose();
4311f5d8b65SAndreas Gohr        }
432de19515fSAndreas Gohr    }
433de19515fSAndreas Gohr
434de19515fSAndreas Gohr    /**
43512a4e4d1SAndreas Gohr     * The HTML representation of the whole form
43612a4e4d1SAndreas Gohr     *
43712a4e4d1SAndreas Gohr     * @return string
43812a4e4d1SAndreas Gohr     */
43912a4e4d1SAndreas Gohr    public function toHTML() {
440de19515fSAndreas Gohr        $this->balanceFieldsets();
441de19515fSAndreas Gohr
44201e3f2b3SMichael Große        $html = '<form ' . buildAttributes($this->attrs()) . '>';
44312a4e4d1SAndreas Gohr
44412a4e4d1SAndreas Gohr        foreach($this->hidden as $name => $value) {
44501e3f2b3SMichael Große            $html .= '<input type="hidden" name="' . $name . '" value="' . formText($value) . '" />';
44612a4e4d1SAndreas Gohr        }
44712a4e4d1SAndreas Gohr
44812a4e4d1SAndreas Gohr        foreach($this->elements as $element) {
44901e3f2b3SMichael Große            $html .= $element->toHTML();
45012a4e4d1SAndreas Gohr        }
45112a4e4d1SAndreas Gohr
45201e3f2b3SMichael Große        $html .= '</form>';
45312a4e4d1SAndreas Gohr
45412a4e4d1SAndreas Gohr        return $html;
45512a4e4d1SAndreas Gohr    }
45612a4e4d1SAndreas Gohr}
457