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