1<?php
2/**
3 * DokuWiki XHTML Form
4 *
5 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6 * @author     Tom N Harris <tnharris@whoopdedo.org>
7 */
8
9// phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
10// phpcs:disable PSR2.Classes.PropertyDeclaration.Underscore
11
12
13/**
14 * Class for creating simple HTML forms.
15 *
16 * The forms is built from a list of pseudo-tags (arrays with expected keys).
17 * Every pseudo-tag must have the key '_elem' set to the name of the element.
18 * When printed, the form class calls functions named 'form_$type' for each
19 * element it contains.
20 *
21 * Standard practice is for non-attribute keys in a pseudo-element to start
22 * with '_'. Other keys are HTML attributes that will be included in the element
23 * tag. That way, the element output functions can pass the pseudo-element
24 * directly to buildAttributes.
25 *
26 * See the form_make* functions later in this file.
27 *
28 * Please note that even though this class is technically deprecated (use dokuwiki\Form instead),
29 * it is still widely used in the core and the related form events. Until those have been rewritten,
30 * this will continue to be used
31 *
32 * @deprecated 2019-07-14
33 * @author Tom N Harris <tnharris@whoopdedo.org>
34 */
35class Doku_Form {
36
37    // Form id attribute
38    public $params = array();
39
40    // Draw a border around form fields.
41    // Adds <fieldset></fieldset> around the elements
42    public $_infieldset = false;
43
44    // Hidden form fields.
45    public $_hidden = array();
46
47    // Array of pseudo-tags
48    public $_content = array();
49
50    /**
51     * Constructor
52     *
53     * Sets parameters and autoadds a security token. The old calling convention
54     * with up to four parameters is deprecated, instead the first parameter
55     * should be an array with parameters.
56     *
57     * @param mixed       $params  Parameters for the HTML form element; Using the deprecated
58     *                             calling convention this is the ID attribute of the form
59     * @param bool|string $action  (optional, deprecated) submit URL, defaults to current page
60     * @param bool|string $method  (optional, deprecated) 'POST' or 'GET', default is POST
61     * @param bool|string $enctype (optional, deprecated) Encoding type of the data
62     *
63     * @author  Tom N Harris <tnharris@whoopdedo.org>
64     */
65    public function __construct($params, $action=false, $method=false, $enctype=false) {
66        if(!is_array($params)) {
67            $this->params = array('id' => $params);
68            if ($action !== false) $this->params['action'] = $action;
69            if ($method !== false) $this->params['method'] = strtolower($method);
70            if ($enctype !== false) $this->params['enctype'] = $enctype;
71        } else {
72            $this->params = $params;
73        }
74
75        if (!isset($this->params['method'])) {
76            $this->params['method'] = 'post';
77        } else {
78            $this->params['method'] = strtolower($this->params['method']);
79        }
80
81        if (!isset($this->params['action'])) {
82            $this->params['action'] = '';
83        }
84
85        $this->addHidden('sectok', getSecurityToken());
86    }
87
88    /**
89     * startFieldset
90     *
91     * Add <fieldset></fieldset> tags around fields.
92     * Usually results in a border drawn around the form.
93     *
94     * @param   string  $legend Label that will be printed with the border.
95     *
96     * @author  Tom N Harris <tnharris@whoopdedo.org>
97     */
98    public function startFieldset($legend) {
99        if ($this->_infieldset) {
100            $this->addElement(array('_elem'=>'closefieldset'));
101        }
102        $this->addElement(array('_elem'=>'openfieldset', '_legend'=>$legend));
103        $this->_infieldset = true;
104    }
105
106    /**
107     * endFieldset
108     *
109     * @author  Tom N Harris <tnharris@whoopdedo.org>
110     */
111    public function endFieldset() {
112        if ($this->_infieldset) {
113            $this->addElement(array('_elem'=>'closefieldset'));
114        }
115        $this->_infieldset = false;
116    }
117
118    /**
119     * addHidden
120     *
121     * Adds a name/value pair as a hidden field.
122     * The value of the field (but not the name) will be passed to
123     * formText() before printing.
124     *
125     * @param   string  $name   Field name.
126     * @param   string  $value  Field value. If null, remove a previously added field.
127     *
128     * @author  Tom N Harris <tnharris@whoopdedo.org>
129     */
130    public function addHidden($name, $value) {
131        if (is_null($value))
132            unset($this->_hidden[$name]);
133        else
134            $this->_hidden[$name] = $value;
135    }
136
137    /**
138     * addElement
139     *
140     * Appends a content element to the form.
141     * The element can be either a pseudo-tag or string.
142     * If string, it is printed without escaping special chars.   *
143     *
144     * @param   string|array  $elem   Pseudo-tag or string to add to the form.
145     *
146     * @author  Tom N Harris <tnharris@whoopdedo.org>
147     */
148    public function addElement($elem) {
149        $this->_content[] = $elem;
150    }
151
152    /**
153     * insertElement
154     *
155     * Inserts a content element at a position.
156     *
157     * @param   string       $pos  0-based index where the element will be inserted.
158     * @param   string|array $elem Pseudo-tag or string to add to the form.
159     *
160     * @author  Tom N Harris <tnharris@whoopdedo.org>
161     */
162    public function insertElement($pos, $elem) {
163        array_splice($this->_content, $pos, 0, array($elem));
164    }
165
166    /**
167     * replaceElement
168     *
169     * Replace with NULL to remove an element.
170     *
171     * @param   int          $pos  0-based index the element will be placed at.
172     * @param   string|array $elem Pseudo-tag or string to add to the form.
173     *
174     * @author  Tom N Harris <tnharris@whoopdedo.org>
175     */
176    public function replaceElement($pos, $elem) {
177        $rep = array();
178        if (!is_null($elem)) $rep[] = $elem;
179        array_splice($this->_content, $pos, 1, $rep);
180    }
181
182    /**
183     * findElementByType
184     *
185     * Gets the position of the first of a type of element.
186     *
187     * @param   string  $type   Element type to look for.
188     * @return  int|false     position of element if found, otherwise false
189     *
190     * @author  Tom N Harris <tnharris@whoopdedo.org>
191     */
192    public function findElementByType($type) {
193        foreach ($this->_content as $pos=>$elem) {
194            if (is_array($elem) && $elem['_elem'] == $type)
195                return $pos;
196        }
197        return false;
198    }
199
200    /**
201     * findElementById
202     *
203     * Gets the position of the element with an ID attribute.
204     *
205     * @param   string  $id     ID of the element to find.
206     * @return  int|false     position of element if found, otherwise false
207     *
208     * @author  Tom N Harris <tnharris@whoopdedo.org>
209     */
210    public function findElementById($id) {
211        foreach ($this->_content as $pos=>$elem) {
212            if (is_array($elem) && isset($elem['id']) && $elem['id'] == $id)
213                return $pos;
214        }
215        return false;
216    }
217
218    /**
219     * findElementByAttribute
220     *
221     * Gets the position of the first element with a matching attribute value.
222     *
223     * @param   string  $name   Attribute name.
224     * @param   string  $value  Attribute value.
225     * @return  int|false     position of element if found, otherwise false
226     *
227     * @author  Tom N Harris <tnharris@whoopdedo.org>
228     */
229    public function findElementByAttribute($name, $value) {
230        foreach ($this->_content as $pos=>$elem) {
231            if (is_array($elem) && isset($elem[$name]) && $elem[$name] == $value)
232                return $pos;
233        }
234        return false;
235    }
236
237    /**
238     * getElementAt
239     *
240     * Returns a reference to the element at a position.
241     * A position out-of-bounds will return either the
242     * first (underflow) or last (overflow) element.
243     *
244     * @param   int     $pos    0-based index
245     * @return  array reference  pseudo-element
246     *
247     * @author  Tom N Harris <tnharris@whoopdedo.org>
248     */
249    public function &getElementAt($pos) {
250        if ($pos < 0) $pos = count($this->_content) + $pos;
251        if ($pos < 0) $pos = 0;
252        if ($pos >= count($this->_content)) $pos = count($this->_content) - 1;
253        return $this->_content[$pos];
254    }
255
256    /**
257     * Return the assembled HTML for the form.
258     *
259     * Each element in the form will be passed to a function named
260     * 'form_$type'. The function should return the HTML to be printed.
261     *
262     * @author  Tom N Harris <tnharris@whoopdedo.org>
263     *
264     * @return string html of the form
265     */
266    public function getForm() {
267        global $lang;
268        $form = '';
269        $this->params['accept-charset'] = $lang['encoding'];
270        $form .= '<form ' . buildAttributes($this->params,false) . '><div class="no">' . DOKU_LF;
271        if (!empty($this->_hidden)) {
272            foreach ($this->_hidden as $name=>$value)
273                $form .= form_hidden(array('name'=>$name, 'value'=>$value));
274        }
275        foreach ($this->_content as $element) {
276            if (is_array($element)) {
277                $elem_type = $element['_elem'];
278                if (function_exists('form_'.$elem_type)) {
279                    $form .= call_user_func('form_'.$elem_type, $element).DOKU_LF;
280                }
281            } else {
282                $form .= $element;
283            }
284        }
285        if ($this->_infieldset) $form .= form_closefieldset().DOKU_LF;
286        $form .= '</div></form>'.DOKU_LF;
287
288        return $form;
289    }
290
291    /**
292     * Print the assembled form
293     *
294     * wraps around getForm()
295     */
296    public function printForm(){
297        echo $this->getForm();
298    }
299
300    /**
301     * Add a radio set
302     *
303     * This function adds a set of radio buttons to the form. If $_POST[$name]
304     * is set, this radio is preselected, else the first radio button.
305     *
306     * @param string    $name    The HTML field name
307     * @param array     $entries An array of entries $value => $caption
308     *
309     * @author Adrian Lang <lang@cosmocode.de>
310     */
311
312    public function addRadioSet($name, $entries) {
313        global $INPUT;
314        $value = (array_key_exists($INPUT->post->str($name), $entries)) ?
315                 $INPUT->str($name) : key($entries);
316        foreach($entries as $val => $cap) {
317            $data = ($value === $val) ? array('checked' => 'checked') : array();
318            $this->addElement(form_makeRadioField($name, $val, $cap, '', '', $data));
319        }
320    }
321
322}
323
324/**
325 * form_makeTag
326 *
327 * Create a form element for a non-specific empty tag.
328 *
329 * @param   string  $tag    Tag name.
330 * @param   array   $attrs  Optional attributes.
331 * @return  array   pseudo-tag
332 *
333 * @author  Tom N Harris <tnharris@whoopdedo.org>
334 */
335function form_makeTag($tag, $attrs=array()) {
336    $elem = array('_elem'=>'tag', '_tag'=>$tag);
337    return array_merge($elem, $attrs);
338}
339
340/**
341 * form_makeOpenTag
342 *
343 * Create a form element for a non-specific opening tag.
344 * Remember to put a matching close tag after this as well.
345 *
346 * @param   string  $tag    Tag name.
347 * @param   array   $attrs  Optional attributes.
348 * @return  array   pseudo-tag
349 *
350 * @author  Tom N Harris <tnharris@whoopdedo.org>
351 */
352function form_makeOpenTag($tag, $attrs=array()) {
353    $elem = array('_elem'=>'opentag', '_tag'=>$tag);
354    return array_merge($elem, $attrs);
355}
356
357/**
358 * form_makeCloseTag
359 *
360 * Create a form element for a non-specific closing tag.
361 * Careless use of this will result in invalid XHTML.
362 *
363 * @param   string  $tag    Tag name.
364 * @return  array   pseudo-tag
365 *
366 * @author  Tom N Harris <tnharris@whoopdedo.org>
367 */
368function form_makeCloseTag($tag) {
369    return array('_elem'=>'closetag', '_tag'=>$tag);
370}
371
372/**
373 * form_makeWikiText
374 *
375 * Create a form element for a textarea containing wiki text.
376 * Only one wikitext element is allowed on a page. It will have
377 * a name of 'wikitext' and id 'wiki__text'. The text will
378 * be passed to formText() before printing.
379 *
380 * @param   string  $text   Text to fill the field with.
381 * @param   array   $attrs  Optional attributes.
382 * @return  array   pseudo-tag
383 *
384 * @author  Tom N Harris <tnharris@whoopdedo.org>
385 */
386function form_makeWikiText($text, $attrs=array()) {
387    $elem = array('_elem'=>'wikitext', '_text'=>$text,
388                        'class'=>'edit', 'cols'=>'80', 'rows'=>'10');
389    return array_merge($elem, $attrs);
390}
391
392/**
393 * form_makeButton
394 *
395 * Create a form element for an action button.
396 * A title will automatically be generated using the value and
397 * accesskey attributes, unless you provide one.
398 *
399 * @param   string  $type   Type attribute. 'submit' or 'cancel'
400 * @param   string  $act    Wiki action of the button, will be used as the do= parameter
401 * @param   string  $value  (optional) Displayed label. Uses $act if not provided.
402 * @param   array   $attrs  Optional attributes.
403 * @return  array   pseudo-tag
404 *
405 * @author  Tom N Harris <tnharris@whoopdedo.org>
406 */
407function form_makeButton($type, $act, $value='', $attrs=array()) {
408    if ($value == '') $value = $act;
409    $elem = array('_elem'=>'button', 'type'=>$type, '_action'=>$act,
410                        'value'=>$value);
411    if (!empty($attrs['accesskey']) && empty($attrs['title'])) {
412        $attrs['title'] = $value . ' ['.strtoupper($attrs['accesskey']).']';
413    }
414    return array_merge($elem, $attrs);
415}
416
417/**
418 * form_makeField
419 *
420 * Create a form element for a labelled input element.
421 * The label text will be printed before the input.
422 *
423 * @param   string  $type   Type attribute of input.
424 * @param   string  $name   Name attribute of the input.
425 * @param   string  $value  (optional) Default value.
426 * @param   string  $class  Class attribute of the label. If this is 'block',
427 *                          then a line break will be added after the field.
428 * @param   string  $label  Label that will be printed before the input.
429 * @param   string  $id     ID attribute of the input. If set, the label will
430 *                          reference it with a 'for' attribute.
431 * @param   array   $attrs  Optional attributes.
432 * @return  array   pseudo-tag
433 *
434 * @author  Tom N Harris <tnharris@whoopdedo.org>
435 */
436function form_makeField($type, $name, $value='', $label=null, $id='', $class='', $attrs=array()) {
437    if (is_null($label)) $label = $name;
438    $elem = array('_elem'=>'field', '_text'=>$label, '_class'=>$class,
439                        'type'=>$type, 'id'=>$id, 'name'=>$name, 'value'=>$value);
440    return array_merge($elem, $attrs);
441}
442
443/**
444 * form_makeFieldRight
445 *
446 * Create a form element for a labelled input element.
447 * The label text will be printed after the input.
448 *
449 * @see     form_makeField
450 * @author  Tom N Harris <tnharris@whoopdedo.org>
451 *
452 * @param string $type
453 * @param string $name
454 * @param string $value
455 * @param null|string $label
456 * @param string $id
457 * @param string $class
458 * @param array $attrs
459 *
460 * @return array
461 */
462function form_makeFieldRight($type, $name, $value='', $label=null, $id='', $class='', $attrs=array()) {
463    if (is_null($label)) $label = $name;
464    $elem = array('_elem'=>'fieldright', '_text'=>$label, '_class'=>$class,
465                        'type'=>$type, 'id'=>$id, 'name'=>$name, 'value'=>$value);
466    return array_merge($elem, $attrs);
467}
468
469/**
470 * form_makeTextField
471 *
472 * Create a form element for a text input element with label.
473 *
474 * @see     form_makeField
475 * @author  Tom N Harris <tnharris@whoopdedo.org>
476 *
477 * @param string $name
478 * @param string $value
479 * @param null|string $label
480 * @param string $id
481 * @param string $class
482 * @param array $attrs
483 *
484 * @return array
485 */
486function form_makeTextField($name, $value='', $label=null, $id='', $class='', $attrs=array()) {
487    if (is_null($label)) $label = $name;
488    $elem = array('_elem'=>'textfield', '_text'=>$label, '_class'=>$class,
489                        'id'=>$id, 'name'=>$name, 'value'=>$value, 'class'=>'edit');
490    return array_merge($elem, $attrs);
491}
492
493/**
494 * form_makePasswordField
495 *
496 * Create a form element for a password input element with label.
497 * Password elements have no default value, for obvious reasons.
498 *
499 * @see     form_makeField
500 * @author  Tom N Harris <tnharris@whoopdedo.org>
501 *
502 * @param string $name
503 * @param null|string $label
504 * @param string $id
505 * @param string $class
506 * @param array $attrs
507 *
508 * @return array
509 */
510function form_makePasswordField($name, $label=null, $id='', $class='', $attrs=array()) {
511    if (is_null($label)) $label = $name;
512    $elem = array('_elem'=>'passwordfield', '_text'=>$label, '_class'=>$class,
513                        'id'=>$id, 'name'=>$name, 'class'=>'edit');
514    return array_merge($elem, $attrs);
515}
516
517/**
518 * form_makeFileField
519 *
520 * Create a form element for a file input element with label
521 *
522 * @see     form_makeField
523 * @author  Michael Klier <chi@chimeric.de>
524 *
525 * @param string $name
526 * @param null|string $label
527 * @param string $id
528 * @param string $class
529 * @param array $attrs
530 *
531 * @return array
532 */
533function form_makeFileField($name, $label=null, $id='', $class='', $attrs=array()) {
534    if (is_null($label)) $label = $name;
535    $elem = array('_elem'=>'filefield', '_text'=>$label, '_class'=>$class,
536                        'id'=>$id, 'name'=>$name, 'class'=>'edit');
537    return array_merge($elem, $attrs);
538}
539
540/**
541 * form_makeCheckboxField
542 *
543 * Create a form element for a checkbox input element with label.
544 * If $value is an array, a hidden field with the same name and the value
545 * $value[1] is constructed as well.
546 *
547 * @see     form_makeFieldRight
548 * @author  Tom N Harris <tnharris@whoopdedo.org>
549 *
550 * @param string $name
551 * @param string $value
552 * @param null|string $label
553 * @param string $id
554 * @param string $class
555 * @param array $attrs
556 *
557 * @return array
558 */
559function form_makeCheckboxField($name, $value='1', $label=null, $id='', $class='', $attrs=array()) {
560    if (is_null($label)) $label = $name;
561    if (is_null($value) || $value=='') $value='0';
562    $elem = array('_elem'=>'checkboxfield', '_text'=>$label, '_class'=>$class,
563                        'id'=>$id, 'name'=>$name, 'value'=>$value);
564    return array_merge($elem, $attrs);
565}
566
567/**
568 * form_makeRadioField
569 *
570 * Create a form element for a radio button input element with label.
571 *
572 * @see     form_makeFieldRight
573 * @author  Tom N Harris <tnharris@whoopdedo.org>
574 *
575 * @param string $name
576 * @param string $value
577 * @param null|string $label
578 * @param string $id
579 * @param string $class
580 * @param array $attrs
581 *
582 * @return array
583 */
584function form_makeRadioField($name, $value='1', $label=null, $id='', $class='', $attrs=array()) {
585    if (is_null($label)) $label = $name;
586    if (is_null($value) || $value=='') $value='0';
587    $elem = array('_elem'=>'radiofield', '_text'=>$label, '_class'=>$class,
588                        'id'=>$id, 'name'=>$name, 'value'=>$value);
589    return array_merge($elem, $attrs);
590}
591
592/**
593 * form_makeMenuField
594 *
595 * Create a form element for a drop-down menu with label.
596 * The list of values can be strings, arrays of (value,text),
597 * or an associative array with the values as keys and labels as values.
598 * An item is selected by supplying its value or integer index.
599 * If the list of values is an associative array, the selected item must be
600 * a string.
601 *
602 * @author  Tom N Harris <tnharris@whoopdedo.org>
603 *
604 * @param string           $name     Name attribute of the input.
605 * @param string[]|array[] $values   The list of values can be strings, arrays of (value,text),
606 *                                   or an associative array with the values as keys and labels as values.
607 * @param string|int       $selected default selected value, string or index number
608 * @param string           $class    Class attribute of the label. If this is 'block',
609 *                                   then a line break will be added after the field.
610 * @param string           $label    Label that will be printed before the input.
611 * @param string           $id       ID attribute of the input. If set, the label will
612 *                                   reference it with a 'for' attribute.
613 * @param array            $attrs    Optional attributes.
614 * @return array   pseudo-tag
615 */
616function form_makeMenuField($name, $values, $selected='', $label=null, $id='', $class='', $attrs=array()) {
617    if (is_null($label)) $label = $name;
618    $options = array();
619    reset($values);
620    // FIXME: php doesn't know the difference between a string and an integer
621    if (is_string(key($values))) {
622        foreach ($values as $val=>$text) {
623            $options[] = array($val,$text, (!is_null($selected) && $val==$selected));
624        }
625    } else {
626        if (is_integer($selected)) $selected = $values[$selected];
627        foreach ($values as $val) {
628            if (is_array($val))
629                @list($val,$text) = $val;
630            else
631                $text = null;
632            $options[] = array($val,$text,$val===$selected);
633        }
634    }
635    $elem = array('_elem'=>'menufield', '_options'=>$options, '_text'=>$label, '_class'=>$class,
636                        'id'=>$id, 'name'=>$name);
637    return array_merge($elem, $attrs);
638}
639
640/**
641 * form_makeListboxField
642 *
643 * Create a form element for a list box with label.
644 * The list of values can be strings, arrays of (value,text),
645 * or an associative array with the values as keys and labels as values.
646 * Items are selected by supplying its value or an array of values.
647 *
648 * @author  Tom N Harris <tnharris@whoopdedo.org>
649 *
650 * @param string           $name     Name attribute of the input.
651 * @param string[]|array[] $values   The list of values can be strings, arrays of (value,text),
652 *                                   or an associative array with the values as keys and labels as values.
653 * @param array|string     $selected value or array of values of the items that need to be selected
654 * @param string           $class    Class attribute of the label. If this is 'block',
655 *                                   then a line break will be added after the field.
656 * @param string           $label    Label that will be printed before the input.
657 * @param string           $id       ID attribute of the input. If set, the label will
658 *                                   reference it with a 'for' attribute.
659 * @param array            $attrs    Optional attributes.
660 * @return array   pseudo-tag
661 */
662function form_makeListboxField($name, $values, $selected='', $label=null, $id='', $class='', $attrs=array()) {
663    if (is_null($label)) $label = $name;
664    $options = array();
665    reset($values);
666    if (is_null($selected) || $selected == '') {
667        $selected = array();
668    } elseif (!is_array($selected)) {
669        $selected = array($selected);
670    }
671    // FIXME: php doesn't know the difference between a string and an integer
672    if (is_string(key($values))) {
673        foreach ($values as $val=>$text) {
674            $options[] = array($val,$text,in_array($val,$selected));
675        }
676    } else {
677        foreach ($values as $val) {
678            $disabled = false;
679            if (is_array($val)) {
680                @list($val,$text,$disabled) = $val;
681            } else {
682                $text = null;
683            }
684            $options[] = array($val,$text,in_array($val,$selected),$disabled);
685        }
686    }
687    $elem = array('_elem'=>'listboxfield', '_options'=>$options, '_text'=>$label, '_class'=>$class,
688                        'id'=>$id, 'name'=>$name);
689    return array_merge($elem, $attrs);
690}
691
692/**
693 * form_tag
694 *
695 * Print the HTML for a generic empty tag.
696 * Requires '_tag' key with name of the tag.
697 * Attributes are passed to buildAttributes()
698 *
699 * @author  Tom N Harris <tnharris@whoopdedo.org>
700 *
701 * @param array $attrs attributes
702 * @return string html of tag
703 */
704function form_tag($attrs) {
705    return '<'.$attrs['_tag'].' '.buildAttributes($attrs,true).'/>';
706}
707
708/**
709 * form_opentag
710 *
711 * Print the HTML for a generic opening tag.
712 * Requires '_tag' key with name of the tag.
713 * Attributes are passed to buildAttributes()
714 *
715 * @author  Tom N Harris <tnharris@whoopdedo.org>
716 *
717 * @param array $attrs attributes
718 * @return string html of tag
719 */
720function form_opentag($attrs) {
721    return '<'.$attrs['_tag'].' '.buildAttributes($attrs,true).'>';
722}
723
724/**
725 * form_closetag
726 *
727 * Print the HTML for a generic closing tag.
728 * Requires '_tag' key with name of the tag.
729 * There are no attributes.
730 *
731 * @author  Tom N Harris <tnharris@whoopdedo.org>
732 *
733 * @param array $attrs attributes
734 * @return string html of tag
735 */
736function form_closetag($attrs) {
737    return '</'.$attrs['_tag'].'>';
738}
739
740/**
741 * form_openfieldset
742 *
743 * Print the HTML for an opening fieldset tag.
744 * Uses the '_legend' key.
745 * Attributes are passed to buildAttributes()
746 *
747 * @author  Tom N Harris <tnharris@whoopdedo.org>
748 *
749 * @param array $attrs attributes
750 * @return string html
751 */
752function form_openfieldset($attrs) {
753    $s = '<fieldset '.buildAttributes($attrs,true).'>';
754    if (!is_null($attrs['_legend'])) $s .= '<legend>'.$attrs['_legend'].'</legend>';
755    return $s;
756}
757
758/**
759 * form_closefieldset
760 *
761 * Print the HTML for a closing fieldset tag.
762 * There are no attributes.
763 *
764 * @author  Tom N Harris <tnharris@whoopdedo.org>
765 *
766 * @return string html
767 */
768function form_closefieldset() {
769    return '</fieldset>';
770}
771
772/**
773 * form_hidden
774 *
775 * Print the HTML for a hidden input element.
776 * Uses only 'name' and 'value' attributes.
777 * Value is passed to formText()
778 *
779 * @author  Tom N Harris <tnharris@whoopdedo.org>
780 *
781 * @param array $attrs attributes
782 * @return string html
783 */
784function form_hidden($attrs) {
785    return '<input type="hidden" name="'.$attrs['name'].'" value="'.formText($attrs['value']).'" />';
786}
787
788/**
789 * form_wikitext
790 *
791 * Print the HTML for the wiki textarea.
792 * Requires '_text' with default text of the field.
793 * Text will be passed to formText(), attributes to buildAttributes()
794 *
795 * @author  Tom N Harris <tnharris@whoopdedo.org>
796 *
797 * @param array $attrs attributes
798 * @return string html
799 */
800function form_wikitext($attrs) {
801    // mandatory attributes
802    unset($attrs['name']);
803    unset($attrs['id']);
804    return '<textarea name="wikitext" id="wiki__text" dir="auto" '
805                 .buildAttributes($attrs,true).'>'.DOKU_LF
806                 .formText($attrs['_text'])
807                 .'</textarea>';
808}
809
810/**
811 * form_button
812 *
813 * Print the HTML for a form button.
814 * If '_action' is set, the button name will be "do[_action]".
815 * Other attributes are passed to buildAttributes()
816 *
817 * @author  Tom N Harris <tnharris@whoopdedo.org>
818 *
819 * @param array $attrs attributes
820 * @return string html
821 */
822function form_button($attrs) {
823    $p = (!empty($attrs['_action'])) ? 'name="do['.$attrs['_action'].']" ' : '';
824    $value = $attrs['value'];
825    unset($attrs['value']);
826    return '<button '.$p.buildAttributes($attrs,true).'>'.$value.'</button>';
827}
828
829/**
830 * form_field
831 *
832 * Print the HTML for a form input field.
833 *   _class : class attribute used on the label tag
834 *   _text  : Text to display before the input. Not escaped.
835 * Other attributes are passed to buildAttributes() for the input tag.
836 *
837 * @author  Tom N Harris <tnharris@whoopdedo.org>
838 *
839 * @param array $attrs attributes
840 * @return string html
841 */
842function form_field($attrs) {
843    $s = '<label';
844    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
845    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
846    $s .= '><span>'.$attrs['_text'].'</span>';
847    $s .= ' <input '.buildAttributes($attrs,true).' /></label>';
848    if (preg_match('/(^| )block($| )/', $attrs['_class']))
849        $s .= '<br />';
850    return $s;
851}
852
853/**
854 * form_fieldright
855 *
856 * Print the HTML for a form input field. (right-aligned)
857 *   _class : class attribute used on the label tag
858 *   _text  : Text to display after the input. Not escaped.
859 * Other attributes are passed to buildAttributes() for the input tag.
860 *
861 * @author  Tom N Harris <tnharris@whoopdedo.org>
862 *
863 * @param array $attrs attributes
864 * @return string html
865 */
866function form_fieldright($attrs) {
867    $s = '<label';
868    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
869    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
870    $s .= '><input '.buildAttributes($attrs,true).' />';
871    $s .= ' <span>'.$attrs['_text'].'</span></label>';
872    if (preg_match('/(^| )block($| )/', $attrs['_class']))
873        $s .= '<br />';
874    return $s;
875}
876
877/**
878 * form_textfield
879 *
880 * Print the HTML for a text input field.
881 *   _class : class attribute used on the label tag
882 *   _text  : Text to display before the input. Not escaped.
883 * Other attributes are passed to buildAttributes() for the input tag.
884 *
885 * @author  Tom N Harris <tnharris@whoopdedo.org>
886 *
887 * @param array $attrs attributes
888 * @return string html
889 */
890function form_textfield($attrs) {
891    // mandatory attributes
892    unset($attrs['type']);
893    $s = '<label';
894    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
895    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
896    $s .= '><span>'.$attrs['_text'].'</span> ';
897    $s .= '<input type="text" '.buildAttributes($attrs,true).' /></label>';
898    if (preg_match('/(^| )block($| )/', $attrs['_class']))
899        $s .= '<br />';
900    return $s;
901}
902
903/**
904 * form_passwordfield
905 *
906 * Print the HTML for a password input field.
907 *   _class : class attribute used on the label tag
908 *   _text  : Text to display before the input. Not escaped.
909 * Other attributes are passed to buildAttributes() for the input tag.
910 *
911 * @author  Tom N Harris <tnharris@whoopdedo.org>
912 *
913 * @param array $attrs attributes
914 * @return string html
915 */
916function form_passwordfield($attrs) {
917    // mandatory attributes
918    unset($attrs['type']);
919    $s = '<label';
920    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
921    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
922    $s .= '><span>'.$attrs['_text'].'</span> ';
923    $s .= '<input type="password" '.buildAttributes($attrs,true).' /></label>';
924    if (preg_match('/(^| )block($| )/', $attrs['_class']))
925        $s .= '<br />';
926    return $s;
927}
928
929/**
930 * form_filefield
931 *
932 * Print the HTML for a file input field.
933 *   _class     : class attribute used on the label tag
934 *   _text      : Text to display before the input. Not escaped
935 *   _maxlength : Allowed size in byte
936 *   _accept    : Accepted mime-type
937 * Other attributes are passed to buildAttributes() for the input tag
938 *
939 * @author  Michael Klier <chi@chimeric.de>
940 *
941 * @param array $attrs attributes
942 * @return string html
943 */
944function form_filefield($attrs) {
945    $s = '<label';
946    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
947    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
948    $s .= '><span>'.$attrs['_text'].'</span> ';
949    $s .= '<input type="file" '.buildAttributes($attrs,true);
950    if (!empty($attrs['_maxlength'])) $s .= ' maxlength="'.$attrs['_maxlength'].'"';
951    if (!empty($attrs['_accept'])) $s .= ' accept="'.$attrs['_accept'].'"';
952    $s .= ' /></label>';
953    if (preg_match('/(^| )block($| )/', $attrs['_class']))
954        $s .= '<br />';
955    return $s;
956}
957
958/**
959 * form_checkboxfield
960 *
961 * Print the HTML for a checkbox input field.
962 *   _class : class attribute used on the label tag
963 *   _text  : Text to display after the input. Not escaped.
964 * Other attributes are passed to buildAttributes() for the input tag.
965 * If value is an array, a hidden field with the same name and the value
966 * $attrs['value'][1] is constructed as well.
967 *
968 * @author  Tom N Harris <tnharris@whoopdedo.org>
969 *
970 * @param array $attrs attributes
971 * @return string html
972 */
973function form_checkboxfield($attrs) {
974    // mandatory attributes
975    unset($attrs['type']);
976    $s = '<label';
977    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
978    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
979    $s .= '>';
980    if (is_array($attrs['value'])) {
981        echo '<input type="hidden" name="' . hsc($attrs['name']) .'"'
982                 . ' value="' . hsc($attrs['value'][1]) . '" />';
983        $attrs['value'] = $attrs['value'][0];
984    }
985    $s .= '<input type="checkbox" '.buildAttributes($attrs,true).' />';
986    $s .= ' <span>'.$attrs['_text'].'</span></label>';
987    if (preg_match('/(^| )block($| )/', $attrs['_class']))
988        $s .= '<br />';
989    return $s;
990}
991
992/**
993 * form_radiofield
994 *
995 * Print the HTML for a radio button input field.
996 *   _class : class attribute used on the label tag
997 *   _text  : Text to display after the input. Not escaped.
998 * Other attributes are passed to buildAttributes() for the input tag.
999 *
1000 * @author  Tom N Harris <tnharris@whoopdedo.org>
1001 *
1002 * @param array $attrs attributes
1003 * @return string html
1004 */
1005function form_radiofield($attrs) {
1006    // mandatory attributes
1007    unset($attrs['type']);
1008    $s = '<label';
1009    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
1010    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
1011    $s .= '><input type="radio" '.buildAttributes($attrs,true).' />';
1012    $s .= ' <span>'.$attrs['_text'].'</span></label>';
1013    if (preg_match('/(^| )block($| )/', $attrs['_class']))
1014        $s .= '<br />';
1015    return $s;
1016}
1017
1018/**
1019 * form_menufield
1020 *
1021 * Print the HTML for a drop-down menu.
1022 *   _options : Array of (value,text,selected) for the menu.
1023 *              Text can be omitted. Text and value are passed to formText()
1024 *              Only one item can be selected.
1025 *   _class : class attribute used on the label tag
1026 *   _text  : Text to display before the menu. Not escaped.
1027 * Other attributes are passed to buildAttributes() for the input tag.
1028 *
1029 * @author  Tom N Harris <tnharris@whoopdedo.org>
1030 *
1031 * @param array $attrs attributes
1032 * @return string html
1033 */
1034function form_menufield($attrs) {
1035    $attrs['size'] = '1';
1036    $s = '<label';
1037    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
1038    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
1039    $s .= '><span>'.$attrs['_text'].'</span>';
1040    $s .= ' <select '.buildAttributes($attrs,true).'>'.DOKU_LF;
1041    if (!empty($attrs['_options'])) {
1042        $selected = false;
1043
1044        $cnt = count($attrs['_options']);
1045        for($n=0; $n < $cnt; $n++){
1046            @list($value,$text,$select) = $attrs['_options'][$n];
1047            $p = '';
1048            if (!is_null($text))
1049                $p .= ' value="'.formText($value).'"';
1050            else
1051                $text = $value;
1052            if (!empty($select) && !$selected) {
1053                $p .= ' selected="selected"';
1054                $selected = true;
1055            }
1056            $s .= '<option'.$p.'>'.formText($text).'</option>';
1057        }
1058    } else {
1059        $s .= '<option></option>';
1060    }
1061    $s .= DOKU_LF.'</select></label>';
1062    if (preg_match('/(^| )block($| )/', $attrs['_class']))
1063        $s .= '<br />';
1064    return $s;
1065}
1066
1067/**
1068 * form_listboxfield
1069 *
1070 * Print the HTML for a list box.
1071 *   _options : Array of (value,text,selected) for the list.
1072 *              Text can be omitted. Text and value are passed to formText()
1073 *   _class : class attribute used on the label tag
1074 *   _text  : Text to display before the menu. Not escaped.
1075 * Other attributes are passed to buildAttributes() for the input tag.
1076 *
1077 * @author  Tom N Harris <tnharris@whoopdedo.org>
1078 *
1079 * @param array $attrs attributes
1080 * @return string html
1081 */
1082function form_listboxfield($attrs) {
1083    $s = '<label';
1084    if ($attrs['_class']) $s .= ' class="'.$attrs['_class'].'"';
1085    if (!empty($attrs['id'])) $s .= ' for="'.$attrs['id'].'"';
1086    $s .= '><span>'.$attrs['_text'].'</span> ';
1087    $s .= '<select '.buildAttributes($attrs,true).'>'.DOKU_LF;
1088    if (!empty($attrs['_options'])) {
1089        foreach ($attrs['_options'] as $opt) {
1090            @list($value,$text,$select,$disabled) = $opt;
1091            $p = '';
1092            if(is_null($text)) $text = $value;
1093            $p .= ' value="'.formText($value).'"';
1094            if (!empty($select)) $p .= ' selected="selected"';
1095            if ($disabled) $p .= ' disabled="disabled"';
1096            $s .= '<option'.$p.'>'.formText($text).'</option>';
1097        }
1098    } else {
1099        $s .= '<option></option>';
1100    }
1101    $s .= DOKU_LF.'</select></label>';
1102    if (preg_match('/(^| )block($| )/', $attrs['_class']))
1103        $s .= '<br />';
1104    return $s;
1105}
1106