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