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.2 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.2
      • 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
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

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