xref: /dokuwiki/inc/Form/Form.php (revision 0dd35558646b5859c816cdbe739beb4a87459c6a)
112a4e4d1SAndreas Gohr<?php
29d01c1d9SSatoshi Sahara
312a4e4d1SAndreas Gohrnamespace dokuwiki\Form;
412a4e4d1SAndreas Gohr
512a4e4d1SAndreas Gohr/**
612a4e4d1SAndreas Gohr * Class Form
712a4e4d1SAndreas Gohr *
812a4e4d1SAndreas Gohr * Represents the whole Form. This is what you work on, and add Elements to
912a4e4d1SAndreas Gohr *
1012a4e4d1SAndreas Gohr * @package dokuwiki\Form
1112a4e4d1SAndreas Gohr */
129d01c1d9SSatoshi Saharaclass Form extends Element
139d01c1d9SSatoshi Sahara{
1412a4e4d1SAndreas Gohr    /**
1512a4e4d1SAndreas Gohr     * @var array name value pairs for hidden values
1612a4e4d1SAndreas Gohr     */
1712a4e4d1SAndreas Gohr    protected $hidden = array();
1812a4e4d1SAndreas Gohr
1912a4e4d1SAndreas Gohr    /**
2012a4e4d1SAndreas Gohr     * @var Element[] the elements of the form
2112a4e4d1SAndreas Gohr     */
2212a4e4d1SAndreas Gohr    protected $elements = array();
2312a4e4d1SAndreas Gohr
2412a4e4d1SAndreas Gohr    /**
2512a4e4d1SAndreas Gohr     * Creates a new, empty form with some default attributes
2612a4e4d1SAndreas Gohr     *
2712a4e4d1SAndreas Gohr     * @param array $attributes
287fa270bcSMichael Große     * @param bool  $unsafe     if true, then the security token is ommited
2912a4e4d1SAndreas Gohr     */
309d01c1d9SSatoshi Sahara    public function __construct($attributes = array(), $unsafe = false)
319d01c1d9SSatoshi Sahara    {
3212a4e4d1SAndreas Gohr        global $ID;
3312a4e4d1SAndreas Gohr
3412a4e4d1SAndreas Gohr        parent::__construct('form', $attributes);
3512a4e4d1SAndreas Gohr
3612a4e4d1SAndreas Gohr        // use the current URL as default action
3712a4e4d1SAndreas Gohr        if (!$this->attr('action')) {
3812a4e4d1SAndreas Gohr            $get = $_GET;
3912a4e4d1SAndreas Gohr            if (isset($get['id'])) unset($get['id']);
406d0ceaf9SAndreas Gohr            $self = wl($ID, $get, false, '&'); //attributes are escaped later
4112a4e4d1SAndreas Gohr            $this->attr('action', $self);
4212a4e4d1SAndreas Gohr        }
4312a4e4d1SAndreas Gohr
4412a4e4d1SAndreas Gohr        // post is default
4512a4e4d1SAndreas Gohr        if (!$this->attr('method')) {
4612a4e4d1SAndreas Gohr            $this->attr('method', 'post');
4712a4e4d1SAndreas Gohr        }
4812a4e4d1SAndreas Gohr
4912a4e4d1SAndreas Gohr        // we like UTF-8
5012a4e4d1SAndreas Gohr        if (!$this->attr('accept-charset')) {
5112a4e4d1SAndreas Gohr            $this->attr('accept-charset', 'utf-8');
5212a4e4d1SAndreas Gohr        }
5312a4e4d1SAndreas Gohr
5412a4e4d1SAndreas Gohr        // add the security token by default
557fa270bcSMichael Große        if (!$unsafe) {
5612a4e4d1SAndreas Gohr            $this->setHiddenField('sectok', getSecurityToken());
577fa270bcSMichael Große        }
5812a4e4d1SAndreas Gohr
596d0ceaf9SAndreas Gohr        // identify this as a new form based form in HTML
606d0ceaf9SAndreas Gohr        $this->addClass('doku_form');
6112a4e4d1SAndreas Gohr    }
6212a4e4d1SAndreas Gohr
6312a4e4d1SAndreas Gohr    /**
6412a4e4d1SAndreas Gohr     * Sets a hidden field
6512a4e4d1SAndreas Gohr     *
667ec97767SGerrit Uitslag     * @param string $name
677ec97767SGerrit Uitslag     * @param string $value
6812a4e4d1SAndreas Gohr     * @return $this
6912a4e4d1SAndreas Gohr     */
709d01c1d9SSatoshi Sahara    public function setHiddenField($name, $value)
719d01c1d9SSatoshi Sahara    {
7212a4e4d1SAndreas Gohr        $this->hidden[$name] = $value;
7312a4e4d1SAndreas Gohr        return $this;
7412a4e4d1SAndreas Gohr    }
7512a4e4d1SAndreas Gohr
76ef0c211bSAndreas Gohr    #region element query function
7764744a10SAndreas Gohr
7812a4e4d1SAndreas Gohr    /**
79ef0c211bSAndreas Gohr     * Returns the numbers of elements in the form
80ef0c211bSAndreas Gohr     *
81ef0c211bSAndreas Gohr     * @return int
82ef0c211bSAndreas Gohr     */
839d01c1d9SSatoshi Sahara    public function elementCount()
849d01c1d9SSatoshi Sahara    {
85ef0c211bSAndreas Gohr        return count($this->elements);
86ef0c211bSAndreas Gohr    }
87ef0c211bSAndreas Gohr
88ef0c211bSAndreas Gohr    /**
895facb9bcSMichael Große     * Get the position of the element in the form or false if it is not in the form
905facb9bcSMichael Große     *
9164159a61SAndreas Gohr     * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
9264159a61SAndreas Gohr     * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
9364159a61SAndreas Gohr     * return value of this function.
945facb9bcSMichael Große     *
955facb9bcSMichael Große     * @param Element $element
965facb9bcSMichael Große     *
975facb9bcSMichael Große     * @return false|int
985facb9bcSMichael Große     */
995facb9bcSMichael Große    public function getElementPosition(Element $element)
1005facb9bcSMichael Große    {
1015facb9bcSMichael Große        return array_search($element, $this->elements, true);
1025facb9bcSMichael Große    }
1035facb9bcSMichael Große
1045facb9bcSMichael Große    /**
105ef0c211bSAndreas Gohr     * Returns a reference to the element at a position.
106ef0c211bSAndreas Gohr     * A position out-of-bounds will return either the
107ef0c211bSAndreas Gohr     * first (underflow) or last (overflow) element.
108ef0c211bSAndreas Gohr     *
1097ec97767SGerrit Uitslag     * @param int $pos
110ef0c211bSAndreas Gohr     * @return Element
111ef0c211bSAndreas Gohr     */
1129d01c1d9SSatoshi Sahara    public function getElementAt($pos)
1139d01c1d9SSatoshi Sahara    {
114ef0c211bSAndreas Gohr        if ($pos < 0) $pos = count($this->elements) + $pos;
115ef0c211bSAndreas Gohr        if ($pos < 0) $pos = 0;
116ef0c211bSAndreas Gohr        if ($pos >= count($this->elements)) $pos = count($this->elements) - 1;
117ef0c211bSAndreas Gohr        return $this->elements[$pos];
118ef0c211bSAndreas Gohr    }
119ef0c211bSAndreas Gohr
120ef0c211bSAndreas Gohr    /**
121ef0c211bSAndreas Gohr     * Gets the position of the first of a type of element
122ef0c211bSAndreas Gohr     *
123ef0c211bSAndreas Gohr     * @param string $type Element type to look for.
124ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
125ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
126ef0c211bSAndreas Gohr     */
1279d01c1d9SSatoshi Sahara    public function findPositionByType($type, $offset = 0)
1289d01c1d9SSatoshi Sahara    {
129ef0c211bSAndreas Gohr        $len = $this->elementCount();
130ef0c211bSAndreas Gohr        for ($pos = $offset; $pos < $len; $pos++) {
131ef0c211bSAndreas Gohr            if ($this->elements[$pos]->getType() == $type) {
132ef0c211bSAndreas Gohr                return $pos;
133ef0c211bSAndreas Gohr            }
134ef0c211bSAndreas Gohr        }
135ef0c211bSAndreas Gohr        return false;
136ef0c211bSAndreas Gohr    }
137ef0c211bSAndreas Gohr
138ef0c211bSAndreas Gohr    /**
139ef0c211bSAndreas Gohr     * Gets the position of the first element matching the attribute
140ef0c211bSAndreas Gohr     *
141ef0c211bSAndreas Gohr     * @param string $name Name of the attribute
142ef0c211bSAndreas Gohr     * @param string $value Value the attribute should have
143ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
144ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
145ef0c211bSAndreas Gohr     */
1469d01c1d9SSatoshi Sahara    public function findPositionByAttribute($name, $value, $offset = 0)
1479d01c1d9SSatoshi Sahara    {
148ef0c211bSAndreas Gohr        $len = $this->elementCount();
149ef0c211bSAndreas Gohr        for ($pos = $offset; $pos < $len; $pos++) {
150ef0c211bSAndreas Gohr            if ($this->elements[$pos]->attr($name) == $value) {
151ef0c211bSAndreas Gohr                return $pos;
152ef0c211bSAndreas Gohr            }
153ef0c211bSAndreas Gohr        }
154ef0c211bSAndreas Gohr        return false;
155ef0c211bSAndreas Gohr    }
156ef0c211bSAndreas Gohr
157ef0c211bSAndreas Gohr    #endregion
158ef0c211bSAndreas Gohr
159ef0c211bSAndreas Gohr    #region Element positioning functions
160ef0c211bSAndreas Gohr
161ef0c211bSAndreas Gohr    /**
162ef0c211bSAndreas Gohr     * Adds or inserts an element to the form
16312a4e4d1SAndreas Gohr     *
16412a4e4d1SAndreas Gohr     * @param Element $element
16512a4e4d1SAndreas Gohr     * @param int $pos 0-based position in the form, -1 for at the end
16612a4e4d1SAndreas Gohr     * @return Element
16712a4e4d1SAndreas Gohr     */
1689d01c1d9SSatoshi Sahara    public function addElement(Element $element, $pos = -1)
1699d01c1d9SSatoshi Sahara    {
17064159a61SAndreas Gohr        if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
17164159a61SAndreas Gohr            'You can\'t add a form to a form'
17264159a61SAndreas Gohr        );
17312a4e4d1SAndreas Gohr        if ($pos < 0) {
17412a4e4d1SAndreas Gohr            $this->elements[] = $element;
17512a4e4d1SAndreas Gohr        } else {
17612a4e4d1SAndreas Gohr            array_splice($this->elements, $pos, 0, array($element));
17712a4e4d1SAndreas Gohr        }
17812a4e4d1SAndreas Gohr        return $element;
17912a4e4d1SAndreas Gohr    }
18012a4e4d1SAndreas Gohr
18112a4e4d1SAndreas Gohr    /**
182ef0c211bSAndreas Gohr     * Replaces an existing element with a new one
183ef0c211bSAndreas Gohr     *
184ef0c211bSAndreas Gohr     * @param Element $element the new element
1857ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to replace
186ef0c211bSAndreas Gohr     */
1879d01c1d9SSatoshi Sahara    public function replaceElement(Element $element, $pos)
1889d01c1d9SSatoshi Sahara    {
18964159a61SAndreas Gohr        if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
19064159a61SAndreas Gohr            'You can\'t add a form to a form'
19164159a61SAndreas Gohr        );
192ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1, array($element));
193ef0c211bSAndreas Gohr    }
194ef0c211bSAndreas Gohr
195ef0c211bSAndreas Gohr    /**
196ef0c211bSAndreas Gohr     * Remove an element from the form completely
197ef0c211bSAndreas Gohr     *
1987ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to remove
199ef0c211bSAndreas Gohr     */
2009d01c1d9SSatoshi Sahara    public function removeElement($pos)
2019d01c1d9SSatoshi Sahara    {
202ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1);
203ef0c211bSAndreas Gohr    }
204ef0c211bSAndreas Gohr
205ef0c211bSAndreas Gohr    #endregion
206ef0c211bSAndreas Gohr
207ef0c211bSAndreas Gohr    #region Element adding functions
208ef0c211bSAndreas Gohr
209ef0c211bSAndreas Gohr    /**
21012a4e4d1SAndreas Gohr     * Adds a text input field
21112a4e4d1SAndreas Gohr     *
2127ec97767SGerrit Uitslag     * @param string $name
2137ec97767SGerrit Uitslag     * @param string $label
21412a4e4d1SAndreas Gohr     * @param int $pos
21512a4e4d1SAndreas Gohr     * @return InputElement
21612a4e4d1SAndreas Gohr     */
2179d01c1d9SSatoshi Sahara    public function addTextInput($name, $label = '', $pos = -1)
2189d01c1d9SSatoshi Sahara    {
21912a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('text', $name, $label), $pos);
22012a4e4d1SAndreas Gohr    }
22112a4e4d1SAndreas Gohr
22212a4e4d1SAndreas Gohr    /**
22312a4e4d1SAndreas Gohr     * Adds a password input field
22412a4e4d1SAndreas Gohr     *
2257ec97767SGerrit Uitslag     * @param string $name
2267ec97767SGerrit Uitslag     * @param string $label
22712a4e4d1SAndreas Gohr     * @param int $pos
22812a4e4d1SAndreas Gohr     * @return InputElement
22912a4e4d1SAndreas Gohr     */
2309d01c1d9SSatoshi Sahara    public function addPasswordInput($name, $label = '', $pos = -1)
2319d01c1d9SSatoshi Sahara    {
23212a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('password', $name, $label), $pos);
23312a4e4d1SAndreas Gohr    }
23412a4e4d1SAndreas Gohr
23512a4e4d1SAndreas Gohr    /**
23612a4e4d1SAndreas Gohr     * Adds a radio button field
23712a4e4d1SAndreas Gohr     *
2387ec97767SGerrit Uitslag     * @param string $name
2397ec97767SGerrit Uitslag     * @param string $label
24012a4e4d1SAndreas Gohr     * @param int $pos
24112a4e4d1SAndreas Gohr     * @return CheckableElement
24212a4e4d1SAndreas Gohr     */
2439d01c1d9SSatoshi Sahara    public function addRadioButton($name, $label = '', $pos = -1)
2449d01c1d9SSatoshi Sahara    {
24512a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
24612a4e4d1SAndreas Gohr    }
24712a4e4d1SAndreas Gohr
24812a4e4d1SAndreas Gohr    /**
24912a4e4d1SAndreas Gohr     * Adds a checkbox field
25012a4e4d1SAndreas Gohr     *
2517ec97767SGerrit Uitslag     * @param string $name
2527ec97767SGerrit Uitslag     * @param string $label
25312a4e4d1SAndreas Gohr     * @param int $pos
25412a4e4d1SAndreas Gohr     * @return CheckableElement
25512a4e4d1SAndreas Gohr     */
2569d01c1d9SSatoshi Sahara    public function addCheckbox($name, $label = '', $pos = -1)
2579d01c1d9SSatoshi Sahara    {
25812a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
25912a4e4d1SAndreas Gohr    }
26012a4e4d1SAndreas Gohr
26112a4e4d1SAndreas Gohr    /**
2628638ead5SAndreas Gohr     * Adds a dropdown field
2638638ead5SAndreas Gohr     *
2647ec97767SGerrit Uitslag     * @param string $name
2658638ead5SAndreas Gohr     * @param array $options
2668638ead5SAndreas Gohr     * @param string $label
2678638ead5SAndreas Gohr     * @param int $pos
2688638ead5SAndreas Gohr     * @return DropdownElement
2698638ead5SAndreas Gohr     */
2709d01c1d9SSatoshi Sahara    public function addDropdown($name, $options, $label = '', $pos = -1)
2719d01c1d9SSatoshi Sahara    {
2728638ead5SAndreas Gohr        return $this->addElement(new DropdownElement($name, $options, $label), $pos);
2738638ead5SAndreas Gohr    }
2748638ead5SAndreas Gohr
2758638ead5SAndreas Gohr    /**
27612a4e4d1SAndreas Gohr     * Adds a textarea field
27712a4e4d1SAndreas Gohr     *
2787ec97767SGerrit Uitslag     * @param string $name
2797ec97767SGerrit Uitslag     * @param string $label
28012a4e4d1SAndreas Gohr     * @param int $pos
28112a4e4d1SAndreas Gohr     * @return TextareaElement
28212a4e4d1SAndreas Gohr     */
2839d01c1d9SSatoshi Sahara    public function addTextarea($name, $label = '', $pos = -1)
2849d01c1d9SSatoshi Sahara    {
28512a4e4d1SAndreas Gohr        return $this->addElement(new TextareaElement($name, $label), $pos);
28612a4e4d1SAndreas Gohr    }
28712a4e4d1SAndreas Gohr
28812a4e4d1SAndreas Gohr    /**
2898f0df229SAndreas Gohr     * Adds a simple button, escapes the content for you
2908f0df229SAndreas Gohr     *
2918f0df229SAndreas Gohr     * @param string $name
2928f0df229SAndreas Gohr     * @param string $content
2938f0df229SAndreas Gohr     * @param int $pos
2948f0df229SAndreas Gohr     * @return Element
2958f0df229SAndreas Gohr     */
2969d01c1d9SSatoshi Sahara    public function addButton($name, $content, $pos = -1)
2979d01c1d9SSatoshi Sahara    {
2988f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
2998f0df229SAndreas Gohr    }
3008f0df229SAndreas Gohr
3018f0df229SAndreas Gohr    /**
3028f0df229SAndreas Gohr     * Adds a simple button, allows HTML for content
3038f0df229SAndreas Gohr     *
3048f0df229SAndreas Gohr     * @param string $name
3058f0df229SAndreas Gohr     * @param string $html
3068f0df229SAndreas Gohr     * @param int $pos
3078f0df229SAndreas Gohr     * @return Element
3088f0df229SAndreas Gohr     */
3099d01c1d9SSatoshi Sahara    public function addButtonHTML($name, $html, $pos = -1)
3109d01c1d9SSatoshi Sahara    {
3118f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, $html), $pos);
3128f0df229SAndreas Gohr    }
3138f0df229SAndreas Gohr
3148f0df229SAndreas Gohr    /**
315a453c16bSAndreas Gohr     * Adds a label referencing another input element, escapes the label for you
316a453c16bSAndreas Gohr     *
3177ec97767SGerrit Uitslag     * @param string $label
318a453c16bSAndreas Gohr     * @param string $for
319a453c16bSAndreas Gohr     * @param int $pos
320a453c16bSAndreas Gohr     * @return Element
321a453c16bSAndreas Gohr     */
3229d01c1d9SSatoshi Sahara    public function addLabel($label, $for='', $pos = -1)
3239d01c1d9SSatoshi Sahara    {
324a453c16bSAndreas Gohr        return $this->addLabelHTML(hsc($label), $for, $pos);
325a453c16bSAndreas Gohr    }
326a453c16bSAndreas Gohr
327a453c16bSAndreas Gohr    /**
328a453c16bSAndreas Gohr     * Adds a label referencing another input element, allows HTML for content
329a453c16bSAndreas Gohr     *
330a453c16bSAndreas Gohr     * @param string $content
331a453c16bSAndreas Gohr     * @param string|Element $for
332a453c16bSAndreas Gohr     * @param int $pos
333a453c16bSAndreas Gohr     * @return Element
334a453c16bSAndreas Gohr     */
3359d01c1d9SSatoshi Sahara    public function addLabelHTML($content, $for='', $pos = -1)
3369d01c1d9SSatoshi Sahara    {
337a453c16bSAndreas Gohr        $element = new LabelElement(hsc($content));
338a453c16bSAndreas Gohr
339a453c16bSAndreas Gohr        if (is_a($for, '\dokuwiki\Form\Element')) {
340a453c16bSAndreas Gohr            /** @var Element $for */
341a453c16bSAndreas Gohr            $for = $for->id();
342a453c16bSAndreas Gohr        }
343a453c16bSAndreas Gohr        $for = (string) $for;
344a453c16bSAndreas Gohr        if ($for !== '') {
345a453c16bSAndreas Gohr            $element->attr('for', $for);
346a453c16bSAndreas Gohr        }
347a453c16bSAndreas Gohr
348a453c16bSAndreas Gohr        return $this->addElement($element, $pos);
349a453c16bSAndreas Gohr    }
350a453c16bSAndreas Gohr
351a453c16bSAndreas Gohr    /**
352de19515fSAndreas Gohr     * Add fixed HTML to the form
353de19515fSAndreas Gohr     *
3547ec97767SGerrit Uitslag     * @param string $html
355de19515fSAndreas Gohr     * @param int $pos
35664744a10SAndreas Gohr     * @return HTMLElement
357de19515fSAndreas Gohr     */
3589d01c1d9SSatoshi Sahara    public function addHTML($html, $pos = -1)
3599d01c1d9SSatoshi Sahara    {
360de19515fSAndreas Gohr        return $this->addElement(new HTMLElement($html), $pos);
361de19515fSAndreas Gohr    }
362de19515fSAndreas Gohr
36364744a10SAndreas Gohr    /**
36464744a10SAndreas Gohr     * Add a closed HTML tag to the form
36564744a10SAndreas Gohr     *
3667ec97767SGerrit Uitslag     * @param string $tag
36764744a10SAndreas Gohr     * @param int $pos
36864744a10SAndreas Gohr     * @return TagElement
36964744a10SAndreas Gohr     */
3709d01c1d9SSatoshi Sahara    public function addTag($tag, $pos = -1)
3719d01c1d9SSatoshi Sahara    {
37264744a10SAndreas Gohr        return $this->addElement(new TagElement($tag), $pos);
37364744a10SAndreas Gohr    }
37464744a10SAndreas Gohr
37564744a10SAndreas Gohr    /**
37664744a10SAndreas Gohr     * Add an open HTML tag to the form
37764744a10SAndreas Gohr     *
37864744a10SAndreas Gohr     * Be sure to close it again!
37964744a10SAndreas Gohr     *
3807ec97767SGerrit Uitslag     * @param string $tag
38164744a10SAndreas Gohr     * @param int $pos
38264744a10SAndreas Gohr     * @return TagOpenElement
38364744a10SAndreas Gohr     */
3849d01c1d9SSatoshi Sahara    public function addTagOpen($tag, $pos = -1)
3859d01c1d9SSatoshi Sahara    {
38664744a10SAndreas Gohr        return $this->addElement(new TagOpenElement($tag), $pos);
38764744a10SAndreas Gohr    }
38864744a10SAndreas Gohr
38964744a10SAndreas Gohr    /**
39064744a10SAndreas Gohr     * Add a closing HTML tag to the form
39164744a10SAndreas Gohr     *
39264744a10SAndreas Gohr     * Be sure it had been opened before
39364744a10SAndreas Gohr     *
3947ec97767SGerrit Uitslag     * @param string $tag
39564744a10SAndreas Gohr     * @param int $pos
39664744a10SAndreas Gohr     * @return TagCloseElement
39764744a10SAndreas Gohr     */
3989d01c1d9SSatoshi Sahara    public function addTagClose($tag, $pos = -1)
3999d01c1d9SSatoshi Sahara    {
40064744a10SAndreas Gohr        return $this->addElement(new TagCloseElement($tag), $pos);
40164744a10SAndreas Gohr    }
40264744a10SAndreas Gohr
40364744a10SAndreas Gohr    /**
40464744a10SAndreas Gohr     * Open a Fieldset
40564744a10SAndreas Gohr     *
4067ec97767SGerrit Uitslag     * @param string $legend
40764744a10SAndreas Gohr     * @param int $pos
40864744a10SAndreas Gohr     * @return FieldsetOpenElement
40964744a10SAndreas Gohr     */
4109d01c1d9SSatoshi Sahara    public function addFieldsetOpen($legend = '', $pos = -1)
4119d01c1d9SSatoshi Sahara    {
41264744a10SAndreas Gohr        return $this->addElement(new FieldsetOpenElement($legend), $pos);
41364744a10SAndreas Gohr    }
41464744a10SAndreas Gohr
41564744a10SAndreas Gohr    /**
41664744a10SAndreas Gohr     * Close a fieldset
41764744a10SAndreas Gohr     *
41864744a10SAndreas Gohr     * @param int $pos
41964744a10SAndreas Gohr     * @return TagCloseElement
42064744a10SAndreas Gohr     */
4219d01c1d9SSatoshi Sahara    public function addFieldsetClose($pos = -1)
4229d01c1d9SSatoshi Sahara    {
42364744a10SAndreas Gohr        return $this->addElement(new FieldsetCloseElement(), $pos);
42464744a10SAndreas Gohr    }
42564744a10SAndreas Gohr
42664744a10SAndreas Gohr    #endregion
42764744a10SAndreas Gohr
4281f5d8b65SAndreas Gohr    /**
4291f5d8b65SAndreas Gohr     * Adjust the elements so that fieldset open and closes are matching
4301f5d8b65SAndreas Gohr     */
4319d01c1d9SSatoshi Sahara    protected function balanceFieldsets()
4329d01c1d9SSatoshi Sahara    {
4331f5d8b65SAndreas Gohr        $lastclose = 0;
4341f5d8b65SAndreas Gohr        $isopen = false;
4351f5d8b65SAndreas Gohr        $len = count($this->elements);
4361f5d8b65SAndreas Gohr
4371f5d8b65SAndreas Gohr        for ($pos = 0; $pos < $len; $pos++) {
4381f5d8b65SAndreas Gohr            $type = $this->elements[$pos]->getType();
4391f5d8b65SAndreas Gohr            if ($type == 'fieldsetopen') {
4401f5d8b65SAndreas Gohr                if ($isopen) {
44180b13baaSGerrit Uitslag                    //close previous fieldset
4421f5d8b65SAndreas Gohr                    $this->addFieldsetClose($pos);
4431f5d8b65SAndreas Gohr                    $lastclose = $pos + 1;
4441f5d8b65SAndreas Gohr                    $pos++;
4451f5d8b65SAndreas Gohr                    $len++;
4461f5d8b65SAndreas Gohr                }
4471f5d8b65SAndreas Gohr                $isopen = true;
4481f5d8b65SAndreas Gohr            } elseif ($type == 'fieldsetclose') {
4491f5d8b65SAndreas Gohr                if (!$isopen) {
4501f5d8b65SAndreas Gohr                    // make sure there was a fieldsetopen
4511f5d8b65SAndreas Gohr                    // either right after the last close or at the begining
4521f5d8b65SAndreas Gohr                    $this->addFieldsetOpen('', $lastclose);
4531f5d8b65SAndreas Gohr                    $len++;
4541f5d8b65SAndreas Gohr                    $pos++;
4551f5d8b65SAndreas Gohr                }
4561f5d8b65SAndreas Gohr                $lastclose = $pos;
4571f5d8b65SAndreas Gohr                $isopen = false;
4581f5d8b65SAndreas Gohr            }
4591f5d8b65SAndreas Gohr        }
4601f5d8b65SAndreas Gohr
4611f5d8b65SAndreas Gohr        // close open fieldset at the end
4621f5d8b65SAndreas Gohr        if ($isopen) {
4631f5d8b65SAndreas Gohr            $this->addFieldsetClose();
4641f5d8b65SAndreas Gohr        }
465de19515fSAndreas Gohr    }
466de19515fSAndreas Gohr
467de19515fSAndreas Gohr    /**
46812a4e4d1SAndreas Gohr     * The HTML representation of the whole form
46912a4e4d1SAndreas Gohr     *
470*0dd35558SSatoshi Sahara     * @param string $eventName  (optional) name of the event: HTMLFORM_{$name}_OUTPUT
47112a4e4d1SAndreas Gohr     * @return string
47212a4e4d1SAndreas Gohr     */
473*0dd35558SSatoshi Sahara    public function toHTML($eventName = null)
4749d01c1d9SSatoshi Sahara    {
475de19515fSAndreas Gohr        $this->balanceFieldsets();
476de19515fSAndreas Gohr
477*0dd35558SSatoshi Sahara        // trigger event to provide an opportunity to modify this form
478*0dd35558SSatoshi Sahara        if (isset($eventName)) {
479*0dd35558SSatoshi Sahara            if (!preg_match('/^HTMLFORM_[A-Z]+?_OUTPUT$/', $eventName)) {
480*0dd35558SSatoshi Sahara                $eventName = 'HTMLFORM_'.strtoupper($eventName).'_OUTPUT';
481*0dd35558SSatoshi Sahara            }
482*0dd35558SSatoshi Sahara            Event::createAndTrigger($eventName, $this, null, false);
483*0dd35558SSatoshi Sahara        }
484*0dd35558SSatoshi Sahara
48501e3f2b3SMichael Große        $html = '<form '. buildAttributes($this->attrs()) .'>';
48612a4e4d1SAndreas Gohr
48712a4e4d1SAndreas Gohr        foreach ($this->hidden as $name => $value) {
48801e3f2b3SMichael Große            $html .= '<input type="hidden" name="'. $name .'" value="'. formText($value) .'" />';
48912a4e4d1SAndreas Gohr        }
49012a4e4d1SAndreas Gohr
49112a4e4d1SAndreas Gohr        foreach ($this->elements as $element) {
49201e3f2b3SMichael Große            $html .= $element->toHTML();
49312a4e4d1SAndreas Gohr        }
49412a4e4d1SAndreas Gohr
49501e3f2b3SMichael Große        $html .= '</form>';
49612a4e4d1SAndreas Gohr
49712a4e4d1SAndreas Gohr        return $html;
49812a4e4d1SAndreas Gohr    }
49912a4e4d1SAndreas Gohr}
500