CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.0 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.0
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Auth
    • Core
    • Error
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AppHelper
  • CacheHelper
  • FormHelper
  • HtmlHelper
  • JqueryEngineHelper
  • JsBaseEngineHelper
  • JsHelper
  • MootoolsEngineHelper
  • NumberHelper
  • PaginatorHelper
  • PrototypeEngineHelper
  • RssHelper
  • SessionHelper
  • TextHelper
  • TimeHelper
   1: <?php
   2: /**
   3:  * Automatic generation of HTML FORMs from given data.
   4:  *
   5:  * Used for scaffolding.
   6:  *
   7:  * PHP 5
   8:  *
   9:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10:  * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11:  *
  12:  * Licensed under The MIT License
  13:  * Redistributions of files must retain the above copyright notice.
  14:  *
  15:  * @copyright   Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16:  * @link        http://cakephp.org CakePHP(tm) Project
  17:  * @package       Cake.View.Helper
  18:  * @since       CakePHP(tm) v 0.10.0.1076
  19:  * @license     MIT License (http://www.opensource.org/licenses/mit-license.php)
  20:  */
  21: 
  22: App::uses('AppHelper', 'View/Helper');
  23: 
  24: /**
  25:  * Form helper library.
  26:  *
  27:  * Automatic generation of HTML FORMs from given data.
  28:  *
  29:  * @package       Cake.View.Helper
  30:  * @property      HtmlHelper $Html
  31:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html
  32:  */
  33: class FormHelper extends AppHelper {
  34: 
  35: /**
  36:  * Other helpers used by FormHelper
  37:  *
  38:  * @var array
  39:  */
  40:     public $helpers = array('Html');
  41: 
  42: /**
  43:  * Options used by DateTime fields
  44:  *
  45:  * @var array
  46:  */
  47:     protected $_options = array(
  48:         'day' => array(), 'minute' => array(), 'hour' => array(),
  49:         'month' => array(), 'year' => array(), 'meridian' => array()
  50:     );
  51: 
  52: /**
  53:  * List of fields created, used with secure forms.
  54:  *
  55:  * @var array
  56:  */
  57:     public $fields = array();
  58: 
  59: /**
  60:  * Constant used internally to skip the securing process,
  61:  * and neither add the field to the hash or to the unlocked fields.
  62:  *
  63:  * @var string
  64:  */
  65:     const SECURE_SKIP = 'skip';
  66: 
  67: /**
  68:  * Defines the type of form being created.  Set by FormHelper::create().
  69:  *
  70:  * @var string
  71:  */
  72:     public $requestType = null;
  73: 
  74: /**
  75:  * The default model being used for the current form.
  76:  *
  77:  * @var string
  78:  */
  79:     public $defaultModel = null;
  80: 
  81: /**
  82:  * Persistent default options used by input(). Set by FormHelper::create().
  83:  *
  84:  * @var array
  85:  */
  86:     protected $_inputDefaults = array();
  87: 
  88: /**
  89:  * An array of field names that have been excluded from
  90:  * the Token hash used by SecurityComponent's validatePost method
  91:  *
  92:  * @see FormHelper::_secure()
  93:  * @see SecurityComponent::validatePost()
  94:  * @var array
  95:  */
  96:     protected $_unlockedFields = array();
  97: 
  98: /**
  99:  * Holds the model references already loaded by this helper
 100:  * product of trying to inspect them out of field names
 101:  *
 102:  * @var array
 103:  */
 104:     protected $_models = array();
 105: 
 106: /**
 107:  * Holds all the validation errors for models loaded and inspected
 108:  * it can also be set manually to be able to display custom error messages
 109:  * in the any of the input fields generated by this helper
 110:  *
 111:  * @var array
 112:  */
 113:     public $validationErrors = array();
 114: 
 115: /**
 116:  * Copies the validationErrors variable from the View object into this instance
 117:  *
 118:  * @param View $View The View this helper is being attached to.
 119:  * @param array $settings Configuration settings for the helper.
 120:  */
 121:     public function __construct(View $View, $settings = array()) {
 122:         parent::__construct($View, $settings);
 123:         $this->validationErrors =& $View->validationErrors;
 124:     }
 125: 
 126: /**
 127:  * Guess the location for a model based on its name and tries to create a new instance
 128:  * or get an already created instance of the model
 129:  *
 130:  * @param string $model
 131:  * @return Model model instance
 132:  */
 133:     protected function _getModel($model) {
 134:         $object = null;
 135:         if (!$model || $model === 'Model') {
 136:             return $object;
 137:         }
 138: 
 139:         if (array_key_exists($model, $this->_models)) {
 140:             return $this->_models[$model];
 141:         }
 142: 
 143:         if (ClassRegistry::isKeySet($model)) {
 144:             $object = ClassRegistry::getObject($model);
 145:         } elseif (isset($this->request->params['models'][$model])) {
 146:             $plugin = $this->request->params['models'][$model]['plugin'];
 147:             $plugin .= ($plugin) ? '.' : null;
 148:             $object = ClassRegistry::init(array(
 149:                 'class' => $plugin . $this->request->params['models'][$model]['className'],
 150:                 'alias' => $model
 151:             ));
 152:         } else {
 153:             $object = ClassRegistry::init($model, true);
 154:         }
 155: 
 156:         $this->_models[$model] = $object;
 157:         if (!$object) {;
 158:             return null;
 159:         }
 160: 
 161:         $this->fieldset[$model] = array('fields' => null, 'key' =>  $object->primaryKey, 'validates' => null);
 162:         return $object;
 163:     }
 164: 
 165: /**
 166:  * Inspects the model properties to extract information from them.
 167:  * Currently it can extract information from the the fields, the primary key and required fields
 168:  *
 169:  * The $key parameter accepts the following list of values:
 170:  *
 171:  *  - key: Returns the name of the primary key for the model
 172:  *  - fields: Returns the model schema
 173:  *  - validates: returns the list of fields that are required
 174:  *  - errors: returns the list of validation errors
 175:  *
 176:  * If the $field parameter is passed if will return the information for that sole field.
 177:  *
 178:  *  `$this->_introspectModel('Post', 'fields', 'title');` will return the schema information for title column
 179:  *
 180:  * @param string $model name of the model to extract information from
 181:  * @param string $key name of the special information key to obtain (key, fields, validates, errors)
 182:  * @param string $field name of the model field to get information from
 183:  * @return mixed information extracted for the special key and field in a model
 184:  */
 185:     protected function _introspectModel($model, $key, $field = null) {
 186:             $object = $this->_getModel($model);
 187:             if (!$object) {
 188:                 return;
 189:             }
 190: 
 191:             if ($key === 'key') {
 192:                 return $this->fieldset[$model]['key'] = $object->primaryKey;
 193:             }
 194: 
 195:             if ($key === 'fields') {
 196:                 if (!isset($this->fieldset[$model]['fields'])) {
 197:                     $fields = $this->fieldset[$model]['fields'] = $object->schema();
 198:                     foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
 199:                         $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple');
 200:                     }
 201:                 }
 202:                 if (empty($field)) {
 203:                     return $this->fieldset[$model]['fields'];
 204:                 } elseif (isset($this->fieldset[$model]['fields'][$field])) {
 205:                     return $this->fieldset[$model]['fields'][$field];
 206:                 } else {
 207:                     return isset($object->hasAndBelongsToMany[$field]) ? array('type' => 'multiple') : null;
 208:                 }
 209:             }
 210: 
 211:             if ($key === 'errors' && !isset($this->validationErrors[$model])) {
 212:                 $this->validationErrors[$model] =& $object->validationErrors;
 213:                 return $this->validationErrors[$model];
 214:             } elseif ($key === 'errors' && isset($this->validationErrors[$model])) {
 215:                 return $this->validationErrors[$model];
 216:             }
 217: 
 218:             if ($key === 'validates' && !isset($this->fieldset[$model]['validates'])) {
 219:                 $validates = array();
 220:                 if (!empty($object->validate)) {
 221:                     foreach ($object->validate as $validateField => $validateProperties) {
 222:                         if ($this->_isRequiredField($validateProperties)) {
 223:                             $validates[$validateField] = true;
 224:                         }
 225:                     }
 226:                 }
 227:                 $this->fieldset[$model]['validates'] = $validates;
 228:             }
 229: 
 230:             if ($key === 'validates') {
 231:                 if (empty($field)) {
 232:                     return $this->fieldset[$model]['validates'];
 233:                 } else {
 234:                     return isset($this->fieldset[$model]['validates'][$field]) ?
 235:                         $this->fieldset[$model]['validates'] : null;
 236:                 }
 237:             }
 238:     }
 239: 
 240: /**
 241:  * Returns if a field is required to be filled based on validation properties from the validating object.
 242:  *
 243:  * @param array $validateProperties
 244:  * @return boolean true if field is required to be filled, false otherwise
 245:  */
 246:     protected function _isRequiredField($validateProperties) {
 247:         $required = false;
 248:         if (is_string($validateProperties)) {
 249:             return true;
 250:         } elseif (is_array($validateProperties)) {
 251: 
 252:             $dims = Set::countDim($validateProperties);
 253:             if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) {
 254:                 $validateProperties = array($validateProperties);
 255:             }
 256: 
 257:             foreach ($validateProperties as $rule => $validateProp) {
 258:                 if (isset($validateProp['allowEmpty']) && $validateProp['allowEmpty'] === true) {
 259:                     return false;
 260:                 }
 261:                 $rule = isset($validateProp['rule']) ? $validateProp['rule'] : false;
 262:                 $required = $rule || empty($validateProp);
 263:                 if ($required) {
 264:                     break;
 265:                 }
 266:             }
 267:         }
 268:         return $required;
 269:     }
 270: 
 271: /**
 272:  * Returns false if given form field described by the current entity has no errors.
 273:  * Otherwise it returns the validation message
 274:  *
 275:  * @return mixed Either false when there or no errors, or the error
 276:  *    string. The error string could be ''.
 277:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::tagIsInvalid
 278:  */
 279:     public function tagIsInvalid() {
 280:         $entity = $this->entity();
 281:         $model = array_shift($entity);
 282:         $errors = array();
 283:         if (!empty($entity) && isset($this->validationErrors[$model])) {
 284:             $errors = $this->validationErrors[$model];
 285:         }
 286:         if (!empty($entity) && empty($errors)) {
 287:             $errors = $this->_introspectModel($model, 'errors');
 288:         }
 289:         if (empty($errors)) {
 290:             return false;
 291:         }
 292:         $error = Set::classicExtract($errors, join('.', $entity));
 293:         return $error === null ? false : $error;
 294:     }
 295: 
 296: /**
 297:  * Returns an HTML FORM element.
 298:  *
 299:  * ### Options:
 300:  *
 301:  * - `type` Form method defaults to POST
 302:  * - `action`  The controller action the form submits to, (optional).
 303:  * - `url`  The url the form submits to. Can be a string or a url array.  If you use 'url'
 304:  *    you should leave 'action' undefined.
 305:  * - `default`  Allows for the creation of Ajax forms. Set this to false to prevent the default event handler.
 306:  *   Will create an onsubmit attribute if it doesn't not exist. If it does, default action suppression
 307:  *   will be appended.
 308:  * - `onsubmit` Used in conjunction with 'default' to create ajax forms.
 309:  * - `inputDefaults` set the default $options for FormHelper::input(). Any options that would
 310:  *   be set when using FormHelper::input() can be set here.  Options set with `inputDefaults`
 311:  *   can be overridden when calling input()
 312:  * - `encoding` Set the accept-charset encoding for the form.  Defaults to `Configure::read('App.encoding')`
 313:  *
 314:  * @param string $model The model object which the form is being defined for.  Should
 315:  *   include the plugin name for plugin forms.  e.g. `ContactManager.Contact`.
 316:  * @param array $options An array of html attributes and options.
 317:  * @return string An formatted opening FORM tag.
 318:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#options-for-create
 319:  */
 320:     public function create($model = null, $options = array()) {
 321:         $created = $id = false;
 322:         $append = '';
 323: 
 324:         if (is_array($model) && empty($options)) {
 325:             $options = $model;
 326:             $model = null;
 327:         }
 328:         if (empty($model) && $model !== false && !empty($this->request->params['models'])) {
 329:             $model = key($this->request->params['models']);
 330:             $this->defaultModel = $model;
 331:         } elseif (empty($model) && empty($this->request->params['models'])) {
 332:             $model = false;
 333:         }
 334: 
 335:         $key = null;
 336:         if ($model !== false) {
 337:             $object = $this->_getModel($model);
 338:             $key = $this->_introspectModel($model, 'key');
 339:             $this->setEntity($model, true);
 340:         }
 341: 
 342:         if ($model !== false && $key) {
 343:             $recordExists = (
 344:                 isset($this->request->data[$model]) &&
 345:                 !empty($this->request->data[$model][$key]) &&
 346:                 !is_array($this->request->data[$model][$key])
 347:             );
 348: 
 349:             if ($recordExists) {
 350:                 $created = true;
 351:                 $id = $this->request->data[$model][$key];
 352:             }
 353:         }
 354: 
 355:         $options = array_merge(array(
 356:             'type' => ($created && empty($options['action'])) ? 'put' : 'post',
 357:             'action' => null,
 358:             'url' => null,
 359:             'default' => true,
 360:             'encoding' => strtolower(Configure::read('App.encoding')),
 361:             'inputDefaults' => array()),
 362:         $options);
 363:         $this->_inputDefaults = $options['inputDefaults'];
 364:         unset($options['inputDefaults']);
 365: 
 366:         if (!isset($options['id'])) {
 367:             $domId = isset($options['action']) ? $options['action'] : $this->request['action'];
 368:             $options['id'] = $this->domId($domId . 'Form');
 369:         }
 370: 
 371:         if ($options['action'] === null && $options['url'] === null) {
 372:             $options['action'] = $this->request->here(false);
 373:         } elseif (empty($options['url']) || is_array($options['url'])) {
 374:             if (empty($options['url']['controller'])) {
 375:                 if (!empty($model) && $model != $this->defaultModel) {
 376:                     $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
 377:                 } elseif (!empty($this->request->params['controller'])) {
 378:                     $options['url']['controller'] = Inflector::underscore($this->request->params['controller']);
 379:                 }
 380:             }
 381:             if (empty($options['action'])) {
 382:                 $options['action'] = $this->request->params['action'];
 383:             }
 384: 
 385:             $plugin = null;
 386:             if ($this->plugin) {
 387:                 $plugin = Inflector::underscore($this->plugin);
 388:             }
 389:             $actionDefaults = array(
 390:                 'plugin' => $plugin,
 391:                 'controller' => $this->_View->viewPath,
 392:                 'action' => $options['action'],
 393:             );
 394:             $options['action'] = array_merge($actionDefaults, (array)$options['url']);
 395:             if (empty($options['action'][0]) && !empty($id)) {
 396:                 $options['action'][0] = $id;
 397:             }
 398:         } elseif (is_string($options['url'])) {
 399:             $options['action'] = $options['url'];
 400:         }
 401:         unset($options['url']);
 402: 
 403:         switch (strtolower($options['type'])) {
 404:             case 'get':
 405:                 $htmlAttributes['method'] = 'get';
 406:             break;
 407:             case 'file':
 408:                 $htmlAttributes['enctype'] = 'multipart/form-data';
 409:                 $options['type'] = ($created) ? 'put' : 'post';
 410:             case 'post':
 411:             case 'put':
 412:             case 'delete':
 413:                 $append .= $this->hidden('_method', array(
 414:                     'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null,
 415:                     'secure' => self::SECURE_SKIP
 416:                 ));
 417:             default:
 418:                 $htmlAttributes['method'] = 'post';
 419:             break;
 420:         }
 421:         $this->requestType = strtolower($options['type']);
 422: 
 423:         $action = $this->url($options['action']);
 424:         unset($options['type'], $options['action']);
 425: 
 426:         if ($options['default'] == false) {
 427:             if (!isset($options['onsubmit'])) {
 428:                 $options['onsubmit'] = '';
 429:             }
 430:             $htmlAttributes['onsubmit'] = $options['onsubmit'] . 'event.returnValue = false; return false;';
 431:         }
 432:         unset($options['default']);
 433: 
 434:         if (!empty($options['encoding'])) {
 435:             $htmlAttributes['accept-charset'] = $options['encoding'];
 436:             unset($options['encoding']);
 437:         }
 438: 
 439:         $htmlAttributes = array_merge($options, $htmlAttributes);
 440: 
 441:         $this->fields = array();
 442:         $append .= $this->_csrfField();
 443: 
 444:         if (!empty($append)) {
 445:             $append = $this->Html->useTag('block', ' style="display:none;"', $append);
 446:         }
 447: 
 448:         if ($model !== false) {
 449:             $this->setEntity($model, true);
 450:             $this->_introspectModel($model, 'fields');
 451:         }
 452:         return $this->Html->useTag('form', $action, $htmlAttributes) . $append;
 453:     }
 454: 
 455: /**
 456:  * Return a CSRF input if the _Token is present.
 457:  * Used to secure forms in conjunction with SecurityComponent
 458:  *
 459:  * @return string
 460:  */
 461:     protected function _csrfField() {
 462:         if (empty($this->request->params['_Token'])) {
 463:             return '';
 464:         }
 465:         if (!empty($this->request['_Token']['unlockedFields'])) {
 466:             foreach ((array)$this->request['_Token']['unlockedFields'] as $unlocked) {
 467:                 $this->_unlockedFields[] = $unlocked;
 468:             }
 469:         }
 470:         return $this->hidden('_Token.key', array(
 471:             'value' => $this->request->params['_Token']['key'], 'id' => 'Token' . mt_rand(),
 472:             'secure' => self::SECURE_SKIP
 473:         ));
 474:     }
 475: 
 476: /**
 477:  * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
 478:  * input fields where appropriate.
 479:  *
 480:  * If $options is set a form submit button will be created. Options can be either a string or an array.
 481:  *
 482:  * {{{
 483:  * array usage:
 484:  *
 485:  * array('label' => 'save'); value="save"
 486:  * array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever"
 487:  * array('name' => 'Whatever'); value="Submit" name="Whatever"
 488:  * array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever"
 489:  * array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever"
 490:  * }}}
 491:  *
 492:  * @param mixed $options as a string will use $options as the value of button,
 493:  * @return string a closing FORM tag optional submit button.
 494:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#closing-the-form
 495:  */
 496:     public function end($options = null) {
 497:         $out = null;
 498:         $submit = null;
 499: 
 500:         if ($options !== null) {
 501:             $submitOptions = array();
 502:             if (is_string($options)) {
 503:                 $submit = $options;
 504:             } else {
 505:                 if (isset($options['label'])) {
 506:                     $submit = $options['label'];
 507:                     unset($options['label']);
 508:                 }
 509:                 $submitOptions = $options;
 510:             }
 511:             $out .= $this->submit($submit, $submitOptions);
 512:         }
 513:         if (isset($this->request['_Token']) && !empty($this->request['_Token'])) {
 514:             $out .= $this->secure($this->fields);
 515:             $this->fields = array();
 516:         }
 517:         $this->setEntity(null);
 518:         $out .= $this->Html->useTag('formend');
 519: 
 520:         $this->_View->modelScope = false;
 521:         return $out;
 522:     }
 523: 
 524: /**
 525:  * Generates a hidden field with a security hash based on the fields used in the form.
 526:  *
 527:  * @param array $fields The list of fields to use when generating the hash
 528:  * @return string A hidden input field with a security hash
 529:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::secure
 530:  */
 531:     public function secure($fields = array()) {
 532:         if (!isset($this->request['_Token']) || empty($this->request['_Token'])) {
 533:             return;
 534:         }
 535:         $locked = array();
 536:         $unlockedFields = $this->_unlockedFields;
 537: 
 538:         foreach ($fields as $key => $value) {
 539:             if (!is_int($key)) {
 540:                 $locked[$key] = $value;
 541:                 unset($fields[$key]);
 542:             }
 543:         }
 544: 
 545:         sort($unlockedFields, SORT_STRING);
 546:         sort($fields, SORT_STRING);
 547:         ksort($locked, SORT_STRING);
 548:         $fields += $locked;
 549: 
 550:         $locked = implode(array_keys($locked), '|');
 551:         $unlocked = implode($unlockedFields, '|');
 552:         $fields = Security::hash(serialize($fields) . $unlocked . Configure::read('Security.salt'));
 553: 
 554:         $out = $this->hidden('_Token.fields', array(
 555:             'value' => urlencode($fields . ':' . $locked),
 556:             'id' => 'TokenFields' . mt_rand()
 557:         ));
 558:         $out .= $this->hidden('_Token.unlocked', array(
 559:             'value' => urlencode($unlocked),
 560:             'id' => 'TokenUnlocked' . mt_rand()
 561:         ));
 562:         return $this->Html->useTag('block', ' style="display:none;"', $out);
 563:     }
 564: 
 565: /**
 566:  * Add to or get the list of fields that are currently unlocked.
 567:  * Unlocked fields are not included in the field hash used by SecurityComponent
 568:  * unlocking a field once its been added to the list of secured fields will remove
 569:  * it from the list of fields.
 570:  *
 571:  * @param string $name The dot separated name for the field.
 572:  * @return mixed Either null, or the list of fields.
 573:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::unlockField
 574:  */
 575:     public function unlockField($name = null) {
 576:         if ($name === null) {
 577:             return $this->_unlockedFields;
 578:         }
 579:         if (!in_array($name, $this->_unlockedFields)) {
 580:             $this->_unlockedFields[] = $name;
 581:         }
 582:         $index = array_search($name, $this->fields);
 583:         if ($index !== false) {
 584:             unset($this->fields[$index]);
 585:         }
 586:         unset($this->fields[$name]);
 587:     }
 588: 
 589: /**
 590:  * Determine which fields of a form should be used for hash.
 591:  * Populates $this->fields
 592:  *
 593:  * @param boolean $lock Whether this field should be part of the validation
 594:  *     or excluded as part of the unlockedFields.
 595:  * @param mixed $field Reference to field to be secured.  Should be dot separated to indicate nesting.
 596:  * @param mixed $value Field value, if value should not be tampered with.
 597:  * @return void
 598:  */
 599:     protected function _secure($lock, $field = null, $value = null) {
 600:         if (!$field) {
 601:             $field = $this->entity();
 602:         } elseif (is_string($field)) {
 603:             $field = Set::filter(explode('.', $field), true);
 604:         }
 605: 
 606:         foreach ($this->_unlockedFields as $unlockField) {
 607:             $unlockParts = explode('.', $unlockField);
 608:             if (array_values(array_intersect($field, $unlockParts)) === $unlockParts) {
 609:                 return;
 610:             }
 611:         }
 612: 
 613:         $last = end($field);
 614:         if (is_numeric($last) || empty($last)) {
 615:             array_pop($field);
 616:         }
 617: 
 618:         $field = implode('.', $field);
 619: 
 620:         if ($lock) {
 621:             if (!in_array($field, $this->fields)) {
 622:                 if ($value !== null) {
 623:                     return $this->fields[$field] = $value;
 624:                 }
 625:                 $this->fields[] = $field;
 626:             }
 627:         } else {
 628:             $this->unlockField($field);
 629:         }
 630:     }
 631: 
 632: /**
 633:  * Returns true if there is an error for the given field, otherwise false
 634:  *
 635:  * @param string $field This should be "Modelname.fieldname"
 636:  * @return boolean If there are errors this method returns true, else false.
 637:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::isFieldError
 638:  */
 639:     public function isFieldError($field) {
 640:         $this->setEntity($field);
 641:         return (bool)$this->tagIsInvalid();
 642:     }
 643: 
 644: /**
 645:  * Returns a formatted error message for given FORM field, NULL if no errors.
 646:  *
 647:  * ### Options:
 648:  *
 649:  * - `escape`  bool  Whether or not to html escape the contents of the error.
 650:  * - `wrap`  mixed  Whether or not the error message should be wrapped in a div. If a
 651:  *   string, will be used as the HTML tag to use.
 652:  * - `class` string  The classname for the error message
 653:  *
 654:  * @param string $field A field name, like "Modelname.fieldname"
 655:  * @param mixed $text Error message as string or array of messages.
 656:  * If array contains `attributes` key it will be used as options for error container
 657:  * @param array $options Rendering options for <div /> wrapper tag
 658:  * @return string If there are errors this method returns an error message, otherwise null.
 659:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::error
 660:  */
 661:     public function error($field, $text = null, $options = array()) {
 662:         $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
 663:         $options = array_merge($defaults, $options);
 664:         $this->setEntity($field);
 665: 
 666:         $error = $this->tagIsInvalid();
 667:         if ($error === false) {
 668:             return null;
 669:         }
 670:         if (is_array($text)) {
 671:             if (isset($text['attributes']) && is_array($text['attributes'])) {
 672:                 $options = array_merge($options, $text['attributes']);
 673:                 unset($text['attributes']);
 674:             }
 675:             $tmp = array();
 676:             foreach ($error as &$e) {
 677:                 if (isset($text[$e])) {
 678:                     $tmp []= $text[$e];
 679:                 } else {
 680:                     $tmp []= $e;
 681:                 }
 682:             }
 683:             $text = $tmp;
 684:         }
 685: 
 686:         if ($text !== null) {
 687:             $error = $text;
 688:         }
 689:         if (is_array($error)) {
 690:             foreach ($error as &$e) {
 691:                 if (is_numeric($e)) {
 692:                     $e = __d('cake', 'Error in field %s', Inflector::humanize($this->field()));
 693:                 }
 694:             }
 695:         }
 696:         if ($options['escape']) {
 697:             $error = h($error);
 698:             unset($options['escape']);
 699:         }
 700:         if (is_array($error)) {
 701:             if (count($error) > 1) {
 702:                 $listParams = array();
 703:                 if (isset($options['listOptions'])) {
 704:                     if (is_string($options['listOptions'])) {
 705:                         $listParams []= $options['listOptions'];
 706:                     } else {
 707:                         if (isset($options['listOptions']['itemOptions'])) {
 708:                             $listParams []= $options['listOptions']['itemOptions'];
 709:                             unset($options['listOptions']['itemOptions']);
 710:                         } else {
 711:                             $listParams []= array();
 712:                         }
 713:                         if (isset($options['listOptions']['tag'])) {
 714:                             $listParams []= $options['listOptions']['tag'];
 715:                             unset($options['listOptions']['tag']);
 716:                         }
 717:                         array_unshift($listParams, $options['listOptions']);
 718:                     }
 719:                     unset($options['listOptions']);
 720:                 }
 721:                 array_unshift($listParams, $error);
 722:                 $error = call_user_func_array(array($this->Html, 'nestedList'), $listParams);
 723:             } else {
 724:                 $error = array_pop($error);
 725:             }
 726:         }
 727:         if ($options['wrap']) {
 728:             $tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
 729:             unset($options['wrap']);
 730:             return $this->Html->tag($tag, $error, $options);
 731:         } else {
 732:             return $error;
 733:         }
 734:     }
 735: 
 736: /**
 737:  * Returns a formatted LABEL element for HTML FORMs. Will automatically generate
 738:  * a for attribute if one is not provided.
 739:  *
 740:  * ### Options
 741:  *
 742:  * - `for` - Set the for attribute, if its not defined the for attribute 
 743:  *   will be generated from the $fieldName parameter using
 744:  *   FormHelper::domId().
 745:  *
 746:  * Examples:
 747:  *
 748:  * The text and for attribute are generated off of the fieldname
 749:  *
 750:  * {{{
 751:  * echo $this->Form->label('Post.published');
 752:  * <label for="PostPublished">Published</label>
 753:  * }}}
 754:  *
 755:  * Custom text:
 756:  *
 757:  * {{{
 758:  * echo $this->Form->label('Post.published', 'Publish');
 759:  * <label for="PostPublished">Publish</label>
 760:  * }}}
 761:  *
 762:  * Custom class name:
 763:  *
 764:  * {{{
 765:  * echo $this->Form->label('Post.published', 'Publish', 'required');
 766:  * <label for="PostPublished" class="required">Publish</label>
 767:  * }}}
 768:  *
 769:  * Custom attributes:
 770:  *
 771:  * {{{
 772:  * echo $this->Form->label('Post.published', 'Publish', array(
 773:  *      'for' => 'post-publish'
 774:  * ));
 775:  * <label for="post-publish">Publish</label>
 776:  * }}}
 777:  *
 778:  * @param string $fieldName This should be "Modelname.fieldname"
 779:  * @param string $text Text that will appear in the label field.  If 
 780:  *   $text is left undefined the text will be inflected from the 
 781:  *   fieldName.
 782:  * @param mixed $options An array of HTML attributes, or a string, to be used as a class name.
 783:  * @return string The formatted LABEL element
 784:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::label
 785:  */
 786:     public function label($fieldName = null, $text = null, $options = array()) {
 787:         if (empty($fieldName)) {
 788:             $fieldName = implode('.', $this->entity());
 789:         }
 790: 
 791:         if ($text === null) {
 792:             if (strpos($fieldName, '.') !== false) {
 793:                 $fieldElements = explode('.', $fieldName);
 794:                 $text = array_pop($fieldElements);
 795:             } else {
 796:                 $text = $fieldName;
 797:             }
 798:             if (substr($text, -3) == '_id') {
 799:                 $text = substr($text, 0, strlen($text) - 3);
 800:             }
 801:             $text = __(Inflector::humanize(Inflector::underscore($text)));
 802:         }
 803: 
 804:         if (is_string($options)) {
 805:             $options = array('class' => $options);
 806:         }
 807: 
 808:         if (isset($options['for'])) {
 809:             $labelFor = $options['for'];
 810:             unset($options['for']);
 811:         } else {
 812:             $labelFor = $this->domId($fieldName);
 813:         }
 814: 
 815:         return $this->Html->useTag('label', $labelFor, $options, $text);
 816:     }
 817: 
 818: /**
 819:  * Generate a set of inputs for `$fields`.  If $fields is null the current model
 820:  * will be used.
 821:  *
 822:  * In addition to controller fields output, `$fields` can be used to control legend
 823:  * and fieldset rendering with the `fieldset` and `legend` keys.
 824:  * `$form->inputs(array('legend' => 'My legend'));` Would generate an input set with
 825:  * a custom legend.  You can customize individual inputs through `$fields` as well.
 826:  *
 827:  * {{{
 828:  *  $form->inputs(array(
 829:  *      'name' => array('label' => 'custom label')
 830:  *  ));
 831:  * }}}
 832:  *
 833:  * In addition to fields control, inputs() allows you to use a few additional options.
 834:  *
 835:  * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
 836:  *    the classname for the fieldset element.
 837:  * - `legend` Set to false to disable the legend for the generated input set. Or supply a string
 838:  *  to customize the legend text.
 839:  *
 840:  * @param mixed $fields An array of fields to generate inputs for, or null.
 841:  * @param array $blacklist a simple array of fields to not create inputs for.
 842:  * @return string Completed form inputs.
 843:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::inputs
 844:  */
 845:     public function inputs($fields = null, $blacklist = null) {
 846:         $fieldset = $legend = true;
 847:         $model = $this->model();
 848:         if (is_array($fields)) {
 849:             if (array_key_exists('legend', $fields)) {
 850:                 $legend = $fields['legend'];
 851:                 unset($fields['legend']);
 852:             }
 853: 
 854:             if (isset($fields['fieldset'])) {
 855:                 $fieldset = $fields['fieldset'];
 856:                 unset($fields['fieldset']);
 857:             }
 858:         } elseif ($fields !== null) {
 859:             $fieldset = $legend = $fields;
 860:             if (!is_bool($fieldset)) {
 861:                 $fieldset = true;
 862:             }
 863:             $fields = array();
 864:         }
 865: 
 866:         if (empty($fields)) {
 867:             $fields = array_keys($this->_introspectModel($model, 'fields'));
 868:         }
 869: 
 870:         if ($legend === true) {
 871:             $actionName = __d('cake', 'New %s');
 872:             $isEdit = (
 873:                 strpos($this->request->params['action'], 'update') !== false ||
 874:                 strpos($this->request->params['action'], 'edit') !== false
 875:             );
 876:             if ($isEdit) {
 877:                 $actionName = __d('cake', 'Edit %s');
 878:             }
 879:             $modelName = Inflector::humanize(Inflector::underscore($model));
 880:             $legend = sprintf($actionName, __($modelName));
 881:         }
 882: 
 883:         $out = null;
 884:         foreach ($fields as $name => $options) {
 885:             if (is_numeric($name) && !is_array($options)) {
 886:                 $name = $options;
 887:                 $options = array();
 888:             }
 889:             $entity = explode('.', $name);
 890:             $blacklisted = (
 891:                 is_array($blacklist) &&
 892:                 (in_array($name, $blacklist) || in_array(end($entity), $blacklist))
 893:             );
 894:             if ($blacklisted) {
 895:                 continue;
 896:             }
 897:             $out .= $this->input($name, $options);
 898:         }
 899: 
 900:         if (is_string($fieldset)) {
 901:             $fieldsetClass = sprintf(' class="%s"', $fieldset);
 902:         } else {
 903:             $fieldsetClass = '';
 904:         }
 905: 
 906:         if ($fieldset && $legend) {
 907:             return $this->Html->useTag('fieldset', $fieldsetClass, $this->Html->useTag('legend', $legend) . $out);
 908:         } elseif ($fieldset) {
 909:             return $this->Html->useTag('fieldset', $fieldsetClass, $out);
 910:         } else {
 911:             return $out;
 912:         }
 913:     }
 914: 
 915: /**
 916:  * Generates a form input element complete with label and wrapper div
 917:  *
 918:  * ### Options
 919:  *
 920:  * See each field type method for more information. Any options that are part of
 921:  * $attributes or $options for the different **type** methods can be included in `$options` for input().i
 922:  * Additionally, any unknown keys that are not in the list below, or part of the selected type's options
 923:  * will be treated as a regular html attribute for the generated input.
 924:  *
 925:  * - `type` - Force the type of widget you want. e.g. `type => 'select'`
 926:  * - `label` - Either a string label, or an array of options for the label. See FormHelper::label()
 927:  * - `div` - Either `false` to disable the div, or an array of options for the div.
 928:  *  See HtmlHelper::div() for more options.
 929:  * - `options` - for widgets that take options e.g. radio, select
 930:  * - `error` - control the error message that is produced
 931:  * - `empty` - String or boolean to enable empty select box options.
 932:  * - `before` - Content to place before the label + input.
 933:  * - `after` - Content to place after the label + input.
 934:  * - `between` - Content to place between the label + input.
 935:  * - `format` - format template for element order. Any element that is not in the array, will not be in the output.
 936:  *  - Default input format order: array('before', 'label', 'between', 'input', 'after', 'error')
 937:  *  - Default checkbox format order: array('before', 'input', 'between', 'label', 'after', 'error')
 938:  *  - Hidden input will not be formatted
 939:  *  - Radio buttons cannot have the order of input and label elements controlled with these settings.
 940:  *
 941:  * @param string $fieldName This should be "Modelname.fieldname"
 942:  * @param array $options Each type of input takes different options.
 943:  * @return string Completed form widget.
 944:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#creating-form-elements
 945:  */
 946:     public function input($fieldName, $options = array()) {
 947:         $this->setEntity($fieldName);
 948: 
 949:         $options = array_merge(
 950:             array('before' => null, 'between' => null, 'after' => null, 'format' => null),
 951:             $this->_inputDefaults,
 952:             $options
 953:         );
 954: 
 955:         $modelKey = $this->model();
 956:         $fieldKey = $this->field();
 957: 
 958:         if (!isset($options['type'])) {
 959:             $magicType = true;
 960:             $options['type'] = 'text';
 961:             if (isset($options['options'])) {
 962:                 $options['type'] = 'select';
 963:             } elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) {
 964:                 $options['type'] = 'password';
 965:             } elseif (isset($options['checked'])) {
 966:                 $options['type'] = 'checkbox';
 967:             } elseif ($fieldDef = $this->_introspectModel($modelKey, 'fields', $fieldKey)) {
 968:                 $type = $fieldDef['type'];
 969:                 $primaryKey = $this->fieldset[$modelKey]['key'];
 970:             }
 971: 
 972:             if (isset($type)) {
 973:                 $map = array(
 974:                     'string' => 'text', 'datetime' => 'datetime',
 975:                     'boolean' => 'checkbox', 'timestamp' => 'datetime',
 976:                     'text' => 'textarea', 'time' => 'time',
 977:                     'date' => 'date', 'float' => 'number',
 978:                     'integer' => 'number'
 979:                 );
 980: 
 981:                 if (isset($this->map[$type])) {
 982:                     $options['type'] = $this->map[$type];
 983:                 } elseif (isset($map[$type])) {
 984:                     $options['type'] = $map[$type];
 985:                 }
 986:                 if ($fieldKey == $primaryKey) {
 987:                     $options['type'] = 'hidden';
 988:                 }
 989:                 if (
 990:                     $options['type'] === 'number' &&
 991:                     $type === 'float' &&
 992:                     !isset($options['step'])
 993:                 ) {
 994:                     $options['step'] = 'any';
 995:                 }
 996:             }
 997:             if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') {
 998:                 $options['type'] = 'select';
 999:             }
