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