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