PageRenderTime 60ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/cake/libs/view/helpers/form.php

http://github.com/Datawalke/Coordino
PHP | 2226 lines | 1444 code | 183 blank | 599 comment | 363 complexity | be02c6f243c03fe8a37c7549801c68c8 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * Automatic generation of HTML FORMs from given data.
  4. *
  5. * Used for scaffolding.
  6. *
  7. * PHP versions 4 and 5
  8. *
  9. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  10. * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. *
  12. * Licensed under The MIT License
  13. * Redistributions of files must retain the above copyright notice.
  14. *
  15. * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
  16. * @link http://cakephp.org CakePHP(tm) Project
  17. * @package cake
  18. * @subpackage cake.cake.libs.view.helpers
  19. * @since CakePHP(tm) v 0.10.0.1076
  20. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  21. */
  22. /**
  23. * Form helper library.
  24. *
  25. * Automatic generation of HTML FORMs from given data.
  26. *
  27. * @package cake
  28. * @subpackage cake.cake.libs.view.helpers
  29. * @link http://book.cakephp.org/view/1383/Form
  30. */
  31. class FormHelper extends AppHelper {
  32. /**
  33. * Other helpers used by FormHelper
  34. *
  35. * @var array
  36. * @access public
  37. */
  38. var $helpers = array('Html');
  39. /**
  40. * Holds the fields array('field_name' => array('type'=> 'string', 'length'=> 100),
  41. * primaryKey and validates array('field_name')
  42. *
  43. * @access public
  44. */
  45. var $fieldset = array();
  46. /**
  47. * Options used by DateTime fields
  48. *
  49. * @var array
  50. */
  51. var $__options = array(
  52. 'day' => array(), 'minute' => array(), 'hour' => array(),
  53. 'month' => array(), 'year' => array(), 'meridian' => array()
  54. );
  55. /**
  56. * List of fields created, used with secure forms.
  57. *
  58. * @var array
  59. * @access public
  60. */
  61. var $fields = array();
  62. /**
  63. * Defines the type of form being created. Set by FormHelper::create().
  64. *
  65. * @var string
  66. * @access public
  67. */
  68. var $requestType = null;
  69. /**
  70. * The default model being used for the current form.
  71. *
  72. * @var string
  73. * @access public
  74. */
  75. var $defaultModel = null;
  76. /**
  77. * Persistent default options used by input(). Set by FormHelper::create().
  78. *
  79. * @var array
  80. * @access protected
  81. */
  82. var $_inputDefaults = array();
  83. /**
  84. * Introspects model information and extracts information related
  85. * to validation, field length and field type. Appends information into
  86. * $this->fieldset.
  87. *
  88. * @return Model Returns a model instance
  89. * @access protected
  90. */
  91. function &_introspectModel($model) {
  92. $object = null;
  93. if (is_string($model) && strpos($model, '.') !== false) {
  94. $path = explode('.', $model);
  95. $model = end($path);
  96. }
  97. if (ClassRegistry::isKeySet($model)) {
  98. $object =& ClassRegistry::getObject($model);
  99. }
  100. if (!empty($object)) {
  101. $fields = $object->schema();
  102. foreach ($fields as $key => $value) {
  103. unset($fields[$key]);
  104. $fields[$key] = $value;
  105. }
  106. if (!empty($object->hasAndBelongsToMany)) {
  107. foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
  108. $fields[$alias] = array('type' => 'multiple');
  109. }
  110. }
  111. $validates = array();
  112. if (!empty($object->validate)) {
  113. foreach ($object->validate as $validateField => $validateProperties) {
  114. if ($this->_isRequiredField($validateProperties)) {
  115. $validates[] = $validateField;
  116. }
  117. }
  118. }
  119. $defaults = array('fields' => array(), 'key' => 'id', 'validates' => array());
  120. $key = $object->primaryKey;
  121. $this->fieldset[$model] = array_merge($defaults, compact('fields', 'key', 'validates'));
  122. }
  123. return $object;
  124. }
  125. /**
  126. * Returns if a field is required to be filled based on validation properties from the validating object
  127. *
  128. * @return boolean true if field is required to be filled, false otherwise
  129. * @access protected
  130. */
  131. function _isRequiredField($validateProperties) {
  132. $required = false;
  133. if (is_array($validateProperties)) {
  134. $dims = Set::countDim($validateProperties);
  135. if ($dims == 1 || ($dims == 2 && isset($validateProperties['rule']))) {
  136. $validateProperties = array($validateProperties);
  137. }
  138. foreach ($validateProperties as $rule => $validateProp) {
  139. if (isset($validateProp['allowEmpty']) && $validateProp['allowEmpty'] === true) {
  140. return false;
  141. }
  142. $rule = isset($validateProp['rule']) ? $validateProp['rule'] : false;
  143. $required = $rule || empty($validateProp);
  144. if ($required) {
  145. break;
  146. }
  147. }
  148. }
  149. return $required;
  150. }
  151. /**
  152. * Returns an HTML FORM element.
  153. *
  154. * ### Options:
  155. *
  156. * - `type` Form method defaults to POST
  157. * - `action` The controller action the form submits to, (optional).
  158. * - `url` The url the form submits to. Can be a string or a url array. If you use 'url'
  159. * you should leave 'action' undefined.
  160. * - `default` Allows for the creation of Ajax forms. Set this to false to prevent the default event handler.
  161. * Will create an onsubmit attribute if it doesn't not exist. If it does, default action suppression
  162. * will be appended.
  163. * - `onsubmit` Used in conjunction with 'default' to create ajax forms.
  164. * - `inputDefaults` set the default $options for FormHelper::input(). Any options that would
  165. * be set when using FormHelper::input() can be set here. Options set with `inputDefaults`
  166. * can be overridden when calling input()
  167. * - `encoding` Set the accept-charset encoding for the form. Defaults to `Configure::read('App.encoding')`
  168. *
  169. * @access public
  170. * @param string $model The model object which the form is being defined for
  171. * @param array $options An array of html attributes and options.
  172. * @return string An formatted opening FORM tag.
  173. * @link http://book.cakephp.org/view/1384/Creating-Forms
  174. */
  175. function create($model = null, $options = array()) {
  176. $created = $id = false;
  177. $append = '';
  178. $view =& ClassRegistry::getObject('view');
  179. if (is_array($model) && empty($options)) {
  180. $options = $model;
  181. $model = null;
  182. }
  183. if (empty($model) && $model !== false && !empty($this->params['models'])) {
  184. $model = $this->params['models'][0];
  185. $this->defaultModel = $this->params['models'][0];
  186. } elseif (empty($model) && empty($this->params['models'])) {
  187. $model = false;
  188. }
  189. $models = ClassRegistry::keys();
  190. foreach ($models as $currentModel) {
  191. if (ClassRegistry::isKeySet($currentModel)) {
  192. $currentObject =& ClassRegistry::getObject($currentModel);
  193. if (is_a($currentObject, 'Model') && !empty($currentObject->validationErrors)) {
  194. $this->validationErrors[Inflector::camelize($currentModel)] =& $currentObject->validationErrors;
  195. }
  196. }
  197. }
  198. $object = $this->_introspectModel($model);
  199. $this->setEntity($model . '.', true);
  200. $modelEntity = $this->model();
  201. if (isset($this->fieldset[$modelEntity]['key'])) {
  202. $data = $this->fieldset[$modelEntity];
  203. $recordExists = (
  204. isset($this->data[$model]) &&
  205. !empty($this->data[$model][$data['key']]) &&
  206. !is_array($this->data[$model][$data['key']])
  207. );
  208. if ($recordExists) {
  209. $created = true;
  210. $id = $this->data[$model][$data['key']];
  211. }
  212. }
  213. $options = array_merge(array(
  214. 'type' => ($created && empty($options['action'])) ? 'put' : 'post',
  215. 'action' => null,
  216. 'url' => null,
  217. 'default' => true,
  218. 'encoding' => strtolower(Configure::read('App.encoding')),
  219. 'inputDefaults' => array()),
  220. $options);
  221. $this->_inputDefaults = $options['inputDefaults'];
  222. unset($options['inputDefaults']);
  223. if (empty($options['url']) || is_array($options['url'])) {
  224. if (empty($options['url']['controller'])) {
  225. if (!empty($model) && $model != $this->defaultModel) {
  226. $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
  227. } elseif (!empty($this->params['controller'])) {
  228. $options['url']['controller'] = Inflector::underscore($this->params['controller']);
  229. }
  230. }
  231. if (empty($options['action'])) {
  232. $options['action'] = $this->params['action'];
  233. }
  234. $actionDefaults = array(
  235. 'plugin' => $this->plugin,
  236. 'controller' => $view->viewPath,
  237. 'action' => $options['action']
  238. );
  239. if (!empty($options['action']) && !isset($options['id'])) {
  240. $options['id'] = $this->domId($options['action'] . 'Form');
  241. }
  242. $options['action'] = array_merge($actionDefaults, (array)$options['url']);
  243. if (empty($options['action'][0])) {
  244. $options['action'][0] = $id;
  245. }
  246. } elseif (is_string($options['url'])) {
  247. $options['action'] = $options['url'];
  248. }
  249. unset($options['url']);
  250. switch (strtolower($options['type'])) {
  251. case 'get':
  252. $htmlAttributes['method'] = 'get';
  253. break;
  254. case 'file':
  255. $htmlAttributes['enctype'] = 'multipart/form-data';
  256. $options['type'] = ($created) ? 'put' : 'post';
  257. case 'post':
  258. case 'put':
  259. case 'delete':
  260. $append .= $this->hidden('_method', array(
  261. 'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null
  262. ));
  263. default:
  264. $htmlAttributes['method'] = 'post';
  265. break;
  266. }
  267. $this->requestType = strtolower($options['type']);
  268. $action = $this->url($options['action']);
  269. unset($options['type'], $options['action']);
  270. if ($options['default'] == false) {
  271. if (!isset($options['onsubmit'])) {
  272. $options['onsubmit'] = '';
  273. }
  274. $htmlAttributes['onsubmit'] = $options['onsubmit'] . 'event.returnValue = false; return false;';
  275. }
  276. unset($options['default']);
  277. if (!empty($options['encoding'])) {
  278. $htmlAttributes['accept-charset'] = $options['encoding'];
  279. unset($options['encoding']);
  280. }
  281. $htmlAttributes = array_merge($options, $htmlAttributes);
  282. $this->fields = array();
  283. if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
  284. $append .= $this->hidden('_Token.key', array(
  285. 'value' => $this->params['_Token']['key'], 'id' => 'Token' . mt_rand())
  286. );
  287. }
  288. if (!empty($append)) {
  289. $append = sprintf($this->Html->tags['block'], ' style="display:none;"', $append);
  290. }
  291. $this->setEntity($model . '.', true);
  292. $attributes = sprintf('action="%s" ', $action) . $this->_parseAttributes($htmlAttributes, null, '');
  293. return sprintf($this->Html->tags['form'], $attributes) . $append;
  294. }
  295. /**
  296. * Closes an HTML form, cleans up values set by FormHelper::create(), and writes hidden
  297. * input fields where appropriate.
  298. *
  299. * If $options is set a form submit button will be created. Options can be either a string or an array.
  300. *
  301. * {{{
  302. * array usage:
  303. *
  304. * array('label' => 'save'); value="save"
  305. * array('label' => 'save', 'name' => 'Whatever'); value="save" name="Whatever"
  306. * array('name' => 'Whatever'); value="Submit" name="Whatever"
  307. * array('label' => 'save', 'name' => 'Whatever', 'div' => 'good') <div class="good"> value="save" name="Whatever"
  308. * array('label' => 'save', 'name' => 'Whatever', 'div' => array('class' => 'good')); <div class="good"> value="save" name="Whatever"
  309. * }}}
  310. *
  311. * @param mixed $options as a string will use $options as the value of button,
  312. * @return string a closing FORM tag optional submit button.
  313. * @access public
  314. * @link http://book.cakephp.org/view/1389/Closing-the-Form
  315. */
  316. function end($options = null) {
  317. if (!empty($this->params['models'])) {
  318. $models = $this->params['models'][0];
  319. }
  320. $out = null;
  321. $submit = null;
  322. if ($options !== null) {
  323. $submitOptions = array();
  324. if (is_string($options)) {
  325. $submit = $options;
  326. } else {
  327. if (isset($options['label'])) {
  328. $submit = $options['label'];
  329. unset($options['label']);
  330. }
  331. $submitOptions = $options;
  332. }
  333. $out .= $this->submit($submit, $submitOptions);
  334. }
  335. if (isset($this->params['_Token']) && !empty($this->params['_Token'])) {
  336. $out .= $this->secure($this->fields);
  337. $this->fields = array();
  338. }
  339. $this->setEntity(null);
  340. $out .= $this->Html->tags['formend'];
  341. $view =& ClassRegistry::getObject('view');
  342. $view->modelScope = false;
  343. return $out;
  344. }
  345. /**
  346. * Generates a hidden field with a security hash based on the fields used in the form.
  347. *
  348. * @param array $fields The list of fields to use when generating the hash
  349. * @return string A hidden input field with a security hash
  350. * @access public
  351. */
  352. function secure($fields = array()) {
  353. if (!isset($this->params['_Token']) || empty($this->params['_Token'])) {
  354. return;
  355. }
  356. $locked = array();
  357. foreach ($fields as $key => $value) {
  358. if (!is_int($key)) {
  359. $locked[$key] = $value;
  360. unset($fields[$key]);
  361. }
  362. }
  363. sort($fields, SORT_STRING);
  364. ksort($locked, SORT_STRING);
  365. $fields += $locked;
  366. $fields = Security::hash(serialize($fields) . Configure::read('Security.salt'));
  367. $locked = implode(array_keys($locked), '|');
  368. $out = $this->hidden('_Token.fields', array(
  369. 'value' => urlencode($fields . ':' . $locked),
  370. 'id' => 'TokenFields' . mt_rand()
  371. ));
  372. $out = sprintf($this->Html->tags['block'], ' style="display:none;"', $out);
  373. return $out;
  374. }
  375. /**
  376. * Determine which fields of a form should be used for hash.
  377. * Populates $this->fields
  378. *
  379. * @param mixed $field Reference to field to be secured
  380. * @param mixed $value Field value, if value should not be tampered with.
  381. * @return void
  382. * @access private
  383. */
  384. function __secure($field = null, $value = null) {
  385. if (!$field) {
  386. $view =& ClassRegistry::getObject('view');
  387. $field = $view->entity();
  388. } elseif (is_string($field)) {
  389. $field = Set::filter(explode('.', $field), true);
  390. }
  391. if (!empty($this->params['_Token']['disabledFields'])) {
  392. foreach ((array)$this->params['_Token']['disabledFields'] as $disabled) {
  393. $disabled = explode('.', $disabled);
  394. if (array_values(array_intersect($field, $disabled)) === $disabled) {
  395. return;
  396. }
  397. }
  398. }
  399. $last = end($field);
  400. if (is_numeric($last) || empty($last)) {
  401. array_pop($field);
  402. }
  403. $field = implode('.', $field);
  404. if (!in_array($field, $this->fields)) {
  405. if ($value !== null) {
  406. return $this->fields[$field] = $value;
  407. }
  408. $this->fields[] = $field;
  409. }
  410. }
  411. /**
  412. * Returns true if there is an error for the given field, otherwise false
  413. *
  414. * @param string $field This should be "Modelname.fieldname"
  415. * @return boolean If there are errors this method returns true, else false.
  416. * @access public
  417. * @link http://book.cakephp.org/view/1426/isFieldError
  418. */
  419. function isFieldError($field) {
  420. $this->setEntity($field);
  421. return (bool)$this->tagIsInvalid();
  422. }
  423. /**
  424. * Returns a formatted error message for given FORM field, NULL if no errors.
  425. *
  426. * ### Options:
  427. *
  428. * - `escape` bool Whether or not to html escape the contents of the error.
  429. * - `wrap` mixed Whether or not the error message should be wrapped in a div. If a
  430. * string, will be used as the HTML tag to use.
  431. * - `class` string The classname for the error message
  432. *
  433. * @param string $field A field name, like "Modelname.fieldname"
  434. * @param mixed $text Error message or array of $options. If array, `attributes` key
  435. * will get used as html attributes for error container
  436. * @param array $options Rendering options for <div /> wrapper tag
  437. * @return string If there are errors this method returns an error message, otherwise null.
  438. * @access public
  439. * @link http://book.cakephp.org/view/1423/error
  440. */
  441. function error($field, $text = null, $options = array()) {
  442. $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
  443. $options = array_merge($defaults, $options);
  444. $this->setEntity($field);
  445. $error = $this->tagIsInvalid();
  446. if ($error !== null) {
  447. if (is_array($error)) {
  448. list(,,$field) = explode('.', $field);
  449. if (isset($error[$field])) {
  450. $error = $error[$field];
  451. } else {
  452. return null;
  453. }
  454. }
  455. if (is_array($text) && is_numeric($error) && $error > 0) {
  456. $error--;
  457. }
  458. if (is_array($text)) {
  459. $options = array_merge($options, array_intersect_key($text, $defaults));
  460. if (isset($text['attributes']) && is_array($text['attributes'])) {
  461. $options = array_merge($options, $text['attributes']);
  462. }
  463. $text = isset($text[$error]) ? $text[$error] : null;
  464. unset($options[$error]);
  465. }
  466. if ($text !== null) {
  467. $error = $text;
  468. } elseif (is_numeric($error)) {
  469. $error = sprintf(__('Error in field %s', true), Inflector::humanize($this->field()));
  470. }
  471. if ($options['escape']) {
  472. $error = h($error);
  473. unset($options['escape']);
  474. }
  475. if ($options['wrap']) {
  476. $tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
  477. unset($options['wrap']);
  478. return $this->Html->tag($tag, $error, $options);
  479. } else {
  480. return $error;
  481. }
  482. } else {
  483. return null;
  484. }
  485. }
  486. /**
  487. * Returns a formatted LABEL element for HTML FORMs. Will automatically generate
  488. * a for attribute if one is not provided.
  489. *
  490. * @param string $fieldName This should be "Modelname.fieldname"
  491. * @param string $text Text that will appear in the label field.
  492. * @param mixed $options An array of HTML attributes, or a string, to be used as a class name.
  493. * @return string The formatted LABEL element
  494. * @link http://book.cakephp.org/view/1427/label
  495. */
  496. function label($fieldName = null, $text = null, $options = array()) {
  497. if (empty($fieldName)) {
  498. $view = ClassRegistry::getObject('view');
  499. $fieldName = implode('.', $view->entity());
  500. }
  501. if ($text === null) {
  502. if (strpos($fieldName, '.') !== false) {
  503. $text = array_pop(explode('.', $fieldName));
  504. } else {
  505. $text = $fieldName;
  506. }
  507. if (substr($text, -3) == '_id') {
  508. $text = substr($text, 0, strlen($text) - 3);
  509. }
  510. $text = __(Inflector::humanize(Inflector::underscore($text)), true);
  511. }
  512. if (is_string($options)) {
  513. $options = array('class' => $options);
  514. }
  515. if (isset($options['for'])) {
  516. $labelFor = $options['for'];
  517. unset($options['for']);
  518. } else {
  519. $labelFor = $this->domId($fieldName);
  520. }
  521. return sprintf(
  522. $this->Html->tags['label'],
  523. $labelFor,
  524. $this->_parseAttributes($options), $text
  525. );
  526. }
  527. /**
  528. * Generate a set of inputs for `$fields`. If $fields is null the current model
  529. * will be used.
  530. *
  531. * In addition to controller fields output, `$fields` can be used to control legend
  532. * and fieldset rendering with the `fieldset` and `legend` keys.
  533. * `$form->inputs(array('legend' => 'My legend'));` Would generate an input set with
  534. * a custom legend. You can customize individual inputs through `$fields` as well.
  535. *
  536. * {{{
  537. * $form->inputs(array(
  538. * 'name' => array('label' => 'custom label')
  539. * ));
  540. * }}}
  541. *
  542. * In addition to fields control, inputs() allows you to use a few additional options.
  543. *
  544. * - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
  545. * the classname for the fieldset element.
  546. * - `legend` Set to false to disable the legend for the generated input set. Or supply a string
  547. * to customize the legend text.
  548. *
  549. * @param mixed $fields An array of fields to generate inputs for, or null.
  550. * @param array $blacklist a simple array of fields to not create inputs for.
  551. * @return string Completed form inputs.
  552. * @access public
  553. */
  554. function inputs($fields = null, $blacklist = null) {
  555. $fieldset = $legend = true;
  556. $model = $this->model();
  557. if (is_array($fields)) {
  558. if (array_key_exists('legend', $fields)) {
  559. $legend = $fields['legend'];
  560. unset($fields['legend']);
  561. }
  562. if (isset($fields['fieldset'])) {
  563. $fieldset = $fields['fieldset'];
  564. unset($fields['fieldset']);
  565. }
  566. } elseif ($fields !== null) {
  567. $fieldset = $legend = $fields;
  568. if (!is_bool($fieldset)) {
  569. $fieldset = true;
  570. }
  571. $fields = array();
  572. }
  573. if (empty($fields)) {
  574. $fields = array_keys($this->fieldset[$model]['fields']);
  575. }
  576. if ($legend === true) {
  577. $actionName = __('New %s', true);
  578. $isEdit = (
  579. strpos($this->action, 'update') !== false ||
  580. strpos($this->action, 'edit') !== false
  581. );
  582. if ($isEdit) {
  583. $actionName = __('Edit %s', true);
  584. }
  585. $modelName = Inflector::humanize(Inflector::underscore($model));
  586. $legend = sprintf($actionName, __($modelName, true));
  587. }
  588. $out = null;
  589. foreach ($fields as $name => $options) {
  590. if (is_numeric($name) && !is_array($options)) {
  591. $name = $options;
  592. $options = array();
  593. }
  594. $entity = explode('.', $name);
  595. $blacklisted = (
  596. is_array($blacklist) &&
  597. (in_array($name, $blacklist) || in_array(end($entity), $blacklist))
  598. );
  599. if ($blacklisted) {
  600. continue;
  601. }
  602. $out .= $this->input($name, $options);
  603. }
  604. if (is_string($fieldset)) {
  605. $fieldsetClass = sprintf(' class="%s"', $fieldset);
  606. } else {
  607. $fieldsetClass = '';
  608. }
  609. if ($fieldset && $legend) {
  610. return sprintf(
  611. $this->Html->tags['fieldset'],
  612. $fieldsetClass,
  613. sprintf($this->Html->tags['legend'], $legend) . $out
  614. );
  615. } elseif ($fieldset) {
  616. return sprintf($this->Html->tags['fieldset'], $fieldsetClass, $out);
  617. } else {
  618. return $out;
  619. }
  620. }
  621. /**
  622. * Generates a form input element complete with label and wrapper div
  623. *
  624. * ### Options
  625. *
  626. * See each field type method for more information. Any options that are part of
  627. * $attributes or $options for the different **type** methods can be included in `$options` for input().i
  628. * Additionally, any unknown keys that are not in the list below, or part of the selected type's options
  629. * will be treated as a regular html attribute for the generated input.
  630. *
  631. * - `type` - Force the type of widget you want. e.g. `type => 'select'`
  632. * - `label` - Either a string label, or an array of options for the label. See FormHelper::label()
  633. * - `div` - Either `false` to disable the div, or an array of options for the div.
  634. * See HtmlHelper::div() for more options.
  635. * - `options` - for widgets that take options e.g. radio, select
  636. * - `error` - control the error message that is produced
  637. * - `empty` - String or boolean to enable empty select box options.
  638. * - `before` - Content to place before the label + input.
  639. * - `after` - Content to place after the label + input.
  640. * - `between` - Content to place between the label + input.
  641. * - `format` - format template for element order. Any element that is not in the array, will not be in the output.
  642. * - Default input format order: array('before', 'label', 'between', 'input', 'after', 'error')
  643. * - Default checkbox format order: array('before', 'input', 'between', 'label', 'after', 'error')
  644. * - Hidden input will not be formatted
  645. * - Radio buttons cannot have the order of input and label elements controlled with these settings.
  646. *
  647. * @param string $fieldName This should be "Modelname.fieldname"
  648. * @param array $options Each type of input takes different options.
  649. * @return string Completed form widget.
  650. * @access public
  651. * @link http://book.cakephp.org/view/1390/Automagic-Form-Elements
  652. */
  653. function input($fieldName, $options = array()) {
  654. $this->setEntity($fieldName);
  655. $options = array_merge(
  656. array('before' => null, 'between' => null, 'after' => null, 'format' => null),
  657. $this->_inputDefaults,
  658. $options
  659. );
  660. $modelKey = $this->model();
  661. $fieldKey = $this->field();
  662. if (!isset($this->fieldset[$modelKey])) {
  663. $this->_introspectModel($modelKey);
  664. }
  665. if (!isset($options['type'])) {
  666. $magicType = true;
  667. $options['type'] = 'text';
  668. if (isset($options['options'])) {
  669. $options['type'] = 'select';
  670. } elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) {
  671. $options['type'] = 'password';
  672. } elseif (isset($this->fieldset[$modelKey]['fields'][$fieldKey])) {
  673. $fieldDef = $this->fieldset[$modelKey]['fields'][$fieldKey];
  674. $type = $fieldDef['type'];
  675. $primaryKey = $this->fieldset[$modelKey]['key'];
  676. }
  677. if (isset($type)) {
  678. $map = array(
  679. 'string' => 'text', 'datetime' => 'datetime',
  680. 'boolean' => 'checkbox', 'timestamp' => 'datetime',
  681. 'text' => 'textarea', 'time' => 'time',
  682. 'date' => 'date', 'float' => 'text'
  683. );
  684. if (isset($this->map[$type])) {
  685. $options['type'] = $this->map[$type];
  686. } elseif (isset($map[$type])) {
  687. $options['type'] = $map[$type];
  688. }
  689. if ($fieldKey == $primaryKey) {
  690. $options['type'] = 'hidden';
  691. }
  692. }
  693. if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') {
  694. $options['type'] = 'select';
  695. }
  696. if ($modelKey === $fieldKey) {
  697. $options['type'] = 'select';
  698. if (!isset($options['multiple'])) {
  699. $options['multiple'] = 'multiple';
  700. }
  701. }
  702. }
  703. $types = array('checkbox', 'radio', 'select');
  704. if (
  705. (!isset($options['options']) && in_array($options['type'], $types)) ||
  706. (isset($magicType) && $options['type'] == 'text')
  707. ) {
  708. $view =& ClassRegistry::getObject('view');
  709. $varName = Inflector::variable(
  710. Inflector::pluralize(preg_replace('/_id$/', '', $fieldKey))
  711. );
  712. $varOptions = $view->getVar($varName);
  713. if (is_array($varOptions)) {
  714. if ($options['type'] !== 'radio') {
  715. $options['type'] = 'select';
  716. }
  717. $options['options'] = $varOptions;
  718. }
  719. }
  720. $autoLength = (!array_key_exists('maxlength', $options) && isset($fieldDef['length']));
  721. if ($autoLength && $options['type'] == 'text') {
  722. $options['maxlength'] = $fieldDef['length'];
  723. }
  724. if ($autoLength && $fieldDef['type'] == 'float') {
  725. $options['maxlength'] = array_sum(explode(',', $fieldDef['length']))+1;
  726. }
  727. $divOptions = array();
  728. $div = $this->_extractOption('div', $options, true);
  729. unset($options['div']);
  730. if (!empty($div)) {
  731. $divOptions['class'] = 'input';
  732. $divOptions = $this->addClass($divOptions, $options['type']);
  733. if (is_string($div)) {
  734. $divOptions['class'] = $div;
  735. } elseif (is_array($div)) {
  736. $divOptions = array_merge($divOptions, $div);
  737. }
  738. if (
  739. isset($this->fieldset[$modelKey]) &&
  740. in_array($fieldKey, $this->fieldset[$modelKey]['validates'])
  741. ) {
  742. $divOptions = $this->addClass($divOptions, 'required');
  743. }
  744. if (!isset($divOptions['tag'])) {
  745. $divOptions['tag'] = 'div';
  746. }
  747. }
  748. $label = null;
  749. if (isset($options['label']) && $options['type'] !== 'radio') {
  750. $label = $options['label'];
  751. unset($options['label']);
  752. }
  753. if ($options['type'] === 'radio') {
  754. $label = false;
  755. if (isset($options['options'])) {
  756. $radioOptions = (array)$options['options'];
  757. unset($options['options']);
  758. }
  759. }
  760. if ($label !== false) {
  761. $label = $this->_inputLabel($fieldName, $label, $options);
  762. }
  763. $error = $this->_extractOption('error', $options, null);
  764. unset($options['error']);
  765. $selected = $this->_extractOption('selected', $options, null);
  766. unset($options['selected']);
  767. if (isset($options['rows']) || isset($options['cols'])) {
  768. $options['type'] = 'textarea';
  769. }
  770. if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') {
  771. $options += array('empty' => false);
  772. }
  773. if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') {
  774. $dateFormat = $this->_extractOption('dateFormat', $options, 'MDY');
  775. $timeFormat = $this->_extractOption('timeFormat', $options, 12);
  776. unset($options['dateFormat'], $options['timeFormat']);
  777. }
  778. $type = $options['type'];
  779. $out = array_merge(
  780. array('before' => null, 'label' => null, 'between' => null, 'input' => null, 'after' => null, 'error' => null),
  781. array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after'])
  782. );
  783. $format = null;
  784. if (is_array($options['format']) && in_array('input', $options['format'])) {
  785. $format = $options['format'];
  786. }
  787. unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']);
  788. switch ($type) {
  789. case 'hidden':
  790. $input = $this->hidden($fieldName, $options);
  791. $format = array('input');
  792. unset($divOptions);
  793. break;
  794. case 'checkbox':
  795. $input = $this->checkbox($fieldName, $options);
  796. $format = $format ? $format : array('before', 'input', 'between', 'label', 'after', 'error');
  797. break;
  798. case 'radio':
  799. $input = $this->radio($fieldName, $radioOptions, $options);
  800. break;
  801. case 'text':
  802. case 'password':
  803. case 'file':
  804. $input = $this->{$type}($fieldName, $options);
  805. break;
  806. case 'select':
  807. $options += array('options' => array());
  808. $list = $options['options'];
  809. unset($options['options']);
  810. $input = $this->select($fieldName, $list, $selected, $options);
  811. break;
  812. case 'time':
  813. $input = $this->dateTime($fieldName, null, $timeFormat, $selected, $options);
  814. break;
  815. case 'date':
  816. $input = $this->dateTime($fieldName, $dateFormat, null, $selected, $options);
  817. break;
  818. case 'datetime':
  819. $input = $this->dateTime($fieldName, $dateFormat, $timeFormat, $selected, $options);
  820. break;
  821. case 'textarea':
  822. default:
  823. $input = $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
  824. break;
  825. }
  826. if ($type != 'hidden' && $error !== false) {
  827. $errMsg = $this->error($fieldName, $error);
  828. if ($errMsg) {
  829. $divOptions = $this->addClass($divOptions, 'error');
  830. $out['error'] = $errMsg;
  831. }
  832. }
  833. $out['input'] = $input;
  834. $format = $format ? $format : array('before', 'label', 'between', 'input', 'after', 'error');
  835. $output = '';
  836. foreach ($format as $element) {
  837. $output .= $out[$element];
  838. unset($out[$element]);
  839. }
  840. if (!empty($divOptions['tag'])) {
  841. $tag = $divOptions['tag'];
  842. unset($divOptions['tag']);
  843. $output = $this->Html->tag($tag, $output, $divOptions);
  844. }
  845. return $output;
  846. }
  847. /**
  848. * Extracts a single option from an options array.
  849. *
  850. * @param string $name The name of the option to pull out.
  851. * @param array $options The array of options you want to extract.
  852. * @param mixed $default The default option value
  853. * @return the contents of the option or default
  854. * @access protected
  855. */
  856. function _extractOption($name, $options, $default = null) {
  857. if (array_key_exists($name, $options)) {
  858. return $options[$name];
  859. }
  860. return $default;
  861. }
  862. /**
  863. * Generate a label for an input() call.
  864. *
  865. * @param array $options Options for the label element.
  866. * @return string Generated label element
  867. * @access protected
  868. */
  869. function _inputLabel($fieldName, $label, $options) {
  870. $labelAttributes = $this->domId(array(), 'for');
  871. if ($options['type'] === 'date' || $options['type'] === 'datetime') {
  872. if (isset($options['dateFormat']) && $options['dateFormat'] === 'NONE') {
  873. $labelAttributes['for'] .= 'Hour';
  874. $idKey = 'hour';
  875. } else {
  876. $labelAttributes['for'] .= 'Month';
  877. $idKey = 'month';
  878. }
  879. if (isset($options['id']) && isset($options['id'][$idKey])) {
  880. $labelAttributes['for'] = $options['id'][$idKey];
  881. }
  882. } elseif ($options['type'] === 'time') {
  883. $labelAttributes['for'] .= 'Hour';
  884. if (isset($options['id']) && isset($options['id']['hour'])) {
  885. $labelAttributes['for'] = $options['id']['hour'];
  886. }
  887. }
  888. if (is_array($label)) {
  889. $labelText = null;
  890. if (isset($label['text'])) {
  891. $labelText = $label['text'];
  892. unset($label['text']);
  893. }
  894. $labelAttributes = array_merge($labelAttributes, $label);
  895. } else {
  896. $labelText = $label;
  897. }
  898. if (isset($options['id']) && is_string($options['id'])) {
  899. $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
  900. }
  901. return $this->label($fieldName, $labelText, $labelAttributes);
  902. }
  903. /**
  904. * Creates a checkbox input widget.
  905. *
  906. * ### Options:
  907. *
  908. * - `value` - the value of the checkbox
  909. * - `checked` - boolean indicate that this checkbox is checked.
  910. * - `hiddenField` - boolean to indicate if you want the results of checkbox() to include
  911. * a hidden input with a value of ''.
  912. * - `disabled` - create a disabled input.
  913. *
  914. * @param string $fieldName Name of a field, like this "Modelname.fieldname"
  915. * @param array $options Array of HTML attributes.
  916. * @return string An HTML text input element.
  917. * @access public
  918. * @link http://book.cakephp.org/view/1414/checkbox
  919. */
  920. function checkbox($fieldName, $options = array()) {
  921. $options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true);
  922. $value = current($this->value());
  923. $output = "";
  924. if (empty($options['value'])) {
  925. $options['value'] = 1;
  926. } elseif (
  927. (!isset($options['checked']) && !empty($value) && $value === $options['value']) ||
  928. !empty($options['checked'])
  929. ) {
  930. $options['checked'] = 'checked';
  931. }
  932. if ($options['hiddenField']) {
  933. $hiddenOptions = array(
  934. 'id' => $options['id'] . '_', 'name' => $options['name'],
  935. 'value' => '0', 'secure' => false
  936. );
  937. if (isset($options['disabled']) && $options['disabled'] == true) {
  938. $hiddenOptions['disabled'] = 'disabled';
  939. }
  940. $output = $this->hidden($fieldName, $hiddenOptions);
  941. }
  942. unset($options['hiddenField']);
  943. return $output . sprintf(
  944. $this->Html->tags['checkbox'],
  945. $options['name'],
  946. $this->_parseAttributes($options, array('name'), null, ' ')
  947. );
  948. }
  949. /**
  950. * Creates a set of radio widgets. Will create a legend and fieldset
  951. * by default. Use $options to control this
  952. *
  953. * ### Attributes:
  954. *
  955. * - `separator` - define the string in between the radio buttons
  956. * - `legend` - control whether or not the widget set has a fieldset & legend
  957. * - `value` - indicate a value that is should be checked
  958. * - `label` - boolean to indicate whether or not labels for widgets show be displayed
  959. * - `hiddenField` - boolean to indicate if you want the results of radio() to include
  960. * a hidden input with a value of ''. This is useful for creating radio sets that non-continuous
  961. *
  962. * @param string $fieldName Name of a field, like this "Modelname.fieldname"
  963. * @param array $options Radio button options array.
  964. * @param array $attributes Array of HTML attributes, and special attributes above.
  965. * @return string Completed radio widget set.
  966. * @access public
  967. * @link http://book.cakephp.org/view/1429/radio
  968. */
  969. function radio($fieldName, $options = array(), $attributes = array()) {
  970. $attributes = $this->_initInputField($fieldName, $attributes);
  971. $legend = false;
  972. if (isset($attributes['legend'])) {
  973. $legend = $attributes['legend'];
  974. unset($attributes['legend']);
  975. } elseif (count($options) > 1) {
  976. $legend = __(Inflector::humanize($this->field()), true);
  977. }
  978. $label = true;
  979. if (isset($attributes['label'])) {
  980. $label = $attributes['label'];
  981. unset($attributes['label']);
  982. }
  983. $inbetween = null;
  984. if (isset($attributes['separator'])) {
  985. $inbetween = $attributes['separator'];
  986. unset($attributes['separator']);
  987. }
  988. if (isset($attributes['value'])) {
  989. $value = $attributes['value'];
  990. } else {
  991. $value = $this->value($fieldName);
  992. }
  993. $out = array();
  994. $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true;
  995. unset($attributes['hiddenField']);
  996. foreach ($options as $optValue => $optTitle) {
  997. $optionsHere = array('value' => $optValue);
  998. if (isset($value) && $value !== '' && $optValue == $value) {
  999. $optionsHere['checked'] = 'checked';
  1000. }
  1001. $parsedOptions = $this->_parseAttributes(
  1002. array_merge($attributes, $optionsHere),
  1003. array('name', 'type', 'id'), '', ' '
  1004. );
  1005. $tagName = Inflector::camelize(
  1006. $attributes['id'] . '_' . Inflector::slug($optValue)
  1007. );
  1008. if ($label) {
  1009. $optTitle = sprintf($this->Html->tags['label'], $tagName, null, $optTitle);
  1010. }
  1011. $out[] = sprintf(
  1012. $this->Html->tags['radio'], $attributes['name'],
  1013. $tagName, $parsedOptions, $optTitle
  1014. );
  1015. }
  1016. $hidden = null;
  1017. if ($hiddenField) {
  1018. if (!isset($value) || $value === '') {
  1019. $hidden = $this->hidden($fieldName, array(
  1020. 'id' => $attributes['id'] . '_', 'value' => '', 'name' => $attributes['name']
  1021. ));
  1022. }
  1023. }
  1024. $out = $hidden . implode($inbetween, $out);
  1025. if ($legend) {
  1026. $out = sprintf(
  1027. $this->Html->tags['fieldset'], '',
  1028. sprintf($this->Html->tags['legend'], $legend) . $out
  1029. );
  1030. }
  1031. return $out;
  1032. }
  1033. /**
  1034. * Creates a text input widget.
  1035. *
  1036. * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
  1037. * @param array $options Array of HTML attributes.
  1038. * @return string A generated HTML text input element
  1039. * @access public
  1040. * @link http://book.cakephp.org/view/1432/text
  1041. */
  1042. function text($fieldName, $options = array()) {
  1043. $options = $this->_initInputField($fieldName, array_merge(
  1044. array('type' => 'text'), $options
  1045. ));
  1046. return sprintf(
  1047. $this->Html->tags['input'],
  1048. $options['name'],
  1049. $this->_parseAttributes($options, array('name'), null, ' ')
  1050. );
  1051. }
  1052. /**
  1053. * Creates a password input widget.
  1054. *
  1055. * @param string $fieldName Name of a field, like in the form "Modelname.fieldname"
  1056. * @param array $options Array of HTML attributes.
  1057. * @return string A generated password input.
  1058. * @access public
  1059. * @link http://book.cakephp.org/view/1428/password
  1060. */
  1061. function password($fieldName, $options = array()) {
  1062. $options = $this->_initInputField($fieldName, $options);
  1063. return sprintf(
  1064. $this->Html->tags['password'],
  1065. $options['name'],
  1066. $this->_parseAttributes($options, array('name'), null, ' ')
  1067. );
  1068. }
  1069. /**
  1070. * Creates a textarea widget.
  1071. *
  1072. * ### Options:
  1073. *
  1074. * - `escape` - Whether or not the contents of the textarea should be escaped. Defaults to true.
  1075. *
  1076. * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
  1077. * @param array $options Array of HTML attributes, and special options above.
  1078. * @return string A generated HTML text input element
  1079. * @access public
  1080. * @link http://book.cakephp.org/view/1433/textarea
  1081. */
  1082. function textarea($fieldName, $options = array()) {
  1083. $options = $this->_initInputField($fieldName, $options);
  1084. $value = null;
  1085. if (array_key_exists('value', $options)) {
  1086. $value = $options['value'];
  1087. if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
  1088. $value = h($value);
  1089. }
  1090. unset($options['value']);
  1091. }
  1092. return sprintf(
  1093. $this->Html->tags['textarea'],
  1094. $options['name'],
  1095. $this->_parseAttributes($options, array('type', 'name'), null, ' '),
  1096. $value
  1097. );
  1098. }
  1099. /**
  1100. * Creates a hidden input field.
  1101. *
  1102. * @param string $fieldName Name of a field, in the form of "Modelname.fieldname"
  1103. * @param array $options Array of HTML attributes.
  1104. * @return string A generated hidden input
  1105. * @access public
  1106. * @link http://book.cakephp.org/view/1425/hidden
  1107. */
  1108. function hidden($fieldName, $options = array()) {
  1109. $secure = true;
  1110. if (isset($options['secure'])) {
  1111. $secure = $options['secure'];
  1112. unset($options['secure']);
  1113. }
  1114. $options = $this->_initInputField($fieldName, array_merge(
  1115. $options, array('secure' => false)
  1116. ));
  1117. $model = $this->model();
  1118. if ($fieldName !== '_method' && $model !== '_Token' && $secure) {
  1119. $this->__secure(null, '' . $options['value']);
  1120. }
  1121. return sprintf(
  1122. $this->Html->tags['hidden'],
  1123. $options['name'],
  1124. $this->_parseAttributes($options, array('name', 'class'), '', ' ')
  1125. );
  1126. }
  1127. /**
  1128. * Creates file input widget.
  1129. *
  1130. * @param string $fieldName Name of a field, in the form "Modelname.fieldname"
  1131. * @param array $options Array of HTML attributes.
  1132. * @return string A generated file input.
  1133. * @access public
  1134. * @link http://book.cakephp.org/view/1424/file
  1135. */
  1136. function file($fieldName, $options = array()) {
  1137. $options = array_merge($options, array('secure' => false));
  1138. $options = $this->_initInputField($fieldName, $options);
  1139. $view =& ClassRegistry::getObject('view');
  1140. $field = $view->entity();
  1141. foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) {
  1142. $this->__secure(array_merge($field, array($suffix)));
  1143. }
  1144. $attributes = $this->_parseAttributes($options, array('name'), '', ' ');
  1145. return sprintf($this->Html->tags['file'], $options['name'], $attributes);
  1146. }
  1147. /**
  1148. * Creates a `<button>` tag. The type attribute defaults to `type="submit"`
  1149. * You can change it to a different value by using `$options['type']`.
  1150. *
  1151. * ### Options:
  1152. *
  1153. * - `escape` - HTML entity encode the $title of the button. Defaults to false.
  1154. *
  1155. * @param string $title The button's caption. Not automatically HTML encoded
  1156. * @param array $options Array of options and HTML attributes.
  1157. * @return string A HTML button tag.
  1158. * @access public
  1159. * @link http://book.cakephp.org/view/1415/button
  1160. */
  1161. function button($title, $options = array()) {
  1162. $options += array('type' => 'submit', 'escape' => false);
  1163. if ($options['escape']) {
  1164. $title = h($title);
  1165. }
  1166. return sprintf(
  1167. $this->Html->tags['button'],
  1168. $options['type'],
  1169. $this->_parseAttributes($options, array('type'), ' ', ''),
  1170. $title
  1171. );
  1172. }
  1173. /**
  1174. * Creates a submit button element. This method will generate `<input />` elements that
  1175. * can be used to submit, and reset forms by using $options. image submits can be created by supplying an
  1176. * image path for $caption.
  1177. *
  1178. * ### Options
  1179. *
  1180. * - `div` - Include a wrapping div? Defaults to true. Accepts sub options similar to
  1181. * FormHelper::input().
  1182. * - `before` - Content to include before the input.
  1183. * - `after` - Content to include after the input.
  1184. * - `type` - Set to 'reset' for reset inputs. Defaults to 'submit'
  1185. * - Other attributes will be assigned to the input element.
  1186. *
  1187. * ### Options
  1188. *
  1189. * - `div` - Include a wrapping div? Defaults to true. Accepts sub options similar to
  1190. * FormHelper::input().
  1191. * - Other attributes will be assigned to the input element.
  1192. *
  1193. * @param string $caption The label appearing on the button OR if string contains :// or the
  1194. * extension .jpg, .jpe, .jpeg, .gif, .png use an image if the extension
  1195. * exists, AND the first character is /, image is relative to webroot,
  1196. * OR if the first character is not /, image is relative to webroot/img.
  1197. * @param array $options Array of options. See above.
  1198. * @return string A HTML submit button
  1199. * @access public
  1200. * @link http://book.cakephp.org/view/1431/submit
  1201. */
  1202. function submit($caption = null, $options = array()) {
  1203. if (!is_string($caption) && empty($caption)) {
  1204. $caption = __('Submit', true);
  1205. }
  1206. $out = null;
  1207. $div = true;
  1208. if (isset($options['div'])) {
  1209. $div = $options['div'];
  1210. unset($options['div']);
  1211. }
  1212. $options += array('type' => 'submit', 'before' => null, 'after' => null);
  1213. $divOptions = array('tag' => 'div');
  1214. if ($div === true) {
  1215. $divOptions['class'] = 'submit';
  1216. } elseif ($div === false) {
  1217. unset($divOptions);
  1218. } elseif (is_string($div)) {
  1219. $divOptions['class'] = $div;
  1220. } elseif (is_array($div)) {
  1221. $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
  1222. }
  1223. $before = $options['before'];
  1224. $after = $options['after'];
  1225. unset($options['before'], $options['after']);
  1226. if (strpos($caption, '://') !== false) {
  1227. unset($options['type']);
  1228. $out .= $before . sprintf(
  1229. $this->Html->tags['submitimage'],
  1230. $caption,
  1231. $this->_parseAttributes($options, null, '', ' ')
  1232. ) . $after;
  1233. } elseif (preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption)) {
  1234. unset($options['type']);
  1235. if ($caption{0} !== '/') {
  1236. $url = $this->webroot(IMAGES_URL . $caption);
  1237. } else {
  1238. $caption = trim($caption, '/');
  1239. $url = $this->webroot($caption);
  1240. }
  1241. $out .= $before . sprintf(
  1242. $this->Html->tags['submitimage'],
  1243. $this->assetTimestamp($url),
  1244. $this->_parseAttributes($options, null, '', ' ')
  1245. ) . $after;
  1246. } else {
  1247. $options['value'] = $caption;
  1248. $out .= $before . sprintf(
  1249. $this->Html->tags['submit'],
  1250. $this->_parseAttributes($options, null, '', ' ')
  1251. ). $after;
  1252. }
  1253. if (isset($divOptions)) {
  1254. $tag = $divOptions['tag'];
  1255. unset($divOptions['tag']);
  1256. $out = $this->Html->tag($tag, $out, $divOptions);
  1257. }
  1258. return $out;
  1259. }
  1260. /**
  1261. * Returns a formatted SELECT element.
  1262. *
  1263. * ### Attributes:
  1264. *
  1265. * - `showParents` - If included in the array and set to true, an additional option element
  1266. * will be added for the parent of each option group. You can set an option with the same name
  1267. * and it's key will be used for the value of the option.
  1268. * - `multiple` - show a multiple select box. If set to 'checkbox' multiple checkboxes will be
  1269. * created instead.
  1270. * - `empty` - If true, the empty select option is shown. If a string,
  1271. * that string is displayed as the empty element.
  1272. * - `escape` - If true contents of options will be HTML entity encoded. Defaults to true.
  1273. * - `class` - When using multiple = checkbox the classname to apply to the divs. Defaults to 'checkbox'.
  1274. *
  1275. * ### Using options
  1276. *
  1277. * A simple array will create normal options:
  1278. *
  1279. * {{{
  1280. * $options = array(1 => 'one', 2 => 'two);
  1281. * $this->Form->select('Model.field', $options));
  1282. * }}}
  1283. *
  1284. * While a nested options array will create optgroups with options inside them.
  1285. * {{{
  1286. * $options = array(
  1287. * 1 => 'bill',
  1288. * 'fred' => array(
  1289. * 2 => 'fred',
  1290. * 3 => 'fred jr.'
  1291. * )
  1292. * );
  1293. * $this->Form->select('Model.field', $options);
  1294. * }}}
  1295. *
  1296. * In the above `2 => 'fred'` will not generate an option element. You should enable the `showParents`
  1297. * attribute to show the fred option.
  1298. *
  1299. * @param string $fieldName Name attribute of the SELECT
  1300. * @param array $options Array of the OPTION elements (as 'value'=>'Text' pairs) to be used in the
  1301. * SELECT element
  1302. * @param mixed $selected The option selected by default. If null, the default value
  1303. * from POST data will be used when available.
  1304. * @param array $attributes The HTML attributes of the select element.
  1305. * @return string Formatted SELECT element
  1306. * @access public
  1307. * @link http://book.cakephp.org/view/1430/select
  1308. */
  1309. function select($fieldName, $options = array(), $selected = null, $attributes = array()) {
  1310. $select = array();
  1311. $style = null;
  1312. $tag = null;
  1313. $attributes += array(
  1314. 'class' => null,
  1315. 'escape' => true,
  1316. 'secure' => null,
  1317. 'empty' => '',
  1318. 'showParents' => false,
  1319. 'hiddenField' => true
  1320. );
  1321. $escapeOptions = $this->_extractOption('escape', $attributes);
  1322. $secure = $this->_extractOption('secure', $attributes);
  1323. $showEmpty = $this->_extractOption('empty', $attributes);
  1324. $showParents = $this->_extractOption('showParents', $attributes);
  1325. $hiddenField = $this->_extractOption('hiddenField', $attributes);
  1326. unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
  1327. $attributes = $this->_initInputField($fieldName, array_merge(
  1328. (array)$attributes, array('secure' => false)
  1329. ));
  1330. if (is_string($options) && isset($this->__options[$options])) {
  1331. $options = $this->__generateOptions($options);
  1332. } elseif (!is_array($options)) {
  1333. $options = array();
  1334. }
  1335. if (isset($attributes['type'])) {
  1336. unset($attributes['type']);
  1337. }
  1338. if (!isset($selected)) {
  1339. $selected = $attributes['value'];
  1340. }
  1341. if (!empty($attributes['multiple'])) {
  1342. $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
  1343. $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
  1344. $tag = $this->Html->tags[$template];
  1345. if ($hiddenField) {
  1346. $hiddenAttributes = array(
  1347. 'value' => '',
  1348. 'id' => $attributes['id'] . ($style ? '' : '_'),
  1349. 'secure' => false,
  1350. 'name' => $attributes['name']
  1351. );
  1352. $select[] = $this->hidden(null, $hiddenAttributes);
  1353. }
  1354. } else {
  1355. $tag = $this->Html->tags['selectstart'];
  1356. }
  1357. if (!empty($tag) || isset($template)) {
  1358. if (!isset($secure) || $secure == true) {
  1359. $this->__secure();
  1360. }
  1361. $select[] = sprintf($tag, $attributes['name'], $this->_parseAttributes(
  1362. $attributes, array('name', 'value'))
  1363. );
  1364. }
  1365. $emptyMulti = (
  1366. $showEmpty !== null && $showEmpty !== false && !(
  1367. empty($showEmpty) && (isset($attributes) &&
  1368. array_key_exists('multiple', $attributes))
  1369. )
  1370. );
  1371. if ($emptyMulti) {
  1372. $showEmpty = ($showEmpty === true) ? '' : $showEmpty;
  1373. $options = array_reverse($options, true);
  1374. $options[''] = $showEmpty;
  1375. $options = array_reverse($options, true);
  1376. }
  1377. $select = array_merge($select, $this->__selectOptions(
  1378. array_reverse($options, true),
  1379. $selected,
  1380. array(),
  1381. $showParents,
  1382. array('escape' => $escapeOptions, 'style' => $style, 'name' => $attributes['name'], 'class' => $attributes['class'])
  1383. ));
  1384. $template = ($style == 'checkbox') ? 'checkboxmultipleend' : 'selectend';
  1385. $select[] = $this->Html->tags[$template];
  1386. return implode("\n", $select);
  1387. }
  1388. /**
  1389. * Returns a SELECT element for days.
  1390. *
  1391. * ### Attributes:
  1392. *
  1393. * - `empty` - If true, the empty select option is shown. If a string,
  1394. * that string is displayed as the empty element.
  1395. *
  1396. * @param string $fieldName Prefix name for the SELECT element
  1397. * @param string $selected Option which is selected.
  1398. * @param array $attributes HTML attributes for the select element
  1399. * @return string A generated day select box.
  1400. * @access public
  1401. * @link http://book.cakephp.org/view/1419/day
  1402. */
  1403. function day($fieldName, $selected = null, $attributes = array()) {
  1404. $attributes += array('empty' => true);
  1405. $selected = $this->__dateTimeSelected('day', $fieldName, $selected, $attributes);
  1406. if (strlen($selected) > 2) {
  1407. $selected = date('d', strtotime($selected));
  1408. } elseif ($selected === false) {
  1409. $selected = null;
  1410. }
  1411. return $this->select($fieldName . ".day", $this->__generateOptions('day'), $selected, $attributes);
  1412. }
  1413. /**
  1414. * Returns a SELECT element for years
  1415. *
  1416. * ### Attributes:
  1417. *
  1418. * - `empty` - If true, the empty select option is shown. If a string,
  1419. * that string is displayed as the empty element.
  1420. * - `orderYear` - Ordering of year values in select options.
  1421. * Possible values 'asc', 'desc'. Default 'desc'
  1422. *
  1423. * @param string $fieldName Prefix name for the SELECT element
  1424. * @param integer $minYear First year in sequence
  1425. * @param integer $maxYear Last year in sequence
  1426. * @param string $selected Option which is selected.
  1427. * @param array $attributes Attribute array for the select elements.
  1428. * @return string Completed year select input
  1429. * @access public
  1430. * @link http://book.cakephp.org/view/1416/year
  1431. */
  1432. function year($fieldName, $minYear = null, $maxYear = null, $selected = null, $attributes = array()) {
  1433. $attributes += array('empty' => true);
  1434. if ((empty($selected) || $selected === true) && $value = $this->value($fieldName)) {
  1435. if (is_array($value)) {
  1436. extract($value);
  1437. $selected = $year;
  1438. } else {
  1439. if (empty($value)) {
  1440. if (!$attributes['empty'] && !$maxYear) {
  1441. $selected = 'now';
  1442. } elseif (!$attributes['empty'] && $maxYear && !$selected) {
  1443. $selected = $maxYear;
  1444. }
  1445. } else {
  1446. $selected = $value;
  1447. }
  1448. }
  1449. }
  1450. if (strlen($selected) > 4 || $selected === 'now') {
  1451. $selected = date('Y', strtotime($selected));
  1452. } elseif ($sel

Large files files are truncated, but you can click here to view the full file