1000: 
1001:             if ($modelKey === $fieldKey) {
1002:                 $options['type'] = 'select';
1003:                 if (!isset($options['multiple'])) {
1004:                     $options['multiple'] = 'multiple';
1005:                 }
1006:             }
1007:         }
1008:         $types = array('checkbox', 'radio', 'select');
1009: 
1010:         if (
1011:             (!isset($options['options']) && in_array($options['type'], $types)) ||
1012:             (isset($magicType) && $options['type'] == 'text')
1013:         ) {
1014:             $varName = Inflector::variable(
1015:                 Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
1016:             );
1017:             $varOptions = $this->_View->getVar($varName);
1018:             if (is_array($varOptions)) {
1019:                 if ($options['type'] !== 'radio') {
1020:                     $options['type'] = 'select';
1021:                 }
1022:                 $options['options'] = $varOptions;
1023:             }
1024:         }
1025: 
1026:         $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
1027:         if ($autoLength && $options['type'] == 'text') {
1028:             $options['maxlength'] = $fieldDef['length'];
1029:         }
1030:         if ($autoLength && $fieldDef['type'] == 'float') {
1031:             $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1;
1032:         }
1033: 
1034:         $divOptions = array();
1035:         $div = $this->_extractOption('div', $options, true);
1036:         unset($options['div']);
1037: 
1038:         if (!empty($div)) {
1039:             $divOptions['class'] = 'input';
1040:             $divOptions = $this->addClass($divOptions, $options['type']);
1041:             if (is_string($div)) {
1042:                 $divOptions['class'] = $div;
1043:             } elseif (is_array($div)) {
1044:                 $divOptions = array_merge($divOptions, $div);
1045:             }
1046:             if ($this->_introspectModel($modelKey, 'validates', $fieldKey)) {
1047:                 $divOptions = $this->addClass($divOptions, 'required');
1048:             }
1049:             if (!isset($divOptions['tag'])) {
1050:                 $divOptions['tag'] = 'div';
1051:             }
1052:         }
1053: 
1054:         $label = null;
1055:         if (isset($options['label']) && $options['type'] !== 'radio') {
1056:             $label = $options['label'];
1057:             unset($options['label']);
1058:         }
1059: 
1060:         if ($options['type'] === 'radio') {
1061:             $label = false;
1062:             if (isset($options['options'])) {
1063:                 $radioOptions = (array)$options['options'];
1064:                 unset($options['options']);
1065:             }
1066:         }
1067: 
1068:         if ($label !== false) {
1069:             $label = $this->_inputLabel($fieldName, $label, $options);
1070:         }
1071: 
1072:         $error = $this->_extractOption('error', $options, null);
1073:         unset($options['error']);
1074: 
1075:         $selected = $this->_extractOption('selected', $options, null);
1076:         unset($options['selected']);
1077: 
1078:         if (isset($options['rows']) || isset($options['cols'])) {
1079:             $options['type'] = 'textarea';
1080:         }
1081: 
1082:         if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') {
1083:             $options += array('empty' => false);
1084:         }
1085:         if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') {
1086:             $dateFormat = $this->_extractOption('dateFormat', $options, 'MDY');
1087:             $timeFormat = $this->_extractOption('timeFormat', $options, 12);
1088:             unset($options['dateFormat'], $options['timeFormat']);
1089:         }
1090: 
1091:         $type = $options['type'];
1092:         $out = array_merge(
1093:             array('before' => null, 'label' => null, 'between' => null, 'input' => null, 'after' => null, 'error' => null),
1094:             array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after'])
1095:         );
1096:         $format = null;
1097:         if (is_array($options['format']) && in_array('input', $options['format'])) {
1098:             $format = $options['format'];
1099:         }
1100:         unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']);
1101: 
1102:         switch ($type) {
1103:             case 'hidden':
1104:                 $input = $this->hidden($fieldName, $options);
1105:                 $format = array('input');
1106:                 unset($divOptions);
1107:             break;
1108:             case 'checkbox':
1109:                 $input = $this->checkbox($fieldName, $options);
1110:                 $format = $format ? $format : array('before', 'input', 'between', 'label', 'after', 'error');
1111:             break;
1112:             case 'radio':
1113:                 $input = $this->radio($fieldName, $radioOptions, $options);
1114:             break;
1115:             case 'file':
1116:                 $input = $this->file($fieldName, $options);
1117:             break;
1118:             case 'select':
1119:                 $options += array('options' => array(), 'value' => $selected);
1120:                 $list = $options['options'];
1121:                 unset($options['options']);
1122:                 $input = $this->select($fieldName, $list, $options);
1123:             break;
1124:             case 'time':
1125:                 $options['value'] = $selected;
1126:                 $input = $this->dateTime($fieldName, null, $timeFormat, $options);
1127:             break;
1128:             case 'date':
1129:                 $options['value'] = $selected;
1130:                 $input = $this->dateTime($fieldName, $dateFormat, null, $options);
1131:             break;
1132:             case 'datetime':
1133:                 $options['value'] = $selected;
1134:                 $input = $this->dateTime($fieldName, $dateFormat, $timeFormat, $options);
1135:             break;
1136:             case 'textarea':
1137:                 $input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
1138:             break;
1139:             case 'url':
1140:                 $input = $this->text($fieldName, array('type' => 'url') + $options);
1141:             break;
1142:             default:
1143:                 $input = $this->{$type}($fieldName, $options);
1144:         }
1145: 
1146:         if ($type != 'hidden' && $error !== false) {
1147:             $errMsg = $this->error($fieldName, $error);
1148:             if ($errMsg) {
1149:                 $divOptions = $this->addClass($divOptions, 'error');
1150:                 $out['error'] = $errMsg;
1151:             }
1152:         }
1153: 
1154:         $out['input'] = $input;
1155:         $format = $format ? $format : array('before', 'label', 'between', 'input', 'after', 'error');
1156:         $output = '';
1157:         foreach ($format as $element) {
1158:             $output .= $out[$element];
1159:             unset($out[$element]);
1160:         }
1161: 
1162:         if (!empty($divOptions['tag'])) {
1163:             $tag = $divOptions['tag'];
1164:             unset($divOptions['tag']);
1165:             $output = $this->Html->tag($tag, $output, $divOptions);
1166:         }
1167:         return $output;
1168:     }
1169: 
1170: /**
1171:  * Extracts a single option from an options array.
1172:  *
1173:  * @param string $name The name of the option to pull out.
1174:  * @param array $options The array of options you want to extract.
1175:  * @param mixed $default The default option value
1176:  * @return mixed the contents of the option or default
1177:  */
1178:     protected function _extractOption($name, $options, $default = null) {
1179:         if (array_key_exists($name, $options)) {
1180:             return $options[$name];
1181:         }
1182:         return $default;
1183:     }
1184: 
1185: /**
1186:  * Generate a label for an input() call.
1187:  *
1188:  * @param string $fieldName
1189:  * @param string $label
1190:  * @param array $options Options for the label element.
1191:  * @return string Generated label element
1192:  */
1193:     protected function _inputLabel($fieldName, $label, $options) {
1194:         $labelAttributes = $this->domId(array(), 'for');
1195:         if ($options['type'] === 'date' || $options['type'] === 'datetime') {
1196:             if (isset($options['dateFormat']) && $options['dateFormat'] === 'NONE') {
1197:                 $labelAttributes['for'] .= 'Hour';
1198:                 $idKey = 'hour';
1199:             } else {
1200:                 $labelAttributes['for'] .= 'Month';
1201:                 $idKey = 'month';
1202:             }
1203:             if (isset($options['id']) && isset($options['id'][$idKey])) {
1204:                 $labelAttributes['for'] = $options['id'][$idKey];
1205:             }
1206:         } elseif ($options['type'] === 'time') {
1207:             $labelAttributes['for'] .= 'Hour';
1208:             if (isset($options['id']) && isset($options['id']['hour'])) {
1209:                 $labelAttributes['for'] = $options['id']['hour'];
1210:             }
1211:         }
1212: 
1213:         if (is_array($label)) {
1214:             $labelText = null;
1215:             if (isset($label['text'])) {
1216:                 $labelText = $label['text'];
1217:                 unset($label['text']);
1218:             }
1219:             $labelAttributes = array_merge($labelAttributes, $label);
1220:         } else {
1221:             $labelText = $label;
1222:         }
1223: 
1224:         if (isset($options['id']) && is_string($options['id'])) {
1225:             $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
1226:         }
1227:         return $this->label($fieldName, $labelText, $labelAttributes);
1228:     }
1229: 
1230: /**
1231:  * Creates a checkbox input widget.
1232:  *
1233:  * ### Options:
1234:  *
1235:  * - `value` - the value of the checkbox
1236:  * - `checked` - boolean indicate that this checkbox is checked.
1237:  * - `hiddenField` - boolean to indicate if you want the results of checkbox() to include
1238:  *    a hidden input with a value of ''.
1239:  * - `disabled` - create a disabled input.
1240:  * - `default` - Set the default value for the checkbox.  This allows you to start checkboxes
1241:  *    as checked, without having to check the POST data.  A matching POST data value, will overwrite
1242:  *    the default value.
1243:  *
1244:  * @param string $fieldName Name of a field, like this "Modelname.fieldname"
1245:  * @param array $options Array of HTML attributes.
1246:  * @return string An HTML text input element.
1247:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#options-for-select-checkbox-and-radio-inputs
1248:  */
1249:     public function checkbox($fieldName, $options = array()) {
1250:         $valueOptions = array();
1251:         if (isset($options['default'])) {
1252:             $valueOptions['default'] = $options['default'];
1253:             unset($options['default']);
1254:         }
1255: 
1256:         $options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true);
1257:         $value = current($this->value($valueOptions));
1258:         $output = "";
1259: 
1260:         if (empty($options['value'])) {
1261:             $options['value'] = 1;
1262:         }
1263:         if (
1264:             (!isset($options['checked']) && !empty($value) && $value == $options['value']) ||
1265:             !empty($options['checked'])
1266:         ) {
1267:             $options['checked'] = 'checked';
1268:         }
1269:         if ($options['hiddenField']) {
1270:             $hiddenOptions = array(
1271:                 'id' => $options['id'] . '_', 'name' => $options['name'],
1272:                 'value' => '0', 'secure' => false
1273:             );
1274:             if (isset($options['disabled']) && $options['disabled'] == true) {
1275:                 $hiddenOptions['disabled'] = 'disabled';
1276:             }
1277:             $output = $this->hidden($fieldName, $hiddenOptions);
1278:         }
1279:         unset($options['hiddenField']);
1280: 
1281:         return $output . $this->Html->useTag('checkbox', $options['name'], array_diff_key($options, array('name' => '')));
1282:     }
1283: 
1284: /**
1285:  * Creates a set of radio widgets. Will create a legend and fieldset
1286:  * by default.  Use $options to control this
1287:  *
1288:  * ### Attributes:
1289:  *
1290:  * - `separator` - define the string in between the radio buttons
1291:  * - `legend` - control whether or not the widget set has a fieldset & legend
1292:  * - `value` - indicate a value that is should be checked
1293:  * - `label` - boolean to indicate whether or not labels for widgets show be displayed
1294:  * - `hiddenField` - boolean to indicate if you want the results of radio() to include
1295:  *    a hidden input with a value of ''. This is useful for creating radio sets that non-continuous
1296:  *
1297:  * @param string $fieldName Name of a field, like this "Modelname.fieldname"
1298:  * @param array $options Radio button options array.
1299:  * @param array $attributes Array of HTML attributes, and special attributes above.
1300:  * @return string Completed radio widget set.
1301:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#options-for-select-checkbox-and-radio-inputs
1302:  */
1303:     public function radio($fieldName, $options = array(), $attributes = array()) {
1304:         $attributes = $this->_initInputField($fieldName, $attributes);
1305:         $legend = false;
1306:         $disabled = array();
1307: 
1308:         if (isset($attributes['legend'])) {
1309:             $legend = $attributes['legend'];
1310:             unset($attributes['legend']);
1311:         } elseif (count($options) > 1) {
1312:             $legend = __(Inflector::humanize($this->field()));
1313:         }
1314:         $label = true;
1315: 
1316:         if (isset($attributes['label'])) {
1317:             $label = $attributes['label'];
1318:             unset($attributes['label']);
1319:         }
1320:         $inbetween = null;
1321: 
1322:         if (isset($attributes['separator'])) {
1323:             $inbetween = $attributes['separator'];
1324:             unset($attributes['separator']);
1325:         }
1326: 
1327:         if (isset($attributes['value'])) {
1328:             $value = $attributes['value'];
1329:         } else {
1330:             $value =  $this->value($fieldName);
1331:         }
1332: 
1333:         if (isset($attributes['disabled'])) {
1334:             $disabled = $attributes['disabled'];
1335:         }
1336: 
1337:         $out = array();
1338: 
1339:         $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true;
1340:         unset($attributes['hiddenField']);
1341: 
1342:         foreach ($options as $optValue => $optTitle) {
1343:             $optionsHere = array('value' => $optValue);
1344: 
1345:             if (isset($value) && $optValue == $value) {
1346:                 $optionsHere['checked'] = 'checked';
1347:             }
1348:             if (!empty($disabled) && in_array($optValue, $disabled)) {
1349:                 $optionsHere['disabled'] = true;
1350:             }
1351:             $tagName = Inflector::camelize(
1352:                 $attributes['id'] . '_' . Inflector::slug($optValue)
1353:             );
1354: 
1355:             if ($label) {
1356:                 $optTitle = $this->Html->useTag('label', $tagName, '', $optTitle);
1357:             }
1358:             $allOptions = array_merge($attributes, $optionsHere);
1359:             $out[] = $this->Html->useTag('radio', $attributes['name'], $tagName,
1360:                 array_diff_key($allOptions, array('name' => '', 'type' => '', 'id' => '')),
1361:                 $optTitle
1362:             );
1363:         }
1364:         $hidden = null;
1365: 
1366:         if ($hiddenField) {
1367:             if (!isset($value) || $value === '') {
1368:                 $hidden = $this->hidden($fieldName, array(
1369:                     'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name']
1370:                 ));
1371:             }
1372:         }
1373:         $out = $hidden . implode($inbetween, $out);
1374: 
1375:         if ($legend) {
1376:             $out = $this->Html->useTag('fieldset', '', $this->Html->useTag('legend', $legend) . $out);
1377:         }
1378:         return $out;
1379:     }
1380: 
1381: /**
1382:  * Missing method handler - implements various simple input types. Is used to create inputs
1383:  * of various types.  e.g. `$this->Form->text();` will create `<input type="text" />` while
1384:  * `$this->Form->range();` will create `<input type="range" />`
1385:  *
1386:  * ### Usage
1387:  *
1388:  * `$this->Form->search('User.query', array('value' => 'test'));`
1389:  *
1390:  * Will make an input like:
1391:  *
1392:  * `<input type="search" id="UserQuery" name="data[User][query]" value="test" />`
1393:  *
1394:  * The first argument to an input type should always be the fieldname, in `Model.field` format.
1395:  * The second argument should always be an array of attributes for the input.
1396:  *
1397:  * @param string $method Method name / input type to make.
1398:  * @param array $params Parameters for the method call
1399:  * @return string Formatted input method.
1400:  * @throws CakeException When there are no params for the method call.
1401:  */
1402:     public function __call($method, $params) {
1403:         $options = array();
1404:         if (empty($params)) {
1405:             throw new CakeException(__d('cake_dev', 'Missing field name for FormHelper::%s', $method));
1406:         }
1407:         if (isset($params[1])) {
1408:             $options = $params[1];
1409:         }
1410:         if (!isset($options['type'])) {
1411:             $options['type'] = $method;
1412:         }
1413:         $options = $this->_initInputField($params[0], $options);
1414:         return $this->Html->useTag('input', $options['name'], array_diff_key($options, array('name' => '')));
1415:     }
1416: 
1417: /**
1418:  * Creates a textarea widget.
1419:  *
1420:  * ### Options:
1421:  *
1422:  * - `escape` - Whether or not the contents of the textarea should be escaped. Defaults to true.
1423:  *
1424:  * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
1425:  * @param array $options Array of HTML attributes, and special options above.
1426:  * @return string A generated HTML text input element
1427:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::textarea
1428:  */
1429:     public function textarea($fieldName, $options = array()) {
1430:         $options = $this->_initInputField($fieldName, $options);
1431:         $value = null;
1432: 
1433:         if (array_key_exists('value', $options)) {
1434:             $value = $options['value'];
1435:             if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
1436:                 $value = h($value);
1437:             }
1438:             unset($options['value']);
1439:         }
1440:         return $this->Html->useTag('textarea', $options['name'], array_diff_key($options, array('type' => '', 'name' => '')), $value);
1441:     }
1442: 
1443: /**
1444:  * Creates a hidden input field.
1445:  *
1446:  * @param string $fieldName Name of a field, in the form of "Modelname.fieldname"
1447:  * @param array $options Array of HTML attributes.
1448:  * @return string A generated hidden input
1449:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::hidden
1450:  */
1451:     public function hidden($fieldName, $options = array()) {
1452:         $secure = true;
1453: 
1454:         if (isset($options['secure'])) {
1455:             $secure = $options['secure'];
1456:             unset($options['secure']);
1457:         }
1458:         $options = $this->_initInputField($fieldName, array_merge(
1459:             $options, array('secure' => self::SECURE_SKIP)
1460:         ));
1461: 
1462:         if ($secure && $secure !== self::SECURE_SKIP) {
1463:             $this->_secure(true, null, '' . $options['value']);
1464:         }
1465: 
1466:         return $this->Html->useTag('hidden', $options['name'], array_diff_key($options, array('name' => '')));
1467:     }
1468: 
1469: /**
1470:  * Creates file input widget.
1471:  *
1472:  * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
1473:  * @param array $options Array of HTML attributes.
1474:  * @return string A generated file input.
1475:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::file
1476:  */
1477:     public function file($fieldName, $options = array()) {
1478:         $options += array('secure' => true);
1479:         $secure = $options['secure'];
1480:         $options['secure'] = self::SECURE_SKIP;
1481: 
1482:         $options = $this->_initInputField($fieldName, $options);
1483:         $field = $this->entity();
1484: 
1485:         foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) {
1486:             $this->_secure($secure, array_merge($field, array($suffix)));
1487:         }
1488: 
1489:         return $this->Html->useTag('file', $options['name'], array_diff_key($options, array('name' => '')));
1490:     }
1491: 
1492: /**
1493:  * Creates a `<button>` tag.  The type attribute defaults to `type="submit"`
1494:  * You can change it to a different value by using `$options['type']`.
1495:  *
1496:  * ### Options:
1497:  *
1498:  * - `escape` - HTML entity encode the $title of the button. Defaults to false.
1499:  *
1500:  * @param string $title The button's caption. Not automatically HTML encoded
1501:  * @param array $options Array of options and HTML attributes.
1502:  * @return string A HTML button tag.
1503:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::button
1504:  */
1505:     public function button($title, $options = array()) {
1506:         $options += array('type' => 'submit', 'escape' => false, 'secure' => false);
1507:         if ($options['escape']) {
1508:             $title = h($title);
1509:         }
1510:         if (isset($options['name'])) {
1511:             $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1512:             $this->_secure($options['secure'], $name);
1513:         }
1514:         return $this->Html->useTag('button', $options['type'], array_diff_key($options, array('type' => '')), $title);
1515:     }
1516: 
1517: /**
1518:  * Create a `<button>` tag with a surrounding `<form>` that submits via POST.
1519:  *
1520:  * This method creates a `<form>` element. So do not use this method in some opened form.
1521:  * Instead use FormHelper::submit() or FormHelper::button() to create buttons inside opened forms.
1522:  *
1523:  * ### Options:
1524:  *
1525:  * - `data` - Array with key/value to pass in input hidden
1526:  * - Other options is the same of button method.
1527:  *
1528:  * @param string $title The button's caption. Not automatically HTML encoded
1529:  * @param mixed $url URL as string or array
1530:  * @param array $options Array of options and HTML attributes.
1531:  * @return string A HTML button tag.
1532:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::postButton
1533:  */
1534:     public function postButton($title, $url, $options = array()) {
1535:         $out = $this->create(false, array('id' => false, 'url' => $url, 'style' => 'display:none;'));
1536:         if (isset($options['data']) && is_array($options['data'])) {
1537:             foreach ($options['data'] as $key => $value) {
1538:                 $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1539:             }
1540:             unset($options['data']);
1541:         }
1542:         $out .= $this->button($title, $options);
1543:         $out .= $this->end();
1544:         return $out;
1545:     }
1546: 
1547: /**
1548:  * Creates an HTML link, but access the url using method POST.
1549:  * Requires javascript to be enabled in browser.
1550:  *
1551:  * This method creates a `<form>` element. So do not use this method inside an existing form.
1552:  * Instead you should add a submit button using FormHelper::submit()
1553:  *
1554:  * ### Options:
1555:  *
1556:  * - `data` - Array with key/value to pass in input hidden
1557:  * - `confirm` - Can be used instead of $confirmMessage.
1558:  * - Other options is the same of HtmlHelper::link() method.
1559:  * - The option `onclick` will be replaced.
1560:  *
1561:  * @param string $title The content to be wrapped by <a> tags.
1562:  * @param mixed $url Cake-relative URL or array of URL parameters, or external URL (starts with http://)
1563:  * @param array $options Array of HTML attributes.
1564:  * @param string $confirmMessage JavaScript confirmation message.
1565:  * @return string An `<a />` element.
1566:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::postLink
1567:  */
1568:     public function postLink($title, $url = null, $options = array(), $confirmMessage = false) {
1569:         if (!empty($options['confirm'])) {
1570:             $confirmMessage = $options['confirm'];
1571:             unset($options['confirm']);
1572:         }
1573: 
1574:         $formName = uniqid('post_');
1575:         $formUrl = $this->url($url);
1576:         $out = $this->Html->useTag('form', $formUrl, array('name' => $formName, 'id' => $formName, 'style' => 'display:none;', 'method' => 'post'));
1577:         $out .= $this->Html->useTag('hidden', '_method', ' value="POST"');
1578:         $out .= $this->_csrfField();
1579: 
1580:         $fields = array();
1581:         if (isset($options['data']) && is_array($options['data'])) {
1582:             foreach ($options['data'] as $key => $value) {
1583:                 $fields[$key] = $value;
1584:                 $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1585:             }
1586:             unset($options['data']);
1587:         }
1588:         $out .= $this->secure($fields);
1589:         $out .= $this->Html->useTag('formend');
1590: 
1591:         $url = '#';
1592:         $onClick = 'document.' . $formName . '.submit();';
1593:         if ($confirmMessage) {
1594:             $confirmMessage = str_replace(array("'", '"'), array("\'", '\"'), $confirmMessage);
1595:             $options['onclick'] = "if (confirm('{$confirmMessage}')) { {$onClick} }";
1596:         } else {
1597:             $options['onclick'] = $onClick;
1598:         }
1599:         $options['onclick'] .= ' event.returnValue = false; return false;';
1600: 
1601:         $out .= $this->Html->link($title, $url, $options);
1602:         return $out;
1603:     }
1604: 
1605: /**
1606:  * Creates a submit button element.  This method will generate `<input />` elements that
1607:  * can be used to submit, and reset forms by using $options.  image submits can be created by supplying an
1608:  * image path for $caption.
1609:  *
1610:  * ### Options
1611:  *
1612:  * - `div` - Include a wrapping div?  Defaults to true.  Accepts sub options similar to
1613:  *   FormHelper::input().
1614:  * - `before` - Content to include before the input.
1615:  * - `after` - Content to include after the input.
1616:  * - `type` - Set to 'reset' for reset inputs.  Defaults to 'submit'
1617:  * - Other attributes will be assigned to the input element.
1618:  *
1619:  * ### Options
1620:  *
1621:  * - `div` - Include a wrapping div?  Defaults to true.  Accepts sub options similar to
1622:  *   FormHelper::input().
1623:  * - Other attributes will be assigned to the input element.
1624:  *
1625:  * @param string $caption The label appearing on the button OR if string contains :// or the
1626:  *  extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension
1627:  *  exists, AND the first character is /, image is relative to webroot,
1628:  *  OR if the first character is not /, image is relative to webroot/img.
1629:  * @param array $options Array of options.  See above.
1630:  * @return string A HTML submit button
1631:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::submit
1632:  */
1633:     public function submit($caption = null, $options = array()) {
1634:         if (!is_string($caption) && empty($caption)) {
1635:             $caption = __d('cake', 'Submit');
1636:         }
1637:         $out = null;
1638:         $div = true;
1639: 
1640:         if (isset($options['div'])) {
1641:             $div = $options['div'];
1642:             unset($options['div']);
1643:         }
1644:         $options += array('type' => 'submit', 'before' => null, 'after' => null, 'secure' => false);
1645:         $divOptions = array('tag' => 'div');
1646: 
1647:         if ($div === true) {
1648:             $divOptions['class'] = 'submit';
1649:         } elseif ($div === false) {
1650:             unset($divOptions);
1651:         } elseif (is_string($div)) {
1652:             $divOptions['class'] = $div;
1653:         } elseif (is_array($div)) {
1654:             $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
1655:         }
1656: 
1657:         if (isset($options['name'])) {
1658:             $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1659:             $this->_secure($options['secure'], $name);
1660:         }
1661:         unset($options['secure']);
1662: 
1663:         $before = $options['before'];
1664:         $after = $options['after'];
1665:         unset($options['before'], $options['after']);
1666: 
1667:         $isUrl = strpos($caption, '://') !== false;
1668:         $isImage = preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption);
1669: 
1670:         if ($isUrl || $isImage) {
1671:             $unlockFields = array('x', 'y');
1672:             if (isset($options['name'])) {
1673:                 $unlockFields = array(
1674:                     $options['name'] . '_x', $options['name'] . '_y'
1675:                 );
1676:             }
1677:             foreach ($unlockFields as $ignore) {
1678:                 $this->unlockField($ignore);
1679:             }
1680:         }
1681: 
1682:         if ($isUrl) {
1683:             unset($options['type']);
1684:             $tag = $this->Html->useTag('submitimage', $caption, $options);
1685:         } elseif ($isImage) {
1686:             unset($options['type']);
1687:             if ($caption{0} !== '/') {
1688:                 $url = $this->webroot(IMAGES_URL . $caption);
1689:             } else {
1690:                 $url = $this->webroot(trim($caption, '/'));
1691:             }
1692:             $url = $this->assetTimestamp($url);
1693:             $tag = $this->Html->useTag('submitimage', $url, $options);
1694:         } else {
1695:             $options['value'] = $caption;
1696:             $tag = $this->Html->useTag('submit', $options);
1697:         }
1698:         $out = $before . $tag . $after;
1699: 
1700:         if (isset($divOptions)) {
1701:             $tag = $divOptions['tag'];
1702:             unset($divOptions['tag']);
1703:             $out = $this->Html->tag($tag, $out, $divOptions);
1704:         }
1705:         return $out;
1706:     }
1707: 
1708: /**
1709:  * Returns a formatted SELECT element.
1710:  *
1711:  * ### Attributes:
1712:  *
1713:  * - `showParents` - If included in the array and set to true, an additional option element
1714:  *   will be added for the parent of each option group. You can set an option with the same name
1715:  *   and it's key will be used for the value of the option.
1716:  * - `multiple` - show a multiple select box.  If set to 'checkbox' multiple checkboxes will be
1717:  *   created instead.
1718:  * - `empty` - If true, the empty select option is shown.  If a string,
1719:  *   that string is displayed as the empty element.
1720:  * - `escape` - If true contents of options will be HTML entity encoded. Defaults to true.
1721:  * - `value` The selected value of the input.
1722:  * - `class` - When using multiple = checkbox the classname to apply to the divs. Defaults to 'checkbox'.
1723:  *
1724:  * ### Using options
1725:  *
1726:  * A simple array will create normal options:
1727:  *
1728:  * {{{
1729:  * $options = array(1 => 'one', 2 => 'two);
1730:  * $this->Form->select('Model.field', $options));
1731:  * }}}
1732:  *
1733:  * While a nested options array will create optgroups with options inside them.
1734:  * {{{
1735:  * $options = array(
1736:  *  1 => 'bill',
1737:  *  'fred' => array(
1738:  *      2 => 'fred',
1739:  *      3 => 'fred jr.'
1740:  *   )
1741:  * );
1742:  * $this->Form->select('Model.field', $options);
1743:  * }}}
1744:  *
1745:  * In the above `2 => 'fred'` will not generate an option element.  You should enable the `showParents`
1746:  * attribute to show the fred option.
1747:  *
1748:  * If you have multiple options that need to have the same value attribute, you can
1749:  * use an array of arrays to express this:
1750:  *
1751:  * {{{
1752:  * $options = array(
1753:  *      array('name' => 'United states', 'value' => 'USA'),
1754:  *      array('name' => 'USA', 'value' => 'USA'),
1755:  * );
1756:  * }}}
1757:  *
1758:  * @param string $fieldName Name attribute of the SELECT
1759:  * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the
1760:  *  SELECT element
1761:  * @param array $attributes The HTML attributes of the select element.
1762:  * @return string Formatted SELECT element
1763:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#options-for-select-checkbox-and-radio-inputs
1764:  */
1765:     public function select($fieldName, $options = array(), $attributes = array()) {
1766:         $select = array();
1767:         $style = null;
1768:         $tag = null;
1769:         $attributes += array(
1770:             'class' => null,
1771:             'escape' => true,
1772:             'secure' => true,
1773:             'empty' => '',
1774:             'showParents' => false,
1775:             'hiddenField' => true
1776:         );
1777: 
1778:         $escapeOptions = $this->_extractOption('escape', $attributes);
1779:         $secure = $this->_extractOption('secure', $attributes);
1780:         $showEmpty = $this->_extractOption('empty', $attributes);
1781:         $showParents = $this->_extractOption('showParents', $attributes);
1782:         $hiddenField = $this->_extractOption('hiddenField', $attributes);
1783:         unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
1784:         $id = $this->_extractOption('id', $attributes);
1785: 
1786:         $attributes = $this->_initInputField($fieldName, array_merge(
1787:             (array)$attributes, array('secure' => self::SECURE_SKIP)
1788:         ));
1789: 
1790:         if (is_string($options) && isset($this->_options[$options])) {
1791:             $options = $this->_generateOptions($options);
1792:         } elseif (!is_array($options)) {
1793:             $options = array();
1794:         }
1795:         if (isset($attributes['type'])) {
1796:             unset($attributes['type']);
1797:         }
1798: 
1799:         if (!empty($attributes['multiple'])) {
1800:             $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
1801:             $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
1802:             $tag = $template;
1803:             if ($hiddenField) {
1804:                 $hiddenAttributes = array(
1805:                     'value' => '',
1806:                     'id' => $attributes['id'] . ($style ? '' : '_'),
1807:                     'secure' => false,
1808:                     'name' => $attributes['name']
1809:                 );
1810:                 $select[] = $this->hidden(null, $hiddenAttributes);
1811:             }
1812:         } else {
1813:             $tag = 'selectstart';
1814:         }
1815: 
1816:         if (!empty($tag) || isset($template)) {
1817:             if (!isset($secure) || $secure == true) {
1818:                 $this->_secure(true);
1819:             }
1820:             $select[] = $this->Html->useTag($tag, $attributes['name'], array_diff_key($attributes, array('name' => '', 'value' => '')));
1821:         }
1822:         $emptyMulti = (
1823:             $showEmpty !== null && $showEmpty !== false && !(
1824:                 empty($showEmpty) && (isset($attributes) &&
1825:                 array_key_exists('multiple', $attributes))
1826:             )
1827:         );
1828: 
1829:         if ($emptyMulti) {
1830:             $showEmpty = ($showEmpty === true) ? '' : $showEmpty;
1831:             $options = array_reverse($options, true);
1832:             $options[''] = $showEmpty;
1833:             $options = array_reverse($options, true);
1834:         }
1835: 
1836:         if (!$id) {
1837:             $attributes['id'] = Inflector::camelize($attributes['id']);
1838:         }
1839: 
1840:         $select = array_merge($select, $this->_selectOptions(
1841:             array_reverse($options, true),
1842:             array(),
1843:             $showParents,
1844:             array(
1845:                 'escape' => $escapeOptions,
1846:                 'style' => $style,
1847:                 'name' => $attributes['name'],
1848:                 'value' => $attributes['value'],
1849:                 'class' => $attributes['class'],
1850:                 'id' => $attributes['id']
1851:             )
1852:         ));
1853: 
1854:         $template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend';
1855:         $select[] = $this->Html->useTag($template);
1856:         return implode("\n", $select);
1857:     }
1858: 
1859: /**
1860:  * Returns a SELECT element for days.
1861:  *
1862:  * ### Attributes:
1863:  *
1864:  * - `empty` - If true, the empty select option is shown.  If a string,
1865:  *   that string is displayed as the empty element.
1866:  * - `value` The selected value of the input.
1867:  *
1868:  * @param string $fieldName Prefix name for the SELECT element
1869:  * @param array $attributes HTML attributes for the select element
1870:  * @return string A generated day select box.
1871:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::day
1872:  */
1873:     public function day($fieldName = null, $attributes = array()) {
1874:         $attributes += array('empty' => true, 'value' => null);
1875:         $attributes = $this->_dateTimeSelected('day', $fieldName, $attributes);
1876: 
1877:         if (strlen($attributes['value']) > 2) {
1878:             $attributes['value'] = date('d', strtotime($attributes['value']));
1879:         } elseif ($attributes['value'] === false) {
1880:             $attributes['value'] = null;
1881:         }
1882:         return $this->select($fieldName . ".day", $this->_generateOptions('day'), $attributes);
1883:     }
1884: 
1885: /**
1886:  * Returns a SELECT element for years
1887:  *
1888:  * ### Attributes:
1889:  *
1890:  * - `empty` - If true, the empty select option is shown.  If a string,
1891:  *   that string is displayed as the empty element.
1892:  * - `orderYear` - Ordering of year values in select options.
1893:  *   Possible values 'asc', 'desc'. Default 'desc'
1894:  * - `value` The selected value of the input.
1895:  *
1896:  * @param string $fieldName Prefix name for the SELECT element
1897:  * @param integer $minYear First year in sequence
1898:  * @param integer $maxYear Last year in sequence
1899:  * @param array $attributes Attribute array for the select elements.
1900:  * @return string Completed year select input
1901:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::year
1902:  */
1903:     public function year($fieldName, $minYear = null, $maxYear = null, $attributes = array()) {
1904:         $attributes += array('empty' => true, 'value' => null);
1905:         if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
1906:             if (is_array($value)) {
1907:                 extract($value);
1908:                 $attributes['value'] = $year;
1909:             } else {
1910:                 if (empty($value)) {
1911:                     if (!$attributes['empty'] && !$maxYear) {
1912:                         $attributes['value'] = 'now';
1913: 
1914:                     } elseif (!$attributes['empty'] && $maxYear && !$attributes['value']) {
1915:                         $attributes['value'] = $maxYear;
1916:                     }
1917:                 } else {
1918:                     $attributes['value'] = $value;
1919:                 }
1920:             }
1921:         }
1922: 
1923:         if (strlen($attributes['value']) > 4 || $attributes['value'] === 'now') {
1924:             $attributes['value'] = date('Y', strtotime($attributes['value']));
1925:         } elseif ($attributes['value'] === false) {
1926:             $attributes['value'] = null;
1927:         }
1928:         $yearOptions = array('min' => $minYear, 'max' => $maxYear, 'order' => 'desc');
1929:         if (isset($attributes['orderYear'])) {
1930:             $yearOptions['order'] = $attributes['orderYear'];
1931:             unset($attributes['orderYear']);
1932:         }
1933:         return $this->select(
1934:             $fieldName . '.year', $this->_generateOptions('year', $yearOptions),
1935:             $attributes
1936:         );
1937:     }
1938: 
1939: /**
1940:  * Returns a SELECT element for months.
1941:  *
1942:  * ### Attributes:
1943:  *
1944:  * - `monthNames` - If false, 2 digit numbers will be used instead of text.
1945:  *   If a array, the given array will be used.
1946:  * - `empty` - If true, the empty select option is shown.  If a string,
1947:  *   that string is displayed as the empty element.
1948:  * - `value` The selected value of the input.
1949:  *
1950:  * @param string $fieldName Prefix name for the SELECT element
1951:  * @param array $attributes Attributes for the select element
1952:  * @return string A generated month select dropdown.
1953:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::month
1954:  */
1955:     public function month($fieldName, $attributes = array()) {
1956:         $attributes += array('empty' => true, 'value' => null);
1957:         $attributes = $this->_dateTimeSelected('month', $fieldName, $attributes);
1958: 
1959:         if (strlen($attributes['value']) > 2) {
1960:             $attributes['value'] = date('m', strtotime($attributes['value']));
1961:         } elseif ($attributes['value'] === false) {
1962:             $attributes['value'] = null;
1963:         }
1964:         $defaults = array('monthNames' => true);
1965:         $attributes = array_merge($defaults, (array) $attributes);
1966:         $monthNames = $attributes['monthNames'];
1967:         unset($attributes['monthNames']);
1968: 
1969:         return $this->select(
1970:             $fieldName . ".month",
1971:             $this->_generateOptions('month', array('monthNames' => $monthNames)),
1972:             $attributes
1973:         );
1974:     }
1975: 
1976: /**
1977:  * Returns a SELECT element for hours.
1978:  *
1979:  * ### Attributes:
1980:  *
1981:  * - `empty` - If true, the empty select option is shown.  If a string,
1982:  *   that string is displayed as the empty element.
1983:  * - `value` The selected value of the input.
1984:  *
1985:  * @param string $fieldName Prefix name for the SELECT element
1986:  * @param boolean $format24Hours True for 24 hours format
1987:  * @param array $attributes List of HTML attributes
1988:  * @return string Completed hour select input
1989:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::hour
1990:  */
1991:     public function hour($fieldName, $format24Hours = false, $attributes = array()) {
1992:         $attributes += array('empty' => true, 'value' => null);
1993:         $attributes = $this->_dateTimeSelected('hour', $fieldName, $attributes);
1994: 
1995:         if (strlen($attributes['value']) > 2) {
1996:             if ($format24Hours) {
1997:                 $attributes['value'] = date('H', strtotime($attributes['value']));
1998:             } else {
1999:                 $attributes['value'] = date('g', strtotime($attributes['value']));
2000:             }
2001:         } elseif ($attributes['value'] === false) {
2002:             $attributes['value'] = null;
2003:         }
2004:         return $this->select(
2005:             $fieldName . ".hour",
2006:             $this->_generateOptions($format24Hours ? 'hour24' : 'hour'),
2007:             $attributes
2008:         );
2009:     }
2010: 
2011: /**
2012:  * Returns a SELECT element for minutes.
2013:  *
2014:  * ### Attributes:
2015:  *
2016:  * - `empty` - If true, the empty select option is shown.  If a string,
2017:  *   that string is displayed as the empty element.
2018:  * - `value` The selected value of the input.
2019:  *
2020:  * @param string $fieldName Prefix name for the SELECT element
2021:  * @param string $attributes Array of Attributes
2022:  * @return string Completed minute select input.
2023:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::minute
2024:  */
2025:     public function minute($fieldName, $attributes = array()) {
2026:         $attributes += array('empty' => true, 'value' => null);
2027:         $attributes = $this->_dateTimeSelected('min', $fieldName, $attributes);
2028: 
2029:         if (strlen($attributes['value']) > 2) {
2030:             $attributes['value'] = date('i', strtotime($attributes['value']));
2031:         } elseif ($attributes['value'] === false) {
2032:             $attributes['value'] = null;
2033:         }
2034:         $minuteOptions = array();
2035: 
2036:         if (isset($attributes['interval'])) {
2037:             $minuteOptions['interval'] = $attributes['interval'];
2038:             unset($attributes['interval']);
2039:         }
2040:         return $this->select(
2041:             $fieldName . ".min", $this->_generateOptions('minute', $minuteOptions),
2042:             $attributes
2043:         );
2044:     }
2045: 
2046: /**
2047:  * Selects values for dateTime selects.
2048:  *
2049:  * @param string $select Name of element field. ex. 'day'
2050:  * @param string $fieldName Name of fieldName being generated ex. Model.created
2051:  * @param array $attributes Array of attributes, must contain 'empty' key.
2052:  * @return array Attributes array with currently selected value.
2053:  */
2054:     protected function _dateTimeSelected($select, $fieldName, $attributes) {
2055:         if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2056:             if (is_array($value) && isset($value[$select])) {
2057:                 $attributes['value'] = $value[$select];
2058:             } else {
2059:                 if (empty($value)) {
2060:                     if (!$attributes['empty']) {
2061:                         $attributes['value'] = 'now';
2062:                     }
2063:                 } else {
2064:                     $attributes['value'] = $value;
2065:                 }
2066:             }
2067:         }
2068:         return $attributes;
2069:     }
2070: 
2071: /**
2072:  * Returns a SELECT element for AM or PM.
2073:  *
2074:  * ### Attributes:
2075:  *
2076:  * - `empty` - If true, the empty select option is shown.  If a string,
2077:  *   that string is displayed as the empty element.
2078:  * - `value` The selected value of the input.
2079:  *
2080:  * @param string $fieldName Prefix name for the SELECT element
2081:  * @param string $attributes Array of Attributes
2082:  * @return string Completed meridian select input
2083:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::meridian
2084:  */
2085:     public function meridian($fieldName, $attributes = array()) {
2086:         $attributes += array('empty' => true, 'value' => null);
2087:         if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2088:             if (is_array($value)) {
2089:                 extract($value);
2090:                 $attributes['value'] = $meridian;
2091:             } else {
2092:                 if (empty($value)) {
2093:                     if (!$attributes['empty']) {
2094:                         $attributes['value'] = date('a');
2095:                     }
2096:                 } else {
2097:                     $attributes['value'] = date('a', strtotime($value));
2098:                 }
2099:             }
2100:         }
2101: 
2102:         if ($attributes['value'] === false) {
2103:             $attributes['value'] = null;
2104:         }
2105:         return $this->select(
2106:             $fieldName . ".meridian", $this->_generateOptions('meridian'),
2107:             $attributes
2108:         );
2109:     }
2110: 
2111: /**
2112:  * Returns a set of SELECT elements for a full datetime setup: day, month and year, and then time.
2113:  *
2114:  * ### Attributes:
2115:  *
2116:  * - `monthNames` If false, 2 digit numbers will be used instead of text.
2117:  *   If a array, the given array will be used.
2118:  * - `minYear` The lowest year to use in the year select
2119:  * - `maxYear` The maximum year to use in the year select
2120:  * - `interval` The interval for the minutes select. Defaults to 1
2121:  * - `separator` The contents of the string between select elements. Defaults to '-'
2122:  * - `empty` - If true, the empty select option is shown.  If a string,
2123:  *   that string is displayed as the empty element.
2124:  * - `value` | `default` The default value to be used by the input.  A value in `$this->data`
2125:  *   matching the field name will override this value.  If no default is provided `time()` will be used.
2126:  *
2127:  * @param string $fieldName Prefix name for the SELECT element
2128:  * @param string $dateFormat DMY, MDY, YMD.
2129:  * @param string $timeFormat 12, 24.
2130:  * @param string $attributes array of Attributes
2131:  * @return string Generated set of select boxes for the date and time formats chosen.
2132:  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/form.html#FormHelper::dateTime
2133:  */
2134:     public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $attributes = array()) {
2135:         $attributes += array('empty' => true, 'value' => null);
2136:         $year = $month = $day = $hour = $min = $meridian = null;
2137: 
2138:         if (empty($attributes['value'])) {
2139:             $attributes = $this->value($attributes, $fieldName);
2140:         }
2141: 
2142:         if ($attributes['value'] === null && $attributes['empty'] != true) {
2143:             $attributes['value'] = time();
2144:         }
2145: 
2146:         if (!empty($attributes['value'])) {
2147:             if (is_array($attributes['value'])) {
2148:                 extract($attributes['value']);
2149:             } else {
2150:                 if (is_numeric($attributes['value'])) {
2151:                     $attributes['value'] = strftime('%Y-%m-%d %H:%M:%S', $attributes['value']);
2152:                 }
2153:                 $meridian = 'am';
2154:                 $pos = strpos($attributes['value'], '-');
2155:                 if ($pos !== false) {
2156:                     $date = explode('-', $attributes['value']);
2157:                     $days = explode(' ', $date[2]);
2158:                     $day = $days[0];
2159:                     $month = $date[1];
2160:                     $year = $date[0];
2161:                 } else {
2162:                     $days[1] = $attributes['value'];
2163:                 }
2164: 
2165:                 if (!empty($timeFormat)) {
2166:                     $time = explode(':', $days[1]);
2167: 
2168:                     if (($time[0] > 12) && $timeFormat == '12') {
2169:                         $time[0] = $time[0] - 12;
2170:                         $meridian = 'pm';
2171:                     } elseif ($time[0] == '12' && $timeFormat == '12') {
2172:                         $meridian = 'pm';
2173:                     } elseif ($time[0] == '00' && $timeFormat == '12') {
2174:                         $time[0] = 12;
2175:                     } elseif ($time[0] >= 12) {
2176:                         $meridian = 'pm';
2177:                     }
2178:                     if ($time[0] == 0 && $timeFormat == '12') {
2179:                         $time[0] = 12;
2180:                     }
2181:                     $hour = $min = null;
2182:                     if (isset($time[1])) {
2183:                         $hour = $time[0];
2184:                         $min = $time[1];
2185:                     }
2186:                 }
2187:             }
2188:         }
2189: 
2190:         $elements = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian');
2191:         $defaults = array(
2192:             'minYear' => null, 'maxYear' => null, 'separator' => '-',
2193:             'interval' => 1, 'monthNames' => true
2194:         );
2195:         $attributes = array_merge($defaults, (array) $attributes);
2196:         if (isset($attributes['minuteInterval'])) {
2197:             $attributes['interval'] = $attributes['minuteInterval'];
2198:             unset($attributes['minuteInterval']);
2199:         }
2200:         $minYear = $attributes['minYear'];
2201:         $maxYear = $attributes['maxYear'];
2202:         $separator = $attributes['separator'];
2203:         $interval = $attributes['interval'];
2204:         $monthNames = $attributes['monthNames'];
2205:         $attributes = array_diff_key($attributes, $defaults);
2206: 
2207:         if (isset($attributes['id'])) {
2208:             if (is_string($attributes['id'])) {
2209:                 // build out an array version
2210:                 foreach ($elements as $element) {
2211:                     $selectAttrName = 'select' . $element . 'Attr';
2212:                     ${$selectAttrName} = $attributes;
2213:                     ${$selectAttrName}['id'] = $attributes['id'] . $element;
2214:                 }
2215:             } elseif (is_array($attributes['id'])) {
2216:                 // check for missing ones and build selectAttr for each element
2217:                 $attributes['id'] += array(
2218:                     'month' => '', 'year' => '', 'day' => '',
2219:                     'hour' => '', 'minute' => '', 'meridian' => ''
2220:                 );
2221:                 foreach ($elements as $element) {
2222:                     $selectAttrName = 'select' . $element . 'Attr';
2223:                     ${$selectAttrName} = $attributes;
2224:                     ${$selectAttrName}['id'] = $attributes['id'][strtolower($element)];
2225:                 }
2226:             }
2227:         } else {
2228:             // build the selectAttrName with empty id's to pass
2229:             foreach ($elements as $element) {
2230:                 $selectAttrName = 'select' . $element . 'Attr';
2231:                 ${$selectAttrName} = $attributes;
2232:             }
2233:         }
2234: 
2235:         $selects = array();
2236:         foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
2237:             switch ($char) {
2238:                 case 'Y':
2239:                     $selectYearAttr['value'] = $year;
2240:                     $selects[] = $this->year(
2241:                         $fieldName, $minYear, $maxYear, $selectYearAttr
2242:                     );
2243:                 break;
2244:                 case 'M':
2245:                     $selectMonthAttr['value'] = $month;
2246:                     $selectMonthAttr['monthNames'] = $monthNames;
2247:                     $selects[] = $this->month($fieldName, $selectMonthAttr);
2248:                 break;
2249:                 case 'D':
2250:                     $selectDayAttr['value'] = $day;
2251:                     $selects[] = $this->day($fieldName, $selectDayAttr);
2252:                 break;
2253:             }
2254:         }
2255:         $opt = implode($separator, $selects);
2256: 
2257:         if (!empty($interval) && $interval > 1 && !empty($min)) {
2258:             $min = round($min * (1 / $interval)) * $interval;
2259:         }
2260:         $selectMinuteAttr['interval'] = $interval;
2261:         switch ($timeFormat) {
2262:             case '24':
2263:                 $selectHourAttr['value'] = $hour;
2264:                 $selectMinuteAttr['value'] = $min;
2265:                 $opt .= $this->hour($fieldName, true, $selectHourAttr) . ':' .
2266:                 $this->minute($fieldName, $selectMinuteAttr);
2267:             break;
2268:             case '12':
2269:                 $selectHourAttr['value'] = $hour;
2270:                 $selectMinuteAttr['value'] = $min;
2271:                 $selectMeridianAttr['value'] = $meridian;
2272:                 $opt .= $this->hour($fieldName, false, $selectHourAttr) . ':' .
2273:                 $this->minute($fieldName, $selectMinuteAttr) . ' ' .
2274:                 $this->meridian($fieldName, $selectMeridianAttr);
2275:             break;
2276:             default:
2277:                 $opt .= '';
2278:             break;
2279:         }
2280:         return $opt;
2281:     }
2282: 
2283: /**
2284:  * Gets the input field name for the current tag
2285:  *
2286:  * @param array $options
2287:  * @param string $field
2288:  * @param string $key
2289:  * @return array
2290:  */
2291:     protected function _name($options = array(), $field = null, $key = 'name') {
2292:         if ($this->requestType == 'get') {
2293:             if ($options === null) {
2294:                 $options = array();
2295:             } elseif (is_string($options)) {
2296:                 $field = $options;
2297:                 $options = 0;
2298:             }
2299: 
2300:             if (!empty($field)) {
2301:                 $this->setEntity($field);
2302:             }
2303: 
2304:             if (is_array($options) && isset($options[$key])) {
2305:                 return $options;
2306:             }
2307: 
2308:             $entity = $this->entity();
2309:             $model = $this->model();
2310:             $name = $model === $entity[0] && isset($entity[1]) ? $entity[1] : $entity[0];
2311:             $last = $entity[count($entity) - 1];
2312:             if (in_array($last, $this->_fieldSuffixes)) {
2313:                 $name .= '[' . $last . ']';
2314:             }
2315: 
2316:             if (is_array($options)) {
2317:                 $options[$key] = $name;
2318:                 return $options;
2319:             } else {
2320:                 return $name;
2321:             }
2322:         }
2323:         return parent::_name($options, $field, $key);
2324:     }
2325: 
2326: /**
2327:  * Returns an array of formatted OPTION/OPTGROUP elements
2328:  *
2329:  * @param array $elements
2330:  * @param array $parents
2331:  * @param boolean $showParents
2332:  * @param array $attributes
2333:  * @return array
2334:  */
2335:     protected function _selectOptions($elements = array(), $parents = array(), $showParents = null, $attributes = array()) {
2336:         $select = array();
2337:         $attributes = array_merge(
2338:             array('escape' => true, 'style' => null, 'value' => null, 'class' => null),
2339:             $attributes
2340:         );
2341:         $selectedIsEmpty = ($attributes['value'] === '' || $attributes['value'] === null);
2342:         $selectedIsArray = is_array($attributes['value']);
2343: 
2344:         foreach ($elements as $name => $title) {
2345:             $htmlOptions = array();
2346:             if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) {
2347:                 if (!empty($name)) {
2348:                     if ($attributes['style'] === 'checkbox') {
2349:                         $select[] = $this->Html->useTag('fieldsetend');
2350:                     } else {
2351:                         $select[] = $this->Html->useTag('optiongroupend');
2352:                     }
2353:                     $parents[] = $name;
2354:                 }
2355:                 $select = array_merge($select, $this->_selectOptions(
2356:                     $title, $parents, $showParents, $attributes
2357:                 ));
2358: 
2359:                 if (!empty($name)) {
2360:                     $name = $attributes['escape'] ? h($name) : $name;
2361:                     if ($attributes['style'] === 'checkbox') {
2362:                         $select[] = $this->Html->useTag('fieldsetstart', $name);
2363:                     } else {
2364:                         $select[] = $this->Html->useTag('optiongroup', $name, '');
2365:                     }
2366:                 }
2367:                 $name = null;
2368:             } elseif (is_array($title)) {
2369:                 $htmlOptions = $title;
2370:                 $name = $title['value'];
2371:                 $title = $title['name'];
2372:                 unset($htmlOptions['name'], $htmlOptions['value']);
2373:             }
2374: 
2375:             if ($name !== null) {
2376:                 if (
2377:                     (!$selectedIsArray && !$selectedIsEmpty && (string)$attributes['value'] == (string)$name) ||
2378:                     ($selectedIsArray && in_array($name, $attributes['value']))
2379:                 ) {
2380:                     if ($attributes['style'] === 'checkbox') {
2381:                         $htmlOptions['checked'] = true;
2382:                     } else {
2383:                         $htmlOptions['selected'] = 'selected';
2384:                     }
2385:                 }
2386: 
2387:                 if ($showParents || (!in_array($title, $parents))) {
2388:                     $title = ($attributes['escape']) ? h($title) : $title;
2389: 
2390:                     if ($attributes['style'] === 'checkbox') {
2391:                         $htmlOptions['value'] = $name;
2392: 
2393:                         $tagName = $attributes['id'] . Inflector::camelize(Inflector::slug($name));
2394:                         $htmlOptions['id'] = $tagName;
2395:                         $label = array('for' => $tagName);
2396: 
2397:                         if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) {
2398:                             $label['class'] = 'selected';
2399:                         }
2400: 
2401:                         $name = $attributes['name'];
2402: 
2403:                         if (empty($attributes['class'])) {
2404:                             $attributes['class'] = 'checkbox';
2405:                         } elseif ($attributes['class'] === 'form-error') {
2406:                             $attributes['class'] = 'checkbox ' . $attributes['class'];
2407:                         }
2408:                         $label = $this->label(null, $title, $label);
2409:                         $item = $this->Html->useTag('checkboxmultiple', $name, $htmlOptions);
2410:                         $select[] = $this->Html->div($attributes['class'], $item . $label);
2411:                     } else {
2412:                         $select[] = $this->Html->useTag('selectoption', $name, $htmlOptions, $title);
2413:                     }
2414:                 }
2415:             }
2416:         }
2417: 
2418:         return array_reverse($select, true);
2419:     }
2420: 
2421: /**
2422:  * Generates option lists for common <select /> menus
2423:  *
2424:  * @param string $name
2425:  * @param array $options
2426:  * @return array
2427:  */
2428:     protected function _generateOptions($name, $options = array()) {
2429:         if (!empty($this->options[$name])) {
2430:             return $this->options[$name];
2431:         }
2432:         $data = array();
2433: 
2434:         switch ($name) {
2435:             case 'minute':
2436:                 if (isset($options['interval'])) {
2437:                     $interval = $options['interval'];
2438:                 } else {
2439:                     $interval = 1;
2440:                 }
2441:                 $i = 0;
2442:                 while ($i < 60) {
2443:                     $data[sprintf('%02d', $i)] = sprintf('%02d', $i);
2444:                     $i += $interval;
2445:                 }
2446:             break;
2447:             case 'hour':
2448:                 for ($i = 1; $i <= 12; $i++) {
2449:                     $data[sprintf('%02d', $i)] = $i;
2450:                 }
2451:             break;
2452:             case 'hour24':
2453:                 for ($i = 0; $i <= 23; $i++) {
2454:                     $data[sprintf('%02d', $i)] = $i;
2455:                 }
2456:             break;
2457:             case 'meridian':
2458:                 $data = array('am' => 'am', 'pm' => 'pm');
2459:             break;
2460:             case 'day':
2461:                 $min = 1;
2462:                 $max = 31;
2463: 
2464:                 if (isset($options['min'])) {
2465:                     $min = $options['min'];
2466:                 }
2467:                 if (isset($options['max'])) {
2468:                     $max = $options['max'];
2469:                 }
2470: 
2471:                 for ($i = $min; $i <= $max; $i++) {
2472:                     $data[sprintf('%02d', $i)] = $i;
2473:                 }
2474:             break;
2475:             case 'month':
2476:                 if ($options['monthNames'] === true) {
2477:                     $data['01'] = __d('cake', 'January');
2478:                     $data['02'] = __d('cake', 'February');
2479:                     $data['03'] = __d('cake', 'March');
2480:                     $data['04'] = __d('cake', 'April');
2481:                     $data['05'] = __d('cake', 'May');
2482:                     $data['06'] = __d('cake', 'June');
2483:                     $data['07'] = __d('cake', 'July');
2484:                     $data['08'] = __d('cake', 'August');
2485:                     $data['09'] = __d('cake', 'September');
2486:                     $data['10'] = __d('cake', 'October');
2487:                     $data['11'] = __d('cake', 'November');
2488:                     $data['12'] = __d('cake', 'December');
2489:                 } else if (is_array($options['monthNames'])) {
2490:                     $data = $options['monthNames'];
2491:                 } else {
2492:                     for ($m = 1; $m <= 12; $m++) {
2493:                         $data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999));
2494:                     }
2495:                 }
2496:             break;
2497:             case 'year':
2498:                 $current = intval(date('Y'));
2499: 
2500:                 $min = !isset($options['min']) ? $current - 20 : (int)$options['min'];
2501:                 $max = !isset($options['max']) ? $current + 20 : (int)$options['max'];
2502: 
2503:                 if ($min > $max) {
2504:                     list($min, $max) = array($max, $min);
2505:                 }
2506:                 for ($i = $min; $i <= $max; $i++) {
2507:                     $data[$i] = $i;
2508:                 }
2509:                 if ($options['order'] != 'asc') {
2510:                     $data = array_reverse($data, true);
2511:                 }
2512:             break;
2513:         }
2514:         $this->_options[$name] = $data;
2515:         return $this->_options[$name];
2516:     }
2517: 
2518: /**
2519:  * Sets field defaults and adds field to form security input hash
2520:  *
2521:  * ### Options
2522:  *
2523:  *  - `secure` - boolean whether or not the field should be added to the security fields.
2524:  *
2525:  * @param string $field Name of the field to initialize options for.
2526:  * @param array $options Array of options to append options into.
2527:  * @return array Array of options for the input.
2528:  */
2529:     protected function _initInputField($field, $options = array()) {
2530:         if (isset($options['secure'])) {
2531:             $secure = $options['secure'];
2532:             unset($options['secure']);
2533:         } else {
2534:             $secure = (isset($this->request['_Token']) && !empty($this->request['_Token']));
2535:         }
2536: 
2537:         $result = parent::_initInputField($field, $options);
2538:         if ($this->tagIsInvalid() !== false) {
2539:             $result = $this->addClass($result, 'form-error');
2540:         }
2541:         if ($secure === self::SECURE_SKIP) {
2542:             return $result;
2543:         }
2544: 
2545:         $fieldName = null;
2546:         if (!empty($options['name'])) {
2547:             preg_match_all('/\[(.*?)\]/', $options['name'], $matches);
2548:             if (isset($matches[1])) {
2549:                 $fieldName = $matches[1];
2550:             }
2551:         }
2552: 
2553:         $this->_secure($secure, $fieldName);
2554:         return $result;
2555:     }
2556: }
2557: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs