1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: App::uses('ClassRegistry', 'Utility');
18: App::uses('AppHelper', 'View/Helper');
19: App::uses('Hash', 'Utility');
20: App::uses('Inflector', 'Utility');
21:
22: 23: 24: 25: 26: 27: 28: 29: 30:
31: class FormHelper extends AppHelper {
32:
33: 34: 35: 36: 37:
38: public $helpers = array('Html');
39:
40: 41: 42: 43: 44:
45: protected $_options = array(
46: 'day' => array(), 'minute' => array(), 'hour' => array(),
47: 'month' => array(), 'year' => array(), 'meridian' => array()
48: );
49:
50: 51: 52: 53: 54:
55: public $fields = array();
56:
57: 58: 59: 60: 61: 62:
63: const SECURE_SKIP = 'skip';
64:
65: 66: 67: 68: 69:
70: public $requestType = null;
71:
72: 73: 74: 75: 76:
77: public $defaultModel = null;
78:
79: 80: 81: 82: 83:
84: protected $_inputDefaults = array();
85:
86: 87: 88: 89: 90: 91: 92: 93:
94: protected $_unlockedFields = array();
95:
96: 97: 98: 99: 100: 101:
102: protected $_models = array();
103:
104: 105: 106: 107: 108: 109: 110:
111: public $validationErrors = array();
112:
113: 114: 115: 116: 117:
118: protected $_domIdSuffixes = array();
119:
120: 121: 122: 123: 124: 125:
126: protected $_lastAction = '';
127:
128: 129: 130: 131: 132: 133:
134: public function __construct(View $View, $settings = array()) {
135: parent::__construct($View, $settings);
136: $this->validationErrors =& $View->validationErrors;
137: }
138:
139: 140: 141: 142: 143: 144: 145:
146: protected function _getModel($model) {
147: $object = null;
148: if (!$model || $model === 'Model') {
149: return $object;
150: }
151:
152: if (array_key_exists($model, $this->_models)) {
153: return $this->_models[$model];
154: }
155:
156: if (ClassRegistry::isKeySet($model)) {
157: $object = ClassRegistry::getObject($model);
158: } elseif (isset($this->request->params['models'][$model])) {
159: $plugin = $this->request->params['models'][$model]['plugin'];
160: $plugin .= ($plugin) ? '.' : null;
161: $object = ClassRegistry::init(array(
162: 'class' => $plugin . $this->request->params['models'][$model]['className'],
163: 'alias' => $model
164: ));
165: } elseif (ClassRegistry::isKeySet($this->defaultModel)) {
166: $defaultObject = ClassRegistry::getObject($this->defaultModel);
167: if ($defaultObject && in_array($model, array_keys($defaultObject->getAssociated()), true) && isset($defaultObject->{$model})) {
168: $object = $defaultObject->{$model};
169: }
170: } else {
171: $object = ClassRegistry::init($model, true);
172: }
173:
174: $this->_models[$model] = $object;
175: if (!$object) {
176: return null;
177: }
178:
179: $this->fieldset[$model] = array('fields' => null, 'key' => $object->primaryKey, 'validates' => null);
180: return $object;
181: }
182:
183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202:
203: protected function _introspectModel($model, $key, $field = null) {
204: $object = $this->_getModel($model);
205: if (!$object) {
206: return null;
207: }
208:
209: if ($key === 'key') {
210: return $this->fieldset[$model]['key'] = $object->primaryKey;
211: }
212:
213: if ($key === 'fields') {
214: if (!isset($this->fieldset[$model]['fields'])) {
215: $this->fieldset[$model]['fields'] = $object->schema();
216: foreach ($object->hasAndBelongsToMany as $alias => $assocData) {
217: $this->fieldset[$object->alias]['fields'][$alias] = array('type' => 'multiple');
218: }
219: }
220: if ($field === null || $field === false) {
221: return $this->fieldset[$model]['fields'];
222: } elseif (isset($this->fieldset[$model]['fields'][$field])) {
223: return $this->fieldset[$model]['fields'][$field];
224: }
225: return isset($object->hasAndBelongsToMany[$field]) ? array('type' => 'multiple') : null;
226: }
227:
228: if ($key === 'errors' && !isset($this->validationErrors[$model])) {
229: $this->validationErrors[$model] =& $object->validationErrors;
230: return $this->validationErrors[$model];
231: } elseif ($key === 'errors' && isset($this->validationErrors[$model])) {
232: return $this->validationErrors[$model];
233: }
234:
235: if ($key === 'validates' && !isset($this->fieldset[$model]['validates'])) {
236: $validates = array();
237: foreach (iterator_to_array($object->validator(), true) as $validateField => $validateProperties) {
238: if ($this->_isRequiredField($validateProperties)) {
239: $validates[$validateField] = true;
240: }
241: }
242: $this->fieldset[$model]['validates'] = $validates;
243: }
244:
245: if ($key === 'validates') {
246: if (empty($field)) {
247: return $this->fieldset[$model]['validates'];
248: }
249: return isset($this->fieldset[$model]['validates'][$field]) ?
250: $this->fieldset[$model]['validates'] : null;
251: }
252: }
253:
254: 255: 256: 257: 258: 259:
260: protected function _isRequiredField($validationRules) {
261: if (empty($validationRules) || count($validationRules) === 0) {
262: return false;
263: }
264:
265: $isUpdate = $this->requestType === 'put';
266: foreach ($validationRules as $rule) {
267: $rule->isUpdate($isUpdate);
268: if ($rule->skip()) {
269: continue;
270: }
271:
272: return !$rule->allowEmpty;
273: }
274: return false;
275: }
276:
277: 278: 279: 280: 281: 282: 283: 284:
285: public function tagIsInvalid() {
286: $entity = $this->entity();
287: $model = array_shift($entity);
288:
289:
290: if (empty($model) || is_numeric($model)) {
291: array_splice($entity, 1, 0, $model);
292: $model = array_shift($entity);
293: }
294:
295: $errors = array();
296: if (!empty($entity) && isset($this->validationErrors[$model])) {
297: $errors = $this->validationErrors[$model];
298: }
299: if (!empty($entity) && empty($errors)) {
300: $errors = $this->_introspectModel($model, 'errors');
301: }
302: if (empty($errors)) {
303: return false;
304: }
305: $errors = Hash::get($errors, implode('.', $entity));
306: return $errors === null ? false : $errors;
307: }
308:
309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334:
335: public function create($model = null, $options = array()) {
336: $created = $id = false;
337: $append = '';
338:
339: if (is_array($model) && empty($options)) {
340: $options = $model;
341: $model = null;
342: }
343:
344: if (empty($model) && $model !== false && !empty($this->request->params['models'])) {
345: $model = key($this->request->params['models']);
346: } elseif (empty($model) && empty($this->request->params['models'])) {
347: $model = false;
348: }
349: $this->defaultModel = $model;
350:
351: $key = null;
352: if ($model !== false) {
353: list($plugin, $model) = pluginSplit($model, true);
354: $key = $this->_introspectModel($plugin . $model, 'key');
355: $this->setEntity($model, true);
356: }
357:
358: if ($model !== false && $key) {
359: $recordExists = (
360: isset($this->request->data[$model]) &&
361: !empty($this->request->data[$model][$key]) &&
362: !is_array($this->request->data[$model][$key])
363: );
364:
365: if ($recordExists) {
366: $created = true;
367: $id = $this->request->data[$model][$key];
368: }
369: }
370:
371: $options += array(
372: 'type' => ($created && empty($options['action'])) ? 'put' : 'post',
373: 'action' => null,
374: 'url' => null,
375: 'default' => true,
376: 'encoding' => strtolower(Configure::read('App.encoding')),
377: 'inputDefaults' => array()
378: );
379: $this->inputDefaults($options['inputDefaults']);
380: unset($options['inputDefaults']);
381:
382: if (!isset($options['id'])) {
383: $domId = isset($options['action']) ? $options['action'] : $this->request['action'];
384: $options['id'] = $this->domId($domId . 'Form');
385: }
386:
387: if ($options['action'] === null && $options['url'] === null) {
388: $options['action'] = $this->request->here(false);
389: } elseif (empty($options['url']) || is_array($options['url'])) {
390: if (empty($options['url']['controller'])) {
391: if (!empty($model)) {
392: $options['url']['controller'] = Inflector::underscore(Inflector::pluralize($model));
393: } elseif (!empty($this->request->params['controller'])) {
394: $options['url']['controller'] = Inflector::underscore($this->request->params['controller']);
395: }
396: }
397: if (empty($options['action'])) {
398: $options['action'] = $this->request->params['action'];
399: }
400:
401: $plugin = null;
402: if ($this->plugin) {
403: $plugin = Inflector::underscore($this->plugin);
404: }
405: $actionDefaults = array(
406: 'plugin' => $plugin,
407: 'controller' => $this->_View->viewPath,
408: 'action' => $options['action'],
409: );
410: $options['action'] = array_merge($actionDefaults, (array)$options['url']);
411: if (!isset($options['action'][0]) && !empty($id)) {
412: $options['action'][0] = $id;
413: }
414: } elseif (is_string($options['url'])) {
415: $options['action'] = $options['url'];
416: }
417: unset($options['url']);
418:
419: switch (strtolower($options['type'])) {
420: case 'get':
421: $htmlAttributes['method'] = 'get';
422: break;
423: case 'file':
424: $htmlAttributes['enctype'] = 'multipart/form-data';
425: $options['type'] = ($created) ? 'put' : 'post';
426: case 'post':
427: case 'put':
428: case 'delete':
429: $append .= $this->hidden('_method', array(
430: 'name' => '_method', 'value' => strtoupper($options['type']), 'id' => null,
431: 'secure' => static::SECURE_SKIP
432: ));
433: default:
434: $htmlAttributes['method'] = 'post';
435: }
436: $this->requestType = strtolower($options['type']);
437:
438: $action = $this->url($options['action']);
439: $this->_lastAction($options['action']);
440: unset($options['type'], $options['action']);
441:
442: if (!$options['default']) {
443: if (!isset($options['onsubmit'])) {
444: $options['onsubmit'] = '';
445: }
446: $htmlAttributes['onsubmit'] = $options['onsubmit'] . 'event.returnValue = false; return false;';
447: }
448: unset($options['default']);
449:
450: if (!empty($options['encoding'])) {
451: $htmlAttributes['accept-charset'] = $options['encoding'];
452: unset($options['encoding']);
453: }
454:
455: $htmlAttributes = array_merge($options, $htmlAttributes);
456:
457: $this->fields = array();
458: if ($this->requestType !== 'get') {
459: $append .= $this->_csrfField();
460: }
461:
462: if (!empty($append)) {
463: $append = $this->Html->useTag('hiddenblock', $append);
464: }
465:
466: if ($model !== false) {
467: $this->setEntity($model, true);
468: $this->_introspectModel($model, 'fields');
469: }
470:
471: return $this->Html->useTag('form', $action, $htmlAttributes) . $append;
472: }
473:
474: 475: 476: 477: 478: 479:
480: protected function _csrfField() {
481: if (empty($this->request->params['_Token'])) {
482: return '';
483: }
484: if (!empty($this->request['_Token']['unlockedFields'])) {
485: foreach ((array)$this->request['_Token']['unlockedFields'] as $unlocked) {
486: $this->_unlockedFields[] = $unlocked;
487: }
488: }
489: return $this->hidden('_Token.key', array(
490: 'value' => $this->request->params['_Token']['key'], 'id' => 'Token' . mt_rand(),
491: 'secure' => static::SECURE_SKIP
492: ));
493: }
494:
495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519:
520: public function end($options = null, $secureAttributes = array()) {
521: $out = null;
522: $submit = null;
523:
524: if ($options !== null) {
525: $submitOptions = array();
526: if (is_string($options)) {
527: $submit = $options;
528: } else {
529: if (isset($options['label'])) {
530: $submit = $options['label'];
531: unset($options['label']);
532: }
533: $submitOptions = $options;
534: }
535: $out .= $this->submit($submit, $submitOptions);
536: }
537: if ($this->requestType !== 'get' &&
538: isset($this->request['_Token']) &&
539: !empty($this->request['_Token'])
540: ) {
541: $out .= $this->secure($this->fields, $secureAttributes);
542: $this->fields = array();
543: }
544: $this->setEntity(null);
545: $out .= $this->Html->useTag('formend');
546:
547: $this->_View->modelScope = false;
548: $this->requestType = null;
549: return $out;
550: }
551:
552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566:
567: public function secure($fields = array(), $secureAttributes = array()) {
568: if (!isset($this->request['_Token']) || empty($this->request['_Token'])) {
569: return null;
570: }
571: $locked = array();
572: $unlockedFields = $this->_unlockedFields;
573:
574: foreach ($fields as $key => $value) {
575: if (!is_int($key)) {
576: $locked[$key] = $value;
577: unset($fields[$key]);
578: }
579: }
580:
581: sort($unlockedFields, SORT_STRING);
582: sort($fields, SORT_STRING);
583: ksort($locked, SORT_STRING);
584: $fields += $locked;
585:
586: $locked = implode(array_keys($locked), '|');
587: $unlocked = implode($unlockedFields, '|');
588: $hashParts = array(
589: $this->_lastAction,
590: serialize($fields),
591: $unlocked,
592: Configure::read('Security.salt')
593: );
594: $fields = Security::hash(implode('', $hashParts), 'sha1');
595:
596: $tokenFields = array_merge($secureAttributes, array(
597: 'value' => urlencode($fields . ':' . $locked),
598: 'id' => 'TokenFields' . mt_rand(),
599: ));
600: $out = $this->hidden('_Token.fields', $tokenFields);
601: $tokenUnlocked = array_merge($secureAttributes, array(
602: 'value' => urlencode($unlocked),
603: 'id' => 'TokenUnlocked' . mt_rand(),
604: ));
605: $out .= $this->hidden('_Token.unlocked', $tokenUnlocked);
606: return $this->Html->useTag('hiddenblock', $out);
607: }
608:
609: 610: 611: 612: 613: 614: 615: 616: 617: 618:
619: public function unlockField($name = null) {
620: if ($name === null) {
621: return $this->_unlockedFields;
622: }
623: if (!in_array($name, $this->_unlockedFields)) {
624: $this->_unlockedFields[] = $name;
625: }
626: $index = array_search($name, $this->fields);
627: if ($index !== false) {
628: unset($this->fields[$index]);
629: }
630: unset($this->fields[$name]);
631: }
632:
633: 634: 635: 636: 637: 638: 639: 640: 641: 642:
643: protected function _secure($lock, $field = null, $value = null) {
644: if (!$field) {
645: $field = $this->entity();
646: } elseif (is_string($field)) {
647: $field = Hash::filter(explode('.', $field));
648: }
649:
650: foreach ($this->_unlockedFields as $unlockField) {
651: $unlockParts = explode('.', $unlockField);
652: if (array_values(array_intersect($field, $unlockParts)) === $unlockParts) {
653: return;
654: }
655: }
656:
657: $field = implode('.', $field);
658: $field = preg_replace('/(\.\d+)+$/', '', $field);
659:
660: if ($lock) {
661: if (!in_array($field, $this->fields)) {
662: if ($value !== null) {
663: return $this->fields[$field] = $value;
664: } elseif (isset($this->fields[$field]) && $value === null) {
665: unset($this->fields[$field]);
666: }
667: $this->fields[] = $field;
668: }
669: } else {
670: $this->unlockField($field);
671: }
672: }
673:
674: 675: 676: 677: 678: 679: 680:
681: public function isFieldError($field) {
682: $this->setEntity($field);
683: return (bool)$this->tagIsInvalid();
684: }
685:
686: 687: 688: 689: 690: 691: 692: 693: 694: 695: 696: 697: 698: 699: 700: 701: 702:
703: public function error($field, $text = null, $options = array()) {
704: $defaults = array('wrap' => true, 'class' => 'error-message', 'escape' => true);
705: $options += $defaults;
706: $this->setEntity($field);
707:
708: $error = $this->tagIsInvalid();
709: if ($error === false) {
710: return null;
711: }
712: if (is_array($text)) {
713: if (isset($text['attributes']) && is_array($text['attributes'])) {
714: $options = array_merge($options, $text['attributes']);
715: unset($text['attributes']);
716: }
717: $tmp = array();
718: foreach ($error as &$e) {
719: if (isset($text[$e])) {
720: $tmp[] = $text[$e];
721: } else {
722: $tmp[] = $e;
723: }
724: }
725: $text = $tmp;
726: }
727:
728: if ($text !== null) {
729: $error = $text;
730: }
731: if (is_array($error)) {
732: foreach ($error as &$e) {
733: if (is_numeric($e)) {
734: $e = __d('cake', 'Error in field %s', Inflector::humanize($this->field()));
735: }
736: }
737: }
738: if ($options['escape']) {
739: $error = h($error);
740: unset($options['escape']);
741: }
742: if (is_array($error)) {
743: if (count($error) > 1) {
744: $listParams = array();
745: if (isset($options['listOptions'])) {
746: if (is_string($options['listOptions'])) {
747: $listParams[] = $options['listOptions'];
748: } else {
749: if (isset($options['listOptions']['itemOptions'])) {
750: $listParams[] = $options['listOptions']['itemOptions'];
751: unset($options['listOptions']['itemOptions']);
752: } else {
753: $listParams[] = array();
754: }
755: if (isset($options['listOptions']['tag'])) {
756: $listParams[] = $options['listOptions']['tag'];
757: unset($options['listOptions']['tag']);
758: }
759: array_unshift($listParams, $options['listOptions']);
760: }
761: unset($options['listOptions']);
762: }
763: array_unshift($listParams, $error);
764: $error = call_user_func_array(array($this->Html, 'nestedList'), $listParams);
765: } else {
766: $error = array_pop($error);
767: }
768: }
769: if ($options['wrap']) {
770: $tag = is_string($options['wrap']) ? $options['wrap'] : 'div';
771: unset($options['wrap']);
772: return $this->Html->tag($tag, $error, $options);
773: }
774: return $error;
775: }
776:
777: 778: 779: 780: 781: 782: 783: 784: 785: 786: 787: 788: 789: 790: 791: 792: 793: 794: 795: 796: 797: 798: 799: 800: 801: 802: 803: 804: 805: 806: 807: 808: 809: 810: 811: 812: 813: 814: 815: 816: 817: 818: 819: 820: 821: 822: 823: 824: 825: 826: 827: 828: 829: 830:
831: public function label($fieldName = null, $text = null, $options = array()) {
832: if ($fieldName === null) {
833: $fieldName = implode('.', $this->entity());
834: }
835:
836: if ($text === null) {
837: if (strpos($fieldName, '.') !== false) {
838: $fieldElements = explode('.', $fieldName);
839: $text = array_pop($fieldElements);
840: } else {
841: $text = $fieldName;
842: }
843: if (substr($text, -3) === '_id') {
844: $text = substr($text, 0, -3);
845: }
846: $text = __(Inflector::humanize(Inflector::underscore($text)));
847: }
848:
849: if (is_string($options)) {
850: $options = array('class' => $options);
851: }
852:
853: if (isset($options['for'])) {
854: $labelFor = $options['for'];
855: unset($options['for']);
856: } else {
857: $labelFor = $this->domId($fieldName);
858: }
859:
860: return $this->Html->useTag('label', $labelFor, $options, $text);
861: }
862:
863: 864: 865: 866: 867: 868: 869: 870: 871: 872: 873: 874: 875: 876: 877: 878: 879: 880: 881: 882: 883: 884: 885: 886: 887: 888: 889:
890: public function inputs($fields = null, $blacklist = null, $options = array()) {
891: $fieldset = $legend = true;
892: $modelFields = array();
893: $model = $this->model();
894: if ($model) {
895: $modelFields = array_keys((array)$this->_introspectModel($model, 'fields'));
896: }
897: if (is_array($fields)) {
898: if (array_key_exists('legend', $fields) && !in_array('legend', $modelFields)) {
899: $legend = $fields['legend'];
900: unset($fields['legend']);
901: }
902:
903: if (isset($fields['fieldset']) && !in_array('fieldset', $modelFields)) {
904: $fieldset = $fields['fieldset'];
905: unset($fields['fieldset']);
906: }
907: } elseif ($fields !== null) {
908: $fieldset = $legend = $fields;
909: if (!is_bool($fieldset)) {
910: $fieldset = true;
911: }
912: $fields = array();
913: }
914:
915: if (isset($options['legend'])) {
916: $legend = $options['legend'];
917: }
918: if (isset($options['fieldset'])) {
919: $fieldset = $options['fieldset'];
920: }
921:
922: if (empty($fields)) {
923: $fields = $modelFields;
924: }
925:
926: if ($legend === true) {
927: $actionName = __d('cake', 'New %s');
928: $isEdit = (
929: strpos($this->request->params['action'], 'update') !== false ||
930: strpos($this->request->params['action'], 'edit') !== false
931: );
932: if ($isEdit) {
933: $actionName = __d('cake', 'Edit %s');
934: }
935: $modelName = Inflector::humanize(Inflector::underscore($model));
936: $legend = sprintf($actionName, __($modelName));
937: }
938:
939: $out = null;
940: foreach ($fields as $name => $options) {
941: if (is_numeric($name) && !is_array($options)) {
942: $name = $options;
943: $options = array();
944: }
945: $entity = explode('.', $name);
946: $blacklisted = (
947: is_array($blacklist) &&
948: (in_array($name, $blacklist) || in_array(end($entity), $blacklist))
949: );
950: if ($blacklisted) {
951: continue;
952: }
953: $out .= $this->input($name, $options);
954: }
955:
956: if (is_string($fieldset)) {
957: $fieldsetClass = sprintf(' class="%s"', $fieldset);
958: } else {
959: $fieldsetClass = '';
960: }
961:
962: if ($fieldset) {
963: if ($legend) {
964: $out = $this->Html->useTag('legend', $legend) . $out;
965: }
966: $out = $this->Html->useTag('fieldset', $fieldsetClass, $out);
967: }
968: return $out;
969: }
970:
971: 972: 973: 974: 975: 976: 977: 978: 979: 980: 981: 982: 983: 984: 985: 986: 987: 988: 989: 990: 991: 992: 993: 994: 995: 996: 997: 998: 999: 1000: 1001: 1002: 1003:
1004: public function input($fieldName, $options = array()) {
1005: $this->setEntity($fieldName);
1006: $options = $this->_parseOptions($options);
1007:
1008: $divOptions = $this->_divOptions($options);
1009: unset($options['div']);
1010:
1011: if ($options['type'] === 'radio' && isset($options['options'])) {
1012: $radioOptions = (array)$options['options'];
1013: unset($options['options']);
1014: }
1015:
1016: $label = $this->_getLabel($fieldName, $options);
1017: if ($options['type'] !== 'radio') {
1018: unset($options['label']);
1019: }
1020:
1021: $error = $this->_extractOption('error', $options, null);
1022: unset($options['error']);
1023:
1024: $errorMessage = $this->_extractOption('errorMessage', $options, true);
1025: unset($options['errorMessage']);
1026:
1027: $selected = $this->_extractOption('selected', $options, null);
1028: unset($options['selected']);
1029:
1030: if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time') {
1031: $dateFormat = $this->_extractOption('dateFormat', $options, 'MDY');
1032: $timeFormat = $this->_extractOption('timeFormat', $options, 12);
1033: unset($options['dateFormat'], $options['timeFormat']);
1034: }
1035:
1036: $type = $options['type'];
1037: $out = array('before' => $options['before'], 'label' => $label, 'between' => $options['between'], 'after' => $options['after']);
1038: $format = $this->_getFormat($options);
1039:
1040: unset($options['type'], $options['before'], $options['between'], $options['after'], $options['format']);
1041:
1042: $out['error'] = null;
1043: if ($type !== 'hidden' && $error !== false) {
1044: $errMsg = $this->error($fieldName, $error);
1045: if ($errMsg) {
1046: $divOptions = $this->addClass($divOptions, 'error');
1047: if ($errorMessage) {
1048: $out['error'] = $errMsg;
1049: }
1050: }
1051: }
1052:
1053: if ($type === 'radio' && isset($out['between'])) {
1054: $options['between'] = $out['between'];
1055: $out['between'] = null;
1056: }
1057: $out['input'] = $this->_getInput(compact('type', 'fieldName', 'options', 'radioOptions', 'selected', 'dateFormat', 'timeFormat'));
1058:
1059: $output = '';
1060: foreach ($format as $element) {
1061: $output .= $out[$element];
1062: }
1063:
1064: if (!empty($divOptions['tag'])) {
1065: $tag = $divOptions['tag'];
1066: unset($divOptions['tag']);
1067: $output = $this->Html->tag($tag, $output, $divOptions);
1068: }
1069: return $output;
1070: }
1071:
1072: 1073: 1074: 1075: 1076: 1077:
1078: protected function _getInput($args) {
1079: extract($args);
1080: switch ($type) {
1081: case 'hidden':
1082: return $this->hidden($fieldName, $options);
1083: case 'checkbox':
1084: return $this->checkbox($fieldName, $options);
1085: case 'radio':
1086: return $this->radio($fieldName, $radioOptions, $options);
1087: case 'file':
1088: return $this->file($fieldName, $options);
1089: case 'select':
1090: $options += array('options' => array(), 'value' => $selected);
1091: $list = $options['options'];
1092: unset($options['options']);
1093: return $this->select($fieldName, $list, $options);
1094: case 'time':
1095: $options += array('value' => $selected);
1096: return $this->dateTime($fieldName, null, $timeFormat, $options);
1097: case 'date':
1098: $options += array('value' => $selected);
1099: return $this->dateTime($fieldName, $dateFormat, null, $options);
1100: case 'datetime':
1101: $options += array('value' => $selected);
1102: return $this->dateTime($fieldName, $dateFormat, $timeFormat, $options);
1103: case 'textarea':
1104: return $this->textarea($fieldName, $options + array('cols' => '30', 'rows' => '6'));
1105: case 'url':
1106: return $this->text($fieldName, array('type' => 'url') + $options);
1107: default:
1108: return $this->{$type}($fieldName, $options);
1109: }
1110: }
1111:
1112: 1113: 1114: 1115: 1116: 1117:
1118: protected function _parseOptions($options) {
1119: $options = array_merge(
1120: array('before' => null, 'between' => null, 'after' => null, 'format' => null),
1121: $this->_inputDefaults,
1122: $options
1123: );
1124:
1125: if (!isset($options['type'])) {
1126: $options = $this->_magicOptions($options);
1127: }
1128:
1129: if (in_array($options['type'], array('radio', 'select'))) {
1130: $options = $this->_optionsOptions($options);
1131: }
1132:
1133: $options = $this->_maxLength($options);
1134:
1135: if (isset($options['rows']) || isset($options['cols'])) {
1136: $options['type'] = 'textarea';
1137: }
1138:
1139: if ($options['type'] === 'datetime' || $options['type'] === 'date' || $options['type'] === 'time' || $options['type'] === 'select') {
1140: $options += array('empty' => false);
1141: }
1142: return $options;
1143: }
1144:
1145: 1146: 1147: 1148: 1149: 1150:
1151: protected function _optionsOptions($options) {
1152: if (isset($options['options'])) {
1153: return $options;
1154: }
1155: $varName = Inflector::variable(
1156: Inflector::pluralize(preg_replace('/_id$/', '', $this->field()))
1157: );
1158: $varOptions = $this->_View->get($varName);
1159: if (!is_array($varOptions)) {
1160: return $options;
1161: }
1162: if ($options['type'] !== 'radio') {
1163: $options['type'] = 'select';
1164: }
1165: $options['options'] = $varOptions;
1166: return $options;
1167: }
1168:
1169: 1170: 1171: 1172: 1173: 1174:
1175: protected function _magicOptions($options) {
1176: $modelKey = $this->model();
1177: $fieldKey = $this->field();
1178: $options['type'] = 'text';
1179: if (isset($options['options'])) {
1180: $options['type'] = 'select';
1181: } elseif (in_array($fieldKey, array('psword', 'passwd', 'password'))) {
1182: $options['type'] = 'password';
1183: } elseif (in_array($fieldKey, array('tel', 'telephone', 'phone'))) {
1184: $options['type'] = 'tel';
1185: } elseif ($fieldKey === 'email') {
1186: $options['type'] = 'email';
1187: } elseif (isset($options['checked'])) {
1188: $options['type'] = 'checkbox';
1189: } elseif ($fieldDef = $this->_introspectModel($modelKey, 'fields', $fieldKey)) {
1190: $type = $fieldDef['type'];
1191: $primaryKey = $this->fieldset[$modelKey]['key'];
1192: $map = array(
1193: 'string' => 'text', 'datetime' => 'datetime',
1194: 'boolean' => 'checkbox', 'timestamp' => 'datetime',
1195: 'text' => 'textarea', 'time' => 'time',
1196: 'date' => 'date', 'float' => 'number',
1197: 'integer' => 'number', 'decimal' => 'number',
1198: 'binary' => 'file'
1199: );
1200:
1201: if (isset($this->map[$type])) {
1202: $options['type'] = $this->map[$type];
1203: } elseif (isset($map[$type])) {
1204: $options['type'] = $map[$type];
1205: }
1206: if ($fieldKey === $primaryKey) {
1207: $options['type'] = 'hidden';
1208: }
1209: if ($options['type'] === 'number' &&
1210: !isset($options['step'])
1211: ) {
1212: if ($type === 'decimal' && isset($fieldDef['length'])) {
1213: $decimalPlaces = substr($fieldDef['length'], strpos($fieldDef['length'], ',') + 1);
1214: $options['step'] = sprintf('%.' . $decimalPlaces . 'F', pow(10, -1 * $decimalPlaces));
1215: } elseif ($type === 'float' || $type === 'decimal') {
1216: $options['step'] = 'any';
1217: }
1218: }
1219: }
1220:
1221: if (preg_match('/_id$/', $fieldKey) && $options['type'] !== 'hidden') {
1222: $options['type'] = 'select';
1223: }
1224:
1225: if ($modelKey === $fieldKey) {
1226: $options['type'] = 'select';
1227: if (!isset($options['multiple'])) {
1228: $options['multiple'] = 'multiple';
1229: }
1230: }
1231: if (in_array($options['type'], array('text', 'number'))) {
1232: $options = $this->_optionsOptions($options);
1233: }
1234: if ($options['type'] === 'select' && array_key_exists('step', $options)) {
1235: unset($options['step']);
1236: }
1237:
1238: return $options;
1239: }
1240:
1241: 1242: 1243: 1244: 1245: 1246:
1247: protected function _getFormat($options) {
1248: if ($options['type'] === 'hidden') {
1249: return array('input');
1250: }
1251: if (is_array($options['format']) && in_array('input', $options['format'])) {
1252: return $options['format'];
1253: }
1254: if ($options['type'] === 'checkbox') {
1255: return array('before', 'input', 'between', 'label', 'after', 'error');
1256: }
1257: return array('before', 'label', 'between', 'input', 'after', 'error');
1258: }
1259:
1260: 1261: 1262: 1263: 1264: 1265: 1266:
1267: protected function _getLabel($fieldName, $options) {
1268: if ($options['type'] === 'radio') {
1269: return false;
1270: }
1271:
1272: $label = null;
1273: if (isset($options['label'])) {
1274: $label = $options['label'];
1275: }
1276:
1277: if ($label === false) {
1278: return false;
1279: }
1280: return $this->_inputLabel($fieldName, $label, $options);
1281: }
1282:
1283: 1284: 1285: 1286: 1287: 1288:
1289: protected function _maxLength($options) {
1290: $fieldDef = $this->_introspectModel($this->model(), 'fields', $this->field());
1291: $autoLength = (
1292: !array_key_exists('maxlength', $options) &&
1293: isset($fieldDef['length']) &&
1294: is_scalar($fieldDef['length']) &&
1295: $fieldDef['length'] < 1000000 &&
1296: $fieldDef['type'] !== 'decimal' &&
1297: $options['type'] !== 'select'
1298: );
1299: if ($autoLength &&
1300: in_array($options['type'], array('text', 'textarea', 'email', 'tel', 'url', 'search'))
1301: ) {
1302: $options['maxlength'] = (int)$fieldDef['length'];
1303: }
1304: return $options;
1305: }
1306:
1307: 1308: 1309: 1310: 1311: 1312:
1313: protected function _divOptions($options) {
1314: if ($options['type'] === 'hidden') {
1315: return array();
1316: }
1317: $div = $this->_extractOption('div', $options, true);
1318: if (!$div) {
1319: return array();
1320: }
1321:
1322: $divOptions = array('class' => 'input');
1323: $divOptions = $this->addClass($divOptions, $options['type']);
1324: if (is_string($div)) {
1325: $divOptions['class'] = $div;
1326: } elseif (is_array($div)) {
1327: $divOptions = array_merge($divOptions, $div);
1328: }
1329: if ($this->_extractOption('required', $options) !== false &&
1330: $this->_introspectModel($this->model(), 'validates', $this->field())
1331: ) {
1332: $divOptions = $this->addClass($divOptions, 'required');
1333: }
1334: if (!isset($divOptions['tag'])) {
1335: $divOptions['tag'] = 'div';
1336: }
1337: return $divOptions;
1338: }
1339:
1340: 1341: 1342: 1343: 1344: 1345: 1346: 1347:
1348: protected function _extractOption($name, $options, $default = null) {
1349: if (array_key_exists($name, $options)) {
1350: return $options[$name];
1351: }
1352: return $default;
1353: }
1354:
1355: 1356: 1357: 1358: 1359: 1360: 1361: 1362: 1363: 1364: 1365: 1366:
1367: protected function _inputLabel($fieldName, $label, $options) {
1368: $labelAttributes = $this->domId(array(), 'for');
1369: $idKey = null;
1370: if ($options['type'] === 'date' || $options['type'] === 'datetime') {
1371: $firstInput = 'M';
1372: if (array_key_exists('dateFormat', $options) &&
1373: ($options['dateFormat'] === null || $options['dateFormat'] === 'NONE')
1374: ) {
1375: $firstInput = 'H';
1376: } elseif (!empty($options['dateFormat'])) {
1377: $firstInput = substr($options['dateFormat'], 0, 1);
1378: }
1379: switch ($firstInput) {
1380: case 'D':
1381: $idKey = 'day';
1382: $labelAttributes['for'] .= 'Day';
1383: break;
1384: case 'Y':
1385: $idKey = 'year';
1386: $labelAttributes['for'] .= 'Year';
1387: break;
1388: case 'M':
1389: $idKey = 'month';
1390: $labelAttributes['for'] .= 'Month';
1391: break;
1392: case 'H':
1393: $idKey = 'hour';
1394: $labelAttributes['for'] .= 'Hour';
1395: }
1396: }
1397: if ($options['type'] === 'time') {
1398: $labelAttributes['for'] .= 'Hour';
1399: $idKey = 'hour';
1400: }
1401: if (isset($idKey) && isset($options['id']) && isset($options['id'][$idKey])) {
1402: $labelAttributes['for'] = $options['id'][$idKey];
1403: }
1404:
1405: if (is_array($label)) {
1406: $labelText = null;
1407: if (isset($label['text'])) {
1408: $labelText = $label['text'];
1409: unset($label['text']);
1410: }
1411: $labelAttributes = array_merge($labelAttributes, $label);
1412: } else {
1413: $labelText = $label;
1414: }
1415:
1416: if (isset($options['id']) && is_string($options['id'])) {
1417: $labelAttributes = array_merge($labelAttributes, array('for' => $options['id']));
1418: }
1419: return $this->label($fieldName, $labelText, $labelAttributes);
1420: }
1421:
1422: 1423: 1424: 1425: 1426: 1427: 1428: 1429: 1430: 1431: 1432: 1433: 1434: 1435: 1436: 1437: 1438: 1439: 1440:
1441: public function checkbox($fieldName, $options = array()) {
1442: $valueOptions = array();
1443: if (isset($options['default'])) {
1444: $valueOptions['default'] = $options['default'];
1445: unset($options['default']);
1446: }
1447:
1448: $options += array('value' => 1, 'required' => false);
1449: $options = $this->_initInputField($fieldName, $options) + array('hiddenField' => true);
1450: $value = current($this->value($valueOptions));
1451: $output = '';
1452:
1453: if ((!isset($options['checked']) && !empty($value) && $value == $options['value']) ||
1454: !empty($options['checked'])
1455: ) {
1456: $options['checked'] = 'checked';
1457: }
1458: if ($options['hiddenField']) {
1459: $hiddenOptions = array(
1460: 'id' => $options['id'] . '_',
1461: 'name' => $options['name'],
1462: 'value' => ($options['hiddenField'] !== true ? $options['hiddenField'] : '0'),
1463: 'form' => isset($options['form']) ? $options['form'] : null,
1464: 'secure' => false,
1465: );
1466: if (isset($options['disabled']) && $options['disabled']) {
1467: $hiddenOptions['disabled'] = 'disabled';
1468: }
1469: $output = $this->hidden($fieldName, $hiddenOptions);
1470: }
1471: unset($options['hiddenField']);
1472:
1473: return $output . $this->Html->useTag('checkbox', $options['name'], array_diff_key($options, array('name' => null)));
1474: }
1475:
1476: 1477: 1478: 1479: 1480: 1481: 1482: 1483: 1484: 1485: 1486: 1487: 1488: 1489: 1490: 1491: 1492: 1493: 1494: 1495: 1496: 1497: 1498: 1499: 1500: 1501: 1502: 1503: 1504: 1505: 1506: 1507: 1508:
1509: public function radio($fieldName, $options = array(), $attributes = array()) {
1510: $attributes['options'] = $options;
1511: $attributes = $this->_initInputField($fieldName, $attributes);
1512: unset($attributes['options']);
1513:
1514: $showEmpty = $this->_extractOption('empty', $attributes);
1515: if ($showEmpty) {
1516: $showEmpty = ($showEmpty === true) ? __d('cake', 'empty') : $showEmpty;
1517: $options = array('' => $showEmpty) + $options;
1518: }
1519: unset($attributes['empty']);
1520:
1521: $legend = false;
1522: if (isset($attributes['legend'])) {
1523: $legend = $attributes['legend'];
1524: unset($attributes['legend']);
1525: } elseif (count($options) > 1) {
1526: $legend = __(Inflector::humanize($this->field()));
1527: }
1528:
1529: $label = true;
1530: if (isset($attributes['label'])) {
1531: $label = $attributes['label'];
1532: unset($attributes['label']);
1533: }
1534:
1535: $separator = null;
1536: if (isset($attributes['separator'])) {
1537: $separator = $attributes['separator'];
1538: unset($attributes['separator']);
1539: }
1540:
1541: $between = null;
1542: if (isset($attributes['between'])) {
1543: $between = $attributes['between'];
1544: unset($attributes['between']);
1545: }
1546:
1547: $value = null;
1548: if (isset($attributes['value'])) {
1549: $value = $attributes['value'];
1550: } else {
1551: $value = $this->value($fieldName);
1552: }
1553:
1554: $disabled = array();
1555: if (isset($attributes['disabled'])) {
1556: $disabled = $attributes['disabled'];
1557: }
1558:
1559: $out = array();
1560:
1561: $hiddenField = isset($attributes['hiddenField']) ? $attributes['hiddenField'] : true;
1562: unset($attributes['hiddenField']);
1563:
1564: if (isset($value) && is_bool($value)) {
1565: $value = $value ? 1 : 0;
1566: }
1567:
1568: $this->_domIdSuffixes = array();
1569: foreach ($options as $optValue => $optTitle) {
1570: $optionsHere = array('value' => $optValue, 'disabled' => false);
1571: if (is_array($optTitle)) {
1572: if (isset($optTitle['value'])) {
1573: $optionsHere['value'] = $optTitle['value'];
1574: }
1575:
1576: $optionsHere += $optTitle;
1577: $optTitle = $optionsHere['name'];
1578: unset($optionsHere['name']);
1579: }
1580:
1581: if (isset($value) && strval($optValue) === strval($value)) {
1582: $optionsHere['checked'] = 'checked';
1583: }
1584: $isNumeric = is_numeric($optValue);
1585: if ($disabled && (!is_array($disabled) || in_array((string)$optValue, $disabled, !$isNumeric))) {
1586: $optionsHere['disabled'] = true;
1587: }
1588: $tagName = $attributes['id'] . $this->domIdSuffix($optValue);
1589:
1590: if ($label) {
1591: $labelOpts = is_array($label) ? $label : array();
1592: $labelOpts += array('for' => $tagName);
1593: $optTitle = $this->label($tagName, $optTitle, $labelOpts);
1594: }
1595:
1596: if (is_array($between)) {
1597: $optTitle .= array_shift($between);
1598: }
1599: $allOptions = $optionsHere + $attributes;
1600: $out[] = $this->Html->useTag('radio', $attributes['name'], $tagName,
1601: array_diff_key($allOptions, array('name' => null, 'type' => null, 'id' => null)),
1602: $optTitle
1603: );
1604: }
1605: $hidden = null;
1606:
1607: if ($hiddenField) {
1608: if (!isset($value) || $value === '') {
1609: $hidden = $this->hidden($fieldName, array(
1610: 'form' => isset($attributes['form']) ? $attributes['form'] : null,
1611: 'id' => $attributes['id'] . '_',
1612: 'value' => '',
1613: 'name' => $attributes['name']
1614: ));
1615: }
1616: }
1617: $out = $hidden . implode($separator, $out);
1618:
1619: if (is_array($between)) {
1620: $between = '';
1621: }
1622: if ($legend) {
1623: $out = $this->Html->useTag('fieldset', '', $this->Html->useTag('legend', $legend) . $between . $out);
1624: }
1625: return $out;
1626: }
1627:
1628: 1629: 1630: 1631: 1632: 1633: 1634: 1635: 1636: 1637: 1638: 1639: 1640: 1641: 1642: 1643: 1644: 1645: 1646: 1647: 1648:
1649: public function __call($method, $params) {
1650: $options = array();
1651: if (empty($params)) {
1652: throw new CakeException(__d('cake_dev', 'Missing field name for FormHelper::%s', $method));
1653: }
1654: if (isset($params[1])) {
1655: $options = $params[1];
1656: }
1657: if (!isset($options['type'])) {
1658: $options['type'] = $method;
1659: }
1660: $options = $this->_initInputField($params[0], $options);
1661: return $this->Html->useTag('input', $options['name'], array_diff_key($options, array('name' => null)));
1662: }
1663:
1664: 1665: 1666: 1667: 1668: 1669: 1670: 1671: 1672: 1673: 1674: 1675:
1676: public function textarea($fieldName, $options = array()) {
1677: $options = $this->_initInputField($fieldName, $options);
1678: $value = null;
1679:
1680: if (array_key_exists('value', $options)) {
1681: $value = $options['value'];
1682: if (!array_key_exists('escape', $options) || $options['escape'] !== false) {
1683: $value = h($value);
1684: }
1685: unset($options['value']);
1686: }
1687: return $this->Html->useTag('textarea', $options['name'], array_diff_key($options, array('type' => null, 'name' => null)), $value);
1688: }
1689:
1690: 1691: 1692: 1693: 1694: 1695: 1696: 1697:
1698: public function hidden($fieldName, $options = array()) {
1699: $options += array('required' => false, 'secure' => true);
1700:
1701: $secure = $options['secure'];
1702: unset($options['secure']);
1703:
1704: $options = $this->_initInputField($fieldName, array_merge(
1705: $options, array('secure' => static::SECURE_SKIP)
1706: ));
1707:
1708: if ($secure === true) {
1709: $this->_secure(true, null, '' . $options['value']);
1710: }
1711:
1712: return $this->Html->useTag('hidden', $options['name'], array_diff_key($options, array('name' => null)));
1713: }
1714:
1715: 1716: 1717: 1718: 1719: 1720: 1721: 1722:
1723: public function file($fieldName, $options = array()) {
1724: $options += array('secure' => true);
1725: $secure = $options['secure'];
1726: $options['secure'] = static::SECURE_SKIP;
1727:
1728: $options = $this->_initInputField($fieldName, $options);
1729: $field = $this->entity();
1730:
1731: foreach (array('name', 'type', 'tmp_name', 'error', 'size') as $suffix) {
1732: $this->_secure($secure, array_merge($field, array($suffix)));
1733: }
1734:
1735: $exclude = array('name' => null, 'value' => null);
1736: return $this->Html->useTag('file', $options['name'], array_diff_key($options, $exclude));
1737: }
1738:
1739: 1740: 1741: 1742: 1743: 1744: 1745: 1746: 1747: 1748: 1749: 1750: 1751:
1752: public function button($title, $options = array()) {
1753: $options += array('type' => 'submit', 'escape' => false, 'secure' => false);
1754: if ($options['escape']) {
1755: $title = h($title);
1756: }
1757: if (isset($options['name'])) {
1758: $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1759: $this->_secure($options['secure'], $name);
1760: }
1761: return $this->Html->useTag('button', $options, $title);
1762: }
1763:
1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: 1778: 1779: 1780:
1781: public function postButton($title, $url, $options = array()) {
1782: $out = $this->create(false, array('id' => false, 'url' => $url));
1783: if (isset($options['data']) && is_array($options['data'])) {
1784: foreach (Hash::flatten($options['data']) as $key => $value) {
1785: $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1786: }
1787: unset($options['data']);
1788: }
1789: $out .= $this->button($title, $options);
1790: $out .= $this->end();
1791: return $out;
1792: }
1793:
1794: 1795: 1796: 1797: 1798: 1799: 1800: 1801: 1802: 1803: 1804: 1805: 1806: 1807: 1808: 1809: 1810: 1811: 1812: 1813: 1814: 1815: 1816: 1817: 1818: 1819: 1820: 1821: 1822: 1823: 1824: 1825:
1826: public function postLink($title, $url = null, $options = array(), $confirmMessage = false) {
1827: $options = (array)$options + array('inline' => true, 'block' => null);
1828: if (!$options['inline'] && empty($options['block'])) {
1829: $options['block'] = __FUNCTION__;
1830: }
1831: unset($options['inline']);
1832:
1833: $requestMethod = 'POST';
1834: if (!empty($options['method'])) {
1835: $requestMethod = strtoupper($options['method']);
1836: unset($options['method']);
1837: }
1838: if (!empty($options['confirm'])) {
1839: $confirmMessage = $options['confirm'];
1840: unset($options['confirm']);
1841: }
1842:
1843: $formName = str_replace('.', '', uniqid('post_', true));
1844: $formUrl = $this->url($url);
1845: $formOptions = array(
1846: 'name' => $formName,
1847: 'id' => $formName,
1848: 'style' => 'display:none;',
1849: 'method' => 'post',
1850: );
1851: if (isset($options['target'])) {
1852: $formOptions['target'] = $options['target'];
1853: unset($options['target']);
1854: }
1855:
1856: $this->_lastAction($url);
1857:
1858: $out = $this->Html->useTag('form', $formUrl, $formOptions);
1859: $out .= $this->Html->useTag('hidden', '_method', array(
1860: 'value' => $requestMethod
1861: ));
1862: $out .= $this->_csrfField();
1863:
1864: $fields = array();
1865: if (isset($options['data']) && is_array($options['data'])) {
1866: foreach (Hash::flatten($options['data']) as $key => $value) {
1867: $fields[$key] = $value;
1868: $out .= $this->hidden($key, array('value' => $value, 'id' => false));
1869: }
1870: unset($options['data']);
1871: }
1872: $out .= $this->secure($fields);
1873: $out .= $this->Html->useTag('formend');
1874:
1875: if ($options['block']) {
1876: $this->_View->append($options['block'], $out);
1877: $out = '';
1878: }
1879: unset($options['block']);
1880:
1881: $url = '#';
1882: $onClick = 'document.' . $formName . '.submit();';
1883: if ($confirmMessage) {
1884: $options['onclick'] = $this->_confirm($confirmMessage, $onClick, '', $options);
1885: } else {
1886: $options['onclick'] = $onClick . ' ';
1887: }
1888: $options['onclick'] .= 'event.returnValue = false; return false;';
1889:
1890: $out .= $this->Html->link($title, $url, $options);
1891: return $out;
1892: }
1893:
1894: 1895: 1896: 1897: 1898: 1899: 1900: 1901: 1902: 1903: 1904: 1905: 1906: 1907: 1908: 1909: 1910: 1911: 1912: 1913: 1914: 1915: 1916: 1917: 1918: 1919: 1920: 1921:
1922: public function submit($caption = null, $options = array()) {
1923: if (!is_string($caption) && empty($caption)) {
1924: $caption = __d('cake', 'Submit');
1925: }
1926: $out = null;
1927: $div = true;
1928:
1929: if (isset($options['div'])) {
1930: $div = $options['div'];
1931: unset($options['div']);
1932: }
1933: $options += array('type' => 'submit', 'before' => null, 'after' => null, 'secure' => false);
1934: $divOptions = array('tag' => 'div');
1935:
1936: if ($div === true) {
1937: $divOptions['class'] = 'submit';
1938: } elseif ($div === false) {
1939: unset($divOptions);
1940: } elseif (is_string($div)) {
1941: $divOptions['class'] = $div;
1942: } elseif (is_array($div)) {
1943: $divOptions = array_merge(array('class' => 'submit', 'tag' => 'div'), $div);
1944: }
1945:
1946: if (isset($options['name'])) {
1947: $name = str_replace(array('[', ']'), array('.', ''), $options['name']);
1948: $this->_secure($options['secure'], $name);
1949: }
1950: unset($options['secure']);
1951:
1952: $before = $options['before'];
1953: $after = $options['after'];
1954: unset($options['before'], $options['after']);
1955:
1956: $isUrl = strpos($caption, '://') !== false;
1957: $isImage = preg_match('/\.(jpg|jpe|jpeg|gif|png|ico)$/', $caption);
1958:
1959: if ($isUrl || $isImage) {
1960: $unlockFields = array('x', 'y');
1961: if (isset($options['name'])) {
1962: $unlockFields = array(
1963: $options['name'] . '_x', $options['name'] . '_y'
1964: );
1965: }
1966: foreach ($unlockFields as $ignore) {
1967: $this->unlockField($ignore);
1968: }
1969: }
1970:
1971: if ($isUrl) {
1972: unset($options['type']);
1973: $tag = $this->Html->useTag('submitimage', $caption, $options);
1974: } elseif ($isImage) {
1975: unset($options['type']);
1976: if ($caption{0} !== '/') {
1977: $url = $this->webroot(Configure::read('App.imageBaseUrl') . $caption);
1978: } else {
1979: $url = $this->webroot(trim($caption, '/'));
1980: }
1981: $url = $this->assetTimestamp($url);
1982: $tag = $this->Html->useTag('submitimage', $url, $options);
1983: } else {
1984: $options['value'] = $caption;
1985: $tag = $this->Html->useTag('submit', $options);
1986: }
1987: $out = $before . $tag . $after;
1988:
1989: if (isset($divOptions)) {
1990: $tag = $divOptions['tag'];
1991: unset($divOptions['tag']);
1992: $out = $this->Html->tag($tag, $out, $divOptions);
1993: }
1994: return $out;
1995: }
1996:
1997: 1998: 1999: 2000: 2001: 2002: 2003: 2004: 2005: 2006: 2007: 2008: 2009: 2010: 2011: 2012: 2013: 2014: 2015: 2016: 2017: 2018: 2019: 2020: 2021: 2022: 2023: 2024: 2025: 2026: 2027: 2028: 2029: 2030: 2031: 2032: 2033: 2034: 2035: 2036: 2037: 2038: 2039: 2040: 2041: 2042: 2043: 2044: 2045: 2046: 2047: 2048: 2049: 2050: 2051: 2052: 2053: 2054: 2055: 2056:
2057: public function select($fieldName, $options = array(), $attributes = array()) {
2058: $select = array();
2059: $style = null;
2060: $tag = null;
2061: $attributes += array(
2062: 'class' => null,
2063: 'escape' => true,
2064: 'secure' => true,
2065: 'empty' => '',
2066: 'showParents' => false,
2067: 'hiddenField' => true,
2068: 'disabled' => false
2069: );
2070:
2071: $escapeOptions = $this->_extractOption('escape', $attributes);
2072: $secure = $this->_extractOption('secure', $attributes);
2073: $showEmpty = $this->_extractOption('empty', $attributes);
2074: $showParents = $this->_extractOption('showParents', $attributes);
2075: $hiddenField = $this->_extractOption('hiddenField', $attributes);
2076: unset($attributes['escape'], $attributes['secure'], $attributes['empty'], $attributes['showParents'], $attributes['hiddenField']);
2077: $id = $this->_extractOption('id', $attributes);
2078:
2079: $attributes = $this->_initInputField($fieldName, array_merge(
2080: (array)$attributes, array('secure' => static::SECURE_SKIP)
2081: ));
2082:
2083: if (is_string($options) && isset($this->_options[$options])) {
2084: $options = $this->_generateOptions($options);
2085: } elseif (!is_array($options)) {
2086: $options = array();
2087: }
2088: if (isset($attributes['type'])) {
2089: unset($attributes['type']);
2090: }
2091:
2092: if (!empty($attributes['multiple'])) {
2093: $style = ($attributes['multiple'] === 'checkbox') ? 'checkbox' : null;
2094: $template = ($style) ? 'checkboxmultiplestart' : 'selectmultiplestart';
2095: $tag = $template;
2096: if ($hiddenField) {
2097: $hiddenAttributes = array(
2098: 'value' => '',
2099: 'id' => $attributes['id'] . ($style ? '' : '_'),
2100: 'secure' => false,
2101: 'form' => isset($attributes['form']) ? $attributes['form'] : null,
2102: 'name' => $attributes['name'],
2103: 'disabled' => $attributes['disabled'] === true || $attributes['disabled'] === 'disabled'
2104: );
2105: $select[] = $this->hidden(null, $hiddenAttributes);
2106: }
2107: } else {
2108: $tag = 'selectstart';
2109: }
2110:
2111: if ($tag === 'checkboxmultiplestart') {
2112: unset($attributes['required']);
2113: }
2114:
2115: if (!empty($tag) || isset($template)) {
2116: $hasOptions = (count($options) > 0 || $showEmpty);
2117:
2118:
2119: if ((!isset($secure) || $secure) &&
2120: empty($attributes['disabled']) &&
2121: (!empty($attributes['multiple']) || $hasOptions)
2122: ) {
2123: $this->_secure(true, $this->_secureFieldName($attributes));
2124: }
2125: $filter = array('name' => null, 'value' => null);
2126: if (is_array($attributes['disabled'])) {
2127: $filter['disabled'] = null;
2128: }
2129: $select[] = $this->Html->useTag($tag, $attributes['name'], array_diff_key($attributes, $filter));
2130: }
2131: $emptyMulti = (
2132: $showEmpty !== null && $showEmpty !== false && !(
2133: empty($showEmpty) && (isset($attributes) &&
2134: array_key_exists('multiple', $attributes))
2135: )
2136: );
2137:
2138: if ($emptyMulti) {
2139: $showEmpty = ($showEmpty === true) ? '' : $showEmpty;
2140: $options = array('' => $showEmpty) + $options;
2141: }
2142:
2143: if (!$id) {
2144: $attributes['id'] = Inflector::camelize($attributes['id']);
2145: }
2146:
2147: $select = array_merge($select, $this->_selectOptions(
2148: array_reverse($options, true),
2149: array(),
2150: $showParents,
2151: array(
2152: 'escape' => $escapeOptions,
2153: 'style' => $style,
2154: 'name' => $attributes['name'],
2155: 'value' => $attributes['value'],
2156: 'class' => $attributes['class'],
2157: 'id' => $attributes['id'],
2158: 'disabled' => $attributes['disabled'],
2159: )
2160: ));
2161:
2162: $template = ($style === 'checkbox') ? 'checkboxmultipleend' : 'selectend';
2163: $select[] = $this->Html->useTag($template);
2164: return implode("\n", $select);
2165: }
2166:
2167: 2168: 2169: 2170: 2171: 2172: 2173: 2174: 2175: 2176: 2177: 2178:
2179: public function domIdSuffix($value, $type = 'html4') {
2180: if ($type === 'html5') {
2181: $value = str_replace(array('@', '<', '>', ' ', '"', '\''), '_', $value);
2182: } else {
2183: $value = Inflector::camelize(Inflector::slug($value));
2184: }
2185: $value = Inflector::camelize($value);
2186: $count = 1;
2187: $suffix = $value;
2188: while (in_array($suffix, $this->_domIdSuffixes)) {
2189: $suffix = $value . $count++;
2190: }
2191: $this->_domIdSuffixes[] = $suffix;
2192: return $suffix;
2193: }
2194:
2195: 2196: 2197: 2198: 2199: 2200: 2201: 2202: 2203: 2204: 2205: 2206: 2207: 2208:
2209: public function day($fieldName = null, $attributes = array()) {
2210: $attributes += array('empty' => true, 'value' => null);
2211: $attributes = $this->_dateTimeSelected('day', $fieldName, $attributes);
2212:
2213: if (strlen($attributes['value']) > 2) {
2214: $date = date_create($attributes['value']);
2215: $attributes['value'] = null;
2216: if ($date) {
2217: $attributes['value'] = $date->format('d');
2218: }
2219: } elseif ($attributes['value'] === false) {
2220: $attributes['value'] = null;
2221: }
2222: return $this->select($fieldName . ".day", $this->_generateOptions('day'), $attributes);
2223: }
2224:
2225: 2226: 2227: 2228: 2229: 2230: 2231: 2232: 2233: 2234: 2235: 2236: 2237: 2238: 2239: 2240: 2241: 2242:
2243: public function year($fieldName, $minYear = null, $maxYear = null, $attributes = array()) {
2244: if (is_array($minYear)) {
2245: $attributes = $minYear;
2246: $minYear = null;
2247: }
2248:
2249: $attributes += array('empty' => true, 'value' => null);
2250: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2251: if (is_array($value)) {
2252: $year = null;
2253: extract($value);
2254: $attributes['value'] = $year;
2255: } else {
2256: if (empty($value)) {
2257: if (!$attributes['empty'] && !$maxYear) {
2258: $attributes['value'] = 'now';
2259:
2260: } elseif (!$attributes['empty'] && $maxYear && !$attributes['value']) {
2261: $attributes['value'] = $maxYear;
2262: }
2263: } else {
2264: $attributes['value'] = $value;
2265: }
2266: }
2267: }
2268:
2269: if (strlen($attributes['value']) > 4 || $attributes['value'] === 'now') {
2270: $date = date_create($attributes['value']);
2271: $attributes['value'] = null;
2272: if ($date) {
2273: $attributes['value'] = $date->format('Y');
2274: }
2275: } elseif ($attributes['value'] === false) {
2276: $attributes['value'] = null;
2277: }
2278: $yearOptions = array('value' => $attributes['value'], 'min' => $minYear, 'max' => $maxYear, 'order' => 'desc');
2279: if (isset($attributes['orderYear'])) {
2280: $yearOptions['order'] = $attributes['orderYear'];
2281: unset($attributes['orderYear']);
2282: }
2283: return $this->select(
2284: $fieldName . '.year', $this->_generateOptions('year', $yearOptions),
2285: $attributes
2286: );
2287: }
2288:
2289: 2290: 2291: 2292: 2293: 2294: 2295: 2296: 2297: 2298: 2299: 2300: 2301: 2302: 2303: 2304:
2305: public function month($fieldName, $attributes = array()) {
2306: $attributes += array('empty' => true, 'value' => null);
2307: $attributes = $this->_dateTimeSelected('month', $fieldName, $attributes);
2308:
2309: if (strlen($attributes['value']) > 2) {
2310: $date = date_create($attributes['value']);
2311: $attributes['value'] = null;
2312: if ($date) {
2313: $attributes['value'] = $date->format('m');
2314: }
2315: } elseif ($attributes['value'] === false) {
2316: $attributes['value'] = null;
2317: }
2318: $defaults = array('monthNames' => true);
2319: $attributes = array_merge($defaults, (array)$attributes);
2320: $monthNames = $attributes['monthNames'];
2321: unset($attributes['monthNames']);
2322:
2323: return $this->select(
2324: $fieldName . ".month",
2325: $this->_generateOptions('month', array('monthNames' => $monthNames)),
2326: $attributes
2327: );
2328: }
2329:
2330: 2331: 2332: 2333: 2334: 2335: 2336: 2337: 2338: 2339: 2340: 2341: 2342: 2343: 2344:
2345: public function hour($fieldName, $format24Hours = false, $attributes = array()) {
2346: if (is_array($format24Hours)) {
2347: $attributes = $format24Hours;
2348: $format24Hours = false;
2349: }
2350:
2351: $attributes += array('empty' => true, 'value' => null);
2352: $attributes = $this->_dateTimeSelected('hour', $fieldName, $attributes);
2353:
2354: if (strlen($attributes['value']) > 2) {
2355: try {
2356: $date = new DateTime($attributes['value']);
2357: if ($format24Hours) {
2358: $attributes['value'] = $date->format('H');
2359: } else {
2360: $attributes['value'] = $date->format('g');
2361: }
2362: } catch (Exception $e) {
2363: $attributes['value'] = null;
2364: }
2365: } elseif ($attributes['value'] === false) {
2366: $attributes['value'] = null;
2367: }
2368:
2369: if ($attributes['value'] > 12 && !$format24Hours) {
2370: $attributes['value'] -= 12;
2371: }
2372: if (($attributes['value'] === 0 || $attributes['value'] === '00') && !$format24Hours) {
2373: $attributes['value'] = 12;
2374: }
2375:
2376: return $this->select(
2377: $fieldName . ".hour",
2378: $this->_generateOptions($format24Hours ? 'hour24' : 'hour'),
2379: $attributes
2380: );
2381: }
2382:
2383: 2384: 2385: 2386: 2387: 2388: 2389: 2390: 2391: 2392: 2393: 2394: 2395: 2396:
2397: public function minute($fieldName, $attributes = array()) {
2398: $attributes += array('empty' => true, 'value' => null);
2399: $attributes = $this->_dateTimeSelected('min', $fieldName, $attributes);
2400:
2401: if (strlen($attributes['value']) > 2) {
2402: $date = date_create($attributes['value']);
2403: $attributes['value'] = null;
2404: if ($date) {
2405: $attributes['value'] = $date->format('i');
2406: }
2407: } elseif ($attributes['value'] === false) {
2408: $attributes['value'] = null;
2409: }
2410: $minuteOptions = array();
2411:
2412: if (isset($attributes['interval'])) {
2413: $minuteOptions['interval'] = $attributes['interval'];
2414: unset($attributes['interval']);
2415: }
2416: return $this->select(
2417: $fieldName . ".min", $this->_generateOptions('minute', $minuteOptions),
2418: $attributes
2419: );
2420: }
2421:
2422: 2423: 2424: 2425: 2426: 2427: 2428: 2429:
2430: protected function _dateTimeSelected($select, $fieldName, $attributes) {
2431: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2432: if (is_array($value)) {
2433: $attributes['value'] = isset($value[$select]) ? $value[$select] : null;
2434: } else {
2435: if (empty($value)) {
2436: if (!$attributes['empty']) {
2437: $attributes['value'] = 'now';
2438: }
2439: } else {
2440: $attributes['value'] = $value;
2441: }
2442: }
2443: }
2444: return $attributes;
2445: }
2446:
2447: 2448: 2449: 2450: 2451: 2452: 2453: 2454: 2455: 2456: 2457: 2458: 2459: 2460:
2461: public function meridian($fieldName, $attributes = array()) {
2462: $attributes += array('empty' => true, 'value' => null);
2463: if ((empty($attributes['value']) || $attributes['value'] === true) && $value = $this->value($fieldName)) {
2464: if (is_array($value)) {
2465: $meridian = null;
2466: extract($value);
2467: $attributes['value'] = $meridian;
2468: } else {
2469: if (empty($value)) {
2470: if (!$attributes['empty']) {
2471: $attributes['value'] = date('a');
2472: }
2473: } else {
2474: $date = date_create($attributes['value']);
2475: $attributes['value'] = null;
2476: if ($date) {
2477: $attributes['value'] = $date->format('a');
2478: }
2479: }
2480: }
2481: }
2482:
2483: if ($attributes['value'] === false) {
2484: $attributes['value'] = null;
2485: }
2486: return $this->select(
2487: $fieldName . ".meridian", $this->_generateOptions('meridian'),
2488: $attributes
2489: );
2490: }
2491:
2492: 2493: 2494: 2495: 2496: 2497: 2498: 2499: 2500: 2501: 2502: 2503: 2504: 2505: 2506: 2507: 2508: 2509: 2510: 2511: 2512: 2513: 2514: 2515:
2516: public function dateTime($fieldName, $dateFormat = 'DMY', $timeFormat = '12', $attributes = array()) {
2517: $attributes += array('empty' => true, 'value' => null);
2518: $year = $month = $day = $hour = $min = $meridian = null;
2519:
2520: if (empty($attributes['value'])) {
2521: $attributes = $this->value($attributes, $fieldName);
2522: }
2523:
2524: if ($attributes['value'] === null && $attributes['empty'] != true) {
2525: $attributes['value'] = time();
2526: if (!empty($attributes['maxYear']) && $attributes['maxYear'] < date('Y')) {
2527: $attributes['value'] = strtotime(date($attributes['maxYear'] . '-m-d'));
2528: }
2529: }
2530:
2531: if (!empty($attributes['value'])) {
2532: list($year, $month, $day, $hour, $min, $meridian) = $this->_getDateTimeValue(
2533: $attributes['value'],
2534: $timeFormat
2535: );
2536: }
2537:
2538: $defaults = array(
2539: 'minYear' => null, 'maxYear' => null, 'separator' => '-',
2540: 'interval' => 1, 'monthNames' => true, 'round' => null
2541: );
2542: $attributes = array_merge($defaults, (array)$attributes);
2543: if (isset($attributes['minuteInterval'])) {
2544: $attributes['interval'] = $attributes['minuteInterval'];
2545: unset($attributes['minuteInterval']);
2546: }
2547: $minYear = $attributes['minYear'];
2548: $maxYear = $attributes['maxYear'];
2549: $separator = $attributes['separator'];
2550: $interval = $attributes['interval'];
2551: $monthNames = $attributes['monthNames'];
2552: $round = $attributes['round'];
2553: $attributes = array_diff_key($attributes, $defaults);
2554:
2555: if (!empty($interval) && $interval > 1 && !empty($min)) {
2556: $current = new DateTime();
2557: if ($year !== null) {
2558: $current->setDate($year, $month, $day);
2559: }
2560: if ($hour !== null) {
2561: $current->setTime($hour, $min);
2562: }
2563: $changeValue = $min * (1 / $interval);
2564: switch ($round) {
2565: case 'up':
2566: $changeValue = ceil($changeValue);
2567: break;
2568: case 'down':
2569: $changeValue = floor($changeValue);
2570: break;
2571: default:
2572: $changeValue = round($changeValue);
2573: }
2574: $change = ($changeValue * $interval) - $min;
2575: $current->modify($change > 0 ? "+$change minutes" : "$change minutes");
2576: $format = ($timeFormat == 12) ? 'Y m d h i a' : 'Y m d H i a';
2577: $newTime = explode(' ', $current->format($format));
2578: list($year, $month, $day, $hour, $min, $meridian) = $newTime;
2579: }
2580:
2581: $keys = array('Day', 'Month', 'Year', 'Hour', 'Minute', 'Meridian');
2582: $attrs = array_fill_keys($keys, $attributes);
2583:
2584: $hasId = isset($attributes['id']);
2585: if ($hasId && is_array($attributes['id'])) {
2586:
2587: $attributes['id'] += array(
2588: 'month' => '',
2589: 'year' => '',
2590: 'day' => '',
2591: 'hour' => '',
2592: 'minute' => '',
2593: 'meridian' => ''
2594: );
2595: foreach ($keys as $key) {
2596: $attrs[$key]['id'] = $attributes['id'][strtolower($key)];
2597: }
2598: }
2599: if ($hasId && is_string($attributes['id'])) {
2600:
2601: foreach ($keys as $key) {
2602: $attrs[$key]['id'] = $attributes['id'] . $key;
2603: }
2604: }
2605:
2606: if (is_array($attributes['empty'])) {
2607: $attributes['empty'] += array(
2608: 'month' => true,
2609: 'year' => true,
2610: 'day' => true,
2611: 'hour' => true,
2612: 'minute' => true,
2613: 'meridian' => true
2614: );
2615: foreach ($keys as $key) {
2616: $attrs[$key]['empty'] = $attributes['empty'][strtolower($key)];
2617: }
2618: }
2619:
2620: $selects = array();
2621: foreach (preg_split('//', $dateFormat, -1, PREG_SPLIT_NO_EMPTY) as $char) {
2622: switch ($char) {
2623: case 'Y':
2624: $attrs['Year']['value'] = $year;
2625: $selects[] = $this->year(
2626: $fieldName, $minYear, $maxYear, $attrs['Year']
2627: );
2628: break;
2629: case 'M':
2630: $attrs['Month']['value'] = $month;
2631: $attrs['Month']['monthNames'] = $monthNames;
2632: $selects[] = $this->month($fieldName, $attrs['Month']);
2633: break;
2634: case 'D':
2635: $attrs['Day']['value'] = $day;
2636: $selects[] = $this->day($fieldName, $attrs['Day']);
2637: break;
2638: }
2639: }
2640: $opt = implode($separator, $selects);
2641:
2642: $attrs['Minute']['interval'] = $interval;
2643: switch ($timeFormat) {
2644: case '24':
2645: $attrs['Hour']['value'] = $hour;
2646: $attrs['Minute']['value'] = $min;
2647: $opt .= $this->hour($fieldName, true, $attrs['Hour']) . ':' .
2648: $this->minute($fieldName, $attrs['Minute']);
2649: break;
2650: case '12':
2651: $attrs['Hour']['value'] = $hour;
2652: $attrs['Minute']['value'] = $min;
2653: $attrs['Meridian']['value'] = $meridian;
2654: $opt .= $this->hour($fieldName, false, $attrs['Hour']) . ':' .
2655: $this->minute($fieldName, $attrs['Minute']) . ' ' .
2656: $this->meridian($fieldName, $attrs['Meridian']);
2657: break;
2658: }
2659: return $opt;
2660: }
2661:
2662: 2663: 2664: 2665: 2666: 2667: 2668:
2669: protected function _getDateTimeValue($value, $timeFormat) {
2670: $year = $month = $day = $hour = $min = $meridian = null;
2671: if (is_array($value)) {
2672: extract($value);
2673: if ($meridian === 'pm') {
2674: $hour += 12;
2675: }
2676: return array($year, $month, $day, $hour, $min, $meridian);
2677: }
2678:
2679: if (is_numeric($value)) {
2680: $value = strftime('%Y-%m-%d %H:%M:%S', $value);
2681: }
2682: $meridian = 'am';
2683: $pos = strpos($value, '-');
2684: if ($pos !== false) {
2685: $date = explode('-', $value);
2686: $days = explode(' ', $date[2]);
2687: $day = $days[0];
2688: $month = $date[1];
2689: $year = $date[0];
2690: } else {
2691: $days[1] = $value;
2692: }
2693:
2694: if (!empty($timeFormat)) {
2695: $time = explode(':', $days[1]);
2696:
2697: if ($time[0] >= 12) {
2698: $meridian = 'pm';
2699: }
2700: $hour = $min = null;
2701: if (isset($time[1])) {
2702: $hour = $time[0];
2703: $min = $time[1];
2704: }
2705: }
2706: return array($year, $month, $day, $hour, $min, $meridian);
2707: }
2708:
2709: 2710: 2711: 2712: 2713: 2714: 2715: 2716:
2717: protected function _name($options = array(), $field = null, $key = 'name') {
2718: if ($this->requestType === 'get') {
2719: if ($options === null) {
2720: $options = array();
2721: } elseif (is_string($options)) {
2722: $field = $options;
2723: $options = 0;
2724: }
2725:
2726: if (!empty($field)) {
2727: $this->setEntity($field);
2728: }
2729:
2730: if (is_array($options) && isset($options[$key])) {
2731: return $options;
2732: }
2733:
2734: $entity = $this->entity();
2735: $model = $this->model();
2736: $name = $model === $entity[0] && isset($entity[1]) ? $entity[1] : $entity[0];
2737: $last = $entity[count($entity) - 1];
2738: if (in_array($last, $this->_fieldSuffixes)) {
2739: $name .= '[' . $last . ']';
2740: }
2741:
2742: if (is_array($options)) {
2743: $options[$key] = $name;
2744: return $options;
2745: }
2746: return $name;
2747: }
2748: return parent::_name($options, $field, $key);
2749: }
2750:
2751: 2752: 2753: 2754: 2755: 2756: 2757: 2758: 2759:
2760: protected function _selectOptions($elements = array(), $parents = array(), $showParents = null, $attributes = array()) {
2761: $select = array();
2762: $attributes = array_merge(
2763: array('escape' => true, 'style' => null, 'value' => null, 'class' => null),
2764: $attributes
2765: );
2766: $selectedIsEmpty = ($attributes['value'] === '' || $attributes['value'] === null);
2767: $selectedIsArray = is_array($attributes['value']);
2768:
2769: $this->_domIdSuffixes = array();
2770: foreach ($elements as $name => $title) {
2771: $htmlOptions = array();
2772: if (is_array($title) && (!isset($title['name']) || !isset($title['value']))) {
2773: if (!empty($name)) {
2774: if ($attributes['style'] === 'checkbox') {
2775: $select[] = $this->Html->useTag('fieldsetend');
2776: } else {
2777: $select[] = $this->Html->useTag('optiongroupend');
2778: }
2779: $parents[] = $name;
2780: }
2781: $select = array_merge($select, $this->_selectOptions(
2782: $title, $parents, $showParents, $attributes
2783: ));
2784:
2785: if (!empty($name)) {
2786: $name = $attributes['escape'] ? h($name) : $name;
2787: if ($attributes['style'] === 'checkbox') {
2788: $select[] = $this->Html->useTag('fieldsetstart', $name);
2789: } else {
2790: $select[] = $this->Html->useTag('optiongroup', $name, '');
2791: }
2792: }
2793: $name = null;
2794: } elseif (is_array($title)) {
2795: $htmlOptions = $title;
2796: $name = $title['value'];
2797: $title = $title['name'];
2798: unset($htmlOptions['name'], $htmlOptions['value']);
2799: }
2800:
2801: if ($name !== null) {
2802: $isNumeric = is_numeric($name);
2803: if ((!$selectedIsArray && !$selectedIsEmpty && (string)$attributes['value'] == (string)$name) ||
2804: ($selectedIsArray && in_array((string)$name, $attributes['value'], !$isNumeric))
2805: ) {
2806: if ($attributes['style'] === 'checkbox') {
2807: $htmlOptions['checked'] = true;
2808: } else {
2809: $htmlOptions['selected'] = 'selected';
2810: }
2811: }
2812:
2813: if ($showParents || (!in_array($title, $parents))) {
2814: $title = ($attributes['escape']) ? h($title) : $title;
2815:
2816: $hasDisabled = !empty($attributes['disabled']);
2817: if ($hasDisabled) {
2818: $disabledIsArray = is_array($attributes['disabled']);
2819: if ($disabledIsArray) {
2820: $disabledIsNumeric = is_numeric($name);
2821: }
2822: }
2823: if ($hasDisabled &&
2824: $disabledIsArray &&
2825: in_array((string)$name, $attributes['disabled'], !$disabledIsNumeric)
2826: ) {
2827: $htmlOptions['disabled'] = 'disabled';
2828: }
2829: if ($hasDisabled && !$disabledIsArray && $attributes['style'] === 'checkbox') {
2830: $htmlOptions['disabled'] = $attributes['disabled'] === true ? 'disabled' : $attributes['disabled'];
2831: }
2832:
2833: if ($attributes['style'] === 'checkbox') {
2834: $htmlOptions['value'] = $name;
2835:
2836: $tagName = $attributes['id'] . $this->domIdSuffix($name);
2837: $htmlOptions['id'] = $tagName;
2838: $label = array('for' => $tagName);
2839:
2840: if (isset($htmlOptions['checked']) && $htmlOptions['checked'] === true) {
2841: $label['class'] = 'selected';
2842: }
2843:
2844: $name = $attributes['name'];
2845:
2846: if (empty($attributes['class'])) {
2847: $attributes['class'] = 'checkbox';
2848: } elseif ($attributes['class'] === 'form-error') {
2849: $attributes['class'] = 'checkbox ' . $attributes['class'];
2850: }
2851: $label = $this->label(null, $title, $label);
2852: $item = $this->Html->useTag('checkboxmultiple', $name, $htmlOptions);
2853: $select[] = $this->Html->div($attributes['class'], $item . $label);
2854: } else {
2855: if ($attributes['escape']) {
2856: $name = h($name);
2857: }
2858: $select[] = $this->Html->useTag('selectoption', $name, $htmlOptions, $title);
2859: }
2860: }
2861: }
2862: }
2863:
2864: return array_reverse($select, true);
2865: }
2866:
2867: 2868: 2869: 2870: 2871: 2872: 2873:
2874: protected function _generateOptions($name, $options = array()) {
2875: if (!empty($this->options[$name])) {
2876: return $this->options[$name];
2877: }
2878: $data = array();
2879:
2880: switch ($name) {
2881: case 'minute':
2882: if (isset($options['interval'])) {
2883: $interval = $options['interval'];
2884: } else {
2885: $interval = 1;
2886: }
2887: $i = 0;
2888: while ($i < 60) {
2889: $data[sprintf('%02d', $i)] = sprintf('%02d', $i);
2890: $i += $interval;
2891: }
2892: break;
2893: case 'hour':
2894: for ($i = 1; $i <= 12; $i++) {
2895: $data[sprintf('%02d', $i)] = $i;
2896: }
2897: break;
2898: case 'hour24':
2899: for ($i = 0; $i <= 23; $i++) {
2900: $data[sprintf('%02d', $i)] = $i;
2901: }
2902: break;
2903: case 'meridian':
2904: $data = array('am' => 'am', 'pm' => 'pm');
2905: break;
2906: case 'day':
2907: for ($i = 1; $i <= 31; $i++) {
2908: $data[sprintf('%02d', $i)] = $i;
2909: }
2910: break;
2911: case 'month':
2912: if ($options['monthNames'] === true) {
2913: $data['01'] = __d('cake', 'January');
2914: $data['02'] = __d('cake', 'February');
2915: $data['03'] = __d('cake', 'March');
2916: $data['04'] = __d('cake', 'April');
2917: $data['05'] = __d('cake', 'May');
2918: $data['06'] = __d('cake', 'June');
2919: $data['07'] = __d('cake', 'July');
2920: $data['08'] = __d('cake', 'August');
2921: $data['09'] = __d('cake', 'September');
2922: $data['10'] = __d('cake', 'October');
2923: $data['11'] = __d('cake', 'November');
2924: $data['12'] = __d('cake', 'December');
2925: } elseif (is_array($options['monthNames'])) {
2926: $data = $options['monthNames'];
2927: } else {
2928: for ($m = 1; $m <= 12; $m++) {
2929: $data[sprintf("%02s", $m)] = strftime("%m", mktime(1, 1, 1, $m, 1, 1999));
2930: }
2931: }
2932: break;
2933: case 'year':
2934: $current = (int)date('Y');
2935:
2936: $min = !isset($options['min']) ? $current - 20 : (int)$options['min'];
2937: $max = !isset($options['max']) ? $current + 20 : (int)$options['max'];
2938:
2939: if ($min > $max) {
2940: list($min, $max) = array($max, $min);
2941: }
2942: if (!empty($options['value']) &&
2943: (int)$options['value'] < $min &&
2944: (int)$options['value'] > 0
2945: ) {
2946: $min = (int)$options['value'];
2947: } elseif (!empty($options['value']) && (int)$options['value'] > $max) {
2948: $max = (int)$options['value'];
2949: }
2950:
2951: for ($i = $min; $i <= $max; $i++) {
2952: $data[$i] = $i;
2953: }
2954: if ($options['order'] !== 'asc') {
2955: $data = array_reverse($data, true);
2956: }
2957: break;
2958: }
2959: $this->_options[$name] = $data;
2960: return $this->_options[$name];
2961: }
2962:
2963: 2964: 2965: 2966: 2967: 2968: 2969: 2970: 2971: 2972: 2973: 2974: 2975: 2976: 2977: 2978: 2979:
2980: protected function _initInputField($field, $options = array()) {
2981: if (isset($options['secure'])) {
2982: $secure = $options['secure'];
2983: unset($options['secure']);
2984: } else {
2985: $secure = (isset($this->request['_Token']) && !empty($this->request['_Token']));
2986: }
2987:
2988: $disabledIndex = array_search('disabled', $options, true);
2989: if (is_int($disabledIndex)) {
2990: unset($options[$disabledIndex]);
2991: $options['disabled'] = true;
2992: }
2993:
2994: $result = parent::_initInputField($field, $options);
2995: if ($this->tagIsInvalid() !== false) {
2996: $result = $this->addClass($result, 'form-error');
2997: }
2998:
2999: $isDisabled = false;
3000: if (isset($result['disabled'])) {
3001: $isDisabled = (
3002: $result['disabled'] === true ||
3003: $result['disabled'] === 'disabled' ||
3004: (is_array($result['disabled']) &&
3005: !empty($result['options']) &&
3006: array_diff($result['options'], $result['disabled']) === array()
3007: )
3008: );
3009: }
3010: if ($isDisabled) {
3011: return $result;
3012: }
3013:
3014: if (!isset($result['required']) &&
3015: $this->_introspectModel($this->model(), 'validates', $this->field())
3016: ) {
3017: $result['required'] = true;
3018: }
3019:
3020: if ($secure === static::SECURE_SKIP) {
3021: return $result;
3022: }
3023:
3024: $this->_secure($secure, $this->_secureFieldName($options));
3025: return $result;
3026: }
3027:
3028: 3029: 3030: 3031: 3032: 3033: 3034: 3035: 3036:
3037: protected function _secureFieldName($options) {
3038: if (isset($options['name'])) {
3039: preg_match_all('/\[(.*?)\]/', $options['name'], $matches);
3040: if (isset($matches[1])) {
3041: return $matches[1];
3042: }
3043: }
3044: return null;
3045: }
3046:
3047: 3048: 3049: 3050: 3051: 3052:
3053: protected function _lastAction($url) {
3054: $action = Router::url($url, true);
3055: $query = parse_url($action, PHP_URL_QUERY);
3056: $query = $query ? '?' . $query : '';
3057: $this->_lastAction = parse_url($action, PHP_URL_PATH) . $query;
3058: }
3059:
3060: 3061: 3062: 3063: 3064: 3065: 3066:
3067: public function inputDefaults($defaults = null, $merge = false) {
3068: if ($defaults !== null) {
3069: if ($merge) {
3070: $this->_inputDefaults = array_merge($this->_inputDefaults, (array)$defaults);
3071: } else {
3072: $this->_inputDefaults = (array)$defaults;
3073: }
3074: }
3075: return $this->_inputDefaults;
3076: }
3077:
3078: }
3079: