xref: /dokuwiki/inc/Form/Form.php (revision 66e555e237c4348c799bba8c03f1a8a490fcc7d6)
112a4e4d1SAndreas Gohr<?php
29d01c1d9SSatoshi Sahara
312a4e4d1SAndreas Gohrnamespace dokuwiki\Form;
412a4e4d1SAndreas Gohr
5*66e555e2SSatoshi Saharause dokuwiki\Extension\Event;
6*66e555e2SSatoshi Sahara
712a4e4d1SAndreas Gohr/**
812a4e4d1SAndreas Gohr * Class Form
912a4e4d1SAndreas Gohr *
1012a4e4d1SAndreas Gohr * Represents the whole Form. This is what you work on, and add Elements to
1112a4e4d1SAndreas Gohr *
1212a4e4d1SAndreas Gohr * @package dokuwiki\Form
1312a4e4d1SAndreas Gohr */
149d01c1d9SSatoshi Saharaclass Form extends Element
159d01c1d9SSatoshi Sahara{
1612a4e4d1SAndreas Gohr    /**
1712a4e4d1SAndreas Gohr     * @var array name value pairs for hidden values
1812a4e4d1SAndreas Gohr     */
1912a4e4d1SAndreas Gohr    protected $hidden = array();
2012a4e4d1SAndreas Gohr
2112a4e4d1SAndreas Gohr    /**
2212a4e4d1SAndreas Gohr     * @var Element[] the elements of the form
2312a4e4d1SAndreas Gohr     */
2412a4e4d1SAndreas Gohr    protected $elements = array();
2512a4e4d1SAndreas Gohr
2612a4e4d1SAndreas Gohr    /**
2712a4e4d1SAndreas Gohr     * Creates a new, empty form with some default attributes
2812a4e4d1SAndreas Gohr     *
2912a4e4d1SAndreas Gohr     * @param array $attributes
307fa270bcSMichael Große     * @param bool  $unsafe     if true, then the security token is ommited
3112a4e4d1SAndreas Gohr     */
329d01c1d9SSatoshi Sahara    public function __construct($attributes = array(), $unsafe = false)
339d01c1d9SSatoshi Sahara    {
3412a4e4d1SAndreas Gohr        global $ID;
3512a4e4d1SAndreas Gohr
3612a4e4d1SAndreas Gohr        parent::__construct('form', $attributes);
3712a4e4d1SAndreas Gohr
3812a4e4d1SAndreas Gohr        // use the current URL as default action
3912a4e4d1SAndreas Gohr        if (!$this->attr('action')) {
4012a4e4d1SAndreas Gohr            $get = $_GET;
4112a4e4d1SAndreas Gohr            if (isset($get['id'])) unset($get['id']);
426d0ceaf9SAndreas Gohr            $self = wl($ID, $get, false, '&'); //attributes are escaped later
4312a4e4d1SAndreas Gohr            $this->attr('action', $self);
4412a4e4d1SAndreas Gohr        }
4512a4e4d1SAndreas Gohr
4612a4e4d1SAndreas Gohr        // post is default
4712a4e4d1SAndreas Gohr        if (!$this->attr('method')) {
4812a4e4d1SAndreas Gohr            $this->attr('method', 'post');
4912a4e4d1SAndreas Gohr        }
5012a4e4d1SAndreas Gohr
5112a4e4d1SAndreas Gohr        // we like UTF-8
5212a4e4d1SAndreas Gohr        if (!$this->attr('accept-charset')) {
5312a4e4d1SAndreas Gohr            $this->attr('accept-charset', 'utf-8');
5412a4e4d1SAndreas Gohr        }
5512a4e4d1SAndreas Gohr
5612a4e4d1SAndreas Gohr        // add the security token by default
577fa270bcSMichael Große        if (!$unsafe) {
5812a4e4d1SAndreas Gohr            $this->setHiddenField('sectok', getSecurityToken());
597fa270bcSMichael Große        }
6012a4e4d1SAndreas Gohr
616d0ceaf9SAndreas Gohr        // identify this as a new form based form in HTML
626d0ceaf9SAndreas Gohr        $this->addClass('doku_form');
6312a4e4d1SAndreas Gohr    }
6412a4e4d1SAndreas Gohr
6512a4e4d1SAndreas Gohr    /**
6612a4e4d1SAndreas Gohr     * Sets a hidden field
6712a4e4d1SAndreas Gohr     *
687ec97767SGerrit Uitslag     * @param string $name
697ec97767SGerrit Uitslag     * @param string $value
7012a4e4d1SAndreas Gohr     * @return $this
7112a4e4d1SAndreas Gohr     */
729d01c1d9SSatoshi Sahara    public function setHiddenField($name, $value)
739d01c1d9SSatoshi Sahara    {
7412a4e4d1SAndreas Gohr        $this->hidden[$name] = $value;
7512a4e4d1SAndreas Gohr        return $this;
7612a4e4d1SAndreas Gohr    }
7712a4e4d1SAndreas Gohr
78ef0c211bSAndreas Gohr    #region element query function
7964744a10SAndreas Gohr
8012a4e4d1SAndreas Gohr    /**
81ef0c211bSAndreas Gohr     * Returns the numbers of elements in the form
82ef0c211bSAndreas Gohr     *
83ef0c211bSAndreas Gohr     * @return int
84ef0c211bSAndreas Gohr     */
859d01c1d9SSatoshi Sahara    public function elementCount()
869d01c1d9SSatoshi Sahara    {
87ef0c211bSAndreas Gohr        return count($this->elements);
88ef0c211bSAndreas Gohr    }
89ef0c211bSAndreas Gohr
90ef0c211bSAndreas Gohr    /**
915facb9bcSMichael Große     * Get the position of the element in the form or false if it is not in the form
925facb9bcSMichael Große     *
9364159a61SAndreas Gohr     * Warning: This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates
9464159a61SAndreas Gohr     * to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the
9564159a61SAndreas Gohr     * return value of this function.
965facb9bcSMichael Große     *
975facb9bcSMichael Große     * @param Element $element
985facb9bcSMichael Große     *
995facb9bcSMichael Große     * @return false|int
1005facb9bcSMichael Große     */
1015facb9bcSMichael Große    public function getElementPosition(Element $element)
1025facb9bcSMichael Große    {
1035facb9bcSMichael Große        return array_search($element, $this->elements, true);
1045facb9bcSMichael Große    }
1055facb9bcSMichael Große
1065facb9bcSMichael Große    /**
107ef0c211bSAndreas Gohr     * Returns a reference to the element at a position.
108ef0c211bSAndreas Gohr     * A position out-of-bounds will return either the
109ef0c211bSAndreas Gohr     * first (underflow) or last (overflow) element.
110ef0c211bSAndreas Gohr     *
1117ec97767SGerrit Uitslag     * @param int $pos
112ef0c211bSAndreas Gohr     * @return Element
113ef0c211bSAndreas Gohr     */
1149d01c1d9SSatoshi Sahara    public function getElementAt($pos)
1159d01c1d9SSatoshi Sahara    {
116ef0c211bSAndreas Gohr        if ($pos < 0) $pos = count($this->elements) + $pos;
117ef0c211bSAndreas Gohr        if ($pos < 0) $pos = 0;
118ef0c211bSAndreas Gohr        if ($pos >= count($this->elements)) $pos = count($this->elements) - 1;
119ef0c211bSAndreas Gohr        return $this->elements[$pos];
120ef0c211bSAndreas Gohr    }
121ef0c211bSAndreas Gohr
122ef0c211bSAndreas Gohr    /**
123ef0c211bSAndreas Gohr     * Gets the position of the first of a type of element
124ef0c211bSAndreas Gohr     *
125ef0c211bSAndreas Gohr     * @param string $type Element type to look for.
126ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
127ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
128ef0c211bSAndreas Gohr     */
1299d01c1d9SSatoshi Sahara    public function findPositionByType($type, $offset = 0)
1309d01c1d9SSatoshi Sahara    {
131ef0c211bSAndreas Gohr        $len = $this->elementCount();
132ef0c211bSAndreas Gohr        for ($pos = $offset; $pos < $len; $pos++) {
133ef0c211bSAndreas Gohr            if ($this->elements[$pos]->getType() == $type) {
134ef0c211bSAndreas Gohr                return $pos;
135ef0c211bSAndreas Gohr            }
136ef0c211bSAndreas Gohr        }
137ef0c211bSAndreas Gohr        return false;
138ef0c211bSAndreas Gohr    }
139ef0c211bSAndreas Gohr
140ef0c211bSAndreas Gohr    /**
141ef0c211bSAndreas Gohr     * Gets the position of the first element matching the attribute
142ef0c211bSAndreas Gohr     *
143ef0c211bSAndreas Gohr     * @param string $name Name of the attribute
144ef0c211bSAndreas Gohr     * @param string $value Value the attribute should have
145ef0c211bSAndreas Gohr     * @param int $offset search from this position onward
146ef0c211bSAndreas Gohr     * @return false|int position of element if found, otherwise false
147ef0c211bSAndreas Gohr     */
1489d01c1d9SSatoshi Sahara    public function findPositionByAttribute($name, $value, $offset = 0)
1499d01c1d9SSatoshi Sahara    {
150ef0c211bSAndreas Gohr        $len = $this->elementCount();
151ef0c211bSAndreas Gohr        for ($pos = $offset; $pos < $len; $pos++) {
152ef0c211bSAndreas Gohr            if ($this->elements[$pos]->attr($name) == $value) {
153ef0c211bSAndreas Gohr                return $pos;
154ef0c211bSAndreas Gohr            }
155ef0c211bSAndreas Gohr        }
156ef0c211bSAndreas Gohr        return false;
157ef0c211bSAndreas Gohr    }
158ef0c211bSAndreas Gohr
159ef0c211bSAndreas Gohr    #endregion
160ef0c211bSAndreas Gohr
161ef0c211bSAndreas Gohr    #region Element positioning functions
162ef0c211bSAndreas Gohr
163ef0c211bSAndreas Gohr    /**
164ef0c211bSAndreas Gohr     * Adds or inserts an element to the form
16512a4e4d1SAndreas Gohr     *
16612a4e4d1SAndreas Gohr     * @param Element $element
16712a4e4d1SAndreas Gohr     * @param int $pos 0-based position in the form, -1 for at the end
16812a4e4d1SAndreas Gohr     * @return Element
16912a4e4d1SAndreas Gohr     */
1709d01c1d9SSatoshi Sahara    public function addElement(Element $element, $pos = -1)
1719d01c1d9SSatoshi Sahara    {
17264159a61SAndreas Gohr        if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
17364159a61SAndreas Gohr            'You can\'t add a form to a form'
17464159a61SAndreas Gohr        );
17512a4e4d1SAndreas Gohr        if ($pos < 0) {
17612a4e4d1SAndreas Gohr            $this->elements[] = $element;
17712a4e4d1SAndreas Gohr        } else {
17812a4e4d1SAndreas Gohr            array_splice($this->elements, $pos, 0, array($element));
17912a4e4d1SAndreas Gohr        }
18012a4e4d1SAndreas Gohr        return $element;
18112a4e4d1SAndreas Gohr    }
18212a4e4d1SAndreas Gohr
18312a4e4d1SAndreas Gohr    /**
184ef0c211bSAndreas Gohr     * Replaces an existing element with a new one
185ef0c211bSAndreas Gohr     *
186ef0c211bSAndreas Gohr     * @param Element $element the new element
1877ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to replace
188ef0c211bSAndreas Gohr     */
1899d01c1d9SSatoshi Sahara    public function replaceElement(Element $element, $pos)
1909d01c1d9SSatoshi Sahara    {
19164159a61SAndreas Gohr        if (is_a($element, '\dokuwiki\Form\Form')) throw new \InvalidArgumentException(
19264159a61SAndreas Gohr            'You can\'t add a form to a form'
19364159a61SAndreas Gohr        );
194ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1, array($element));
195ef0c211bSAndreas Gohr    }
196ef0c211bSAndreas Gohr
197ef0c211bSAndreas Gohr    /**
198ef0c211bSAndreas Gohr     * Remove an element from the form completely
199ef0c211bSAndreas Gohr     *
2007ec97767SGerrit Uitslag     * @param int $pos 0-based position of the element to remove
201ef0c211bSAndreas Gohr     */
2029d01c1d9SSatoshi Sahara    public function removeElement($pos)
2039d01c1d9SSatoshi Sahara    {
204ef0c211bSAndreas Gohr        array_splice($this->elements, $pos, 1);
205ef0c211bSAndreas Gohr    }
206ef0c211bSAndreas Gohr
207ef0c211bSAndreas Gohr    #endregion
208ef0c211bSAndreas Gohr
209ef0c211bSAndreas Gohr    #region Element adding functions
210ef0c211bSAndreas Gohr
211ef0c211bSAndreas Gohr    /**
21212a4e4d1SAndreas Gohr     * Adds a text input field
21312a4e4d1SAndreas Gohr     *
2147ec97767SGerrit Uitslag     * @param string $name
2157ec97767SGerrit Uitslag     * @param string $label
21612a4e4d1SAndreas Gohr     * @param int $pos
21712a4e4d1SAndreas Gohr     * @return InputElement
21812a4e4d1SAndreas Gohr     */
2199d01c1d9SSatoshi Sahara    public function addTextInput($name, $label = '', $pos = -1)
2209d01c1d9SSatoshi Sahara    {
22112a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('text', $name, $label), $pos);
22212a4e4d1SAndreas Gohr    }
22312a4e4d1SAndreas Gohr
22412a4e4d1SAndreas Gohr    /**
22512a4e4d1SAndreas Gohr     * Adds a password input field
22612a4e4d1SAndreas Gohr     *
2277ec97767SGerrit Uitslag     * @param string $name
2287ec97767SGerrit Uitslag     * @param string $label
22912a4e4d1SAndreas Gohr     * @param int $pos
23012a4e4d1SAndreas Gohr     * @return InputElement
23112a4e4d1SAndreas Gohr     */
2329d01c1d9SSatoshi Sahara    public function addPasswordInput($name, $label = '', $pos = -1)
2339d01c1d9SSatoshi Sahara    {
23412a4e4d1SAndreas Gohr        return $this->addElement(new InputElement('password', $name, $label), $pos);
23512a4e4d1SAndreas Gohr    }
23612a4e4d1SAndreas Gohr
23712a4e4d1SAndreas Gohr    /**
23812a4e4d1SAndreas Gohr     * Adds a radio button field
23912a4e4d1SAndreas Gohr     *
2407ec97767SGerrit Uitslag     * @param string $name
2417ec97767SGerrit Uitslag     * @param string $label
24212a4e4d1SAndreas Gohr     * @param int $pos
24312a4e4d1SAndreas Gohr     * @return CheckableElement
24412a4e4d1SAndreas Gohr     */
2459d01c1d9SSatoshi Sahara    public function addRadioButton($name, $label = '', $pos = -1)
2469d01c1d9SSatoshi Sahara    {
24712a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('radio', $name, $label), $pos);
24812a4e4d1SAndreas Gohr    }
24912a4e4d1SAndreas Gohr
25012a4e4d1SAndreas Gohr    /**
25112a4e4d1SAndreas Gohr     * Adds a checkbox field
25212a4e4d1SAndreas Gohr     *
2537ec97767SGerrit Uitslag     * @param string $name
2547ec97767SGerrit Uitslag     * @param string $label
25512a4e4d1SAndreas Gohr     * @param int $pos
25612a4e4d1SAndreas Gohr     * @return CheckableElement
25712a4e4d1SAndreas Gohr     */
2589d01c1d9SSatoshi Sahara    public function addCheckbox($name, $label = '', $pos = -1)
2599d01c1d9SSatoshi Sahara    {
26012a4e4d1SAndreas Gohr        return $this->addElement(new CheckableElement('checkbox', $name, $label), $pos);
26112a4e4d1SAndreas Gohr    }
26212a4e4d1SAndreas Gohr
26312a4e4d1SAndreas Gohr    /**
2648638ead5SAndreas Gohr     * Adds a dropdown field
2658638ead5SAndreas Gohr     *
2667ec97767SGerrit Uitslag     * @param string $name
2678638ead5SAndreas Gohr     * @param array $options
2688638ead5SAndreas Gohr     * @param string $label
2698638ead5SAndreas Gohr     * @param int $pos
2708638ead5SAndreas Gohr     * @return DropdownElement
2718638ead5SAndreas Gohr     */
2729d01c1d9SSatoshi Sahara    public function addDropdown($name, $options, $label = '', $pos = -1)
2739d01c1d9SSatoshi Sahara    {
2748638ead5SAndreas Gohr        return $this->addElement(new DropdownElement($name, $options, $label), $pos);
2758638ead5SAndreas Gohr    }
2768638ead5SAndreas Gohr
2778638ead5SAndreas Gohr    /**
27812a4e4d1SAndreas Gohr     * Adds a textarea field
27912a4e4d1SAndreas Gohr     *
2807ec97767SGerrit Uitslag     * @param string $name
2817ec97767SGerrit Uitslag     * @param string $label
28212a4e4d1SAndreas Gohr     * @param int $pos
28312a4e4d1SAndreas Gohr     * @return TextareaElement
28412a4e4d1SAndreas Gohr     */
2859d01c1d9SSatoshi Sahara    public function addTextarea($name, $label = '', $pos = -1)
2869d01c1d9SSatoshi Sahara    {
28712a4e4d1SAndreas Gohr        return $this->addElement(new TextareaElement($name, $label), $pos);
28812a4e4d1SAndreas Gohr    }
28912a4e4d1SAndreas Gohr
29012a4e4d1SAndreas Gohr    /**
2918f0df229SAndreas Gohr     * Adds a simple button, escapes the content for you
2928f0df229SAndreas Gohr     *
2938f0df229SAndreas Gohr     * @param string $name
2948f0df229SAndreas Gohr     * @param string $content
2958f0df229SAndreas Gohr     * @param int $pos
2968f0df229SAndreas Gohr     * @return Element
2978f0df229SAndreas Gohr     */
2989d01c1d9SSatoshi Sahara    public function addButton($name, $content, $pos = -1)
2999d01c1d9SSatoshi Sahara    {
3008f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, hsc($content)), $pos);
3018f0df229SAndreas Gohr    }
3028f0df229SAndreas Gohr
3038f0df229SAndreas Gohr    /**
3048f0df229SAndreas Gohr     * Adds a simple button, allows HTML for content
3058f0df229SAndreas Gohr     *
3068f0df229SAndreas Gohr     * @param string $name
3078f0df229SAndreas Gohr     * @param string $html
3088f0df229SAndreas Gohr     * @param int $pos
3098f0df229SAndreas Gohr     * @return Element
3108f0df229SAndreas Gohr     */
3119d01c1d9SSatoshi Sahara    public function addButtonHTML($name, $html, $pos = -1)
3129d01c1d9SSatoshi Sahara    {
3138f0df229SAndreas Gohr        return $this->addElement(new ButtonElement($name, $html), $pos);
3148f0df229SAndreas Gohr    }
3158f0df229SAndreas Gohr
3168f0df229SAndreas Gohr    /**
317a453c16bSAndreas Gohr     * Adds a label referencing another input element, escapes the label for you
318a453c16bSAndreas Gohr     *
3197ec97767SGerrit Uitslag     * @param string $label
320a453c16bSAndreas Gohr     * @param string $for
321a453c16bSAndreas Gohr     * @param int $pos
322a453c16bSAndreas Gohr     * @return Element
323a453c16bSAndreas Gohr     */
3249d01c1d9SSatoshi Sahara    public function addLabel($label, $for='', $pos = -1)
3259d01c1d9SSatoshi Sahara    {
326a453c16bSAndreas Gohr        return $this->addLabelHTML(hsc($label), $for, $pos);
327a453c16bSAndreas Gohr    }
328a453c16bSAndreas Gohr
329a453c16bSAndreas Gohr    /**
330a453c16bSAndreas Gohr     * Adds a label referencing another input element, allows HTML for content
331a453c16bSAndreas Gohr     *
332a453c16bSAndreas Gohr     * @param string $content
333a453c16bSAndreas Gohr     * @param string|Element $for
334a453c16bSAndreas Gohr     * @param int $pos
335a453c16bSAndreas Gohr     * @return Element
336a453c16bSAndreas Gohr     */
3379d01c1d9SSatoshi Sahara    public function addLabelHTML($content, $for='', $pos = -1)
3389d01c1d9SSatoshi Sahara    {
339a453c16bSAndreas Gohr        $element = new LabelElement(hsc($content));
340a453c16bSAndreas Gohr
341a453c16bSAndreas Gohr        if (is_a($for, '\dokuwiki\Form\Element')) {
342a453c16bSAndreas Gohr            /** @var Element $for */
343a453c16bSAndreas Gohr            $for = $for->id();
344a453c16bSAndreas Gohr        }
345a453c16bSAndreas Gohr        $for = (string) $for;
346a453c16bSAndreas Gohr        if ($for !== '') {
347a453c16bSAndreas Gohr            $element->attr('for', $for);
348a453c16bSAndreas Gohr        }
349a453c16bSAndreas Gohr
350a453c16bSAndreas Gohr        return $this->addElement($element, $pos);
351a453c16bSAndreas Gohr    }
352a453c16bSAndreas Gohr
353a453c16bSAndreas Gohr    /**
354de19515fSAndreas Gohr     * Add fixed HTML to the form
355de19515fSAndreas Gohr     *
3567ec97767SGerrit Uitslag     * @param string $html
357de19515fSAndreas Gohr     * @param int $pos
35864744a10SAndreas Gohr     * @return HTMLElement
359de19515fSAndreas Gohr     */
3609d01c1d9SSatoshi Sahara    public function addHTML($html, $pos = -1)
3619d01c1d9SSatoshi Sahara    {
362de19515fSAndreas Gohr        return $this->addElement(new HTMLElement($html), $pos);
363de19515fSAndreas Gohr    }
364de19515fSAndreas Gohr
36564744a10SAndreas Gohr    /**
36664744a10SAndreas Gohr     * Add a closed HTML tag to the form
36764744a10SAndreas Gohr     *
3687ec97767SGerrit Uitslag     * @param string $tag
36964744a10SAndreas Gohr     * @param int $pos
37064744a10SAndreas Gohr     * @return TagElement
37164744a10SAndreas Gohr     */
3729d01c1d9SSatoshi Sahara    public function addTag($tag, $pos = -1)
3739d01c1d9SSatoshi Sahara    {
37464744a10SAndreas Gohr        return $this->addElement(new TagElement($tag), $pos);
37564744a10SAndreas Gohr    }
37664744a10SAndreas Gohr
37764744a10SAndreas Gohr    /**
37864744a10SAndreas Gohr     * Add an open HTML tag to the form
37964744a10SAndreas Gohr     *
38064744a10SAndreas Gohr     * Be sure to close it again!
38164744a10SAndreas Gohr     *
3827ec97767SGerrit Uitslag     * @param string $tag
38364744a10SAndreas Gohr     * @param int $pos
38464744a10SAndreas Gohr     * @return TagOpenElement
38564744a10SAndreas Gohr     */
3869d01c1d9SSatoshi Sahara    public function addTagOpen($tag, $pos = -1)
3879d01c1d9SSatoshi Sahara    {
38864744a10SAndreas Gohr        return $this->addElement(new TagOpenElement($tag), $pos);
38964744a10SAndreas Gohr    }
39064744a10SAndreas Gohr
39164744a10SAndreas Gohr    /**
39264744a10SAndreas Gohr     * Add a closing HTML tag to the form
39364744a10SAndreas Gohr     *
39464744a10SAndreas Gohr     * Be sure it had been opened before
39564744a10SAndreas Gohr     *
3967ec97767SGerrit Uitslag     * @param string $tag
39764744a10SAndreas Gohr     * @param int $pos
39864744a10SAndreas Gohr     * @return TagCloseElement
39964744a10SAndreas Gohr     */
4009d01c1d9SSatoshi Sahara    public function addTagClose($tag, $pos = -1)
4019d01c1d9SSatoshi Sahara    {
40264744a10SAndreas Gohr        return $this->addElement(new TagCloseElement($tag), $pos);
40364744a10SAndreas Gohr    }
40464744a10SAndreas Gohr
40564744a10SAndreas Gohr    /**
40664744a10SAndreas Gohr     * Open a Fieldset
40764744a10SAndreas Gohr     *
4087ec97767SGerrit Uitslag     * @param string $legend
40964744a10SAndreas Gohr     * @param int $pos
41064744a10SAndreas Gohr     * @return FieldsetOpenElement
41164744a10SAndreas Gohr     */
4129d01c1d9SSatoshi Sahara    public function addFieldsetOpen($legend = '', $pos = -1)
4139d01c1d9SSatoshi Sahara    {
41464744a10SAndreas Gohr        return $this->addElement(new FieldsetOpenElement($legend), $pos);
41564744a10SAndreas Gohr    }
41664744a10SAndreas Gohr
41764744a10SAndreas Gohr    /**
41864744a10SAndreas Gohr     * Close a fieldset
41964744a10SAndreas Gohr     *
42064744a10SAndreas Gohr     * @param int $pos
42164744a10SAndreas Gohr     * @return TagCloseElement
42264744a10SAndreas Gohr     */
4239d01c1d9SSatoshi Sahara    public function addFieldsetClose($pos = -1)
4249d01c1d9SSatoshi Sahara    {
42564744a10SAndreas Gohr        return $this->addElement(new FieldsetCloseElement(), $pos);
42664744a10SAndreas Gohr    }
42764744a10SAndreas Gohr
42864744a10SAndreas Gohr    #endregion
42964744a10SAndreas Gohr
4301f5d8b65SAndreas Gohr    /**
4311f5d8b65SAndreas Gohr     * Adjust the elements so that fieldset open and closes are matching
4321f5d8b65SAndreas Gohr     */
4339d01c1d9SSatoshi Sahara    protected function balanceFieldsets()
4349d01c1d9SSatoshi Sahara    {
4351f5d8b65SAndreas Gohr        $lastclose = 0;
4361f5d8b65SAndreas Gohr        $isopen = false;
4371f5d8b65SAndreas Gohr        $len = count($this->elements);
4381f5d8b65SAndreas Gohr
4391f5d8b65SAndreas Gohr        for ($pos = 0; $pos < $len; $pos++) {
4401f5d8b65SAndreas Gohr            $type = $this->elements[$pos]->getType();
4411f5d8b65SAndreas Gohr            if ($type == 'fieldsetopen') {
4421f5d8b65SAndreas Gohr                if ($isopen) {
44380b13baaSGerrit Uitslag                    //close previous fieldset
4441f5d8b65SAndreas Gohr                    $this->addFieldsetClose($pos);
4451f5d8b65SAndreas Gohr                    $lastclose = $pos + 1;
4461f5d8b65SAndreas Gohr                    $pos++;
4471f5d8b65SAndreas Gohr                    $len++;
4481f5d8b65SAndreas Gohr                }
4491f5d8b65SAndreas Gohr                $isopen = true;
4501f5d8b65SAndreas Gohr            } elseif ($type == 'fieldsetclose') {
4511f5d8b65SAndreas Gohr                if (!$isopen) {
4521f5d8b65SAndreas Gohr                    // make sure there was a fieldsetopen
4531f5d8b65SAndreas Gohr                    // either right after the last close or at the begining
4541f5d8b65SAndreas Gohr                    $this->addFieldsetOpen('', $lastclose);
4551f5d8b65SAndreas Gohr                    $len++;
4561f5d8b65SAndreas Gohr                    $pos++;
4571f5d8b65SAndreas Gohr                }
4581f5d8b65SAndreas Gohr                $lastclose = $pos;
4591f5d8b65SAndreas Gohr                $isopen = false;
4601f5d8b65SAndreas Gohr            }
4611f5d8b65SAndreas Gohr        }
4621f5d8b65SAndreas Gohr
4631f5d8b65SAndreas Gohr        // close open fieldset at the end
4641f5d8b65SAndreas Gohr        if ($isopen) {
4651f5d8b65SAndreas Gohr            $this->addFieldsetClose();
4661f5d8b65SAndreas Gohr        }
467de19515fSAndreas Gohr    }
468de19515fSAndreas Gohr
469de19515fSAndreas Gohr    /**
47012a4e4d1SAndreas Gohr     * The HTML representation of the whole form
47112a4e4d1SAndreas Gohr     *
4720dd35558SSatoshi Sahara     * @param string $eventName  (optional) name of the event: HTMLFORM_{$name}_OUTPUT
47312a4e4d1SAndreas Gohr     * @return string
47412a4e4d1SAndreas Gohr     */
4750dd35558SSatoshi Sahara    public function toHTML($eventName = null)
4769d01c1d9SSatoshi Sahara    {
477de19515fSAndreas Gohr        $this->balanceFieldsets();
478de19515fSAndreas Gohr
4790dd35558SSatoshi Sahara        // trigger event to provide an opportunity to modify this form
4800dd35558SSatoshi Sahara        if (isset($eventName)) {
4810dd35558SSatoshi Sahara            if (!preg_match('/^HTMLFORM_[A-Z]+?_OUTPUT$/', $eventName)) {
4820dd35558SSatoshi Sahara                $eventName = 'HTMLFORM_'.strtoupper($eventName).'_OUTPUT';
4830dd35558SSatoshi Sahara            }
4840dd35558SSatoshi Sahara            Event::createAndTrigger($eventName, $this, null, false);
4850dd35558SSatoshi Sahara        }
4860dd35558SSatoshi Sahara
48701e3f2b3SMichael Große        $html = '<form '. buildAttributes($this->attrs()) .'>';
48812a4e4d1SAndreas Gohr
48912a4e4d1SAndreas Gohr        foreach ($this->hidden as $name => $value) {
49001e3f2b3SMichael Große            $html .= '<input type="hidden" name="'. $name .'" value="'. formText($value) .'" />';
49112a4e4d1SAndreas Gohr        }
49212a4e4d1SAndreas Gohr
49312a4e4d1SAndreas Gohr        foreach ($this->elements as $element) {
49401e3f2b3SMichael Große            $html .= $element->toHTML();
49512a4e4d1SAndreas Gohr        }
49612a4e4d1SAndreas Gohr
49701e3f2b3SMichael Große        $html .= '</form>';
49812a4e4d1SAndreas Gohr
49912a4e4d1SAndreas Gohr        return $html;
50012a4e4d1SAndreas Gohr    }
50112a4e4d1SAndreas Gohr}
502