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

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