b2evolution PHP Cross Reference Blogging Systems

Source: /inc/_core/ui/forms/_form.class.php - 3457 lines - 108418 bytes - Summary - Text - Print

Description: This file implements the Fast Form handling class. This file is part of the evoCore framework - {@link http://evocore.net/} See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * This file implements the Fast Form handling class.
   4   *
   5   * This file is part of the evoCore framework - {@link http://evocore.net/}
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
   9   * Parts of this file are copyright (c)2004 by PROGIDISTRI - {@link http://progidistri.com/}.
  10   * Parts of this file are copyright (c)2004-2005 by Daniel HAHLER - {@link http://thequod.de/contact}.
  11   *
  12   * {@internal License choice
  13   * - If you have received this file as part of a package, please find the license.txt file in
  14   *   the same folder or the closest folder above for complete license terms.
  15   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  16   *   then you must choose one of the following licenses before using the file:
  17   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  18   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  19   * }}
  20   *
  21   * {@internal Open Source relicensing agreement:
  22   * Daniel HAHLER grants Francois PLANQUE the right to license
  23   * Daniel HAHLER's contributions to this file and the b2evolution project
  24   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  25   *
  26   * PROGIDISTRI grants Francois PLANQUE the right to license
  27   * PROGIDISTRI's contributions to this file and the b2evolution project
  28   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  29   * }}
  30   *
  31   * @package evocore
  32   *
  33   * {@internal Below is a list of authors who have contributed to design/coding of this file: }}
  34   * @author blueyed: Daniel HAHLER
  35   * @author fplanque: Francois PLANQUE.
  36   * @author fsaya: Fabrice SAYA-GASNIER / PROGIDISTRI
  37   * @author mbruneau: Marc BRUNEAU / PROGIDISTRI
  38   *
  39   * @todo Provide buffering of whole Form to be able to add onsubmit-JS to enable/disabled
  40   *       (group) checkboxes again and other useful stuff.
  41   *
  42   * NOTE: we use an member array ($_common_params) for exchanging params between functions.
  43   * This will most probably cause problems, when nesting inputs. This should be refactored
  44   * to use a field_name-based member array. (blueyed)
  45   *
  46   * @version $Id: _form.class.php 6136 2014-03-08 07:59:48Z manuel $
  47   */
  48  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  49  
  50  load_class( '_core/ui/_uiwidget.class.php', 'Table' );
  51  load_class( '_core/ui/_uiwidget.class.php', 'Widget' );
  52  
  53  /**
  54   * Form class
  55   *
  56   * @package evocore
  57   */
  58  class Form extends Widget
  59  {
  60      /**
  61       * Output HTML or just return it?
  62       * @var boolean
  63       */
  64      var $output = true;
  65  
  66      /**
  67       * Remember number of open tags that need to be handled in {@link end_form()}.
  68       *
  69       * @var array
  70       */
  71      var $_opentags = array( 'fieldset' => 0 );
  72  
  73      /**
  74       * Suffix for LABELs.
  75       * @var string
  76       */
  77      var $label_suffix = ':';
  78  
  79  
  80      /**
  81       * Common params shared between methods.
  82       *
  83       * These can all be used with the $field_params argument of the functions.
  84       *
  85       * - 'note': The note associated with the field.
  86       * - 'note_format': The format of the note. %s gets replaced by the note.
  87       * - 'label': The label for the field.
  88       * - 'required': is the element required to be filled/checked? This will add a visual hint (boolean; default: false)
  89       *
  90       * @see handle_common_params()
  91       * @var array
  92       */
  93      var $_common_params = array();
  94  
  95      /**
  96       * This is the default note format, where {@link handle_common_params()} falls
  97       * back to, when not given with $field_params.
  98       * @todo This might be used in switch_layout().
  99       * @var string
 100       */
 101      var $note_format = ' <span class="notes">%s</span>';
 102  
 103      /**
 104       * This is a buffer for hidden fields. We'll display all of them just before the end of form </form>. This avoids having them caught in between table rows.
 105       *
 106       * @var string
 107       */
 108      var $hiddens = array();
 109  
 110      /**
 111       * Do we need to add javascript for check/uncheck all functionality
 112       */
 113      var $check_all = false;
 114  
 115      /**
 116       * Additional Javascript to append to the form, in {@link Form::end_form()}.
 117       *
 118       * @access protected
 119       * @var array
 120       */
 121      var $append_javascript = array();
 122  
 123      /**
 124       * Display param errors with fields, appended to the note?
 125       * @var boolean
 126       */
 127      var $disp_param_err_messages_with_fields = true;
 128  
 129      /**
 130       * Stack of previous used layouts
 131       * @see Form::switch_layout()
 132       * @var array
 133       */
 134      var $saved_layouts;
 135      var $saved_templates;
 136  
 137  
 138      /**
 139       * A list of input field names that have been used in the form.
 140       * This gets used in {@link hiddens_by_key()} to exclude those.
 141       * @var array
 142       */
 143      var $included_input_field_names = array();
 144  
 145  
 146      /**
 147       * Indicates if the form is ended
 148       * @var boolean
 149       */
 150      var $_form_ended = false;
 151  
 152  
 153      /**
 154       * Constructor
 155       *
 156       * @param string the action destination of the form (NULL for pagenow)
 157       * @param string the name of the form (will be used as an ID)
 158       * @param string the method used to send data ("post" (Default), "get")
 159       * @param string the form layout : 'fieldset', 'table' or '' (NULL means: if there is an {@link $AdminUI} object get it from there, otherwise use 'fieldset')
 160       * @param string Form encoding ("application/x-www-form-urlencoded" (default), "multipart/form-data" (uploads))
 161       */
 162  	function Form( $form_action = NULL, $form_name = '', $form_method = 'post', $layout = NULL, $enctype = '' )
 163      {
 164          global $AdminUI, $pagenow, $Skin;
 165  
 166          $this->form_name = $form_name;
 167          $this->form_action = (is_null($form_action) ? $pagenow : $form_action );
 168          $this->form_method = $form_method;
 169          $this->enctype = $enctype;
 170  
 171          if( is_object($AdminUI) )
 172          {
 173              if( empty( $layout ) || $layout == 'split' || $layout == 'none' || $layout == 'fieldset' )
 174              { // Get default skin setting:
 175                  $template = $AdminUI->get_template( 'Form' );
 176                  $layout = $template['layout'];
 177              }
 178              else
 179              {
 180                  $template = $AdminUI->get_template( $layout.'_form' );
 181                  $layout = $template['layout'];
 182              }
 183          }
 184          else
 185          {    // This happens for comment forms & login screen for example...
 186              $template_is_empty = true;
 187              if( ( is_object( $Skin ) ) && ( !empty( $layout ) ) )
 188              { // Get skin setting:
 189                  $template = $Skin->get_template( $layout.'_form' );
 190                  $template_is_empty = empty( $template );
 191                  if( !$template_is_empty )
 192                  {
 193                      $layout = $template['layout'];
 194                  }
 195              }
 196  
 197              if( $template_is_empty )
 198              {
 199                  $template = array(
 200                      'layout' => 'fieldset',
 201                      'formstart' => '<div>',// required before (no_)title_fmt for validation
 202                      'title_fmt' => '<span style="float:right">$global_icons$</span><h2>$title$</h2>'."\n",
 203                      'no_title_fmt' => '<span style="float:right">$global_icons$</span>'."\n",
 204                      'fieldset_begin' => '<fieldset $fieldset_attribs$>'."\n"
 205                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n",
 206                      'fieldset_end' => '</fieldset>'."\n",
 207                      'fieldstart' => '<fieldset$ID$>'."\n",
 208                      'labelstart' => '<div class="label">',
 209                      'labelend' => "</div>\n",
 210                      'labelempty' => '<div class="label"></div>', // so that IE6 aligns DIV.input correcctly
 211                      'inputstart' => '<div class="input">',
 212                      'infostart' => '<div class="info">',
 213                      'inputend' => "</div>\n",
 214                      'fieldend' => "</fieldset>\n\n",
 215                      'buttonsstart' => '<fieldset><div class="label"></div><div class="input">', // DIV.label for IE6
 216                      'buttonsend' => "</div></fieldset>\n\n",
 217                      'customstart' => '<div class="custom_content">',
 218                      'customend' => "</div>\n",
 219                      'formend' => '</div>',
 220                  );
 221                  $layout = 'fieldset';
 222              }
 223          }
 224  
 225          $this->saved_layouts = array($layout);
 226          $this->saved_templates = array($template);
 227          $this->switch_layout( NULL );    // "restore" saved layout.
 228  
 229  
 230          // Add any GET params from $form_action as hidden inputs (GET forms only)
 231          // Otherwise those params would be overwritten by the form submit and lost.
 232          if( strpos($this->form_action, '?') !== false && $this->form_method == 'get' )
 233          {
 234              $pos_args = strpos($this->form_action, '?');
 235              $query_str = substr($this->form_action, $pos_args+1);
 236              // Split args by "&" (but leave "&amp;" alone).
 237              $query_args = preg_split('~&(?!amp;)~', $query_str, -1, PREG_SPLIT_NO_EMPTY);
 238              foreach( $query_args as $query_arg )
 239              {
 240                  list($field_name, $field_value) = explode('=', $query_arg, 2);
 241                  // Remember all pairs, and add them in end_form (so that used fieldnames can be skipped):
 242                  $this->possible_hiddens_from_action[] = array($field_name, $field_value);
 243              }
 244              // Remove params.
 245              $this->form_action = substr($form_action, 0, $pos_args);
 246          }
 247      }
 248  
 249  
 250      /**
 251       * @param string|NULL the form layout : 'fieldset', 'table' or ''; NULL to restore previsouly saved layout
 252       */
 253  	function switch_layout( $layout )
 254      {
 255          if( $layout == NULL )
 256          { // we want to restore previous layout:
 257              if( count($this->saved_layouts) )
 258              {
 259                  $this->layout = array_shift($this->saved_layouts);
 260                  $template = $this->saved_templates[0];
 261                  if( !empty($template ) )
 262                  {
 263                      $this->template       = $template;
 264                      $this->formstart      = $template['formstart'];
 265                      $this->title_fmt      = $template['title_fmt'];
 266                      $this->no_title_fmt   = $template['no_title_fmt'];
 267                      $this->fieldset_begin = $template['fieldset_begin'];
 268                      $this->fieldset_end   = $template['fieldset_end'];
 269                      $this->fieldstart     = $template['fieldstart'];
 270                      $this->labelstart     = $template['labelstart'];
 271                      $this->labelend       = $template['labelend'];
 272                      $this->labelempty     = $template['labelempty'];
 273                      $this->inputstart     = $template['inputstart'];
 274                      $this->infostart      = $template['infostart'];
 275                      $this->inputend       = $template['inputend'];
 276                      $this->fieldend       = $template['fieldend'];
 277                      $this->buttonsstart   = $template['buttonsstart'];
 278                      $this->buttonsend     = $template['buttonsend'];
 279                      $this->customstart    = $template['customstart'];
 280                      $this->customend      = $template['customend'];
 281                      $this->formend        = $template['formend'];
 282                  }
 283              }
 284          }
 285          else
 286          { // We want to switch to a new layout
 287              array_unshift( $this->saved_layouts, $this->layout );
 288              $this->layout = $layout;
 289  
 290              switch( $this->layout )
 291              {
 292                  case 'table':
 293                      $this->formstart = '<table cellspacing="0" class="fform">'."\n";
 294                      // Note: no thead in here until you can safely add a tbody to the rest of the content...
 295                      $this->title_fmt = '<tr class="formtitle"><th colspan="2"><div class="results_title">'
 296                                                              .'<span class="right_icons">$global_icons$</span>'
 297                                                              .'$title$</div></th></tr>'."\n";
 298                      $this->no_title_fmt = '<tr><th colspan="2"><span class="right_icons">$global_icons$</span></th></tr>'."\n";
 299                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 300                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 301                      $this->fieldset_end = '</fieldset>'."\n";
 302                      $this->fieldstart = '<tr$ID$>'."\n";
 303                      $this->labelstart = '<td class="label">';
 304                      $this->labelend = "</td>\n";
 305                      $this->labelempty = '<td class="label">&nbsp;</td>'."\n";
 306                      $this->inputstart = '<td class="input">';
 307                      $this->infostart = '<td class="info">';
 308                      $this->inputend = "</td>\n";
 309                      $this->fieldend = "</tr>\n\n";
 310                      $this->buttonsstart = '<tr class="buttons"><td colspan="2">';
 311                      $this->buttonsend = "</td></tr>\n";
 312                      $this->customstart = '<tr><td colspan="2" class="custom_content">';
 313                      $this->customend = "</td></tr>\n";
 314                      $this->formend = "</table>\n";
 315                      break;
 316  
 317                  case 'fieldset':
 318                      $this->formstart = '<div>';// required before (no_)title_fmt for validation
 319                      $this->title_fmt = '<span style="float:right">$global_icons$</span><h2>$title$</h2>'."\n";
 320                      $this->no_title_fmt = '<span style="float:right">$global_icons$</span>'."\n";
 321                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 322                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 323                      $this->fieldset_end = '</fieldset>'."\n";
 324                      $this->fieldstart = '<fieldset$ID$>'."\n";
 325                      $this->labelstart = '<div class="label">';
 326                      $this->labelend = "</div>\n";
 327                      $this->labelempty = '<div class="label"></div>'; // so that IE6 aligns DIV.input correcctly
 328                      $this->inputstart = '<div class="input">';
 329                      $this->infostart = '<div class="info">';
 330                      $this->inputend = "</div>\n";
 331                      $this->fieldend = "</fieldset>\n\n";
 332                      $this->buttonsstart = '<fieldset><div class="label"></div><div class="input">'; // DIV.label for IE6
 333                      $this->buttonsend = "</div></fieldset>\n\n";
 334                      $this->customstart = '<div class="custom_content">';
 335                      $this->customend = "</div>\n";
 336                      $this->formend = '</div>';
 337                      break;
 338  
 339                  case 'chicago':        // Temporary dirty hack
 340                      $this->formstart = '<div>';// required before (no_)title_fmt for validation
 341                      $this->title_fmt = '<span style="float:right">$global_icons$</span><h2>$title$</h2>'."\n";
 342                      $this->no_title_fmt = '<span style="float:right">$global_icons$</span>'."\n";
 343                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 344                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 345                      $this->fieldset_end = '</fieldset>'."\n";
 346                      $this->fieldstart = '<fieldset$ID$>'."\n";
 347                      $this->labelstart = '<div class="label">';
 348                      $this->labelend = "</div>\n";
 349                      $this->labelempty = '<div class="label"></div>'; // so that IE6 aligns DIV.input correcctly
 350                      $this->inputstart = '<div class="input">';
 351                      $this->infostart = '<div class="info">';
 352                      $this->inputend = "</div>\n";
 353                      $this->fieldend = "</fieldset>\n\n";
 354                      $this->buttonsstart = '<fieldset><div class="label"></div><div class="input">'; // DIV.label for IE6
 355                      $this->buttonsend = "</div></fieldset>\n\n";
 356                      $this->customstart = '<div class="custom_content">';
 357                      $this->customend = "</div>\n";
 358                      $this->formend = '</div>';
 359                      break;
 360  
 361                  case 'linespan':
 362                      $this->formstart = '';
 363                      $this->title_fmt = '<span style="float:right">$global_icons$</span><h2>$title$</h2>'."\n";
 364                      $this->no_title_fmt = '<span style="float:right">$global_icons$</span>&nbsp;'."\n";
 365                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 366                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 367                      $this->fieldset_end = '</fieldset>'."\n";
 368                      $this->fieldstart = '<div class="tile"$ID$>';
 369                      $this->labelstart = '<strong>';
 370                      $this->labelend = "</strong>\n";
 371                      $this->labelempty = '';
 372                      $this->inputstart = '';
 373                      $this->infostart = '';
 374                      $this->inputend = "\n";
 375                      $this->fieldend = "</div>\n";
 376                      $this->buttonsstart = '';
 377                      $this->buttonsend = "\n";
 378                      $this->customstart = '';
 379                      $this->customend = "\n";
 380                      $this->formend = '';
 381                      break;
 382  
 383                  case 'blockspan':
 384                      $this->formstart = '';
 385                      $this->title_fmt = '$title$'."\n"; // TODO: icons
 386                      $this->no_title_fmt = '';          //           "
 387                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 388                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 389                      $this->fieldset_end = '</fieldset>'."\n";
 390                      $this->fieldstart = '<span class="block"$ID$>';
 391                      $this->labelstart = '';
 392                      $this->labelend = "\n";
 393                      $this->labelempty = '';
 394                      $this->inputstart = '';
 395                      $this->infostart = '';
 396                      $this->inputend = "\n";
 397                      $this->fieldend = '</span>'.get_icon( 'pixel' )."\n";
 398                      $this->buttonsstart = '';
 399                      $this->buttonsend = "\n";
 400                      $this->customstart = '';
 401                      $this->customend = '';
 402                      $this->formend = '';
 403                      break;
 404  
 405                  default:
 406                      // "none" (no layout)
 407                      $this->formstart = '';
 408                      $this->title_fmt = '$title$'."\n"; // TODO: icons
 409                      $this->no_title_fmt = '';          //           "
 410                      $this->fieldset_begin = '<fieldset $fieldset_attribs$>'."\n"
 411                                                              .'<legend $title_attribs$>$fieldset_title$</legend>'."\n";
 412                      $this->fieldset_end = '</fieldset>'."\n";
 413                      $this->fieldstart = ''; // fp> shall we still use $ID$ here ?
 414                      $this->labelstart = '';
 415                      $this->labelend = "\n";
 416                      $this->labelempty = '';
 417                      $this->inputstart = '';
 418                      $this->infostart = '';
 419                      $this->inputend = "\n";
 420                      $this->fieldend = "\n";
 421                      $this->buttonsstart = '';
 422                      $this->buttonsend = "\n";
 423                      $this->customstart = '';
 424                      $this->customend = "\n";
 425                      $this->formend = '';
 426              }
 427  
 428          }
 429      }
 430  
 431  
 432      /**
 433       * Set a parts of form from array
 434       *
 435       * @param array:
 436       *    formstart
 437       *    title_fmt
 438       *    no_title_fmt
 439       *    fieldset_begin
 440       *    fieldset_end
 441       *    fieldstart
 442       *    labelstart
 443       *    labelend
 444       *    labelempty
 445       *    inputstart
 446       *    infostart
 447       *    inputend
 448       *    fieldend
 449       *    buttonsstart
 450       *    buttonsend
 451       *    formend
 452       */
 453  	function switch_template_parts( $parts )
 454      {
 455          if( is_array( $parts ) && count( $parts ) > 0 )
 456          {    // Change a default form params from defined vars on current skin
 457              foreach( $parts as $part => $value )
 458              {
 459                  $this->$part = $value;
 460              }
 461          }
 462      }
 463  
 464  
 465      /**
 466       * Start an input field.
 467       *
 468       * A field is a fielset containing a label div and an input div.
 469       *
 470       * @uses $_common_params
 471       * @param string The name of the field
 472       * @param string The field label
 473       * @param boolean Reset {@link $_common_params}? This should be used if you build a field by yourself.
 474       * @return The generated HTML
 475       */
 476  	function begin_field( $field_name = NULL, $field_label = NULL, $reset_common_params = false )
 477      {
 478          if( $reset_common_params )
 479          {
 480              $this->_common_params = array();
 481          }
 482  
 483          $field_classes = array();
 484          if( !empty( $this->_common_params['wide'] ) && $this->_common_params['wide'] )
 485          { // Wide field
 486              $field_classes[] = 'field_wide';
 487          }
 488          if( !empty( $this->_common_params['inline'] ) && $this->_common_params['inline'] )
 489          { // Inline field
 490              $field_classes[] = 'field_inline';
 491          }
 492  
 493          // Remember these, to make them available to get_label()
 494          if( isset($field_name) )
 495          {
 496              $this->_common_params['name'] = $field_name;
 497          }
 498          if( isset($field_label) )
 499          {
 500              $this->_common_params['label'] = $field_label;
 501          }
 502  
 503          // Start the new form field and inject an automatic DOM id
 504          // This is useful to show/hide the whole field by JS.
 505          if( !empty( $this->_common_params['id'] ) )
 506          {
 507              $ffield_id = ' id="ffield_'.$this->_common_params['id'].'" ';
 508          }
 509          else
 510          { // No ID in case there's no id/name given for a field.
 511              $ffield_id = '';
 512          }
 513          // quick and dirty "required_field" addition, needs tidying once tested
 514          if( !empty( $this->_common_params['required'] ) )
 515          { // required field
 516              $field_classes[] = 'field_required';
 517          }
 518  
 519          if( count( $field_classes ) > 0 )
 520          { // Add class attribute
 521              $ffield_id .= ' class="'.implode( ' ', $field_classes ).'"';
 522          }
 523  
 524          $r = str_replace( '$ID$', $ffield_id, $this->fieldstart );
 525  
 526          if( isset($this->_common_params['field_prefix']) )
 527          {
 528              $r .= $this->_common_params['field_prefix'];
 529          }
 530  
 531          $r .= $this->get_label();
 532  
 533          $r .= $this->inputstart;
 534  
 535          return $r;
 536      }
 537  
 538  
 539      /**
 540       * End an input field.
 541       *
 542       * A field is a fielset containing a label div and an input div.
 543       *
 544       * @return The generated HTML
 545       */
 546  	function end_field()
 547      {
 548  
 549          $r = '';
 550  
 551          if( !empty($this->_common_params['note']) )
 552          { // We have a note
 553              $r .= sprintf( $this->_common_params['note_format'], $this->_common_params['note'] );
 554          }
 555  
 556          if( isset($this->_common_params['field_suffix']) )
 557          {
 558              $r .= $this->_common_params['field_suffix'];
 559          }
 560  
 561          $r .= $this->inputend.$this->fieldend;
 562  
 563          return $r;
 564      }
 565  
 566  
 567      /**
 568       * Builds a fieldset tag. This is a "fieldset" element by default, but a "th" element
 569       * for table layout.
 570       *
 571       * @param string the title of the fieldset
 572       * @param string the field params to the fieldset
 573       *               additionally 'legend_params' can be used to give an array of field params
 574       * @return true|string true (if output) or the generated HTML if not outputting
 575       */
 576  	function begin_fieldset( $title = '', $field_params = array() )
 577      {
 578          $field_params = array_merge( array(
 579                  'class' => 'fieldset',
 580              ), $field_params );
 581  
 582          switch( $this->layout )
 583          {
 584              case 'table':
 585                  $r = '<tr'.get_field_attribs_as_string($field_params).'><th colspan="2">'."\n";
 586                  // NOTE: empty THs can be rendered and/or are DHTML scriptable
 587  
 588                  if( $title != '' )
 589                  { // there is a title to display
 590                      $r .= $title;
 591                  }
 592  
 593                  $r .= "</th></tr>\n";
 594                  break;
 595  
 596              default:
 597                  if( ! empty($field_params['legend_params']) )
 598                  {    // We have params specifically passed for the title
 599                      $legend_params = $field_params['legend_params'];
 600                      unset( $field_params['legend_params'] );
 601                  }
 602  
 603                  $r = str_replace( '$fieldset_attribs$', get_field_attribs_as_string($field_params), $this->fieldset_begin );
 604  
 605                  $r = str_replace( '$fieldset_title$', $title, $r );
 606                  if( isset($field_params['id']) )
 607                  {
 608                      $r = str_replace( '$id$', $field_params['id'], $r );
 609                  }
 610                  $r = str_replace( '$class$', $field_params['class'], $r );
 611  
 612                  if( empty($legend_params) )
 613                  { // there are no legend_params, remove the placeholder
 614                      $r = str_replace( '$title_attribs$', '', $r );
 615                  }
 616                  else
 617                  {
 618                      $r = str_replace( '$title_attribs$', get_field_attribs_as_string($legend_params), $r );
 619                  }
 620  
 621                  // Remove any empty legend tags: they cause a small gap in the fieldset border (FF 2.0.0.11)
 622                  $r = preg_replace('~<legend[^>]*></legend>~', '', $r);
 623  
 624                  $this->_opentags['fieldset']++;
 625          }
 626  
 627          return $this->display_or_return( $r );
 628      }
 629  
 630  
 631      /**
 632       * Ends a fieldset.
 633       *
 634       * @return true|string true (if output) or the generated HTML if not outputting
 635       */
 636  	function end_fieldset()
 637      {
 638          switch( $this->layout )
 639          {
 640              case 'table':
 641                  $r = '';
 642                  break;
 643  
 644              default:
 645                  $r = $this->fieldset_end;
 646                  $this->_opentags['fieldset']--;
 647          }
 648  
 649          return $this->display_or_return( $r );
 650      }
 651  
 652  
 653      /**
 654       * Builds a text (or password) input field.
 655       *
 656       * Note: please use {@link Form::password_input()} for password fields.
 657       *
 658       * @param string The name of the input field. This gets used for id also, if no id given in $field_params.
 659       * @param string Initial value
 660       * @param integer Size of the input field
 661       * @param string Label displayed with the field
 662       * @param string "help" note (Should provide something useful, otherwise leave it empty)
 663       * @param array Extended attributes/params.
 664       *                 - 'maxlength': if not set, $field_size gets used (use '' to disable it)
 665       *                 - 'class': the CSS class to use for the <input> element
 666       *                 - 'type': 'text', 'password' (defaults to 'text')
 667       *                 - 'force_to': 'UpperCase' (JS onchange handler)
 668       *                 - NOTE: any other attributes will be used as is (onchange, onkeyup, id, ..).
 669       * @return true|string true (if output) or the generated HTML if not outputting
 670       */
 671  	function text_input( $field_name, $field_value, $field_size, $field_label, $field_note = '', $field_params = array() )
 672      {
 673          $field_params = array_merge( array(
 674                  'inline'    => false,
 675                  'type'      => 'text',
 676                  'value'     => $field_value,
 677                  'note'      => $field_note,
 678                  'size'      => $field_size,
 679                  'maxlength' => $field_size,
 680                  'name'      => $field_name,
 681                  'label'     => $field_label,
 682                  'class'     => '', // default class 'form_text_input'
 683              ), $field_params );
 684  
 685          if( isset( $field_params['force_to'] ) )
 686          {
 687              if( $field_params['force_to'] == 'UpperCase' )
 688              { // Force input to uppercase (at front of onchange event)
 689                  $field_params['onchange'] = 'this.value = this.value.toUpperCase();'
 690                      .( empty( $field_params['onchange'] ) ? '' : ' '.$field_params['onchange'] );
 691              }
 692              unset( $field_params['force_to'] ); // not a html attrib
 693          }
 694  
 695          // Give it a class, so it can be selected for CSS in IE6
 696          $field_params['class'] = empty( $field_params['class'] ) ? 'form_text_input' : $field_params['class'].' form_text_input';
 697  
 698          return $this->input_field( $field_params );
 699      }
 700  
 701  
 702      /**
 703       * Builds a text (or password) input field.
 704       *
 705       * Note: please use {@link Form::password()} for password fields
 706       *
 707       * @param string the name of the input field
 708       * @param string initial value
 709       * @param integer size of the input field
 710       * @param string label displayed in front of the field
 711       * @param string note displayed with field
 712       * @param integer max length of the value (if 0 field_size will be used!)
 713       * @param string the CSS class to use
 714       * @param string input type (only 'text' or 'password' makes sense)
 715       * @return mixed true (if output) or the generated HTML if not outputting
 716       */
 717  	function text( $field_name, $field_value, $field_size, $field_label, $field_note = '',
 718                                              $field_maxlength = 0, $field_class = '', $inputtype = 'text', $force_to = '' )
 719      {
 720          $field_params = array();
 721  
 722          if( $field_maxlength !== 0 )
 723          {
 724              $field_params['maxlength'] = $field_maxlength;
 725          }
 726          if( $field_class !== '' )
 727          {
 728              $field_params['class'] = $field_class;
 729          }
 730          if( $inputtype !== 'text' )
 731          {
 732              $field_params['type'] = $inputtype;
 733          }
 734          if( $force_to !== '' )
 735          {
 736              $field_params['force_to'] = $force_to;
 737          }
 738  
 739          return $this->text_input( $field_name, $field_value, $field_size, $field_label, $field_note, $field_params );
 740      }
 741  
 742  
 743      /**
 744       * Builds a color input field.
 745       *
 746       * @param string The name of the input field. This gets used for id also, if no id given in $field_params.
 747       * @param string Initial value
 748       * @param string Label displayed with the field
 749       * @param string "help" note (Should provide something useful, otherwise leave it empty)
 750       * @param array Extended attributes/params.
 751       *                 - 'maxlength': if not set, $field_size gets used (use '' to disable it)
 752       *                 - 'class': the CSS class to use for the <input> element
 753       *                 - 'type': 'text', 'password' (defaults to 'text')
 754       *                 - 'force_to': 'UpperCase' (JS onchange handler)
 755       *                 - NOTE: any other attributes will be used as is (onchange, onkeyup, id, ..).
 756       * @return true|string true (if output) or the generated HTML if not outputting
 757       */
 758  	function color_input( $field_name, $field_value, $field_label, $field_note = '', $field_params = array() )
 759      {
 760          $field_params = array_merge( array(
 761                  'inline'    => false,
 762                  'type'      => 'text',
 763                  'value'     => $field_value,
 764                  'note'      => $field_note,
 765                  'size'      => 7,
 766                  'maxlength' => 7,
 767                  'name'      => $field_name,
 768                  'label'     => $field_label,
 769                  'class'     => '', // default class 'form_text_input form_color_input'
 770              ), $field_params );
 771  
 772          if( isset( $field_params['force_to'] ) )
 773          {
 774              if( $field_params['force_to'] == 'UpperCase' )
 775              { // Force input to uppercase (at front of onchange event)
 776                  $field_params['onchange'] = 'this.value = this.value.toUpperCase();'
 777                      .( empty( $field_params['onchange'] ) ? '' : ' '.$field_params['onchange'] );
 778              }
 779              unset( $field_params['force_to'] ); // not a html attrib
 780          }
 781  
 782          // Give it a class, so it can be selected for CSS in IE6
 783          $field_params['class'] = empty( $field_params['class'] ) ? 'form_text_input' : $field_params['class'].' form_text_input';
 784          $field_params['class'] .= ' form_color_input';
 785  
 786          return $this->input_field( $field_params );
 787      }
 788  
 789  
 790      /**
 791       * Builds a password input field.
 792       *
 793       * Calls the text_input() method with type == 'password'.
 794       *
 795       * @param string The name of the input field. This gets used for id also, if no id given in $field_params.
 796       * @param string Initial value
 797       * @param integer Size of the input field
 798       * @param string Label displayed in front of the field
 799       * @param string Note displayed with field
 800       * @param integer Max length of the value (if 0 field_size will be used!)
 801       * @param string Extended attributes, see {@link text_input()}.
 802       * @return mixed true (if output) or the generated HTML if not outputting
 803       */
 804  	function password_input( $field_name, $field_value, $field_size, $field_label, $field_params = array() )
 805      {
 806          $field_params['type'] = 'password';
 807  
 808          return $this->text_input( $field_name, $field_value, $field_size, $field_label, '', $field_params );    // TEMP: Note already in params
 809      }
 810  
 811  
 812      /**
 813       * Builds a password input field.
 814       *
 815       * Calls the text() method with a 'password' parameter.
 816       *
 817       * @param string the name of the input field
 818       * @param string initial value
 819       * @param integer size of the input field
 820       * @param string label displayed in front of the field
 821       * @param string note displayed with field
 822       * @param integer max length of the value (if 0 field_size will be used!)
 823       * @param string the CSS class to use
 824       * @return mixed true (if output) or the generated HTML if not outputting
 825       */
 826  	function password( $field_name, $field_value, $field_size, $field_label, $field_note = '',
 827                                              $field_maxlength = 0 , $field_class = '' )
 828      {
 829          $field_params = array( 'type' => 'password' );
 830  
 831          if( $field_maxlength !== 0 )
 832          {
 833              $field_params['maxlength'] = $field_maxlength;
 834          }
 835          if( !empty($field_class) )
 836          {
 837              $field_params['class'] = $field_class;
 838          }
 839  
 840          return $this->text_input( $field_name, $field_value, $field_size, $field_label, $field_note, $field_params );
 841      }
 842  
 843  
 844      /**
 845       * Build username/login field.
 846       *
 847       * @param string the name of the input field
 848       * @param User initial value
 849       * @param integer size of the input field
 850       * @param string label displayed in front of the field
 851       * @param string note displayed with field
 852       * @return mixed true (if output) or the generated HTML if not outputting
 853       */
 854  	function username( $field_name, &$User, $field_label, $field_note = '' )
 855      {
 856          global $htsrv_url;
 857  
 858          $field_params = array();
 859  
 860          if( !empty($field_note) )
 861          {
 862              $field_params['note'] = $field_note;
 863          }
 864  
 865          $this->handle_common_params( $field_params, $field_name, $field_label );
 866  
 867          $r = $this->begin_field();
 868  
 869          // Add jQuery hintbox (autocompletion).
 870          // Form 'username' field requires the following JS and CSS.
 871          // fp> TODO: think about a way to bundle this with other JS on the page -- maybe always load hintbox in the backoffice
 872          //     dh> Handle it via http://www.appelsiini.net/projects/lazyload ?
 873          // dh> TODO: should probably also get ported to use jquery.ui.autocomplete (or its successor)
 874          global $rsc_url;
 875          $r .= '<script type="text/javascript" src="'.$rsc_url.'js/jquery/jquery.hintbox.min.js"></script>';
 876          $r .= '<script type="text/javascript">jQuery("<link>").appendTo("head").attr({
 877              rel: "stylesheet",
 878              type: "text/css",
 879              href: "'.$rsc_url.'css/jquery/jquery.hintbox.css"
 880              });</script>';
 881  
 882          $r .= '<script type="text/javascript">';
 883          $r .= 'jQuery(function(){';
 884          $r .= 'jQuery(\'#'.$field_name.'\').hintbox({';
 885          $r .= ' url: \''.$htsrv_url.'async.php?action=get_login_list\',';
 886          $r .= ' matchHint: true,';
 887          $r .= ' autoDimentions: true';
 888          $r .= '});';
 889          $r .= '});';
 890          $r .= '</script>';
 891          $r .= '<input type="text" class="form_text_input" value="'.$User->login.'" name="'.$field_name.'" id="'.$field_name.'" />';
 892  
 893          $r .= $this->end_field();
 894  
 895          return $this->display_or_return( $r );
 896      }
 897  
 898  
 899      /**
 900       * Builds a date input field.
 901       *
 902       * @param string the name of the input field
 903       * @param string initial value (ISO datetime (YYYY-MM-DD HH:MM:SS)
 904       *               or erroneous if the field is in error state)
 905       * @param string label displayed in front of the field
 906       * @param array Optional params. Additionally to {@link $_common_params} you can use:
 907       *              - date_format: Format of the date (string, PHP format, default taken from {@link locale_datefmt()})
 908       *              - add_date_format_note: If true, date format note gets prepended to the field's note
 909       * @return mixed true (if output) or the generated HTML if not outputting
 910       */
 911  	function date_input( $field_name, $field_value, $field_label, $field_params = array() )
 912      {
 913          global $month, $weekday_letter;
 914  
 915          if( empty($field_params['date_format']) )
 916          {    // Use locale date format:
 917              $date_format = locale_datefmt();
 918          }
 919          else
 920          {
 921              $date_format = $field_params['date_format'];
 922          }
 923          // Don't keep that attrib in the list:
 924          unset( $field_params['date_format'] );
 925  
 926          // Convert PHP date format to JS library date format:
 927          // NOTE: when editing/extending this here, you probably also have to adjust param_check_date()!
 928          $js_date_format = preg_replace_callback( '~(\\\)?(\w)~', create_function( '$m', '
 929              if( $m[1] == "\\\" ) return "\\\".$m[0]; // leave escaped
 930              switch( $m[2] )
 931              {
 932                  case "d": return "dd"; // day, 01-31
 933                  case "j": return "d"; // day, 1-31
 934                  case "l": return "EE"; // weekday (name)
 935                  case "D": return "E"; // weekday (abbr)
 936                  case "e": return ""; // weekday letter, not supported
 937  
 938                  case "m": return "MM"; // month, 01-12
 939                  case "n": return "M"; // month, 1-12
 940                  case "F": return "MMM"; // full month name; "name or abbr" in date.js
 941                  case "M": return "NNN"; // month name abbr
 942  
 943                  case "y": return "yy"; // year, 00-99
 944                  case "Y": return "yyyy"; // year, XXXX
 945                  default:
 946                      return $m[0];
 947              }' ), $date_format );
 948  
 949  
 950          if( param_has_error( $field_name )
 951              &&  ! preg_match('~^\d\d\d\d-\d\d-\d\d(?: \d\d:\d\d:\d\d)?$~', $field_value) )
 952          { // There is an error message for this field:
 953  
 954              // We do not try to format the date, we keep the erroneous date (if it is not obviously valid).
 955              // We could have used param_error() ourself (e.g. "date outside of range"), and the erroneous
 956              // field should have the correct format.
 957  
 958              //echo 'error on '.$field_name.' keep erroneous entry intact ';
 959  
 960              // Keep original value, but strip off the time part (if any).
 961              $field_params['value'] = preg_replace( '~ \d\d:\d\d:\d\d$~', '', $field_value );
 962          }
 963          else
 964          { // Make the date value clean for display:
 965  
 966              // The date value may be compact, in this case we have to decompact it
 967              if( preg_match( '/^[0-9]+$/', $field_value ) )
 968              {    // The date is compact, so we decompact it
 969                  $field_value = decompact_date( $field_value );
 970              }
 971  
 972              // Get DATE part of datetime and format it to locale format:
 973              $field_params['value'] = mysql2date( $date_format, $field_value );
 974              if( ! $field_params['value'] )
 975              { // Conversion failed (e.g. for dates before ~1902 / PHP < 5.1 (Windows) / 32bit / without DateTime support), use the original value (better than 1970-01-01 anyway!).
 976                  $field_params['value'] = $field_value;
 977              }
 978          }
 979  
 980  
 981          if( !empty($field_params['add_date_format_note']) )
 982          { // Prepend $date_format to note
 983              $field_params['note'] = empty($field_params['note'])
 984                  ? '('.$date_format.')'
 985                  : '('.$date_format.') '.$field_params['note'];
 986          }
 987          unset( $field_params['add_date_format_note'] );
 988  
 989  
 990          if( !isset($field_params['size']) )
 991          { // Get size out of $date_format if not explicitly set
 992              $field_params['size'] = strlen( $js_date_format );
 993          }
 994  
 995          /*
 996          dh> do not use maxlength by default. Makes no sense IMHO and fails with dateformats like "j \d\e F, Y"
 997          if( !isset($field_params['maxlength']) )
 998          {
 999              $field_params['maxlength'] = $field_params['size'];
1000          }
1001          */
1002          /*
1003           Afwas > In the existing locales only d m y and Y are used. Currently the jQuery Datepicker
1004           can't handle other dateformats. I will see to some basic check or enable all.
1005           @TODO ^^ fp> It might make sense to have 2 date formats for locales: 1 for display and 1 for inputs. Input formats could be forced to used numeric data only.
1006           */
1007          // Give it a class, so it can be selected for CSS in IE6
1008          if( empty($field_params['class']) ) $field_params['class'] = 'form_date_input';
1009          else $field_params['class'] .= ' form_date_input';
1010  
1011          $this->handle_common_params( $field_params, $field_name, $field_label );
1012  
1013          $r = $this->begin_field() . $this->get_input_element($field_params, false);
1014          $r .= $this->end_field();
1015  
1016          return $this->display_or_return( $r );
1017      }
1018  
1019  
1020      /**
1021       * Builds a date input field.
1022       *
1023       * @param string the name of the input field
1024       * @param string initial value (ISO datetime)
1025       * @param string label displayed in front of the field
1026       * @param string date format (php format)
1027       * @return mixed true (if output) or the generated HTML if not outputting
1028       */
1029  	function date( $field_name, $field_value, $field_label, $date_format = NULL )
1030      {
1031          $field_params = array( 'date_format' => $date_format, 'type' => 'text' );
1032  
1033          return $this->date_input( $field_name, $field_value, $field_label, $field_params );
1034      }
1035  
1036  
1037      /**
1038       * Builds a time input field.
1039       *
1040       * @param string The name of the input field
1041       * @param string Initial value (ISO datetime)
1042       * @param string Label displayed in front of the field
1043       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1044       *              - 'time_format': Format of the time (string, default 'hh:mm:ss')
1045       * @return mixed true (if output) or the generated HTML if not outputting
1046       */
1047  	function time_input( $field_name, $field_value, $field_label, $field_params = array() )
1048      {
1049          if( isset($field_params['time_format']) )
1050          {
1051              $field_format = $field_params['time_format'];
1052              unset( $field_params['time_format'] ); // not an attribute
1053          }
1054          else
1055          {
1056              $field_format = 'hh:mm:ss';
1057          }
1058  
1059          // Use format as field note
1060          if( ! isset($field_params['note']) )
1061          {
1062              $field_params['note'] = '('.$field_format.')';
1063          }
1064  
1065          if( ! isset($field_params['size']) )
1066          {
1067              $field_size = strlen($field_format);
1068          }
1069          else
1070          {
1071              $field_size = $field_params['size'];
1072          }
1073  
1074          // Get time part of datetime:
1075          $field_value = substr( $field_value, 11, $field_size );
1076  
1077          return $this->text_input( $field_name, $field_value, $field_size, $field_label, '', $field_params ); // TEMP: Note already in params
1078      }
1079  
1080  
1081      /**
1082       * Builds a time input field.
1083       *
1084       * @param string the name of the input field
1085       * @param string initial value (ISO datetime)
1086       * @param string label displayed in front of the field
1087       * @return mixed true (if output) or the generated HTML if not outputting
1088       */
1089  	function time( $field_name, $field_value, $field_label, $field_format = 'hh:mm:ss', $note = NULL )
1090      {
1091          $field_params = array(
1092                  'time_format' => $field_format,
1093                  'note' => $note,
1094              );
1095  
1096          return $this->time_input( $field_name, $field_value, $field_label, $field_params );
1097      }
1098  
1099  
1100      /**
1101       * Builds a time select input field
1102       *
1103       * @param string field name
1104       * @param string initial value (ISO datetime or time only)
1105       * @param string precison xmn or xsec (x:integer) for the options minutes or secondes
1106       * @param string field label to be display before the field
1107       * @param string note to be displayed after the field
1108       * @param string CSS class for select
1109       * @param string Javascript to add for onchange event (trailing ";").
1110       */
1111  	function time_select( $field_name, $field_value = NULL, $precision = '5mn', $field_label, $field_note = NULL, $field_class = NULL, $field_onchange = NULL )
1112      {
1113          preg_match( '#([0-9]+)(mn|s)#', $precision, $matches );
1114  
1115          if( !isset( $matches[1] ) && !isset( $matches[2] ) )
1116          {    // precison has a bad format
1117              return;
1118          }
1119  
1120          $field_params = array(
1121          'note' => $field_note,
1122          'class' => $field_class,
1123          'onchange' => $field_onchange );
1124  
1125          /***  instantiate the precison for the minutes and secondes select options  ****/
1126  
1127          if( $matches[2] == 'mn' )
1128          {
1129              $precision_mn = $matches[1];
1130              $precision_s = 0;
1131              // convert the precision in sec
1132              $precision *= 60;
1133          }
1134          else
1135          {
1136              $precision_mn = 1;
1137              $precision_s = $matches[1];
1138          }
1139  
1140          // Check if field value is only a time
1141          if( strlen( $field_value ) <= 8 )
1142          {    // Add date part:
1143              $field_value = '2000-01-01 '.$field_value;
1144          }
1145  
1146          /***  set round time with the precision  ***/
1147          // Get nb sec since unix...
1148          $nbsec = mysql2timestamp( $field_value );
1149          $modulo =  $nbsec % $precision;
1150  
1151          if( $modulo < ( $precision / 2 ) )
1152          { // The round time is before
1153              $nbsec -= $modulo;
1154          }
1155          else
1156          { // The round time is after
1157              $nbsec += $precision - $modulo;
1158          }
1159  
1160          /******************************************************/
1161  
1162          $this->handle_common_params( $field_params, $field_name, $field_label );
1163  
1164          $r = $this->begin_field();
1165  
1166          /**********   select options for the hours *************/
1167  
1168          $field_params['name'] = $field_name . '_h';
1169          $field_params['id'] = $field_params['name'];
1170          // Get Hour part of datetime:
1171          $hour = date( 'H', $nbsec );
1172  
1173          $r .= $this->_number_select(  $hour, 23 , 1, $field_params);
1174  
1175          /*********  select options for the minutes *************/
1176  
1177          $field_params['name'] = $field_name . '_mn';
1178          $field_params['id'] = $field_params['name'];
1179          // Get Minute part of datetime:
1180          $minute = date('i',$nbsec);
1181  
1182          $r .= ':'.$this->_number_select(  $minute, 59, $precision_mn, $field_params);
1183  
1184          if( $precision_s )
1185          {/*********  select options for the minutes  ***********/
1186  
1187              $field_params['name'] = $field_name . '_s';
1188              $field_params['id'] = $field_params['name'];
1189              // Get Secondes part of datetime:
1190              $seconde = substr( $field_value, 17, 2 );
1191  
1192              $r .=':'.$this->_number_select(  $seconde, 59, $precision_s, $field_params);
1193          }
1194  
1195          $r .= $this->end_field();
1196  
1197          return $this->display_or_return( $r );
1198      }
1199  
1200  
1201      /**
1202       * Build a select input field number
1203       * @access private
1204       *
1205       * @param string     field value of selected
1206       * @param integer maximum value for the input select
1207       * @param integer increment for the loop (precision)
1208       * @param array params
1209       */
1210  	function _number_select( $field_value, $max, $precision = 1, $field_params )
1211      {
1212              $r    =    '<select'.get_field_attribs_as_string( $field_params ).'>';
1213  
1214              for( $i=0; $i <= $max ; $i += $precision)
1215              {
1216                  $val = sprintf( '%02d', $i );
1217                  $r .= '<option value="'.$val.'"'.
1218                                  ($field_value == $val ? ' selected="selected"' : '') .
1219                                  '>'    .$val.'</option>';
1220              }
1221  
1222              $r .= '</select>';
1223  
1224              return $r;
1225      }
1226  
1227  
1228      /**
1229       * Builds a duration input field.
1230       *
1231       * @todo fp> move params array to the end
1232       *
1233       * @param string the name of the input field
1234       * @param string initial value (seconds)
1235       * @param string label displayed in front of the field
1236       * @param string display from field: months, days, hours, minutes, seconds
1237       * @param string display to field: months, days, hours, minutes, seconds
1238       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1239       *              - minutes_step ( default = 15 );
1240       * @return mixed true (if output) or the generated HTML if not outputting
1241       */
1242  	function duration_input( $field_prefix, $duration, $field_label, $from_subfield = 'days', $to_subfield = 'minutes', $field_params = array() )
1243      {
1244          $this->handle_common_params( $field_params, $field_prefix, $field_label );
1245  
1246          $r = $this->begin_field();
1247  
1248          switch( $from_subfield )
1249          {
1250              case 'months':
1251                  // Display months field
1252                  $month_seconds = 2592000; // 1 month
1253                  $months = floor( $duration / $month_seconds );
1254                  $duration = $duration - $months * $month_seconds;
1255                  $r .= "\n".'<select name="'.$field_prefix.'_months" id="'.$this->get_valid_id($field_prefix).'_months">';
1256                  $r .= '<option value="0"'.( 0 == $months ? ' selected="selected"' : '' ).">---</option>\n";
1257                  for( $i = 1; $i <= 30; $i++ )
1258                  {
1259                      $r .= '<option value="'.$i.'"'.( $i == $months ? ' selected="selected"' : '' ).'>'.$i."</option>\n";
1260                  }
1261                  $r .= '</select>'.T_('months')."\n";
1262  
1263                  if( $to_subfield == 'months' ) break;
1264  
1265              case 'days':
1266                  // Display days field
1267                  $day_seconds = 86400; // 1 day
1268                  $days = floor( $duration / $day_seconds );
1269                  $duration = $duration - $days * $day_seconds;
1270                  $r .= "\n".'<select name="'.$field_prefix.'_days" id="'.$this->get_valid_id($field_prefix).'_days">';
1271                  $r .= '<option value="0"'.( 0 == $days ? ' selected="selected"' : '' ).">---</option>\n";
1272                  for( $i = 1; $i <= 30; $i++ )
1273                  {
1274                      $r .= '<option value="'.$i.'"'.( $i == $days ? ' selected="selected"' : '' ).'>'.$i."</option>\n";
1275                  }
1276                  $r .= '</select>'.T_('days')."\n";
1277  
1278                  if( $to_subfield == 'days' ) break;
1279  
1280              case 'hours':
1281                  // Display hours field
1282                  $hour_seconds = 3600; // 1 hour
1283                  $hours = floor( $duration / $hour_seconds );
1284                  $duration = $duration - $hours * $hour_seconds;
1285                  $r .= "\n".'<select name="'.$field_prefix.'_hours" id="'.$this->get_valid_id($field_prefix).'_hours">';
1286                  $r .= '<option value="0"'.( 0 == $hours ? ' selected="selected"' : '' ).">---</option>\n";
1287                  for( $i = 1; $i <= 23; $i++ )
1288                  {
1289                      $r .= '<option value="'.$i.'"'.( $i == $hours ? ' selected="selected"' : '' ).'>'.$i."</option>\n";
1290                  }
1291                  $r .= '</select>'.T_('hours')."\n";
1292  
1293                  if( $to_subfield == 'hours' ) break;
1294  
1295              case 'minutes':
1296                  // Display minutes field
1297                  $minute_seconds = 60; // 1 minute
1298                  $minutes = floor( $duration / $minute_seconds );
1299                  $duration = $duration - $minutes * $minute_seconds;
1300                  $minutes_step = ( empty($field_params['minutes_step']) ? 15 : $field_params['minutes_step'] );
1301                  $r .= "\n".'<select name="'.$field_prefix.'_minutes" id="'.$this->get_valid_id($field_prefix).'_minutes">';
1302                  for( $i = 0; $i <= 59 ; $i += $minutes_step )
1303                  {
1304                      $r .= '<option value="'.$i.'"'.( ($minutes>=$i && $minutes<($i+$minutes_step)) ? ' selected="selected"' : '' ).'>'
1305                                  .($i == 0 ? '---' : substr('0'.$i,-2))."</option>\n";
1306                  }
1307                  $r .= '</select>'.T_('minutes')."\n";
1308  
1309                  if( $to_subfield == 'minutes' ) break;
1310  
1311              case 'seconds':
1312                  // Display seconds field
1313                  $seconds = $duration;
1314                  $r .= "\n".'<select name="'.$field_prefix.'_seconds" id="'.$this->get_valid_id($field_prefix).'_seconds">';
1315                  $r .= '<option value="0"'.( 0 == $seconds ? ' selected="selected"' : '' ).">---</option>\n";
1316                  for( $i = 1; $i <= 59; $i++ )
1317                  {
1318                      $r .= '<option value="'.$i.'"'.( $i == $seconds ? ' selected="selected"' : '' ).'>'.$i."</option>\n";
1319                  }
1320                  $r .= '</select>'.T_('seconds')."\n";
1321          }
1322  
1323          $r .= $this->end_field();
1324  
1325          return $this->display_or_return( $r );
1326      }
1327  
1328  
1329      /**
1330       * Builds a duration input field.
1331       *
1332       * @param string the name of the input field
1333       * @param string initial value (seconds)
1334       * @param string label displayed in front of the field
1335       * @return mixed true (if output) or the generated HTML if not outputting
1336       */
1337  	function duration( $field_prefix, $duration, $field_label )
1338      {
1339          return $this->duration_input( $field_prefix, $duration, $field_label );
1340      }
1341  
1342  
1343      /**
1344       * Build a select to choose a weekday.
1345       *
1346       * @uses select_input_options()
1347       *
1348       * @return true|string
1349       */
1350  	function dayOfWeek_input( $field_name, $field_value, $field_label, $field_params = array() )
1351      {
1352          global $weekday_abbrev;
1353  
1354          $field_options = '';
1355  
1356          foreach( $weekday_abbrev as $lNumber => $lWeekday )
1357          {
1358              $field_options .= '<option';
1359  
1360              if( $field_value == $lNumber )
1361              {
1362                  $field_options .= ' selected="selected"';
1363              }
1364              $field_options .= ' value="'.$lNumber.'">'.T_($lWeekday).'</option>';
1365          }
1366  
1367          return $this->select_input_options( $field_name, $field_options, $field_label, '', $field_params );
1368      }
1369  
1370  
1371      /**
1372       * Build a select to choose a weekday.
1373       *
1374       * @return true|string
1375       */
1376  	function dayOfWeek( $field_name, $field_value, $field_label, $field_note = NULL, $field_class = NULL )
1377      {
1378          $field_params = array();
1379          if( isset($field_note) )
1380          {
1381              $field_params['note'] = $field_note;
1382          }
1383  
1384          if( isset($field_class) )
1385          {
1386              $field_params['class'] = $field_class;
1387          }
1388  
1389          return $this->dayOfWeek_input( $field_name, $field_value, $field_label, $field_params );
1390      }
1391  
1392  
1393      /**
1394       * Builds a checkbox field
1395       *
1396       * @param string the name of the checkbox
1397       * @param boolean indicating if the checkbox must be checked by default
1398       * @param string label
1399       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1400       *              'value': the value attribute of the checkbox (default 1)
1401       * @return mixed true (if output) or the generated HTML if not outputting
1402       */
1403  	function checkbox_input( $field_name, $field_checked, $field_label, $field_params = array() )
1404      {
1405          $field_params = array_merge( array(
1406                  'value' => 1,
1407                  'name'  => $field_name,
1408                  'label' => $field_label,
1409                  'type'  => 'checkbox',
1410                  'class' => 'checkbox',
1411              ), $field_params );
1412  
1413          if( $field_checked )
1414          {
1415              $field_params['checked'] = 'checked';
1416          }
1417  
1418          return $this->input_field( $field_params );
1419      }
1420  
1421  
1422      /**
1423       * EXPERIMENTAL: simpler method of obtaining basic checkboxes
1424       */
1425  	function checkbox_basic_input( $field_name, $field_checked, $field_label, $field_params = array() )
1426      {
1427          $field_params = array_merge( array(
1428                  'name'  => $field_name,
1429                  'type'  => 'checkbox',
1430                  'value' => 1,
1431                  'class' => 'checkbox',
1432              ), $field_params );
1433  
1434          if( $field_checked )
1435          {
1436              $field_params['checked'] = 'checked';
1437          }
1438  
1439          echo '<label>';
1440          echo $this->get_input_element($field_params);
1441          echo $field_label;
1442          echo '</label>';
1443      }
1444  
1445  
1446      /**
1447       * Builds a checkbox field
1448       *
1449       * @param string the name of the checkbox
1450       * @param boolean indicating if the checkbox must be checked
1451       * @param string label
1452       * @param string note
1453       * @param string CSS class
1454       * @param string value to use
1455       * @param boolean an optional indicating whether the box is disabled or not
1456       * @return mixed true (if output) or the generated HTML if not outputting
1457       */
1458  	function checkbox( $field_name, $field_checked, $field_label, $field_note = '',
1459                                              $field_class = '', $field_value = 1, $field_disabled = false )
1460      {
1461          $field_params = array();
1462  
1463          if( $field_note !== '' )
1464          {
1465              $field_params['note'] = $field_note;
1466          }
1467          if( $field_class !== '' )
1468          {
1469              $field_params['class'] = $field_class;
1470          }
1471          if( $field_value !== 1 )
1472          {
1473              $field_params['value'] = $field_value;
1474          }
1475          if( $field_disabled != false )
1476          {
1477              $field_params['disabled'] = 'disabled';
1478          }
1479  
1480          return $this->checkbox_input( $field_name, $field_checked, $field_label, $field_params );
1481      }
1482  
1483  
1484      /**
1485       * Return links to check and uncheck all check boxes of the form
1486       */
1487  	function check_all()
1488      {
1489          // Need to add event click on links at the form end.
1490          $this->check_all = true;
1491  
1492          // fp> This is "name=" and I mean it!!! The JS is looking for all elements with this name!
1493          return '<a name="check_all_nocheckchanges" href="'.regenerate_url().'">'
1494                  //.T_('Check all').' '
1495                  .get_icon( 'check_all', 'imgtag', NULL, true )
1496                  .'</a> | <a name="uncheck_all_nocheckchanges" href="'.regenerate_url().'">'
1497                  //.T_('Uncheck all').' '
1498                  .get_icon( 'uncheck_all', 'imgtag', NULL, true ).'</a> '.'&nbsp;';
1499      }
1500  
1501  
1502      /**
1503       * Builds the form field
1504       *
1505       * @param string the class to use for the form tag
1506       * @param string title to display on top of the form
1507       * @param array Additional params to the form element. See {@link $_common_params}.
1508       *              These may override class members.
1509       * @return mixed true (if output) or the generated HTML if not outputting
1510       */
1511  	function begin_form( $form_class = NULL, $form_title = '', $form_params = array() )
1512      {
1513          global $use_strict;
1514          if( $use_strict ) unset( $form_params[ 'target' ] );// target isn't valid for XHTML Strict
1515  
1516          $this->handle_common_params( $form_params, NULL /* "name" attribute is deprecated in xhtml */ );
1517  
1518          if( ! empty($this->form_name) )
1519          {
1520              $form_params['id'] = $this->form_name;
1521          }
1522  
1523          // Set non-mandatory attributes if given in $form_params
1524          if( !isset($form_params['enctype']) && !empty( $this->enctype ) )
1525          {
1526              $form_params['enctype'] = $this->enctype;
1527          }
1528  
1529          if( !isset($form_params['class']) && !empty( $form_class ) )
1530          {
1531              $form_params['class'] = $form_class;
1532          }
1533  
1534          if( !isset($form_params['method']) )
1535          {
1536              $form_params['method'] = $this->form_method;
1537          }
1538  
1539          if( !isset($form_params['action']) )
1540          {
1541              $form_params['action'] = $this->form_action;
1542          }
1543  
1544          if( !empty($form_params['bozo_start_modified']) )
1545          {
1546              $bozo_start_modified = true;
1547              unset( $form_params['bozo_start_modified'] );
1548          }
1549          unset( $form_params['disp_edit_categories'] );
1550          unset( $form_params['edit_form_params'] );
1551          unset( $form_params['skin_form_params'] );
1552  
1553          $r = "\n\n<form".get_field_attribs_as_string($form_params).">\n";
1554  
1555          // $r .= '<div>'; // for XHTML (dh> removed 'style="display:inline"' because it's buggy with FireFox 1.0.x, at least at the "Write" admin page; see http://forums.b2evolution.net/viewtopic.php?t=10130)
1556          // fp> inline was needed for inline forms like the DELETE confirmation.
1557          // fp> why does XHTML require all forms to have an embedded DIV?
1558  
1559          $r .= $this->formstart;
1560  
1561          if( empty($form_title) )
1562          {
1563              $r .= $this->replace_vars( $this->no_title_fmt );
1564          }
1565          else
1566          {
1567              $this->title = $form_title;
1568  
1569              $r .= $this->replace_vars( $this->title_fmt );
1570          }
1571  
1572          // Initialization of javascript vars used to create parent_child select lists
1573          // TODO: does this make sense to add it to every form??
1574          $r .= '<script type="text/javascript">
1575                              var nb_dynamicSelects = 0;
1576                              var tab_dynamicSelects = Array();
1577                          </script>';
1578  
1579          global $UserSettings;
1580          if( isset($UserSettings) && $UserSettings->get( 'control_form_abortions' )
1581              && preg_match( '#^(.*)_checkchanges#', $this->form_name ) )
1582          { // This form will trigger the bozo validator, preset a localized bozo confirm message:
1583  
1584              $r .= '<script type="text/javascript">
1585                      if( typeof bozo == "object" )
1586                      {    // If Bozo validator is active:
1587                          bozo.confirm_mess = \'';
1588              if( empty( $this->title ) )
1589              { // No form title:
1590                  $r .= TS_( 'You have modified this form but you haven\'t submitted it yet.\nYou are about to lose your edits.\nAre you sure?' );
1591              }
1592              else
1593              { // with form title:
1594                  $r .= sprintf( TS_( 'You have modified the form \"%s\"\nbut you haven\'t submitted it yet.\nYou are about to lose your edits.\nAre you sure?' ), $this->title );
1595              }
1596  
1597              $r .= '\';';
1598  
1599              if(    !empty($bozo_start_modified) )
1600              {
1601                  $r .= '
1602                      // Update number of changes for this form:
1603                      bozo.tab_changes["'.$this->form_name.'"] = 1;
1604                      // Update Total # of changes:
1605                      bozo.nb_changes++;
1606                  ';
1607              }
1608  
1609  
1610              $r .='    }
1611                  </script>';
1612          }
1613  
1614          return $this->display_or_return( $r );
1615      }
1616  
1617  
1618      /**
1619       * Ends the form, optionally displays buttons and closes all open fieldsets.
1620       *
1621       * @param array Optional array to display the buttons before the end of the form, see {@link buttons_input()}
1622       * @return true|string true (if output) or the generated HTML if not outputting.
1623       */
1624  	function end_form( $buttons = array() )
1625      {
1626          if( $this->_form_ended )
1627          {    // The form is already ended
1628              return;
1629          }
1630  
1631          $r = '';
1632          if( !empty( $buttons ) )
1633          {
1634              $save_output = $this->output;
1635              $this->output = 0;
1636  
1637              $r .= $this->buttons( $buttons ); // converts old-style to new style, through convert_button_to_field_params()
1638  
1639              $this->output = $save_output;
1640          }
1641  
1642          while( $this->_opentags['fieldset']-- > 0 )
1643          {
1644              $r .= "\n</fieldset>\n";
1645          }
1646  
1647          $r .= $this->formend;
1648  
1649          // Add hiddens extracted from Form action:
1650          if( ! empty($this->possible_hiddens_from_action) )
1651          {
1652              foreach($this->possible_hiddens_from_action as $pair)
1653              {
1654                  list($name, $value) = $pair;
1655                  if( ! in_array($name, $this->included_input_field_names) )
1656                  {
1657                      $this->hidden($name, $value);
1658                  }
1659              }
1660          }
1661  
1662          // Display all buffered hidden fields in a 0 height DIV (for XHTML):
1663          $r .= '<div class="inline">'.implode( '', $this->hiddens ).'</div>';
1664  
1665          // $r .= '</div>';
1666          $r .= "\n</form>\n\n";
1667  
1668          // When the page loads, Initialize all the parent child select lists + other javascripts
1669          $r .= '
1670              <script type="text/javascript">
1671                  //<![CDATA[
1672                  if( typeof addEvent == "function" && typeof init_dynamicSelect == "function" )
1673                  {
1674                      addEvent( window, "load", init_dynamicSelect, false );
1675                      ';
1676                      if( $this->check_all )
1677                      { // Init check_all event on check_all links
1678                          $r .= 'addEvent( window, "load", init_check_all, false );';
1679                      }
1680                      $r .= '
1681                  }
1682                  ';
1683  
1684                  if( $this->append_javascript )
1685                  { // Append Javascript that we have added
1686                      $r .= implode( "\n", $this->append_javascript );
1687                  }
1688                  $r .= '
1689                  //]]>
1690              </script>';
1691  
1692          // Reset (in case we re-use begin_form! NOTE: DO NOT REUSE begin_form, it's against the spec.)
1693          $this->hiddens = array();
1694          $this->_form_ended = true;
1695  
1696          return $this->display_or_return( $r );
1697      }
1698  
1699  
1700      /**
1701       * Builds a checkbox list
1702       *
1703       * the two-dimension array must indicate, for each checkbox:
1704       *  - the name,
1705       *  - the value -- in practice this will often be just 1
1706       *  - the comment to put between <input> and <br />
1707       *  - "effective value": a boolean indicating whether the box should be checked or not on display
1708       *  - an optional boolean indicating whether the box is disabled or not
1709       *  - an optional note
1710       *  - 'required': is the box required to be checked (boolean; default: false)
1711       *
1712       * @todo Transform to $field_params schema.
1713       * @param array a two-dimensional array containing the parameters of the input tag
1714       * @param string name
1715       * @param string label
1716       * @param boolean true to surround checkboxes if they are required
1717       * @param boolean true add a surround_check span, used by check_all mouseover
1718       * @param array Params
1719       * @return mixed true (if output) or the generated HTML if not outputting
1720       */
1721  	function checklist( $options, $field_name, $field_label, $required = false, $add_highlight_spans = false, $field_params = array() )
1722      {
1723          $field_params = array_merge( array(
1724                  'wide' => false,
1725                  'type' => 'checkbox',
1726                  'input_prefix' => '',
1727                  'input_suffix' => '',
1728              ), $field_params );
1729  
1730          $this->handle_common_params( $field_params, $field_name, $field_label );
1731  
1732          $r = $this->begin_field( $field_name, $field_label );
1733  
1734          $r .= $field_params['input_prefix'];
1735  
1736          foreach( $options as $option )
1737          { //loop to construct the list of 'input' tags
1738  
1739              $loop_field_name = $option[0];
1740  
1741              $loop_field_note = isset($option[5]) ? $option[5] : '';
1742  
1743              // asimo>> add id for label: id = label_for_fieldname_fieldvalue
1744              $r .= '<label class="" id="label_for_'.$loop_field_name.'_'.$option[1].'">';
1745  
1746              if( $add_highlight_spans )
1747              { // Need it to highlight checkbox for check_all and uncheck_all mouseover
1748                  $r .= '<span name="surround_check" class="checkbox_surround_init">';
1749                  $after_field_highlight = '</span>';
1750              }
1751              else
1752              {
1753                  $after_field_highlight = '';
1754              }
1755  
1756              $after_field = '';
1757              if( param_has_error( $field_name ) )
1758              { // There is an error message for this field, we want to mark the checkboxes with a red border:
1759                  $r .= '<span class="checkbox_error">';
1760                  $after_field .= '</span>';
1761              }
1762              if( $required )
1763              {    //this field is required
1764                  $r .= '<span class="checkbox_required">';
1765                  $after_field .= '</span>';
1766              }
1767  
1768              $r .= "\t".'<input type="checkbox" name="'.$loop_field_name.'" value="'.$option[1].'" ';
1769              if( $option[3] )
1770              { //the checkbox has to be checked by default
1771                  $r .= ' checked="checked" ';
1772              }
1773              if( isset( $option[4] ) && $option[4] )
1774              { // the checkbox has to be disabled
1775                  $r .= ' disabled="disabled" ';
1776              }
1777              $r .= ' class="checkbox" />';
1778  
1779              $r .= $after_field;
1780  
1781              $r .= $after_field_highlight;
1782  
1783              $r .= ' '.$option[2];
1784  
1785              $r .='</label>';
1786  
1787              if( !empty($loop_field_note) )
1788              { // We want to display a note:
1789                  $r .= ' <span class="notes">'.$loop_field_note.'</span>';
1790              }
1791              $r .= "<br />\n";
1792          }
1793  
1794          $r .= $field_params['input_suffix'];
1795  
1796          $r .= $this->end_field();
1797  
1798          return $this->display_or_return( $r );
1799      }
1800  
1801  
1802      /**
1803       * Display a select field and populate it with a callback function.
1804       *
1805       * @param string field name
1806       * @param string default field value
1807       * @param callback callback function
1808       * @param string field label to be display before the field
1809       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1810       *              Nothing yet.
1811       * @return mixed true (if output) or the generated HTML if not outputting
1812       */
1813  	function select_input( $field_name, $field_value, $field_list_callback, $field_label, $field_params = array() )
1814      {
1815          $field_options = call_user_func( $field_list_callback, $field_value );
1816  
1817          return $this->select_input_options( $field_name, $field_options, $field_label, '', $field_params );
1818      }
1819  
1820  
1821      /**
1822       * Display a select field and populate it with a callback function.
1823       *
1824       * @param string field name
1825       * @param string default field value
1826       * @param callback callback function
1827       * @param string field label to be display before the field
1828       * @param string note to be displayed after the field
1829       * @param string CSS class for select
1830       * @param string Javascript to add for onchange event (trailing ";").
1831       * @return mixed true (if output) or the generated HTML if not outputting
1832       */
1833  	function select(
1834          $field_name,
1835          $field_value,
1836          $field_list_callback,
1837          $field_label,
1838          $field_note = '',
1839          $field_class = '',
1840          $field_onchange = NULL )
1841      {
1842          $field_params = array();
1843          if( $field_note !== '' )
1844          {
1845              $field_params['note'] = $field_note;
1846          }
1847          if( $field_class !== '' )
1848          {
1849              $field_params['class'] = $field_class;
1850          }
1851          if( isset($field_onchange) )
1852          {
1853              $field_params['onchange'] = $field_onchange;
1854          }
1855  
1856          return $this->select_input( $field_name, $field_value, $field_list_callback, $field_label, $field_params );
1857      }
1858  
1859  
1860      /**
1861       * Display a select field and populate it with a cache object by using a callback
1862       * method.
1863       *
1864       * @uses select_input_options()
1865       * @param string Field name
1866       * @param string Default field value
1867       * @param DataObjectCache Cache containing values for list
1868       * @param string Field label to be display with the field
1869       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1870       *              - 'allow_none': allow to select [none] in list (boolean, default false)
1871       *              - 'object_callback': Object's callback method name (string, default 'get_option_list')
1872       *              - 'loop_object_method': The method on the objects inside the callback (string, default NULL)
1873       * @return mixed true (if output) or the generated HTML if not outputting
1874       */
1875  	function select_input_object( $field_name, $field_value, & $field_object, $field_label, $field_params = array() )
1876      {
1877          if( isset($field_params['allow_none']) )
1878          {
1879              $allow_none = $field_params['allow_none'];
1880              unset( $field_params['allow_none'] );
1881          }
1882          else
1883          {
1884              $allow_none = false;
1885          }
1886  
1887          if( isset($field_params['object_callback']) )
1888          {
1889              $field_object_callback = $field_params['object_callback'];
1890              unset( $field_params['object_callback'] );
1891          }
1892          else
1893          {
1894              $field_object_callback = 'get_option_list';
1895          }
1896  
1897          if( isset($field_params['loop_object_method']) )
1898          {
1899              $field_options = $field_object->$field_object_callback( $field_value, $allow_none, $field_params['loop_object_method'] );
1900              unset( $field_params['loop_object_method'] );
1901          }
1902          else
1903          {
1904              $field_options = $field_object->$field_object_callback( $field_value, $allow_none );
1905          }
1906  
1907          if( isset($field_params['note']) )
1908          {
1909              $field_note = $field_params['note'];
1910              unset( $field_params['note'] );
1911          }
1912          else
1913          {
1914              $field_note = '';
1915          }
1916  
1917          return $this->select_input_options( $field_name, $field_options, $field_label, $field_note, $field_params );
1918      }
1919  
1920  
1921      /**
1922       * Display counrty select field and populate it with a cache object by using a callback
1923       * method.
1924       *
1925       * @uses select_input_options()
1926       * @param string Field name
1927       * @param string Default field value
1928       * @param DataObjectCache Cache containing values for list
1929       * @param string Field label to be display with the field
1930       * @param array Optional params. Additionally to {@link $_common_params} you can use:
1931       *              - 'allow_none': allow to select [none] in list (boolean, default false)
1932       *              - 'object_callback': Object's callback method name (string, default 'get_option_list')
1933       *              - 'loop_object_method': The method on the objects inside the callback (string, default NULL)
1934       * @return mixed true (if output) or the generated HTML if not outputting
1935       */
1936  	function select_country( $field_name, $field_value, & $field_object, $field_label, $field_params = array() )
1937      {
1938          global $edited_User;
1939  
1940          if( isset($field_params['allow_none']) )
1941          {
1942              $allow_none = $field_params['allow_none'];
1943              unset( $field_params['allow_none'] );
1944          }
1945          else
1946          {
1947              $allow_none = false;
1948          }
1949  
1950          if( isset($field_params['object_callback']) )
1951          {
1952              $field_object_callback = $field_params['object_callback'];
1953              unset( $field_params['object_callback'] );
1954          }
1955          else
1956          {
1957              if (empty($edited_User->ctry_ID))
1958              {    // if edited user didn't select a country then we form a dropdown list of countries using 'get_group_country_option_list' function.
1959                  $field_object_callback = 'get_group_country_option_list';
1960              }
1961              else
1962              {    // if edited user has a selected country then we display a usual list of countries.
1963                  $field_object_callback = 'get_option_list';
1964              }
1965  
1966          }
1967  
1968          if( isset($field_params['loop_object_method']) )
1969          {
1970              $field_options = $field_object->$field_object_callback( $field_value, $allow_none, $field_params['loop_object_method'] );
1971              unset( $field_params['loop_object_method'] );
1972          }
1973          else
1974          {
1975              $field_options = $field_object->$field_object_callback( $field_value, $allow_none );
1976          }
1977  
1978          if( isset($field_params['note']) )
1979          {
1980              $field_note = $field_params['note'];
1981              unset( $field_params['note'] );
1982          }
1983          else
1984          {
1985              $field_note = '';
1986          }
1987  
1988          return $this->select_input_options( $field_name, $field_options, $field_label, $field_note, $field_params );
1989      }
1990      /**
1991       * Display a select field and populate it with a cache object.
1992       *
1993       * @uses select_input_object()
1994       * @param string field name
1995       * @param string default field value
1996       * @param DataObjectCache Cache containing values for list
1997       * @param string field label to be display before the field
1998       * @param string note to be displayed after the field
1999       * @param boolean allow to select [none] in list
2000       * @param string CSS class for select
2001       * @param string Object's callback method name.
2002       * @param string Javascript to add for onchange event (trailing ";").
2003       * @return mixed true (if output) or the generated HTML if not outputting
2004       */
2005  	function select_object(
2006          $field_name,
2007          $field_value,
2008          & $field_object,
2009          $field_label,
2010          $field_note = '',
2011          $allow_none = false,
2012          $field_class = '',
2013          $field_object_callback = 'get_option_list',
2014          $field_onchange = NULL )
2015      {
2016          $field_params = array(
2017              'note' => $field_note,
2018              'allow_none' => $allow_none,
2019              'class' => $field_class,
2020              'object_callback' => $field_object_callback,
2021              'onchange' => $field_onchange );
2022  
2023          return $this->select_input_object( $field_name, $field_value, $field_object, $field_label, $field_params  );
2024      }
2025  
2026  
2027      /**
2028       * Display a select field and populate it with a cache object.
2029       *
2030       * @param string field name
2031       * @param string string containing options '<option>...</option>'
2032       * @param string field label to be display before the field
2033       * @param string "help" note (Should provide something useful, otherwise leave it empty)
2034       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2035       *              - 'label': Field label to be display before the field
2036       *              - 'class': CSS class for select
2037       * @return mixed true (if output) or the generated HTML if not outputting
2038       */
2039  	function select_input_options( $field_name, $field_options, $field_label, $field_note = '', $field_params = array() )
2040      {
2041          $this->handle_common_params( $field_params, $field_name, $field_label, $field_note );
2042  
2043          $r = $this->begin_field();
2044          if( !empty( $field_params['parent'] ) )
2045          {// need to display an arrow to show that this select list options has a preselection from a parent
2046              $r .= get_icon( 'parent_childto_arrow' );
2047          }
2048  
2049          $r .="\n<select".get_field_attribs_as_string($field_params).'>'
2050               .$field_options
2051               ."</select>\n";
2052  
2053          $r .= $this->end_field();
2054  
2055          if( !empty( $field_params['parent'] ) )
2056          { // Set up the dynamic preselection array from the parent to this select list options
2057              $r .= "<script type='text/javascript'>
2058                                  tab_dynamicSelects[nb_dynamicSelects] = Array();
2059                                  tab_dynamicSelects[nb_dynamicSelects]['parent'] = '".$field_params['parent']."';
2060                                  tab_dynamicSelects[nb_dynamicSelects]['child'] = '$field_name';
2061                                  nb_dynamicSelects++;
2062                              </script>";
2063          }
2064  
2065          return $this->display_or_return( $r );
2066      }
2067  
2068  
2069      /**
2070       * Display a select field and populate it with a cache object.
2071       *
2072       * @uses select_input_options()
2073       * @param string field name
2074       * @param string string containing options
2075       * @param string field label to be display before the field
2076       * @param string note to be displayed after the field
2077       * @param string CSS class for select
2078       * @param string Javascript to add for onchange event (trailing ";").
2079       * @return mixed true (if output) or the generated HTML if not outputting
2080       */
2081  	function select_options(
2082          $field_name,
2083          $field_options,
2084          $field_label,
2085          $field_note = NULL,
2086          $field_class = NULL,
2087          $field_onchange = NULL )
2088      {
2089          $field_params = array(
2090              'note' => $field_note,
2091              'class' => $field_class,
2092              'onchange' => $field_onchange,
2093               );
2094  
2095          return $this->select_input_options( $field_name, $field_options, $field_label, '', $field_params );
2096      }
2097  
2098  
2099      /**
2100       * This is a stub for {@link select_input_options()} which builds the required list
2101       * of <option> elements from a given list of options ($field_options) and
2102       * the selected value ($field_value).
2103       *
2104       * @uses select_input_options()
2105       * @param string field name
2106       * @param mixed Initial value
2107       * @param array Options. If an associative key (string) is used, this gets the value attribute.
2108       *              NOTE: numeric strings get converted to integers by PHP!
2109       * @param string Field label to be display before the field
2110       * @param string Note
2111       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2112       *              - 'force_keys_as_values': Use the key of $field_options for "value" attrib always.
2113       *              - Plus all of {@link select_input_options()}.
2114       * @return mixed true (if output) or the generated HTML if not outputting
2115       */
2116  	function select_input_array( $field_name, $field_value, $field_options, $field_label, $field_note = NULL, $field_params = array() )
2117      {
2118          if( isset($field_params['force_keys_as_values']) )
2119          {
2120              $force_keys_as_values = $field_params['force_keys_as_values'];
2121              unset($field_params['force_keys_as_values']); // not an attribute to <select>
2122          }
2123          else
2124          {
2125              $force_keys_as_values = false;
2126          }
2127  
2128          if( isset($field_params['background_color']) )
2129          {
2130              $color_array = $field_params['background_color'];
2131              unset($field_params['background_color']); // not an attribute to <select>
2132          }
2133          else
2134          {
2135              $color_array = false;
2136          }
2137  
2138  
2139          // Build $options_list
2140          $options_list = Form::get_select_options_string($field_options, $field_value, $force_keys_as_values, $color_array);
2141  
2142          return $this->select_input_options( $field_name, $options_list, $field_label, $field_note, $field_params );
2143      }
2144  
2145  
2146      /**
2147       * Get the OPTION list as string for use in a SELECT.
2148       * @static
2149       * @param array Options (key => value)
2150       * @param string Selected value (if any)
2151       * @param boolean Force keys from $options as values? (Default: false, only array keys,
2152       *                which are strings will be used).
2153       * @return string
2154       */
2155  	function get_select_options_string($field_options, $field_value = NULL, $force_keys_as_values = false, $color_array = false)
2156      {
2157          $r = '';
2158  
2159          foreach( $field_options as $l_key => $l_option )
2160          {
2161              // Get the value attribute from key if is_string():
2162              $l_value = ($force_keys_as_values || is_string($l_key)) ? $l_key : $l_option;
2163  
2164              if (empty($color_array) || !isset($color_array[$l_value]))
2165              {
2166                  $r .= '<option value="'.format_to_output($l_value, 'formvalue').'"';
2167              }
2168              else
2169              {
2170                  $r .= '<option style="background-color: #'.$color_array[$l_value].'" value="'.format_to_output($l_value, 'formvalue').'"';
2171              }
2172              if(
2173                      ( is_array( $field_value ) && in_array( $l_value, $field_value ) ) ||
2174                      ( !is_array( $field_value ) && (string)$l_value == (string)$field_value ) // cast to string so "1,2" is != 1
2175                  )
2176              {
2177                  $r .= ' selected="selected"';
2178              }
2179  
2180              $r .= '>'.format_to_output($l_option).'</option>';
2181          }
2182          return $r;
2183      }
2184  
2185  
2186      /**
2187       * Combo box
2188       * Display a select options list with an option 'new',
2189       * and when this one is seleted, display a combo input text to add a new value
2190       *
2191       * @param string field name
2192       * @param string field value
2193       * @param string containing options
2194       * @param string field label
2195       * @param array Optional params
2196       *
2197       * @return mixed true (if output) or the generated HTML if not outputting
2198       */
2199  	function combo_box( $field_name, $field_value, $field_options, $field_label, $field_params = array() )
2200      {
2201          if( param_has_error( $field_name) )
2202          {    // There is an error on the combo, so we need to set the combo input text class to 'field_error'
2203              $input_class = 'field_error';
2204          }
2205          else
2206          {
2207              if( isset( $field_params['required'] ) && $field_params['required'] )
2208              {    // The field is required, so update its class:
2209                  $input_class = ' field_required';
2210              }
2211              else
2212              {
2213                  $input_class = '';
2214              }
2215          }
2216          unset($field_params['required']); // already handled above, do not pass to handle_common_params()
2217  
2218          // Set size param for input with new value
2219          if( isset( $field_params['new_field_size'] ) )
2220          {
2221              $new_field_size = $field_params['new_field_size'];
2222          }
2223          else
2224          {
2225              $new_field_size = 30;
2226          }
2227          unset( $field_params['new_field_size'] );
2228  
2229          // Set onchange event on the select, when the select changes, we check the value to display or hide an input text after it
2230          $field_params['onchange']= 'check_combo( this.id, this.options[this.selectedIndex].value, "'.$input_class.'")';
2231  
2232          $this->handle_common_params( $field_params, $field_name, $field_label );
2233  
2234          $r = $this->begin_field();
2235  
2236          // Select option to add after the select list a combo input text:
2237          $option_new  = '<option value="new">'.T_('New').': </option>'."\n";
2238  
2239          // Add the new option to the select list:
2240          $field_options = $option_new . $field_options;
2241  
2242          // Select list
2243          $r .="\n<select".get_field_attribs_as_string($field_params).'>'
2244               .$field_options
2245               ."</select>\n";
2246  
2247          if( $field_options == $option_new  || strpos( $input_class, 'field_error' ) !== false || $field_value != '' )
2248          {    // The list is empty or there is an error on the combo or no field value, so we have to display the input text:
2249              $visible = 'inline';
2250          }
2251          else
2252          { // Hide the input text:
2253              $visible = 'none' ;
2254          }
2255  
2256          $r .= '<input type="text" id="'.$field_name.'_combo" name="'.$field_name.'_combo" size="'.$new_field_size.'" class="'.$input_class.'" style="display:'.$visible.'" value="'.$field_value.'" />';
2257  
2258          if( $visible == 'none' )
2259          { // The input text is hidden, so if no javascript activated, we always display input text:
2260              $r .= '<script type="text/javascript"></script>'; // We need <script> tag here to use a <noscript> tag when javascript is deactivated:
2261              $r .= '<noscript>
2262                              <input type="text" id="'.$field_name.'_combo" name="'.$field_name.'_combo" size="30" class="'.$input_class.'">
2263                          </noscript>';
2264          }
2265  
2266          $r .= $this->end_field();
2267  
2268          return $this->display_or_return( $r );
2269      }
2270  
2271  
2272      /**
2273       * Build a text area.
2274       *
2275       * @param string Name of the field
2276       * @param string Value of the field
2277       * @param integer Number of rows
2278       * @param string Label for the field
2279       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2280       *              - 'cols': Number of columns (integer, default 50)
2281       *              - 'note_format': Format of the note (%s being replaced by the note)
2282       *              - 'format_value': Format of value content, see {@link format_to_output()} (string, default 'formvalue')
2283       */
2284  	function textarea_input( $field_name, $field_value, $field_rows, $field_label, $field_params = array() )
2285      {
2286          global $rsc_url;
2287  
2288          // Default params
2289          $field_params += array(
2290              'cols' => 50,
2291              'note_format' => '<br/><span class="notes">%s</span>', // handled as common param
2292              'format_value' => 'formvalue',
2293          );
2294          $format_value = $field_params['format_value'];
2295          unset($field_params['format_value']); // no HTML attrib
2296  
2297          $this->handle_common_params( $field_params, $field_name, $field_label );
2298  
2299          // Give it a class, so it can be selected for CSS in IE6
2300          if( empty($field_params['class']) ) $field_params['class'] = 'form_textarea_input';
2301          else $field_params['class'] .= ' form_textarea_input';
2302  
2303          if( isset($field_params['maxlength']) )
2304          { // attach event to the textarea to accomplish max length:
2305              $this->append_javascript['textarea_maxlength'.$field_name] = '
2306                  if( typeof jQuery == "function" )
2307                  {
2308                  jQuery("#'.$field_params['id'].'").bind( "keyup", function(event)
2309                      {
2310                          if( this.value.length > '.$field_params['maxlength'].' )
2311                          {
2312                              this.value = this.value.substr(0,'.$field_params['maxlength'].');
2313                              event.preventDefault();
2314                          }
2315                      } );
2316                  }';
2317              unset($field_params['maxlength']); // not a HTML attribute for textarea
2318          }
2319  
2320          $r = $this->begin_field()
2321              // NOTE: The following pixel is needed to avoid the dity IE textarea expansion bug
2322              // see http://fplanque.net/2003/Articles/iecsstextarea/index.html
2323              .get_icon( 'pixel' )
2324              .'<textarea'
2325              .get_field_attribs_as_string( $field_params )
2326              .' rows="'.$field_rows.'">'
2327              .format_to_output( $field_value, $format_value )
2328              .'</textarea>'
2329              // NOTE: this one is for compensating the previous pixel in case of center aligns.
2330              .get_icon( 'pixel' )
2331              .$this->end_field();
2332  
2333          return $this->display_or_return( $r );
2334      }
2335  
2336  
2337      /**
2338       * Build a text area.
2339       *
2340       * @uses textarea_input()
2341       *
2342       * @param string
2343       * @param string
2344       * @param integer
2345       * @param string
2346       * @param string
2347       * @param integer
2348       * @param string
2349       * @param boolean
2350       */
2351  	function textarea( $field_name, $field_value, $field_rows, $field_label, $field_note = '', $field_cols = 50 , $field_class = '', $required = false )
2352      {
2353          $field_params = array(
2354              'note' => $field_note,
2355              'cols' => $field_cols,
2356              'class' => $field_class);
2357          if( $required )
2358          { // Set required only for case TRUE, because in the following code we have a condition "isset($required)" instead of "$required == true"
2359              $field_params['required'] = $required;
2360          }
2361  
2362          return $this->textarea_input( $field_name, $field_value, $field_rows, $field_label, $field_params );
2363      }
2364  
2365  
2366      /**
2367       * Builds an info field.
2368       * An info field is a fieldset containing a label div and an info div.
2369       *
2370       * {@internal
2371       * NOTE: we don't use {@link begin_field()} here, because the label is meant
2372       * to be always on the left and this avoids fiddling with the <label> tag.
2373       * }}
2374       *
2375       * @param string the field label
2376       * @param string the field info
2377       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2378       *              - 'format_info': Format of info content, see {@link format_to_output()} (string, default 'htmlbody')
2379       * @return mixed true (if output) or the generated HTML if not outputting
2380       */
2381  	function info_field( $field_label, $field_info, $field_params = array() )
2382      {
2383          $field_params = array_merge( array(
2384                  'note_format' => ' <small class="notes">%s</small>',
2385              ), $field_params );
2386  
2387          if( isset($field_params['format_info']) )
2388          {
2389              $format_info = $field_params['format_info'];
2390              unset($field_params['format_info']); // not an HTML element
2391          }
2392          else
2393          {
2394              $format_info = 'htmlbody';
2395          }
2396  
2397          $this->handle_common_params( $field_params, NULL, $field_label );
2398  
2399          $r = $this->fieldstart;
2400  
2401          // Start the new form field and inject an automatic DOM id
2402          // This is useful to show/hide the whole field by JS.
2403          if( !empty( $this->_common_params['id'] ) )
2404          {
2405              $ffield_id = ' id="ffield_'.$this->_common_params['id'].'" ';
2406          }
2407          else
2408          {    // No ID in case there's no id/name given for a field.
2409              $ffield_id = '';
2410          }
2411          if( !empty( $field_params['class'] ) )
2412          {    // Add class attribute
2413              $ffield_id .= ' class="'.$field_params['class'].'"';
2414          }
2415          $r = str_replace( '$ID$', $ffield_id, $this->fieldstart );
2416  
2417          if( strlen($field_label) )
2418          {
2419              $r .= $this->labelstart.$field_label;
2420              $r .= $this->label_suffix;
2421              $r .= $this->labelend;
2422          }
2423          else
2424          { // Empty label:
2425              $r .= $this->labelempty;
2426          }
2427  
2428          $r .= $this->infostart;
2429  
2430          // PAYLOAD:
2431          $r .= format_to_output( $field_info, $format_info );
2432  
2433  
2434          // Taken from end_field() - but we use $infoend:
2435          if( !empty($this->_common_params['note']) )
2436          { // We have a note
2437              $r .= sprintf( $this->_common_params['note_format'], $this->_common_params['note'] );
2438          }
2439  
2440          if( isset($this->_common_params['field_suffix']) )
2441          {
2442              $r .= $this->_common_params['field_suffix'];
2443          }
2444  
2445          $r .= ( isset($this->infoend) ? $this->infoend : $this->inputend ).$this->fieldend;
2446  
2447          return $this->display_or_return( $r );
2448      }
2449  
2450  
2451      /**
2452       * Builds an info field.
2453       * An info field is a fieldset containing a label div and an info div.
2454       *
2455       * @param string the field label
2456       * @param string the field info
2457       * @param string see {@link format_to_output()}
2458       * @return mixed true (if output) or the generated HTML if not outputting
2459       */
2460  	function info( $field_label, $field_info, $field_note = NULL, $format = 'htmlbody' )
2461      {
2462          $field_params = array(
2463              'note' => $field_note,
2464              'format_info' => $format );
2465  
2466          return $this->info_field( $field_label, $field_info, $field_params );
2467      }
2468  
2469  
2470      /**
2471       * Builds a button list.
2472       *
2473       * The array contains an associative array for each button (params to {@link button_input()}.
2474       *
2475       * @param array a two-dimension array containing the elements of the input tags
2476       * @return mixed true (if output) or the generated HTML if not outputting
2477       */
2478  	function buttons_input( $buttons = array() )
2479      {
2480          $r = '';
2481          $hidden = true; // boolean that tests if the buttons are all hidden
2482  
2483          $save_output = $this->output;
2484          $this->output = false;
2485  
2486          foreach( $buttons as $l_button )
2487          {
2488              if( !isset($l_button['type']) || $l_button['type'] != 'hidden' )
2489              { // not a hidden button
2490                  $hidden = false;
2491              }
2492  
2493              $r .= $this->button_input( $l_button );
2494          }
2495          /*
2496          else
2497          { // Default: Save and Reset
2498              $r .= $this->get_input_element( array(
2499                  'type' => 'submit',
2500                  'value' => T_('Save !'),
2501                  'class' => 'SaveButton',
2502                  'input_prefix => "\t\t\t" );
2503              $r .= $this->get_input_element( array(
2504                  'type' => 'reset',
2505                  'value' => T_('Reset'),
2506                  'class' => 'ResetButton',
2507                  'input_prefix => "\t\t\t" );
2508          }*/
2509  
2510          $this->output = $save_output;
2511  
2512          if( ! $hidden )
2513          { // there are not only hidden buttons : additional tags
2514              $r = $this->buttonsstart.$r.$this->buttonsend;
2515          }
2516  
2517          return $this->display_or_return( $r );
2518      }
2519  
2520  
2521      /**
2522       * Builds a button list.
2523       *
2524       * Array entries with numeric (deprecated) keys are converted to their equivalent string indexes.
2525       *
2526       * the two-dimension array must contain :
2527       *  - the button type
2528       *  - the name (optional)
2529       *  - the value (optional)
2530       *  - the class (optional)
2531       *  - the onclick attribute (optional)
2532       *  - the style (optional)
2533       *
2534       * @param array a two-dimension array containing the elements of the input tags
2535       * @param boolean to select or not the default display
2536       * @return mixed true (if output) or the generated HTML if not outputting
2537       */
2538  	function buttons( $buttons = array() )
2539      {
2540          $buttons_list = array();
2541  
2542          foreach( $buttons as $l_button )
2543          {
2544              $buttons_list[] = $this->convert_button_to_field_params( $l_button );
2545          }
2546  
2547          return $this->buttons_input( $buttons_list );
2548      }
2549  
2550  
2551      /**
2552       * Builds a button.
2553       *
2554       * You probably want to use {@link buttons_input()}, which uses
2555       * {@link $buttonsstart}/{@link $buttonsend} to align the buttons properly.
2556       *
2557       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2558       *              - type: The type attribute (string, default 'submit')
2559       * @return mixed true (if output) or the generated HTML if not outputting
2560       */
2561  	function button_input( $field_params = array() )
2562      {
2563          $field_params = array_merge( array(
2564                  'type'         => 'submit',
2565                  'input_prefix' => "\t\t\t",
2566              ), $field_params );
2567  
2568          if( empty($field_params['type']) )
2569          { // default type
2570              $field_params['type'] = 'submit';
2571          }
2572  
2573          return $this->display_or_return( $this->get_input_element( $field_params ) );
2574      }
2575  
2576  
2577      /**
2578       * Builds a button.
2579       *
2580       * You probably want to use {@link buttons_input()}, which uses
2581       * {@link $buttonsstart}/{@link $buttonsend} to align the buttons properly.
2582       *
2583       * The array must contain :
2584       *  - the button type
2585       *  - the name (optional)
2586       *  - the value (optional)
2587       *  - the class (optional)
2588       *  - the onclick attribute (optional)
2589       *  - the style (optional)
2590       *
2591       * @param array a two-dimension array containing the elements of the input tags
2592       * @return mixed true (if output) or the generated HTML if not outputting
2593       */
2594  	function button( $options )
2595      {
2596          $field_params = $this->convert_button_to_field_params( $options );
2597  
2598          if( empty($field_params['type']) )
2599          {
2600              $field_params['type'] = 'submit'; // default type
2601          }
2602  
2603          return $this->button_input( $field_params );
2604      }
2605  
2606  
2607      /**
2608       * Convert a deprecated, numeric button array to a field_params array.
2609       *
2610       * @deprecated
2611       * @param array A button array like button() and buttons() are getting.
2612       * @return array The button array converted to a string indexed button array (field_params).
2613       */
2614  	function convert_button_to_field_params( $options )
2615      {
2616          $field_params = array();
2617  
2618          foreach( array_keys($options) as $l_key )
2619          {
2620              if( is_int($l_key) )
2621              {
2622                  switch( $l_key )
2623                  {
2624                      case 0: $field_params['type'] = $options[0]; break;
2625                      case 1: $field_params['name'] = $options[1]; break;
2626                      case 2: $field_params['value'] = $options[2]; break;
2627                      case 3: $field_params['class'] = $options[3]; break;
2628                      case 4: $field_params['onclick'] = $options[4]; break;
2629                      case 5: $field_params['style'] = $options[5]; break;
2630                  }
2631              }
2632              else
2633              {
2634                  $field_params[$l_key] = $options[$l_key];
2635              }
2636          }
2637  
2638          return $field_params;
2639      }
2640  
2641  
2642      /**
2643       * Builds an hidden input tag, overwriting any previous hidden values (except for "foo[]").
2644       *
2645       * This generates no output and returns nothing: the hidden fields get added to {@link $hiddens},
2646       * and get appended to the end of the form.
2647       *
2648       * @param string Field name
2649       * @param string Field value
2650       * @param array Optional params. This is e.g. useful for "id".
2651       *              See {@link $_common_params}.
2652       */
2653  	function hidden( $field_name, $field_value, $field_params = array() )
2654      {
2655          if( is_array( $field_value ) )
2656          { // this happens for example when we've POSTed an array (for PHP it's an array then)
2657              foreach( $field_value as $l_key => $l_value )
2658              {
2659                  // Recursion:
2660                  $this->hidden( $field_name.'['.$l_key.']', $l_value, $field_params );
2661              }
2662          }
2663          else
2664          {
2665              $field_params['name'] = $field_name;
2666              $field_params['type'] = 'hidden';
2667              $field_params['value'] = $field_value;
2668  
2669              if( strpos($field_name, '[]') )
2670              { // array-style name or we don't want to overwrite, just add it:
2671                  $this->hiddens[] = $this->get_input_element( $field_params );
2672              }
2673              else
2674              {
2675                  if( isset($this->existing_hiddens[$field_name]) )
2676                  {
2677                      unset($this->hiddens[$this->existing_hiddens[$field_name]]);
2678                  }
2679  
2680                  // add the field and remember that it already exists:
2681                  end($this->hiddens);
2682                  $key = key($this->hiddens)+1;
2683                  $this->hiddens[$key] = $this->get_input_element( $field_params );
2684                  $this->existing_hiddens[$field_name] = $key;
2685              }
2686          }
2687      }
2688  
2689  
2690      /**
2691       * Add a crumb to the current form
2692       *
2693       * Use this for all forms leading to DATA CHANGING actions
2694       * (anything other than info retrieval), for example:
2695       * -search forms don't need this
2696       * -create, update, delete actions definitely need this
2697       * -change order, change status and more subtle actions also need this
2698       *
2699       * @param string crumb name
2700       */
2701  	function add_crumb( $crumb_name )
2702      {
2703          $this->hidden( 'crumb_'.$crumb_name, get_crumb($crumb_name) );
2704      }
2705  
2706  
2707      /**
2708       * Add the "ctrl" param, used in the backoffice, as a hidden field.
2709       */
2710  	function hidden_ctrl()
2711      {
2712          global $ctrl;
2713          if( !empty( $ctrl) )
2714          {
2715              $this->hidden( 'ctrl', $ctrl );
2716          }
2717      }
2718  
2719  
2720      /**
2721       * Builds a list of hidden inputs.
2722       *
2723       * @param array Array of parameters to {@link hidden()}:
2724       *               - 0: field_name
2725       *               - 1: field_value
2726       *               - 2: field_params
2727       * @return mixed true (if output) or the generated HTML if not outputting
2728       */
2729  	function hiddens( $hiddens )
2730      {
2731          $save_output = $this->output;
2732          $this->output = false;
2733          foreach( $hiddens as $hidden )
2734          {
2735              $this->hidden( $hidden[0], $hidden[1], isset($hidden[2]) ? $hidden[2] : array() );
2736          }
2737          $this->output = $save_output;
2738      }
2739  
2740  
2741      /**
2742       * Builds a list of hidden inputs from an array where the keys are the field names.
2743       *
2744       * It supports array values (one-dimensional) and generates appropriate key-value pairs.
2745       *
2746       * @uses Form::hidden()
2747       * @param array associative array ( name => value ) of hidden fields.
2748       * @param array|NULL A list of keys to ignore, in addition to {@link $included_input_field_names}.
2749       */
2750  	function hiddens_by_key( $hiddens, $exclude = NULL )
2751      {
2752          if( $this->output )
2753          { // only save output once, if necessary (recursion!)
2754              $save_output = $this->output;
2755              $this->output = false;
2756          }
2757  
2758          if( is_null($exclude) )
2759          {
2760              $exclude = $this->included_input_field_names;
2761          }
2762          else
2763          {
2764              $exclude = array_merge($this->included_input_field_names, $exclude);
2765          }
2766  
2767          foreach( $hiddens as $l_name => $l_value )
2768          {
2769              if( isset($exclude) && in_array( $l_name, $exclude ) )
2770              {
2771                  continue;
2772              }
2773              if( is_array( $l_value ) )
2774              { // this happens for example when we've POSTed an array (for PHP it's an array then)
2775                  foreach( $l_value as $ll_key => $ll_value )
2776                  {
2777                      // Recursion:
2778                      $this->hiddens_by_key( array( $l_name.'['.$ll_key.']' => $ll_value ), $exclude );
2779                  }
2780              }
2781              else
2782              {
2783                  $this->hidden( $l_name, $l_value );
2784              }
2785          }
2786  
2787          if( isset($save_output) )
2788          {
2789              $this->output = $save_output;
2790          }
2791      }
2792  
2793  
2794      /**
2795       * Builds a submit input tag.
2796       *
2797       * You probably want to use {@link buttons_input()}, which uses
2798       * {@link $buttonsstart}/{@link $buttonsend} to align the buttons properly.
2799       *
2800       * the array must contain :
2801       *  - the name (optional)
2802       *  - the value (optional)
2803       *  - the class (optional)
2804       *  - the onclick attribute (optional)
2805       *  - the style (optional)
2806       *
2807       * @param array Optional params. See {@link $_common_params}.
2808       * @return mixed true (if output) or the generated HTML if not outputting
2809       */
2810  	function submit_input( $field_params = array() )
2811      {
2812          $field_params['type'] = 'submit';
2813          return $this->button_input( $field_params );
2814      }
2815  
2816  
2817      /**
2818       * Builds a submit input tag
2819       *
2820       * You probably want to use {@link buttons_input()}, which uses
2821       * {@link $buttonsstart}/{@link $buttonsend} to align the buttons properly.
2822       *
2823       * the array must contain :
2824       *  - the name (optional)
2825       *  - the value (optional)
2826       *  - the class (optional)
2827       *  - the onclick attribute (optional)
2828       *  - the style (optional)
2829       *
2830       * @param array an array containing the elements of the input tags
2831       * @return mixed true (if output) or the generated HTML if not outputting
2832       */
2833  	function submit( $options )
2834      {
2835          array_unshift( $options, 'submit' );
2836          return $this->button( $options );
2837      }
2838  
2839  
2840      /**
2841       * Generate set of radio options.
2842       *
2843       * @param string The name of the radio options
2844       * @param string The checked option's value
2845       * @param array of arrays The radio options
2846       *        Keys:
2847       *         - 'value' (required)
2848       *         - 'label' (required)
2849       *         - 'note'
2850       *         - 'type' (default: "radio")
2851       *         - 'class' (default: "radio")
2852       *         - 'checked' (default: 'value' gets compared to $field_value)
2853       *         - 'name' (default: $field_name)
2854       *         - 'suffix' (gets used after the radio's label)
2855       *         - Plus everything for {@link get_input_element()} )
2856       * @param string Label
2857       * @param array Optional params. Additionally to {@link $_common_params} you can use:
2858       *              - lines: Options on seperate lines (DIVs) (boolean, default false)
2859       *              NOTE: these params/attribs get used as default for every INPUT field,
2860       *                    overridden by $field_options
2861       * @return mixed true (if output) or the generated HTML if not outputting
2862       */
2863  	function radio_input( $field_name, $field_value, $field_options, $field_label, $field_params = array() )
2864      {
2865          $field_params = array_merge( array(
2866                  'note_format' => '<span class="notes">%s</span>',
2867              ), $field_params );
2868  
2869          if( isset($field_params['lines']) )
2870          {
2871              $field_lines = $field_params['lines'];
2872              unset($field_params['lines']); // no HTML attribute
2873          }
2874          else
2875          {
2876              $field_lines = false;
2877          }
2878  
2879          if( $field_lines )
2880          {
2881              $field_params['note_format'] = '<div>'.$field_params['note_format'].'</div>';
2882          }
2883  
2884          $field_params['id'] = false; // No ID attribute for the label
2885          $this->handle_common_params( $field_params, $field_name, $field_label );
2886          unset($field_params['id']);  // unset, so it gets handled correctly as default below
2887  
2888          $r = $this->begin_field();
2889  
2890          /*
2891           * Build options list:
2892           */
2893          $count_options = 0; // used for unique IDs (label/radio)
2894          foreach( $field_options as $loop_radio )
2895          {
2896              // Merge defaults from $field_params:
2897              $loop_radio = array_merge( $field_params, $loop_radio );
2898  
2899              if( $field_lines ) $r .= "<div>\n";
2900  
2901              // Defaults:
2902              if( ! isset($loop_radio['type']) )  $loop_radio['type'] = 'radio';
2903              if( ! isset($loop_radio['class']) ) $loop_radio['class'] = 'radio';
2904              if( ! isset($loop_radio['name']) )  $loop_radio['name'] = $field_name;
2905              if( ! isset($loop_radio['id']) )
2906              { // build unique id:
2907                  $loop_radio['id'] = $this->get_valid_id($field_params['name'].'_radio_'.(++$count_options));
2908              }
2909  
2910              if( isset($loop_radio['checked']) )
2911              { // convert boolean:
2912                  if( $loop_radio['checked'] ) $loop_radio['checked'] = 'checked';
2913              }
2914              elseif( $field_value == $loop_radio['value'] )
2915              { // Current selection:
2916                  $loop_radio['checked'] = 'checked';
2917              }
2918  
2919              // Unset non-HTML attribs:
2920              $label = $loop_radio['label'];
2921              $note = isset($loop_radio['note']) ? $loop_radio['note'] : null;
2922              $suffix = isset($loop_radio['suffix']) ? $loop_radio['suffix'] : '';
2923              unset($loop_radio['label'], $loop_radio['note'], $loop_radio['suffix']);
2924  
2925              // the radio element:
2926              $r .= $this->get_input_element( $loop_radio, false );
2927  
2928              // the label:
2929              $r .= '<label class="radiooption" for="'.$loop_radio['id'].'">'.$label.'</label>';
2930  
2931              if( ! empty($note) )
2932              { // Add a note for the current radio option:
2933                  $r .= '<span class="notes">'.$note.'</span>';
2934              }
2935  
2936              // optional text for radio option (like additional fieldsets or input boxes)
2937              $r .= $suffix;
2938  
2939              // Split radio options by whitespace:
2940              $r .= "\n";
2941  
2942              if( $field_lines ) $r .= "</div>\n";
2943          }
2944  
2945          $r .= $this->end_field();
2946  
2947          return $this->display_or_return( $r );
2948      }
2949  
2950  
2951      /**
2952       * Generate set of radio options.
2953       *
2954       * @param string the name of the radio options
2955       * @param string the checked option
2956       * @param array of arrays the radio options (0: value, 1: label, 2: notes, 3: additional HTML [input field, ..], 4: attribs for <input tag> )
2957       * @param string label
2958       * @param boolean options on seperate lines (DIVs)
2959       * @param string notes
2960       * @param boolean required
2961       * @return mixed true (if output) or the generated HTML if not outputting
2962       */
2963  	function radio( $field_name, $field_value, $field_options, $field_label, $field_lines = false, $field_note = '', $field_required = false )
2964      {
2965          $new_field_options = array();
2966  
2967          foreach( $field_options as $l_key => $l_options )
2968          {
2969              $new_field_options[$l_key] = array(
2970                  'value' => $l_options[0],
2971                  'label' => $l_options[1] );
2972  
2973              if( isset($l_options[2]) )
2974              {
2975                  $new_field_options[$l_key]['note'] = $l_options[2];
2976              }
2977              if( isset($l_options[4]) )
2978              { // Convert "inline attribs" to "params" array
2979                  preg_match_all( '#(\w+)=[\'"](.*)[\'"]#', $l_options[4], $matches, PREG_SET_ORDER );
2980  
2981                  foreach( $matches as $l_set_nr => $l_match )
2982                  {
2983                      $new_field_options[$l_key][$l_match[1]] = $l_match[2];
2984                  }
2985              }
2986  
2987              if( isset($l_options[3]) )
2988              {
2989                  $new_field_options[$l_key]['suffix'] = $l_options[3];
2990              }
2991          }
2992  
2993          $field_params = array( 'lines' => $field_lines, 'note' => $field_note );
2994          if( $field_required )
2995          {    // Field is required
2996              $field_params['required'] = true;
2997          }
2998  
2999          return $this->radio_input( $field_name, $field_value, $new_field_options, $field_label, $field_params );
3000      }
3001  
3002  
3003      /**
3004       * Builds an interval input fields.
3005       *
3006       * @param string The name of the input field "From". This gets used for id also, if no id given in $field_params.
3007       * @param string Initial value "From"
3008       * @param string The name of the input field "To". This gets used for id also, if no id given in $field_params.
3009       * @param string Initial value "To"
3010       * @param integer Size of the input field
3011       * @param string Label displayed with the field
3012       * @param string "help" note (Should provide something useful, otherwise leave it empty)
3013       * @param array Extended attributes/params.
3014       *                 - 'maxlength': if not set, $field_size gets used (use '' to disable it)
3015       *                 - 'class': the CSS class to use for the <input> element
3016       *                 - 'type': 'text', 'password' (defaults to 'text')
3017       *                 - 'force_to': 'UpperCase' (JS onchange handler)
3018       *                 - NOTE: any other attributes will be used as is (onchange, onkeyup, id, ..).
3019       * @return true|string true (if output) or the generated HTML if not outputting
3020       */
3021  	function interval( $field_name_from, $field_value_from, $field_name_to, $field_value_to, $field_size, $field_label, $field_note = '', $field_params = array() )
3022      {
3023          $save_output = $this->output;
3024          $save_params = array(
3025              'labelempty' => $this->labelempty,
3026              'fieldstart' => $this->fieldstart,
3027              'fieldend' => $this->fieldend,
3028              'inputstart' => $this->inputstart,
3029              'inputend' => $this->inputend,
3030          );
3031  
3032          // clear form params
3033          $this->switch_template_parts( array_fill_keys( array_keys( $save_params ), '' ) );
3034          $this->output = false;
3035  
3036          // Field "From"
3037          $field_params['input_suffix'] = T_(' to ').$this->text_input( $field_name_to, $field_value_to, $field_size, '', '', $field_params );
3038  
3039          // return saved params
3040          $this->output = $save_output;
3041          $this->switch_template_parts( $save_params );
3042  
3043          return $this->text_input( $field_name_from, $field_value_from, $field_size, $field_label, $field_note, $field_params );
3044      }
3045  
3046  
3047      /**
3048       * Generate a general input field.
3049       *
3050       * This is the base function for text_input(), checkbox_input(), ..
3051       *
3052       * @uses get_input_element() to generate the <input> element
3053       *
3054       * @param array Optional params. Additionally to {@link $_common_params} you can use:
3055       *              - see {@link get_input_element()}
3056       * @return true|string true (if output) or the generated HTML if not outputting
3057       */
3058  	function input_field( $field_params = array() )
3059      {
3060          $field_params = array_merge( array(
3061                  'inline' => false,
3062              ), $field_params );
3063  
3064          $element = $this->get_input_element( $field_params );
3065  
3066          if( $field_params['inline'] )
3067          { // Display label and input field in one line
3068              $this->_common_params['label'] = sprintf( $this->_common_params['label'], $element );
3069  
3070              // Save the form elements:
3071              $label_suffix = $this->label_suffix;
3072              $labelstart = $this->labelstart;
3073              $labelend = $this->labelend;
3074              $inputstart = $this->inputstart;
3075              $inputend = $this->inputend;
3076  
3077              // Remove suffix ':' after label and change other form elements for inline mode
3078              $this->label_suffix = '';
3079              if( isset( $this->inline_labelstart ) )
3080              {
3081                  $this->labelstart = $this->inline_labelstart;
3082              }
3083              if( isset( $this->inline_labelend ) )
3084              {
3085                  $this->labelend = $this->inline_labelend;
3086              }
3087              if( isset( $this->inline_inputstart ) )
3088              {
3089                  $this->inputstart = $this->inline_inputstart;
3090              }
3091              if( isset( $this->inline_inputend ) )
3092              {
3093                  $this->inputend = $this->inline_inputend;
3094              }
3095          }
3096  
3097          $r = $this->begin_field();
3098          if( !$field_params['inline'] )
3099          { // Append input field
3100              $r .= $element;
3101          }
3102          $r .= $this->end_field();
3103  
3104          if( $field_params['inline'] )
3105          { // Restore the changed form elements in mode 'inline'
3106              $this->label_suffix = $label_suffix;
3107              $this->labelstart = $labelstart;
3108              $this->labelend = $labelend;
3109              $this->inputstart = $inputstart;
3110              $this->inputend = $inputend;
3111          }
3112  
3113          return $this->display_or_return( $r );
3114      }
3115  
3116  
3117      /**
3118       * Generate a general input element.
3119       *
3120       * @param array Optional params.
3121       *    Additionally to {@link $_common_params} you can use:
3122       *    - input_prefix: Text before <input /> (string, default '')
3123       *    - input_suffix: Text after <input /> (string, default "\n")
3124       *    - input_help: Gets used as default value on empty input (type=text)
3125       *      elements. It gets attached through JavaScript (onfocus, onblur and form.onsubmit).
3126       *    - format_to_output: Use format_to_output in get_field_attribs_as_string? (boolean, default True)
3127       *
3128       * @return string The <input /> element.
3129       */
3130  	function get_input_element( $field_params = array(), $parse_common = true )
3131      {
3132          if( $parse_common )
3133          {
3134              $this->handle_common_params( $field_params );
3135          }
3136  
3137          if( isset($field_params['input_prefix']) )
3138          {
3139              $input_prefix = $field_params['input_prefix'];
3140              unset($field_params['input_prefix']); // no HTML attribute
3141          }
3142          else
3143          {
3144              $input_prefix = '';
3145          }
3146  
3147          if( isset($field_params['input_suffix']) )
3148          {
3149              $input_suffix = $field_params['input_suffix'];
3150              unset($field_params['input_suffix']); // no HTML attribute
3151          }
3152          else
3153          {
3154              $input_suffix = "\n";
3155          }
3156  
3157          if( isset($field_params['input_help']) && ( empty($field_params['type']) || $field_params['type'] == 'text' ) )
3158          {
3159              $this->append_javascript[] = 'input_decorated_help( "'.$field_params['id'].'", "'.format_to_output($field_params['input_help'], 'formvalue').'" );';
3160  
3161              unset($field_params['input_help']); // no HTML attribute
3162          }
3163  
3164          if( isset($field_params['format_to_output']) )
3165          {
3166              $format_to_output = $field_params['format_to_output'];
3167              unset($field_params['format_to_output']);
3168          }
3169          else
3170          {
3171              $format_to_output = true;
3172          }
3173  
3174          if( isset( $field_params['inline'] ) )
3175          { // Delete 'inline' param from attributes list
3176              unset( $field_params['inline'] );
3177          }
3178  
3179          $r = $input_prefix
3180              .'<input'.get_field_attribs_as_string( $field_params, $format_to_output ).' />'
3181              .$input_suffix;
3182  
3183          return $r;
3184      }
3185  
3186  
3187      /**
3188       * Convert a given string (e.g. fieldname) to a valid HTML id.
3189       *
3190       * @static
3191       * @return string
3192       */
3193  	function get_valid_id( $id )
3194      {
3195          static $id_count = 0;
3196          if( substr( $id, -2 ) == '[]' )
3197          {
3198              $id = substr( $id, 0, -2 ).'_A'.(++$id_count);
3199          }
3200          return str_replace( array( '[', ']' ), '_', $id );
3201      }
3202  
3203  
3204      /**
3205       * Get the label of a field. This is used by {@link begin_field()} or {@link end_field()},
3206       *
3207       * @access protected
3208       * @return string
3209       */
3210  	function get_label()
3211      {
3212          $r = '';
3213  
3214          $label = $this->_common_params['label'];
3215  
3216          if( strlen($label) )
3217          {
3218              $r .= $this->labelstart;
3219  
3220              if( isset( $this->_common_params['clickable_label'] ) && ! $this->_common_params['clickable_label'] )
3221              {    // Not set if this method is invoked by ::begin_field()
3222  
3223                  if( ! empty( $this->_common_params['required'] ) )
3224                  {
3225                      $r .= '<span class="label_field_required">*</span>';
3226                  }
3227  
3228                  $r .= format_to_output($label, 'htmlbody').$this->label_suffix;
3229              }
3230              else
3231              {
3232                  $r .= '<label'
3233                      .( ! empty( $this->_common_params['id'] )
3234                          ? ' for="'.format_to_output( $this->_common_params['id'], 'htmlattr' ).'"'
3235                          : '' )
3236                      .'>';
3237  
3238                      if( ! empty( $this->_common_params['required'] ) )
3239                      {
3240                          $r .= '<span class="label_field_required">*</span>';
3241                      }
3242  
3243                      $r .= format_to_output($label, 'htmlbody');
3244  
3245                  $r .= $this->label_suffix;
3246  
3247                  $r .= '</label>';
3248              }
3249  
3250              $r .= $this->labelend;
3251          }
3252          else
3253          { // Empty label:
3254              $r .= $this->labelempty;
3255          }
3256  
3257          return $r;
3258      }
3259  
3260  
3261      /**
3262       * Extract common params out of $field_params into {@link $_common_params} and unsets them in $field_params.
3263       *
3264       * Also handles adding errors to the note.
3265       *
3266       * @access protected
3267       * @param array An array passed to a field generating function like {@link text_input()}. By reference!
3268       * @param string|NULL The name of the field. If not empty it gets used to build the id attribute.
3269       */
3270  	function handle_common_params( & $field_params, $field_name = NULL, $field_label = NULL, $field_note = NULL )
3271      {
3272          #pre_dump( 'handle_common_params (before)', $field_params );
3273  
3274          $this->_common_params = array(); // Reset
3275  
3276          // Copy optional variables, if given:
3277          if( isset($field_name) )
3278          {
3279              $field_params['name'] = $field_name;
3280          }
3281  
3282          if( isset($field_label) )
3283          {
3284              $field_params['label'] = $field_label;
3285          }
3286  
3287          if( isset($field_params['note']) )
3288          {
3289              $this->_common_params['note'] = $field_params['note'];
3290              unset($field_params['note']); // no HTML attribute
3291          }
3292          elseif( isset($field_note) ) // Note: allow "0" as a note
3293          {
3294              $this->_common_params['note'] = $field_note;
3295          }
3296          else
3297          {
3298              $this->_common_params['note'] = NULL;
3299          }
3300  
3301          if( isset($field_params['note_format']) )
3302          {
3303              $this->_common_params['note_format'] = $field_params['note_format'];
3304              unset($field_params['note_format']); // no HTML attribute
3305          }
3306          else
3307          {
3308              $this->_common_params['note_format'] = $this->note_format;
3309          }
3310  
3311          if( isset($field_params['label']) )
3312          {
3313              $this->_common_params['label'] = $field_params['label'];
3314              unset($field_params['label']); // no HTML attribute
3315          }
3316          else
3317          {
3318              $this->_common_params['label'] = '';
3319          }
3320  
3321          if( isset($field_params['clickable_label']) )
3322          {
3323              $this->_common_params['clickable_label'] = $field_params['clickable_label'];
3324              unset($field_params['clickable_label']); // no HTML attribute
3325          }
3326          else
3327          {
3328              $this->_common_params['clickable_label'] = true;
3329          }
3330  
3331          if( isset($field_params['field_prefix']) )
3332          {
3333              $this->_common_params['field_prefix'] = $field_params['field_prefix'];
3334              unset( $field_params['field_prefix'] );
3335          }
3336  
3337          if( isset($field_params['field_suffix']) )
3338          {
3339              $this->_common_params['field_suffix'] = $field_params['field_suffix'];
3340              unset( $field_params['field_suffix'] );
3341          }
3342  
3343          if( isset($field_params['required']) )
3344          {
3345              $this->_common_params['required'] = $field_params['required'];
3346              unset($field_params['required']);
3347          }
3348  
3349  
3350          if( !empty($field_params['name']) )
3351          {
3352              if( !isset($field_params['id']) )
3353              { // Autogenerate id attrib (not for hidden, radio and submit types)
3354                  if( empty($field_params['type'])
3355                          || ( $field_params['type'] != 'hidden'
3356                                      && $field_params['type'] != 'radio'
3357                                      && $field_params['type'] != 'submit'
3358                                      ) )
3359                  { // Save ID with field_params and _common_params (for get_label())
3360                      $field_params['id'] = $this->_common_params['id'] = $this->get_valid_id($field_params['name']);
3361                  }
3362              }
3363              else
3364              {
3365                  $this->_common_params['id'] = $field_params['id'];
3366              }
3367  
3368              // Remember the field name, so hiddens_by_key can skip it.
3369              $this->included_input_field_names[] = $field_params['name'];
3370          }
3371  
3372          // Mark required fields:
3373          if( isset($this->_common_params['required']) && $this->_common_params['required'] )
3374          { // add "field_required" class:
3375              if( isset($field_params['type']) && $field_params['type'] == 'checkbox' )
3376              { // checkboxes need a span
3377                  $field_params['input_prefix'] = ( isset($field_params['input_prefix']) ? $field_params['input_prefix'] : '' ).'<span class="checkbox_required">';
3378                  $field_params['input_suffix'] = '</span>'.( isset($field_params['input_suffix']) ? $field_params['input_suffix'] : '' );
3379              }
3380              else
3381              {
3382                  $field_params['class'] = isset( $field_params['class'] ) ? $field_params['class'].' field_required' : 'field_required';
3383              }
3384          }
3385  
3386          // Error handling:
3387          if( isset($field_params['name']) && param_has_error( $field_params['name'] ) )
3388          { // There is an error message for this field:
3389              if( isset($field_params['type']) && $field_params['type'] == 'checkbox' )
3390              { // checkboxes need a span
3391                  $field_params['input_prefix'] = ( isset($field_params['input_prefix']) ? $field_params['input_prefix'] : '' ).'<span class="checkbox_error">';
3392                  $field_params['input_suffix'] = '</span>'.( isset($field_params['input_suffix']) ? $field_params['input_suffix'] : '' );
3393              }
3394              else
3395              {
3396                  $field_params['class'] = isset( $field_params['class'] ) ? $field_params['class'].' field_error' : 'field_error';
3397              }
3398  
3399              if( $this->disp_param_err_messages_with_fields )
3400              {
3401                  $this->_common_params['note'] .= ' <span class="field_error">'.param_get_error_msg( $field_params['name'] ).'</span>';
3402              }
3403          }
3404  
3405          if( isset( $field_params['wide'] ) && $field_params['wide'] )
3406          {
3407              $this->_common_params['wide'] = $field_params['wide'];
3408              unset( $field_params['wide'] );
3409          }
3410  
3411          if( isset( $field_params['inline'] ) && $field_params['inline'] )
3412          {
3413              $this->_common_params['inline'] = $field_params['inline'];
3414              unset( $field_params['inline'] );
3415          }
3416  
3417          #pre_dump( 'handle_common_params (after)', $field_params );
3418      }
3419  
3420  
3421      /**
3422       * Display or return, according to {@link $output}.
3423       *
3424       * @return true|string True, if we want to display, the string if not.
3425       */
3426  	function display_or_return( $r )
3427      {
3428          if( $this->output )
3429          {
3430              echo $r;
3431              return true;
3432          }
3433          else
3434          {
3435              return $r;
3436          }
3437      }
3438  
3439  
3440      /**
3441       * Builds a custom content field.
3442       *
3443       * @param string Content
3444       * @return true|string true (if output) or the generated HTML if not outputting
3445       */
3446  	function custom_content( $content )
3447      {
3448          $r = $this->customstart;
3449          $r .= $content;
3450          $r .= $this->customend;
3451  
3452          return $this->display_or_return( $r );
3453      }
3454  
3455  }
3456  
3457  ?>

title

Description

title

Description

title

Description

title

title

Body