PageRenderTime 84ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

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

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

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