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