form.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: form_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * Automatic generation of HTML FORMs from given data.
00005  *
00006  * Used for scaffolding.
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.view.helpers
00023  * @since           CakePHP(tm) v 0.10.0.1076
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 /**
00030  * Form helper library.
00031  *
00032  * Automatic generation of HTML FORMs from given data.
00033  *
00034  * @package     cake
00035  * @subpackage  cake.cake.libs.view.helpers
00036  */
00037 class FormHelper extends AppHelper {
00038 /**
00039  * Other helpers used by FormHelper
00040  *
00041  * @var array
00042  * @access public
00043  */
00044     var $helpers = array('Html');
00045 /**
00046  * Holds the fields array('field_name' => array('type'=> 'string', 'length'=> 100),
00047  * primaryKey and validates array('field_name')
00048  *
00049  * @access public
00050  */
00051     var $fieldset = array('fields' => array(), 'key' => 'id', 'validates' => array());
00052 /**
00053  * Enter description here...
00054  *
00055  * @var array
00056  */
00057     var $__options = array(
00058         'day' => array(), 'minute' => array(), 'hour' => array(),
00059         'month' => array(), 'year' => array(), 'meridian' => array()
00060     );
00061 /**
00062  * Enter description here...
00063  *
00064  * @var array
00065  * @access public
00066  */
00067     var $fields = array();
00068 /**
00069  * Defines the type of form being created.  Set by FormHelper::create().
00070  *
00071  * @var string
00072  * @access public
00073  */
00074     var $requestType = null;
00075 /**
00076  * Returns an HTML FORM element.
00077  *
00078  * @access public
00079  * @param string $model The model object which the form is being defined for
00080  * @param array  $options
00081  *          'type' Form method defaults to POST
00082  *          'action'  The Action the form submits to. Can be a string or array,
00083  *          'url'  The url the form submits to. Can be a string or a url array,
00084  *          'default'  Allows for the creation of Ajax forms.
00085  *          'onsubmit' Used with 'default' to create ajax forms.
00086  *
00087  * @return string An formatted opening FORM tag.
00088  */
00089     function create($model = null, $options = array()) {
00090         $defaultModel = null;
00091         $view =& ClassRegistry::getObject('view');
00092 
00093         if (is_array($model) && empty($options)) {
00094             $options = $model;
00095             $model = null;
00096         }
00097 
00098         if (empty($model) && $model !== false && !empty($this->params['models'])) {
00099             $model = $this->params['models'][0];
00100             $defaultModel = $this->params['models'][0];
00101         } elseif (empty($model) && empty($this->params['models'])) {
00102             $model = false;
00103         } elseif (is_string($model) && (strpos($model, '/') !== false || strpos($model, '.') !== false)) {
00104             $path = preg_split('/\/|\./', $model);
00105             $model = $path[count($path) - 1];
00106         }
00107 
00108         if (ClassRegistry::isKeySet($model)) {
00109             $object =& ClassRegistry::getObject($model);
00110         }
00111 
00112         $models = ClassRegistry::keys();
00113         foreach ($models as $currentModel) {
00114             if (ClassRegistry::isKeySet($currentModel)) {
00115                 $currentObject =& ClassRegistry::getObject($currentModel);
00116                 if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
00117                     $this->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors;
00118                 }
00119             }
00120         }
00121 
00122         $this->setEntity($model . '.', true);
00123         $append = '';
00124         $created = $id = false;
00125 
00126         if (isset($object)) {
00127             $fields = $object->schema();
00128             if (!empty($object->hasAndBelongsToMany)) {
00129                 foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
00130                     $fields[$alias] = array('type' => 'multiple');
00131                 }
00132             }
00133             $validates = array();
00134             if (!empty($object->validate)) {
00135                 foreach ($object->validate as $validateField => $validateProperties) {
00136                     if (is_array($validateProperties)) {
00137                         $dims = Set::countDim($validateProperties);
00138                         if (($dims == 1 && !isset($validateProperties['required']) || (array_key_exists('required', $validateProperties) && $validateProperties['required'] !== false))) {
00139                             $validates[] = $validateField;
00140                         } elseif ($dims > 1) {
00141                             foreach ($validateProperties as $rule => $validateProp) {
00142                                 if (is_array($validateProp) && (array_key_exists('required', $validateProp) && $validateProp['required'] !== false)) {
00143                                     $validates[] = $validateField;
00144                                 }
00145                             }
00146                         }
00147                     }
00148                 }
00149             }
00150             $this->fieldset = array('fields' => $fields, 'key' => $object->primaryKey, 'validates' => $validates);
00151         }
00152         $data = $this->fieldset;
00153 
00154         if (isset($this->data[$model]) && isset($this->data[$model][$data['key']]) && !empty($this->data[$model][$data['key']])) {
00155             $created = true;
00156             $id = $this->data[$model][$data['key']];
00157         }
00158         $options = array_merge(array(
00159             'type' => ($created && empty($options['action'])) ? 'put' : 'post',
00160             'action' => null,
00161             'url' => null,
00162             'default' => true),
00163         $options);
00164 
00165         if (empty($options['url']) || is_array($options['url'])) {
00166             if (empty($options['url']['controller'])) {
00167                 if (!empty($model) && $model != $defaultModel) {
00168                     $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
00169                 } elseif (!empty($this->params['controller'])) {
00170                     $options['url']['controller'] = Inflector::underscore($this->params['controller']);
00171                 }
00172             }
00173             if (empty($options['action'])) {
00174                 $options['action'] = ife($created, 'edit', 'add');
00175             }
00176 
00177             $actionDefaults = array(
00178                 'plugin' => $this->plugin,
00179                 'controller' => $view->viewPath,
00180                 'action' => $options['action'],
00181                 'id' => $id
00182             );
00183             if (!empty($options['action']) && !isset($options['id'])) {
00184                 $options['id'] = $model . Inflector::camelize($options['action']) . 'Form';
00185             }
00186             $options['action'] = array_merge($actionDefaults, (array)$options['url']);
00187         } elseif (is_string($options['url'])) {
00188             $options['action'] = $options['url'];
00189         }
00190         unset($options['url']);
00191 
00192         switch (strtolower($options['type'])) {
00193             case 'get':
00194                 $htmlAttributes['method'] = 'get';
00195             break;
00196             case 'file':
00197                 $htmlAttributes['enctype'] = 'multipart/form-data';
00198                 $options['type'] = ife($created, 'put', 'post');
00199             case 'post':
00200             case 'put':
00201             case 'delete':
00202                 $append .= $this->hidden('_method', array('name' => '_method', 'value' => strtoupper($options['type']), 'id' => null));
00203             default:
00204                 $htmlAttributes['method'] = 'post';
00205             break;
00206         }
00207         $this->requestType = strtolower($options['type']);
00208 
00209         $htmlAttributes['action'] = $this->url($options['action']);
00210         unset($options['type'], $options['action']);
00211 
00212         if ($options['default'] == false) {
00213             if (isset($htmlAttributes['onSubmit']) || isset($htmlAttributes['onsubmit'])) {
00214                 $htmlAttributes['onsubmit'] .= ' event.returnValue = false; return false;';
00215             } else {
00216                 $htmlAttributes['onsubmit'] = 'event.returnValue = false; return false;';
00217             }
00218         }
00219         unset($options['default']);
00220         $htmlAttributes = array_merge($options, $htmlAttributes);
00221 
00222         if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
00223             $append .= $this->hidden('_Token.key', array('value' => $this->params['_Token']['key'], 'id' => 'Token' . mt_rand()));
00224         }
00225 
00226         if (!empty($append)) {
00227             $append = sprintf($this->Html->tags['fieldset'], ' style="display:none;"', $append);
00228         }
00229 
00230         $this->setEntity($model . '.', true);
00231         return $this->output(sprintf($this->Html->tags['form'], $this->Html->_parseAttributes($htmlAttributes, null, ''))) . $append;
00232     }
00233 /**
00234  * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
00235  * input fields where appropriate.
00236  *
00237  * If $options is set a form submit button will be created.
00238  *
00239  * @param mixed $options as a string will use $options as the value of button,
00240  *  array usage:
00241  *      array('label' => 'save'); value="save"
00242  *      array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever"
00243  *      array('name' => 'Whatever'); value="Submit" name="Whatever"
00244  *      array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever"
00245  *      array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever"
00246  *
00247  * @return string a closing FORM tag optional submit button.
00248  * @access public
00249  */
00250     function end($options = null) {
00251         if (!empty($this->params['models'])) {
00252             $models = $this->params['models'][0];
00253         }
00254         $out = null;
00255         $submit = null;
00256 
00257         if ($options !== null) {
00258             $submitOptions = array();
00259             if (is_string($options)) {
00260                 $submit = $options;
00261             } else {
00262                 if (isset($options['label'])) {
00263                     $submit = $options['label'];
00264                     unset($options['label']);
00265                 }
00266                 $submitOptions = $options;
00267 
00268                 if (!$submit) {
00269                     $submit = __('Submit', true);
00270                 }
00271             }
00272             $out .= $this->submit($submit, $submitOptions);
00273         } elseif (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
00274             $out .= $this->secure($this->fields);
00275             $this->fields = array();
00276         }
00277         $this->setEntity(null);
00278         $out .= $this->Html->tags['formend'];
00279 
00280         $view =& ClassRegistry::getObject('view');
00281         $view->modelScope = false;
00282         return $this->output($out);
00283     }
00284 /**
00285  * Generates a hidden field with a security hash based on the fields used in the form.
00286  *
00287  * @param array $fields The list of fields to use when generating the hash
00288  * @return string A hidden input field with a security hash
00289  * @access public
00290  */
00291     function secure($fields) {
00292         if (!empty($fields)) {
00293             $append = '<fieldset style="display:none;">';
00294 
00295             foreach ($fields as $key => $value) {
00296                 if ($key[0] != '_' && is_array($fields[$key])) {
00297                     sort($fields[$key]);
00298                 } else {
00299                     $model = substr($key, 1);
00300                     if ($key !== '__Token' && !isset($fields[$model])) {
00301                         $fields[$model] = array();
00302                     }
00303                 }
00304             }
00305             ksort($fields, SORT_STRING);
00306             $append .= $this->hidden('_Token.fields', array('value' => urlencode(Security::hash(serialize($fields) . Configure::read('Security.salt'))), 'id' => 'TokenFields' . mt_rand()));
00307             $append .= '</fieldset>';
00308             return $append;
00309         }
00310         return null;
00311     }
00312 /**
00313  * Determine which fields of a form should be used for hash
00314  *
00315  * @param string $model Model name
00316  * @param mixed $options Options
00317  * @access private
00318  */
00319     function __secure($model = null, $options = null) {
00320         if (!$model) {
00321             $model = $this->model();
00322         }
00323         $view =& ClassRegistry::getObject('view');
00324         $field = $view->field;
00325         $fieldSuffix = $view->fieldSuffix;
00326 
00327         if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
00328             if (!empty($this->params['_Token']['disabledFields'])) {
00329                 foreach ($this->params['_Token']['disabledFields'] as $value) {
00330                     $parts = preg_split('/\/|\./', $value);
00331                     if (count($parts) == 1) {
00332                         if ($parts[0] === $field || $parts[0] === $fieldSuffix) {
00333                             return;
00334                         }
00335                     } elseif (count($parts) == 2) {
00336                         if ($parts[0] === $model && ($parts[1] === $field || $parts[1] === $fieldSuffix)) {
00337                             return;
00338                         }
00339                     }
00340                 }
00341             }
00342             $this->__fields($model, $field, $fieldSuffix, $options);
00343             return;
00344         }
00345     }
00346 /**
00347  * Generates a field list used to secure forms
00348  *
00349  * @param string $model
00350  * @param string $field
00351  * @param string $fieldSuffix
00352  * @param mixed $options
00353  * @access private
00354  */
00355     function __fields($model, $field, $fieldSuffix, $options) {
00356         if (!is_null($options)) {
00357             if (is_numeric($field)) {
00358                 $this->fields[$model][$field][$fieldSuffix] = $options;
00359             } else {
00360                 $this->fields[$model][$field] = $options;
00361             }
00362             return;
00363         }
00364         if ((isset($this->fields[$model]) && !in_array($field, $this->fields[$model], true)) || !isset($this->fields[$model])) {
00365             if (is_numeric($field)) {
00366                 $this->fields[$model][$field][] = $fieldSuffix;
00367             } else if (is_null($field)) {
00368                 $this->fields[] = $model;
00369             } else {
00370                 $this->fields[$model][] = $field;
00371             }
00372         }
00373         return;
00374     }
00375 /**
00376  * Returns true if there is an error for the given field, otherwise false
00377  *
00378  * @param string $field This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated
00379  * @return boolean If there are errors this method returns true, else false.
00380  * @access public
00381  */
00382     function isFieldError($field) {
00383         $this->setEntity($field);
00384         return (bool)$this->tagIsInvalid();
00385     }
00386 /**
00387  * Returns a formatted error message for given FORM field, NULL if no errors.
00388  *
00389  * @param string $field A field name, like "Modelname.fieldname", "Modelname/fieldname" is deprecated
00390  * @param mixed $text       Error message or array of $options
00391  * @param array $options    Rendering options for <div /> wrapper tag
00392  *          'escape'  bool  Whether or not to html escape the contents of the error.
00393  *          'wrap'  mixed  Whether or not the error message should be wrapped in a div. If a string, will be used as the HTML tag to use.
00394  *          'class'  string  The classname for the error message
00395  * @return string If there are errors this method returns an error message, otherwise null.
00396  * @access public
00397  */
00398     function error($field, $text = null, $options = array()) {
00399         $this->setEntity($field);
00400         $options = array_merge(array('wrap' => true, 'class' => 'error-message', 'escape' => true), $options);
00401 
00402         if ($error = $this->tagIsInvalid()) {
00403             if (is_array($error)) {
00404                 list(,,$field) = explode('.', $field);
00405                 if (isset($error[$field])) {
00406                     $error = $error[$field];
00407                 } else {
00408                     return null;
00409                 }
00410             }
00411 
00412             if (is_array($text) && is_numeric($error) && $error > 0) {
00413                 $error--;
00414             }
00415             if (is_array($text) && isset($text[$error])) {
00416                 $text = $text[$error];
00417             } elseif (is_array($text)) {
00418                 $options = array_merge($options, $text);
00419                 $text = null;
00420             }
00421 
00422             if ($text != null) {
00423                 $error = $text;
00424             } elseif (is_numeric($error)) {
00425                 $error = sprintf(__('Error in field %s', true), Inflector::humanize($this->field()));
00426             }
00427             if ($options['escape']) {
00428                 $error = h($error);
00429                 unset($options['escape']);
00430             }
00431             if ($options['wrap']) {
00432                 $tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
00433                 unset($options['wrap']);
00434                 return $this->Html->tag($tag, $error, $options);
00435             } else {
00436                 return $error;
00437             }
00438         } else {
00439             return null;
00440         }
00441     }
00442 /**
00443  * Returns a formatted LABEL element for HTML FORMs.
00444  *
00445  * @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated
00446  * @param string $text Text that will appear in the label field.
00447  * @return string The formatted LABEL element
00448  */
00449     function label($fieldName = null, $text = null, $attributes = array()) {
00450         if (empty($fieldName)) {
00451             $view = ClassRegistry::getObject('view');
00452             $fieldName = implode('.', $view->entity());
00453         }
00454 
00455         if ($text === null) {
00456             if (strpos($fieldName, '/') !== false || strpos($fieldName, '.') !== false) {
00457                 $text = array_pop(preg_split('/[\/\.]+/', $fieldName));
00458             } else {
00459                 $text = $fieldName;
00460             }
00461             if (substr($text, -3) == '_id') {
00462                 $text = substr($text, 0, strlen($text) - 3);
00463             }
00464             $text = __(Inflector::humanize(Inflector::underscore($text)), true);
00465         }
00466 
00467         if (isset($attributes['for'])) {
00468             $labelFor = $attributes['for'];
00469             unset($attributes['for']);
00470         } else {
00471             $labelFor = $this->domId($fieldName);
00472         }
00473 
00474         return $this->output(sprintf($this->Html->tags['label'], $labelFor, $this->_parseAttributes($attributes), $text));
00475     }
00476 /**
00477  * Will display all the fields passed in an array expects fieldName as an array key
00478  * replaces generateFields
00479  *
00480  * @access public
00481  * @param array $fields works well with Controller::generateFields() or on its own;
00482  * @param array $blacklist a simple array of fields to skip
00483  * @return output
00484  */
00485     function inputs($fields = null, $blacklist = null) {
00486         $fieldset = $legend = true;
00487 
00488         if (is_array($fields)) {
00489             if (array_key_exists('legend', $fields)) {
00490                 $legend = $fields['legend'];
00491                 unset($fields['legend']);
00492             }
00493 
00494             if (isset($fields['fieldset'])) {
00495                 $fieldset = $fields['fieldset'];
00496                 unset($fields['fieldset']);
00497             }
00498         } elseif ($fields !== null) {
00499             $fieldset = $legend = $fields;
00500             if (!is_bool($fieldset)) {
00501                 $fieldset = true;
00502             }
00503             $fields = array();
00504         }
00505 
00506         if (empty($fields)) {
00507             $fields = array_keys($this->fieldset['fields']);
00508         }
00509 
00510         if ($legend === true) {
00511             $actionName = __('New', true);
00512             if (strpos($this->action, 'update') !== false || strpos($this->action, 'edit') !== false) {
00513                 $actionName = __('Edit', true);
00514             }
00515             $modelName = Inflector::humanize(Inflector::underscore($this->model()));
00516             $legend = $actionName .' '. __($modelName, true);
00517         }
00518 
00519         $out = null;
00520         foreach ($fields as $name => $options) {
00521             if (is_numeric($name) && !is_array($options)) {
00522                     $name = $options;
00523                     $options = array();
00524             }
00525             if (is_array($blacklist) && in_array($name, $blacklist)) {
00526                 continue;
00527             }
00528             $out .= $this->input($name, $options);
00529         }
00530 
00531         if (is_string($fieldset)) {
00532             $fieldsetClass = sprintf(' class="%s"', $fieldset);
00533         } else {
00534             $fieldsetClass = '';
00535         }
00536 
00537         if ($fieldset && $legend) {
00538             return sprintf($this->Html->tags['fieldset'], $fieldsetClass, sprintf($this->Html->tags['legend'], $legend) . $out);
00539         } elseif ($fieldset) {
00540             return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out);
00541         } else {
00542             return $out;
00543         }
00544     }
00545 /**
00546  * Generates a form input element complete with label and wrapper div
00547  *
00548  * @param string $fieldName This should be "Modelname.fieldname", "Modelname/fieldname" is deprecated
00549  * @param array $options - Each type of input takes different options. See each field type method for more information.
00550  * @return string
00551  */
00552     function input($fieldName, $options = array()) {
00553         $view =& ClassRegistry::getObject('view');
00554         $this->setEntity($fieldName);
00555         $options = array_merge(array('before' => null, 'between' => null, 'after' => null), $options);
00556 
00557         if (!isset($options['type'])) {
00558             $options['type'] = 'text';
00559             if (isset($options['options'])) {
00560                 $options['type'] = 'select';
00561             } elseif (in_array($this->field(), array('psword', 'passwd', 'password'))) {
00562                 $options['type'] = 'password';
00563             } elseif (isset($this->fieldset['fields'][$this->field()])) {
00564                 $fieldDef = $this->fieldset['fields'][$this->field()];
00565                 $type = $fieldDef['type'];
00566                 $primaryKey = $this->fieldset['key'];
00567             } elseif (ClassRegistry::isKeySet($this->model())) {
00568                 $model =& ClassRegistry::getObject($this->model());
00569                 $type = $model->getColumnType($this->field());
00570                 $fieldDef = $model->schema();
00571                 if (isset($fieldDef[$this->field()])) {
00572                     $fieldDef = $fieldDef[$this->field()];
00573                 } else {
00574                     $fieldDef = array();
00575                 }
00576                 $primaryKey = $model->primaryKey;
00577             }
00578 
00579             if (isset($type)) {
00580                 $map = array(
00581                     'string'    => 'text',      'datetime'  => 'datetime',
00582                     'boolean'   => 'checkbox',  'timestamp' => 'datetime',
00583                     'text'      => 'textarea',  'time'      => 'time',
00584                     'date'      => 'date', 'float' => 'text'
00585                 );
00586 
00587                 if (isset($this->map[$type])) {
00588                     $options['type'] = $this->map[$type];
00589                 } elseif (isset($map[$type])) {
00590                     $options['type'] = $map[$type];
00591                 }
00592                 if ($this->field() == $primaryKey) {
00593                     $options['type'] = 'hidden';
00594                 }
00595             }
00596 
00597             if ($this->model() === $this->field()) {
00598                 $options['type'] = 'select';
00599                 if (!isset($options['multiple'])) {
00600                     $options['multiple'] = 'multiple';
00601                 }
00602             }
00603         }
00604 
00605         if (!isset($options['options']) && in_array($options['type'], array('text', 'checkbox', 'radio', 'select'))) {
00606             $view =& ClassRegistry::getObject('view');
00607             $varName = Inflector::variable(Inflector::pluralize(preg_replace('/_id$/', '', $this->field())));
00608             $varOptions = $view->getVar($varName);
00609             if (is_array($varOptions)) {
00610                 if ($options['type'] !== 'radio') {
00611                     $options['type'] = 'select';
00612                 }
00613                 $options['options'] = $varOptions;
00614             }
00615         }
00616 
00617         $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
00618         if ($autoLength && $options['type'] == 'text') {
00619             $options['maxlength'] = $fieldDef['length'];
00620         }
00621         if ($autoLength && $fieldDef['type'] == 'float') {
00622             $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1;
00623         }
00624 
00625         $out = '';
00626         $div = true;
00627         $divOptions = array();
00628 
00629         if (array_key_exists('div', $options)) {
00630             $div = $options['div'];
00631             unset($options['div']);
00632         }
00633 
00634         if (!empty($div)) {
00635             $divOptions['class'] = 'input';
00636             $divOptions = $this->addClass($divOptions, $options['type']);
00637             if (is_string($div)) {
00638                 $divOptions['class'] = $div;
00639             } elseif (is_array($div)) {
00640                 $divOptions = array_merge($divOptions, $div);
00641             }
00642             if (in_array($this->field(), $this->fieldset['validates'])) {
00643                 $divOptions = $this->addClass($divOptions, 'required');
00644             }
00645             if (!isset($divOptions['tag'])) {
00646                 $divOptions['tag'] = 'div';
00647             }
00648         }
00649 
00650         $label = null;
00651         if (isset($options['label']) && $options['type'] !== 'radio') {
00652             $label = $options['label'];
00653             unset($options['label']);
00654         }
00655 
00656         if ($options['type'] === 'radio') {
00657             $label = false;
00658             if (isset($options['options'])) {
00659                 if (is_array($options['options'])) {
00660                     $radioOptions = $options['options'];
00661                 } else {
00662                     $radioOptions = array($options['options']);
00663                 }
00664                 unset($options['options']);
00665             }
00666         }
00667 
00668         if ($label !== false) {
00669             $labelAttributes = $this->domId(array(), 'for');
00670             if (in_array($options['type'], array('date', 'datetime'))) {
00671                 $labelAttributes['for'] .= 'Month';
00672             } else if ($options['type'] === 'time') {
00673                 $labelAttributes['for'] .= 'Hour';
00674             }
00675 
00676             if (is_array($label)) {
00677                 $labelText = null;
00678                 if (isset($label['text'])) {
00679                     $labelText = $label['text'];
00680                     unset($label['text']);
00681                 }
00682                 $labelAttributes = array_merge($labelAttributes, $label);
00683             } else {
00684                 $labelText = $label;
00685             }
00686 
00687             if (isset($options['id'])) {
00688                 $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
00689             }
00690             $out = $this->label($fieldName, $labelText, $labelAttributes);
00691         }
00692 
00693         $error = null;
00694         if (isset($options['error'])) {
00695             $error = $options['error'];
00696             unset($options['error']);
00697         }
00698 
00699         $selected = null;
00700         if (array_key_exists('selected', $options)) {
00701             $selected = $options['selected'];
00702             unset($options['selected']);
00703         }
00704         if (isset($options['rows']) || isset($options['cols'])) {
00705             $options['type'] = 'textarea';
00706         }
00707 
00708         $empty = false;
00709         if (isset($options['empty'])) {
00710             $empty = $options['empty'];
00711             unset($options['empty']);
00712         }
00713 
00714         $timeFormat = 12;
00715         if (isset($options['timeFormat'])) {
00716             $timeFormat = $options['timeFormat'];
00717             unset($options['timeFormat']);
00718         }
00719 
00720         $dateFormat = 'MDY';
00721         if (isset($options['dateFormat'])) {
00722             $dateFormat = $options['dateFormat'];
00723             unset($options['dateFormat']);
00724         }
00725 
00726         $type    = $options['type'];
00727         $before  = $options['before'];
00728         $between = $options['between'];
00729         $after   = $options['after'];
00730         unset($options['type'], $options['before'], $options['between'], $options['after']);
00731 
00732         switch ($type) {
00733             case 'hidden':
00734                 $out = $this->hidden($fieldName, $options);
00735                 unset($divOptions);
00736             break;
00737             case 'checkbox':
00738                 $out = $before . $this->checkbox($fieldName, $options) . $between . $out;
00739             break;
00740             case 'radio':
00741                 $out = $before . $out . $this->radio($fieldName, $radioOptions, $options) . $between;
00742             break;
00743             case 'text':
00744             case 'password':
00745                 $out = $before . $out . $between . $this->{$type}($fieldName, $options);
00746             break;
00747             case 'file':
00748                 $out = $before . $out . $between . $this->file($fieldName, $options);
00749             break;
00750             case 'select':
00751                 $options = array_merge(array('options' => array()), $options);
00752                 $list = $options['options'];
00753                 unset($options['options']);
00754                 $out = $before . $out . $between . $this->select($fieldName, $list, $selected, $options, $empty);
00755             break;
00756             case 'time':
00757                 $out = $before . $out . $between . $this->dateTime($fieldName, null, $timeFormat, $selected, $options, $empty);
00758             break;
00759             case 'date':
00760                 $out = $before . $out . $between . $this->dateTime($fieldName, $dateFormat, null, $selected, $options, $empty);
00761             break;
00762             case 'datetime':
00763                 $out = $before . $out . $between . $this->dateTime($fieldName, $dateFormat, $timeFormat, $selected, $options, $empty);
00764             break;
00765             case 'textarea':
00766             default:
00767                 $out = $before . $out . $between . $this->textarea($fieldName, array_merge(array('cols' => '30', 'rows' => '6'), $options));
00768             break;
00769         }
00770 
00771         if ($type != 'hidden') {
00772             $out .= $after;
00773             if ($error !== false) {
00774                 $errMsg = $this->error($fieldName, $error);
00775                 if ($errMsg) {
00776                     $out .= $errMsg;
00777                     $divOptions = $this->addClass($divOptions, 'error');
00778                 }
00779             }
00780         }
00781         if (isset($divOptions) && isset($divOptions['tag'])) {
00782             $tag = $divOptions['tag'];
00783             unset($divOptions['tag']);
00784             $out = $this->Html->tag($tag, $out, $divOptions);
00785         }
00786         return $out;
00787     }
00788 /**
00789  * Creates a checkbox input widget.
00790  *
00791  * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00792  * @param array $options Array of HTML attributes.
00793  *          'value'  the value of the checkbox
00794  * @return string An HTML text input element
00795  */
00796     function checkbox($fieldName, $options = array()) {
00797         $value = 1;
00798         if (isset($options['value'])) {
00799             $value = $options['value'];
00800             unset($options['value']);
00801         }
00802 
00803         $options = $this->__initInputField($fieldName, $options);
00804         $this->__secure();
00805 
00806         $model = $this->model();
00807         if (ClassRegistry::isKeySet($model)) {
00808             $object =& ClassRegistry::getObject($model);
00809         }
00810 
00811         $output = null;
00812         if (isset($object) && isset($options['value']) && ($options['value'] == 0 || $options['value'] == 1)) {
00813             $db =& ConnectionManager::getDataSource($object->useDbConfig);
00814             $value = $db->boolean($options['value'], false);
00815             $options['value'] = 1;
00816         }
00817         $output = $this->hidden($fieldName, array('value' => '0', 'id' => $options['id'] . '_'), true);
00818 
00819         if (isset($options['value']) && $value == $options['value']) {
00820             $options['checked'] = 'checked';
00821         } elseif (!empty($value)) {
00822             $options['value'] = $value;
00823         }
00824 
00825         $output .= sprintf($this->Html->tags['checkbox'], $options['name'], $this->_parseAttributes($options, array('name'), null, ' '));
00826         return $this->output($output);
00827     }
00828 /**
00829  * Creates a set of radio widgets.
00830  *
00831  * @param  string   $fieldName      Name of a field, like this "Modelname.fieldname"
00832  * @param  array    $options        Radio button options array.
00833  * @param  array    $attributes     Array of HTML attributes. Use the 'separator' key to
00834  *                                  define the string in between the radio buttons
00835  * @return string
00836  */
00837     function radio($fieldName, $options = array(), $attributes = array()) {
00838         $attributes = $this->__initInputField($fieldName, $attributes);
00839         $this->__secure();
00840         $legend = false;
00841 
00842         if (isset($attributes['legend'])) {
00843             $legend = $attributes['legend'];
00844             unset($attributes['legend']);
00845         } elseif (count($options) > 1) {
00846             $legend = __(Inflector::humanize($this->field()), true);
00847         }
00848         $label = true;
00849 
00850         if (isset($attributes['label'])) {
00851             $label = $attributes['label'];
00852             unset($attributes['label']);
00853         }
00854         $inbetween = null;
00855 
00856         if (isset($attributes['separator'])) {
00857             $inbetween = $attributes['separator'];
00858             unset($attributes['separator']);
00859         }
00860 
00861         if (isset($attributes['value'])) {
00862             $value = $attributes['value'];
00863         } else {
00864             $value =  $this->value($fieldName);
00865         }
00866         $out = array();
00867 
00868         foreach ($options as $optValue => $optTitle) {
00869             $optionsHere = array('value' => $optValue);
00870 
00871             if (isset($value) && $optValue == $value) {
00872                 $optionsHere['checked'] = 'checked';
00873             }
00874             $parsedOptions = $this->_parseAttributes(array_merge($attributes, $optionsHere), array('name', 'type', 'id'), '', ' ');
00875             $tagName = Inflector::camelize($this->model() . '_' . $this->field() . '_'.Inflector::underscore($optValue));
00876 
00877             if ($label) {
00878                 $optTitle =  sprintf($this->Html->tags['label'], $tagName, null, $optTitle);
00879             }
00880             $out[] =  sprintf($this->Html->tags['radio'], $attributes['name'], $tagName, $parsedOptions, $optTitle);
00881         }
00882         $hidden = null;
00883 
00884         if (!isset($value) || $value === '') {
00885             $hidden = $this->hidden($fieldName, array('value' => '', 'id' => $attributes['id'] . '_'), true);
00886         }
00887         $out = $hidden . join($inbetween, $out);
00888 
00889         if ($legend) {
00890             $out = sprintf($this->Html->tags['fieldset'], '', sprintf($this->Html->tags['legend'], $legend) . $out);
00891         }
00892         return $this->output($out);
00893     }
00894 /**
00895  * Creates a text input widget.
00896  *
00897  * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00898  * @param array  $options Array of HTML attributes.
00899  * @return string An HTML text input element
00900  */
00901     function text($fieldName, $options = array()) {
00902         $options = $this->__initInputField($fieldName, array_merge(array('type' => 'text'), $options));
00903         $this->__secure();
00904         return $this->output(sprintf($this->Html->tags['input'], $options['name'], $this->_parseAttributes($options, array('name'), null, ' ')));
00905     }
00906 /**
00907  * Creates a password input widget.
00908  *
00909  * @param  string  $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00910  * @param  array    $options Array of HTML attributes.
00911  * @return string
00912  */
00913     function password($fieldName, $options = array()) {
00914         $options = $this->__initInputField($fieldName, $options);
00915         $this->__secure();
00916         return $this->output(sprintf($this->Html->tags['password'], $options['name'], $this->_parseAttributes($options, array('name'), null, ' ')));
00917     }
00918 /**
00919  * Creates a textarea widget.
00920  *
00921  * @param string $fieldNamem Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00922  * @param array $options Array of HTML attributes.
00923  * @return string An HTML text input element
00924  */
00925     function textarea($fieldName, $options = array()) {
00926         $options = $this->__initInputField($fieldName, $options);
00927         $this->__secure();
00928         $value = null;
00929 
00930         if (array_key_exists('value', $options)) {
00931             $value = $options['value'];
00932             if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
00933                 $value = h($value);
00934             }
00935             unset($options['value']);
00936         }
00937         return $this->output(sprintf($this->Html->tags['textarea'], $options['name'], $this->_parseAttributes($options, array('type', 'name'), null, ' '), $value));
00938     }
00939 /**
00940  * Creates a hidden input field.
00941  *
00942  * @param  string  $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00943  * @param  array    $options Array of HTML attributes.
00944  * @return string
00945  * @access public
00946  */
00947     function hidden($fieldName, $options = array()) {
00948         $options = $this->__initInputField($fieldName, $options);
00949         $model = $this->model();
00950         $value = '';
00951         $key = '_' . $model;
00952 
00953         if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
00954             $options['name'] = preg_replace("/$model/", $key, $options['name'], 1);
00955         }
00956 
00957         if (!empty($options['value']) || $options['value'] === '0') {
00958             $value = $options['value'];
00959         }
00960 
00961         if (!in_array($fieldName, array('_method'))) {
00962             $this->__secure($key, $value);
00963         }
00964         return $this->output(sprintf($this->Html->tags['hidden'], $options['name'], $this->_parseAttributes($options, array('name', 'class'), '', ' ')));
00965     }
00966 /**
00967  * Creates file input widget.
00968  *
00969  * @param string $fieldName Name of a field, like this "Modelname.fieldname", "Modelname/fieldname" is deprecated
00970  * @param array $options Array of HTML attributes.
00971  * @return string
00972  * @access public
00973  */
00974     function file($fieldName, $options = array()) {
00975         $options = $this->__initInputField($fieldName, $options);
00976         $this->__secure();
00977         return $this->output(sprintf($this->Html->tags['file'], $options['name'], $this->_parseAttributes($options, array('name'), '', ' ')));
00978     }
00979 /**
00980  * Creates a button tag.
00981  *
00982  * @param  string  $title  The button's caption
00983  * @param  array  $options Array of options.
00984  * @return string A HTML button tag.
00985  * @access public
00986  */
00987     function button($title, $options = array()) {
00988         $options = array_merge(array('type' => 'button', 'value' => $title), $options);
00989 
00990         if (isset($options['name']) && (strpos($options['name'], "/") !== false || strpos($options['name'], ".") !== false)) {
00991             if ($this->value($options['name'])) {
00992                 $options['checked'] = 'checked';
00993             }
00994             $name = $options['name'];
00995             unset($options['name']);
00996             $options = $this->__initInputField($name, $options);
00997         }
00998         return $this->output(sprintf($this->Html->tags['button'], $options['type'], $this->_parseAttributes($options, array('type'), '', ' ')));
00999     }
01000 /**
01001  * Creates a submit button element.
01002  *
01003  * @param  string  $caption  The label appearing on the button OR
01004  *                  if string contains :// or the extension .jpg, .jpe, .jpeg, .gif, .png use an image
01005  *                      if the extension exists, AND the first character is /, image is relative to webroot,
01006  *                      OR if the first character is not /, image is relative to webroot/img,
01007  * @param  array   $options
01008  * @return string A HTML submit button
01009  */
01010     function submit($caption = null, $options = array()) {
01011         if (!$caption) {
01012             $caption = __('Submit', true);
01013         }
01014 
01015         $secured = null;
01016         if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
01017             $secured = $this->secure($this->fields);
01018             $this->fields = array();
01019         }
01020         $div = true;
01021 
01022         if (isset($options['div'])) {
01023             $div = $options['div'];
01024             unset($options['div']);
01025         }
01026         $divOptions = array('tag' => 'div');
01027 
01028         if ($div === true) {
01029             $divOptions['class'] = 'submit';
01030         } elseif ($div === false) {
01031             unset($divOptions);
01032         } elseif (is_string($div)) {
01033             $divOptions['class'] = $div;
01034         } elseif (is_array($div)) {
01035             $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
01036         }
01037 
01038         $out = $secured;
01039         if (strpos($caption, '://') !== false) {
01040             $out .= $this->output(sprintf($this->Html->tags['submitimage'], $caption, $this->_parseAttributes($options, null, '', ' ')));
01041         } elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png)$/', $caption)) {
01042             if ($caption{0} !== '/') {
01043                 $url = $this->webroot(IMAGES_URL . $caption);
01044             } else {
01045                 $caption = trim($caption, '/');
01046                 $url = $this->webroot($caption);
01047             }
01048             $out .= $this->output(sprintf($this->Html->tags['submitimage'], $url, $this->_parseAttributes($options, null, '', ' ')));
01049         } else {
01050             $options['value'] = $caption;
01051             $out .= $this->output(sprintf($this->Html->tags['submit'], $this->_parseAttributes($options, null, '', ' ')));
01052         }
01053 
01054         if (isset($divOptions)) {
01055             $tag = $divOptions['tag'];
01056             unset($divOptions['tag']);
01057             $out = $this->Html->tag($tag, $out, $divOptions);
01058         }
01059         return $out;
01060     }
01061 /**
01062  * Returns a formatted SELECT element.
01063  *
01064  * @param string $fieldName Name attribute of the SELECT
01065  * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the SELECT element
01066  * @param mixed $selected The option selected by default.  If null, the default value
01067  *                        from POST data will be used when available.
01068  * @param array $attributes  The HTML attributes of the select element.  If
01069  *                           'showParents' is included in the array and set to true,
01070  *                           an additional option element will be added for the parent
01071  *                           of each option group.
01072  * @param mixed $showEmpty If true, the empty select option is shown.  If a string,
01073  *                         that string is displayed as the empty element.
01074  * @return string Formatted SELECT element
01075  */
01076     function select($fieldName, $options = array(), $selected = null, $attributes = array(), $showEmpty = '') {
01077         $select = array();
01078         $showParents = false;
01079         $escapeOptions = true;
01080         $style = null;
01081         $tag = null;
01082 
01083         if (isset($attributes['escape'])) {
01084             $escapeOptions = $attributes['escape'];
01085             unset($attributes['escape']);
01086         }
01087         $attributes = $this->__initInputField($fieldName, $attributes);
01088 
01089         if (is_string($options) && isset($this->__options[$options])) {
01090             $options = $this->__generateOptions($options);
01091         } elseif (!is_array($options)) {
01092             $options = array();
01093         }
01094         if (isset($attributes['type'])) {
01095             unset($attributes['type']);
01096         }
01097         if (in_array('showParents', $attributes)) {
01098             $showParents = true;
01099             unset($attributes['showParents']);
01100         }
01101 
01102         if (!isset($selected)) {
01103             $selected = $attributes['value'];
01104         }
01105 
01106         if (isset($attributes) && array_key_exists('multiple', $attributes)) {
01107             if ($attributes['multiple'] === 'checkbox') {
01108                 $tag = $this->Html->tags['checkboxmultiplestart'];
01109                 $style = 'checkbox';
01110             } else {
01111                 $tag = $this->Html->tags['selectmultiplestart'];
01112             }
01113             $select[] = $this->hidden(null, array('value' => '', 'id' => null));
01114         } else {
01115             $tag = $this->Html->tags['selectstart'];
01116         }
01117 
01118         if (!empty($tag)) {
01119             $this->__secure();
01120             $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes($attributes, array('name', 'value')));
01121         }
01122 
01123         if ($showEmpty !== null && $showEmpty !== false && !(empty($showEmpty) && (isset($attributes) && array_key_exists('multiple', $attributes)))) {
01124             if ($showEmpty === true) {
01125                 $showEmpty = '';
01126             }
01127             $options = array_reverse($options, true);
01128             $options[''] = $showEmpty;
01129             $options = array_reverse($options, true);
01130         }
01131         $select = array_merge($select, $this->__selectOptions(array_reverse($options, true), $selected, array(), $showParents, array('escape' => $escapeOptions, 'style' => $style)));
01132 
01133         if ($style == 'checkbox') {
01134             $select[] = $this->Html->tags['checkboxmultipleend'];
01135         } else {
01136             $select[] = $this->Html->tags['selectend'];
01137         }
01138         return $this->output(implode("\n", $select));
01139     }
01140 /**
01141  * Returns a SELECT element for days.
01142  *
01143  * @param string $fieldName Prefix name for the SELECT element
01144  * @param string $selected Option which is selected.
01145  * @param array  $attributes HTML attributes for the select element
01146  * @param mixed $showEmpty Show/hide the empty select option
01147  * @return string
01148  */
01149     function day($fieldName, $selected = null, $attributes = array(), $showEmpty = true) {
01150         if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
01151             if (is_array($value)) {
01152                 extract($value);
01153                 $selected = $day;
01154             } else {
01155                 if (empty($value)) {
<